Add version check around WEBKIT_LOAD_FAILED
[xxxterm.git] / xxxterm.c
blob033f5cec95dd6d3c1e4c49c6660ab61592d7392e
1 /* $xxxterm$ */
2 /*
3 * Copyright (c) 2010 Marco Peereboom <marco@peereboom.us>
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 * TODO:
20 * inverse color browsing
21 * favs
22 * download files status
23 * multi letter commands
24 * pre and post counts for commands
25 * search on page
26 * search on engines
27 * fav icon
28 * close tab X
29 * autocompletion on various inputs
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <err.h>
35 #include <pwd.h>
36 #include <string.h>
37 #include <unistd.h>
38 #include <util.h>
40 #include <sys/queue.h>
41 #include <sys/types.h>
42 #include <sys/stat.h>
44 #include <gtk/gtk.h>
45 #include <gdk/gdkkeysyms.h>
46 #include <webkit/webkit.h>
47 #include <libsoup/soup.h>
48 #include <JavaScriptCore/JavaScript.h>
50 static char *version = "$xxxterm$";
52 #define XT_DEBUG
53 /* #define XT_DEBUG */
54 #ifdef XT_DEBUG
55 #define DPRINTF(x...) do { if (swm_debug) fprintf(stderr, x); } while (0)
56 #define DNPRINTF(n,x...) do { if (swm_debug & n) fprintf(stderr, x); } while (0)
57 #define XT_D_MOVE 0x0001
58 #define XT_D_KEY 0x0002
59 #define XT_D_TAB 0x0004
60 #define XT_D_URL 0x0008
61 #define XT_D_CMD 0x0010
62 #define XT_D_NAV 0x0020
63 #define XT_D_DOWNLOAD 0x0040
64 #define XT_D_CONFIG 0x0080
65 u_int32_t swm_debug = 0
66 | XT_D_MOVE
67 | XT_D_KEY
68 | XT_D_TAB
69 | XT_D_URL
70 | XT_D_CMD
71 | XT_D_NAV
72 | XT_D_DOWNLOAD
73 | XT_D_CONFIG
75 #else
76 #define DPRINTF(x...)
77 #define DNPRINTF(n,x...)
78 #endif
80 #define LENGTH(x) (sizeof x / sizeof x[0])
81 #define CLEAN(mask) (mask & ~(GDK_MOD2_MASK) & \
82 ~(GDK_BUTTON1_MASK) & \
83 ~(GDK_BUTTON2_MASK) & \
84 ~(GDK_BUTTON3_MASK) & \
85 ~(GDK_BUTTON4_MASK) & \
86 ~(GDK_BUTTON5_MASK))
88 struct tab {
89 TAILQ_ENTRY(tab) entry;
90 GtkWidget *vbox;
91 GtkWidget *label;
92 GtkWidget *uri_entry;
93 GtkWidget *search_entry;
94 GtkWidget *toolbar;
95 GtkWidget *browser_win;
96 GtkWidget *cmd;
97 GtkToolItem *backward;
98 GtkToolItem *forward;
99 GtkToolItem *stop;
100 guint tab_id;
102 /* adjustments for browser */
103 GtkScrollbar *sb_h;
104 GtkScrollbar *sb_v;
105 GtkAdjustment *adjust_h;
106 GtkAdjustment *adjust_v;
108 /* flags */
109 int focus_wv;
110 int ctrl_click;
111 gchar *hover;
113 /* search */
114 char *search_text;
116 WebKitWebView *wv;
117 WebKitWebSettings *settings;
119 TAILQ_HEAD(tab_list, tab);
121 struct karg {
122 int i;
123 char *s;
126 /* defines */
127 #define XT_DIR (".xxxterm")
128 #define XT_CONF_FILE ("xxxterm.conf")
129 #define XT_CB_HANDLED (TRUE)
130 #define XT_CB_PASSTHROUGH (FALSE)
132 /* actions */
133 #define XT_MOVE_INVALID (0)
134 #define XT_MOVE_DOWN (1)
135 #define XT_MOVE_UP (2)
136 #define XT_MOVE_BOTTOM (3)
137 #define XT_MOVE_TOP (4)
138 #define XT_MOVE_PAGEDOWN (5)
139 #define XT_MOVE_PAGEUP (6)
140 #define XT_MOVE_LEFT (7)
141 #define XT_MOVE_FARLEFT (8)
142 #define XT_MOVE_RIGHT (9)
143 #define XT_MOVE_FARRIGHT (10)
145 #define XT_TAB_PREV (-2)
146 #define XT_TAB_NEXT (-1)
147 #define XT_TAB_INVALID (0)
148 #define XT_TAB_NEW (1)
149 #define XT_TAB_DELETE (2)
150 #define XT_TAB_DELQUIT (3)
151 #define XT_TAB_OPEN (4)
153 #define XT_NAV_INVALID (0)
154 #define XT_NAV_BACK (1)
155 #define XT_NAV_FORWARD (2)
156 #define XT_NAV_RELOAD (3)
158 #define XT_FOCUS_INVALID (0)
159 #define XT_FOCUS_URI (1)
161 #define XT_SEARCH_INVALID (0)
162 #define XT_SEARCH_NEXT (1)
163 #define XT_SEARCH_PREV (2)
165 /* globals */
166 extern char *__progname;
167 struct passwd *pwd;
168 GtkWidget *main_window;
169 GtkNotebook *notebook;
170 struct tab_list tabs;
172 /* settings */
173 int showtabs = 1; /* show tabs on notebook */
174 int showurl = 1; /* show url toolbar on notebook */
175 int tabless = 0; /* allow only 1 tab */
176 int ctrl_click_focus = 0; /* ctrl click gets focus */
177 int cookies_enabled = 1; /* enable cookies */
178 int read_only_cookies = 0; /* enable to not write cookies */
179 int enable_scripts = 1;
180 int enable_plugins = 1;
181 int default_font_size = 12;
182 int fancy_bar = 1; /* fancy toolbar */
184 char *home = "http://www.peereboom.us";
185 char *search_string = NULL;
186 char *http_proxy = NULL;
187 SoupURI *proxy_uri = NULL;
188 char work_dir[PATH_MAX];
189 char cookie_file[PATH_MAX];
190 char download_dir[PATH_MAX];
191 SoupSession *session;
192 SoupCookieJar *cookiejar;
194 /* protos */
195 void create_new_tab(char *, int);
196 void delete_tab(struct tab *);
198 struct valid_url_types {
199 char *type;
200 } vut[] = {
201 { "http://" },
202 { "https://" },
203 { "ftp://" },
204 { "file://" },
208 valid_url_type(char *url)
210 int i;
212 for (i = 0; i < LENGTH(vut); i++)
213 if (!strncasecmp(vut[i].type, url, strlen(vut[i].type)))
214 return (0);
216 return (1);
219 char *
220 guess_url_type(char *url_in)
222 struct stat sb;
223 char *url_out = NULL;
225 /* XXX not sure about this heuristic */
226 if (stat(url_in, &sb) == 0) {
227 if (asprintf(&url_out, "file://%s", url_in) == -1)
228 err(1, "aprintf file");
229 } else {
230 /* guess http */
231 if (asprintf(&url_out, "http://%s", url_in) == -1)
232 err(1, "aprintf http");
235 if (url_out == NULL)
236 err(1, "asprintf pointer");
238 DNPRINTF(XT_D_URL, "guess_url_type: guessed %s\n", url_out);
240 return (url_out);
243 #define WS "\n= \t"
244 void
245 config_parse(char *filename)
247 FILE *config;
248 char *line, *cp, *var, *val;
249 size_t len, lineno = 0;
251 DNPRINTF(XT_D_CONFIG, "config_parse: filename %s\n", filename);
253 if (filename == NULL)
254 return;
256 if ((config = fopen(filename, "r")) == NULL) {
257 warn("config_parse: cannot open %s", filename);
258 return;
261 for (;;) {
262 if ((line = fparseln(config, &len, &lineno, NULL, 0)) == NULL)
263 if (feof(config))
264 break;
266 cp = line;
267 cp += (long)strspn(cp, WS);
268 if (cp[0] == '\0') {
269 /* empty line */
270 free(line);
271 continue;
274 if ((var = strsep(&cp, WS)) == NULL || cp == NULL)
275 break;
277 cp += (long)strspn(cp, WS);
279 if ((val = strsep(&cp, "\0")) == NULL)
280 break;
282 DNPRINTF(XT_D_CONFIG, "config_parse: %s=%s\n",var ,val);
284 /* get settings */
285 if (!strcmp(var, "home"))
286 home = strdup(val);
287 else if (!strcmp(var, "ctrl_click_focus"))
288 ctrl_click_focus = atoi(val);
289 else if (!strcmp(var, "read_only_cookies"))
290 read_only_cookies = atoi(val);
291 else if (!strcmp(var, "cookies_enabled"))
292 cookies_enabled = atoi(val);
293 else if (!strcmp(var, "enable_scripts"))
294 enable_scripts = atoi(val);
295 else if (!strcmp(var, "enable_plugins"))
296 enable_plugins = atoi(val);
297 else if (!strcmp(var, "default_font_size"))
298 default_font_size = atoi(val);
299 else if (!strcmp(var, "fancy_bar"))
300 fancy_bar = atoi(val);
301 else if (!strcmp(var, "http_proxy")) {
302 http_proxy = strdup(val);
303 if (http_proxy == NULL)
304 err(1, "http_proxy");
305 } else if (!strcmp(var, "search_string")) {
306 search_string = strdup(val);
307 if (search_string == NULL)
308 err(1, "search_string");
309 } else if (!strcmp(var, "download_dir")) {
310 if (val[0] == '~')
311 snprintf(download_dir, sizeof download_dir,
312 "%s/%s", pwd->pw_dir, &val[1]);
313 else
314 strlcpy(download_dir, val, sizeof download_dir);
315 fprintf(stderr, "download dir: %s\n", download_dir);
316 } else
317 errx(1, "invalid conf file entry: %s=%s", var, val);
319 free(line);
322 fclose(config);
325 quit(struct tab *t, struct karg *args)
327 gtk_main_quit();
329 return (1);
333 focus(struct tab *t, struct karg *args)
335 if (t == NULL)
336 errx(1, "focus");
338 if (args->i == XT_FOCUS_URI)
339 gtk_widget_grab_focus(GTK_WIDGET(t->uri_entry));
341 return (0);
345 help(struct tab *t, struct karg *args)
347 if (t == NULL)
348 errx(1, "help");
350 webkit_web_view_load_string(t->wv,
351 "<html><body><h1>XXXTerm</h1></body></html>",
352 NULL,
353 NULL,
354 NULL);
356 return (0);
360 navaction(struct tab *t, struct karg *args)
362 DNPRINTF(XT_D_NAV, "navaction: tab %d opcode %d\n",
363 t->tab_id, args->i);
365 switch (args->i) {
366 case XT_NAV_BACK:
367 webkit_web_view_go_back(t->wv);
368 break;
369 case XT_NAV_FORWARD:
370 webkit_web_view_go_forward(t->wv);
371 break;
372 case XT_NAV_RELOAD:
373 webkit_web_view_reload(t->wv);
374 break;
376 return (XT_CB_PASSTHROUGH);
380 move(struct tab *t, struct karg *args)
382 GtkAdjustment *adjust;
383 double pi, si, pos, ps, upper, lower, max;
385 switch (args->i) {
386 case XT_MOVE_DOWN:
387 case XT_MOVE_UP:
388 case XT_MOVE_BOTTOM:
389 case XT_MOVE_TOP:
390 case XT_MOVE_PAGEDOWN:
391 case XT_MOVE_PAGEUP:
392 adjust = t->adjust_v;
393 break;
394 default:
395 adjust = t->adjust_h;
396 break;
399 pos = gtk_adjustment_get_value(adjust);
400 ps = gtk_adjustment_get_page_size(adjust);
401 upper = gtk_adjustment_get_upper(adjust);
402 lower = gtk_adjustment_get_lower(adjust);
403 si = gtk_adjustment_get_step_increment(adjust);
404 pi = gtk_adjustment_get_page_increment(adjust);
405 max = upper - ps;
407 DNPRINTF(XT_D_MOVE, "move: opcode %d %s pos %f ps %f upper %f lower %f "
408 "max %f si %f pi %f\n",
409 args->i, adjust == t->adjust_h ? "horizontal" : "vertical",
410 pos, ps, upper, lower, max, si, pi);
412 switch (args->i) {
413 case XT_MOVE_DOWN:
414 case XT_MOVE_RIGHT:
415 pos += si;
416 gtk_adjustment_set_value(adjust, MIN(pos, max));
417 break;
418 case XT_MOVE_UP:
419 case XT_MOVE_LEFT:
420 pos -= si;
421 gtk_adjustment_set_value(adjust, MAX(pos, lower));
422 break;
423 case XT_MOVE_BOTTOM:
424 case XT_MOVE_FARRIGHT:
425 gtk_adjustment_set_value(adjust, max);
426 break;
427 case XT_MOVE_TOP:
428 case XT_MOVE_FARLEFT:
429 gtk_adjustment_set_value(adjust, lower);
430 break;
431 case XT_MOVE_PAGEDOWN:
432 pos += pi;
433 gtk_adjustment_set_value(adjust, MIN(pos, max));
434 break;
435 case XT_MOVE_PAGEUP:
436 pos -= pi;
437 gtk_adjustment_set_value(adjust, MAX(pos, lower));
438 break;
439 default:
440 return (XT_CB_PASSTHROUGH);
443 DNPRINTF(XT_D_MOVE, "move: new pos %f %f\n", pos, MIN(pos, max));
445 return (XT_CB_HANDLED);
448 char *
449 getparams(char *cmd, char *cmp)
451 char *rv = NULL;
453 if (cmd && cmp) {
454 if (!strncmp(cmd, cmp, strlen(cmp))) {
455 rv = cmd + strlen(cmp);
456 while (*rv == ' ')
457 rv++;
458 if (strlen(rv) == 0)
459 rv = NULL;
463 return (rv);
467 tabaction(struct tab *t, struct karg *args)
469 int rv = XT_CB_HANDLED;
470 char *url = NULL, *newuri = NULL;
472 DNPRINTF(XT_D_TAB, "tabaction: %p %d %d\n", t, args->i, t->focus_wv);
474 if (t == NULL)
475 return (XT_CB_PASSTHROUGH);
477 switch (args->i) {
478 case XT_TAB_NEW:
479 if ((url = getparams(args->s, "tabnew")))
480 create_new_tab(url, 1);
481 else
482 create_new_tab(NULL, 1);
483 break;
484 case XT_TAB_DELETE:
485 delete_tab(t);
486 break;
487 case XT_TAB_DELQUIT:
488 if (gtk_notebook_get_n_pages(notebook) > 1)
489 delete_tab(t);
490 else
491 quit(t, args);
492 break;
493 case XT_TAB_OPEN:
494 if ((url = getparams(args->s, "open")) ||
495 ((url = getparams(args->s, "op"))) ||
496 ((url = getparams(args->s, "o"))))
498 else {
499 rv = XT_CB_PASSTHROUGH;
500 goto done;
503 if (valid_url_type(url)) {
504 newuri = guess_url_type(url);
505 url = newuri;
507 webkit_web_view_load_uri(t->wv, url);
508 if (newuri)
509 free(newuri);
510 break;
511 default:
512 rv = XT_CB_PASSTHROUGH;
513 goto done;
516 done:
517 if (args->s) {
518 free(args->s);
519 args->s = NULL;
522 return (rv);
526 movetab(struct tab *t, struct karg *args)
528 struct tab *tt;
529 int x;
531 DNPRINTF(XT_D_TAB, "movetab: %p %d\n", t, args->i);
533 if (t == NULL)
534 return (XT_CB_PASSTHROUGH);
536 if (args->i == XT_TAB_INVALID)
537 return (XT_CB_PASSTHROUGH);
539 if (args->i < XT_TAB_INVALID) {
540 /* next or previous tab */
541 if (TAILQ_EMPTY(&tabs))
542 return (XT_CB_PASSTHROUGH);
544 if (args->i == XT_TAB_NEXT)
545 gtk_notebook_next_page(notebook);
546 else
547 gtk_notebook_prev_page(notebook);
549 return (XT_CB_HANDLED);
552 /* jump to tab */
553 x = args->i - 1;
554 if (t->tab_id == x) {
555 DNPRINTF(XT_D_TAB, "movetab: do nothing\n");
556 return (XT_CB_HANDLED);
559 TAILQ_FOREACH(tt, &tabs, entry) {
560 if (tt->tab_id == x) {
561 gtk_notebook_set_current_page(notebook, x);
562 DNPRINTF(XT_D_TAB, "movetab: going to %d\n", x);
563 if (tt->focus_wv)
564 gtk_widget_grab_focus(GTK_WIDGET(tt->wv));
568 return (XT_CB_HANDLED);
572 command(struct tab *t, struct karg *args)
574 char *s = NULL;
575 GdkColor color;
577 if (t == NULL || args == NULL)
578 errx(1, "command");
580 if (args->i == '/')
581 s = "/";
582 else if (args->i == ':')
583 s = ":";
584 else {
585 warnx("invalid command %c\n", args->i);
586 return (XT_CB_PASSTHROUGH);
589 DNPRINTF(XT_D_CMD, "command: type %s\n", s);
591 gtk_entry_set_text(GTK_ENTRY(t->cmd), s);
592 gdk_color_parse("white", &color);
593 gtk_widget_modify_base(t->cmd, GTK_STATE_NORMAL, &color);
594 gtk_widget_show(t->cmd);
595 gtk_widget_grab_focus(GTK_WIDGET(t->cmd));
596 gtk_editable_set_position(GTK_EDITABLE(t->cmd), -1);
598 return (XT_CB_HANDLED);
602 search(struct tab *t, struct karg *args)
604 gboolean d;
606 if (t == NULL || args == NULL)
607 errx(1, "search");
608 if (t->search_text == NULL)
609 return (XT_CB_PASSTHROUGH);
611 DNPRINTF(XT_D_CMD, "search: tab %d opc %d text %s\n",
612 t->tab_id, args->i, t->search_text);
614 switch (args->i) {
615 case XT_SEARCH_NEXT:
616 d = TRUE;
617 break;
618 case XT_SEARCH_PREV:
619 d = FALSE;
620 break;
621 default:
622 return (XT_CB_PASSTHROUGH);
625 webkit_web_view_search_text(t->wv, t->search_text, FALSE, d, TRUE);
627 return (XT_CB_HANDLED);
630 /* inherent to GTK not all keys will be caught at all times */
631 struct key {
632 guint mask;
633 guint modkey;
634 guint key;
635 int (*func)(struct tab *, struct karg *);
636 struct karg arg;
637 } keys[] = {
638 { 0, 0, GDK_slash, command, {.i = '/'} },
639 { GDK_SHIFT_MASK, 0, GDK_colon, command, {.i = ':'} },
640 { GDK_CONTROL_MASK, 0, GDK_q, quit, {0} },
642 /* search */
643 { 0, 0, GDK_n, search, {.i = XT_SEARCH_NEXT} },
644 { GDK_SHIFT_MASK, 0, GDK_N, search, {.i = XT_SEARCH_PREV} },
646 /* focus */
647 { 0, 0, GDK_F6, focus, {.i = XT_FOCUS_URI} },
649 /* navigation */
650 { 0, 0, GDK_BackSpace, navaction, {.i = XT_NAV_BACK} },
651 { GDK_MOD1_MASK, 0, GDK_Left, navaction, {.i = XT_NAV_BACK} },
652 { GDK_SHIFT_MASK, 0, GDK_BackSpace, navaction, {.i = XT_NAV_FORWARD} },
653 { GDK_MOD1_MASK, 0, GDK_Right, navaction, {.i = XT_NAV_FORWARD} },
654 { 0, 0, GDK_F5, navaction, {.i = XT_NAV_RELOAD} },
656 /* vertical movement */
657 { 0, 0, GDK_j, move, {.i = XT_MOVE_DOWN} },
658 { 0, 0, GDK_Down, move, {.i = XT_MOVE_DOWN} },
659 { 0, 0, GDK_Up, move, {.i = XT_MOVE_UP} },
660 { 0, 0, GDK_k, move, {.i = XT_MOVE_UP} },
661 { GDK_SHIFT_MASK, 0, GDK_G, move, {.i = XT_MOVE_BOTTOM} },
662 { 0, 0, GDK_End, move, {.i = XT_MOVE_BOTTOM} },
663 { 0, 0, GDK_Home, move, {.i = XT_MOVE_TOP} },
664 { 0, GDK_g, GDK_g, move, {.i = XT_MOVE_TOP} }, /* XXX make this work */
665 { 0, 0, GDK_space, move, {.i = XT_MOVE_PAGEDOWN} },
666 { GDK_CONTROL_MASK, 0, GDK_f, move, {.i = XT_MOVE_PAGEDOWN} },
667 { 0, 0, GDK_Page_Down, move, {.i = XT_MOVE_PAGEDOWN} },
668 { 0, 0, GDK_Page_Up, move, {.i = XT_MOVE_PAGEUP} },
669 { GDK_CONTROL_MASK, 0, GDK_b, move, {.i = XT_MOVE_PAGEUP} },
670 /* horizontal movement */
671 { 0, 0, GDK_l, move, {.i = XT_MOVE_RIGHT} },
672 { 0, 0, GDK_Right, move, {.i = XT_MOVE_RIGHT} },
673 { 0, 0, GDK_Left, move, {.i = XT_MOVE_LEFT} },
674 { 0, 0, GDK_h, move, {.i = XT_MOVE_LEFT} },
675 { GDK_SHIFT_MASK, 0, GDK_dollar, move, {.i = XT_MOVE_FARRIGHT} },
676 { 0, 0, GDK_0, move, {.i = XT_MOVE_FARLEFT} },
678 /* tabs */
679 { GDK_CONTROL_MASK, 0, GDK_t, tabaction, {.i = XT_TAB_NEW} },
680 { GDK_CONTROL_MASK, 0, GDK_w, tabaction, {.i = XT_TAB_DELETE} },
681 { GDK_CONTROL_MASK, 0, GDK_1, movetab, {.i = 1} },
682 { GDK_CONTROL_MASK, 0, GDK_2, movetab, {.i = 2} },
683 { GDK_CONTROL_MASK, 0, GDK_3, movetab, {.i = 3} },
684 { GDK_CONTROL_MASK, 0, GDK_4, movetab, {.i = 4} },
685 { GDK_CONTROL_MASK, 0, GDK_5, movetab, {.i = 5} },
686 { GDK_CONTROL_MASK, 0, GDK_6, movetab, {.i = 6} },
687 { GDK_CONTROL_MASK, 0, GDK_7, movetab, {.i = 7} },
688 { GDK_CONTROL_MASK, 0, GDK_8, movetab, {.i = 8} },
689 { GDK_CONTROL_MASK, 0, GDK_9, movetab, {.i = 9} },
690 { GDK_CONTROL_MASK, 0, GDK_0, movetab, {.i = 10} },
693 struct cmd {
694 char *cmd;
695 int params;
696 int (*func)(struct tab *, struct karg *);
697 struct karg arg;
698 } cmds[] = {
699 { "q!", 0, quit, {0} },
700 { "qa", 0, quit, {0} },
701 { "qa!", 0, quit, {0} },
702 { "help", 0, help, {0} },
704 /* tabs */
705 { "o", 1, tabaction, {.i = XT_TAB_OPEN} },
706 { "op", 1, tabaction, {.i = XT_TAB_OPEN} },
707 { "open", 1, tabaction, {.i = XT_TAB_OPEN} },
708 { "tabnew", 1, tabaction, {.i = XT_TAB_NEW} },
709 { "tabedit", 1, tabaction, {.i = XT_TAB_NEW} },
710 { "tabe", 1, tabaction, {.i = XT_TAB_NEW} },
711 { "tabclose", 0, tabaction, {.i = XT_TAB_DELETE} },
712 { "tabc", 0, tabaction, {.i = XT_TAB_DELETE} },
713 { "quit", 0, tabaction, {.i = XT_TAB_DELQUIT} },
714 { "q", 0, tabaction, {.i = XT_TAB_DELQUIT} },
715 /* XXX add count to these commands and add tabl and friends */
716 { "tabprevious", 0, movetab, {.i = XT_TAB_PREV} },
717 { "tabp", 0, movetab, {.i = XT_TAB_PREV} },
718 { "tabnext", 0, movetab, {.i = XT_TAB_NEXT} },
719 { "tabn", 0, movetab, {.i = XT_TAB_NEXT} },
722 void
723 focus_uri_entry_cb(GtkWidget* w, GtkDirectionType direction, struct tab *t)
725 DNPRINTF(XT_D_URL, "focus_uri_entry_cb: tab %d focus_wv %d\n",
726 t->tab_id, t->focus_wv);
728 if (t == NULL)
729 errx(1, "focus_uri_entry_cb");
731 /* focus on wv instead */
732 if (t->focus_wv)
733 gtk_widget_grab_focus(GTK_WIDGET(t->wv));
736 void
737 activate_uri_entry_cb(GtkWidget* entry, struct tab *t)
739 const gchar *uri = gtk_entry_get_text(GTK_ENTRY(entry));
740 char *newuri = NULL;
742 DNPRINTF(XT_D_URL, "activate_uri_entry_cb: %s\n", uri);
744 if (t == NULL)
745 errx(1, "activate_uri_entry_cb");
747 if (uri == NULL)
748 errx(1, "uri");
750 if (valid_url_type((char *)uri)) {
751 newuri = guess_url_type((char *)uri);
752 uri = newuri;
755 webkit_web_view_load_uri(t->wv, uri);
756 gtk_widget_grab_focus(GTK_WIDGET(t->wv));
758 if (newuri)
759 free(newuri);
762 void
763 activate_search_entry_cb(GtkWidget* entry, struct tab *t)
765 const gchar *search = gtk_entry_get_text(GTK_ENTRY(entry));
766 char *newuri = NULL;
768 DNPRINTF(XT_D_URL, "activate_search_entry_cb: %s\n", search);
770 if (t == NULL)
771 errx(1, "activate_search_entry_cb");
773 if (search_string == NULL) {
774 warnx("no search_string");
775 return;
778 if (asprintf(&newuri, search_string, search) == -1)
779 err(1, "activate_search_entry_cb");
781 webkit_web_view_load_uri(t->wv, newuri);
782 gtk_widget_grab_focus(GTK_WIDGET(t->wv));
784 if (newuri)
785 free(newuri);
788 void
789 notify_load_status_cb(WebKitWebView* wview, GParamSpec* pspec, struct tab *t)
791 WebKitWebFrame *frame;
792 const gchar *uri;
794 if (t == NULL)
795 errx(1, "notify_load_status_cb");
797 switch (webkit_web_view_get_load_status(wview)) {
798 case WEBKIT_LOAD_COMMITTED:
799 frame = webkit_web_view_get_main_frame(wview);
800 uri = webkit_web_frame_get_uri(frame);
801 if (uri)
802 gtk_entry_set_text(GTK_ENTRY(t->uri_entry), uri);
804 gtk_widget_set_sensitive(GTK_WIDGET(t->stop), TRUE);
805 t->focus_wv = 1;
807 /* take focus if we are visible */
808 if (gtk_notebook_get_current_page(notebook) == t->tab_id)
809 gtk_widget_grab_focus(GTK_WIDGET(t->wv));
810 break;
811 case WEBKIT_LOAD_FIRST_VISUALLY_NON_EMPTY_LAYOUT:
812 uri = webkit_web_view_get_title(wview);
813 if (uri == NULL) {
814 frame = webkit_web_view_get_main_frame(wview);
815 uri = webkit_web_frame_get_uri(frame);
817 gtk_label_set_text(GTK_LABEL(t->label), uri);
818 break;
819 case WEBKIT_LOAD_PROVISIONAL:
820 case WEBKIT_LOAD_FINISHED:
821 #if WEBKIT_CHECK_VERSION(1, 1, 18)
822 case WEBKIT_LOAD_FAILED:
823 #endif
824 gtk_widget_set_sensitive(GTK_WIDGET(t->stop), FALSE);
825 default:
826 break;
829 gtk_widget_set_sensitive(GTK_WIDGET(t->backward),
830 webkit_web_view_can_go_back(wview));
832 gtk_widget_set_sensitive(GTK_WIDGET(t->forward),
833 webkit_web_view_can_go_forward(wview));
837 webview_npd_cb(WebKitWebView *wv, WebKitWebFrame *wf,
838 WebKitNetworkRequest *request, WebKitWebNavigationAction *na,
839 WebKitWebPolicyDecision *pd, struct tab *t)
841 char *uri;
843 if (t == NULL)
844 errx(1, "webview_npd_cb");
846 DNPRINTF(XT_D_NAV, "webview_npd_cb: %s\n",
847 webkit_network_request_get_uri(request));
849 if (t->ctrl_click) {
850 uri = (char *)webkit_network_request_get_uri(request);
851 create_new_tab(uri, ctrl_click_focus);
852 t->ctrl_click = 0;
853 webkit_web_policy_decision_ignore(pd);
855 return (TRUE); /* we made the decission */
858 return (FALSE);
862 webview_event_cb(GtkWidget *w, GdkEventButton *e, struct tab *t)
864 /* we can not eat the event without throwing gtk off so defer it */
866 /* catch ctrl click */
867 if (e->type == GDK_BUTTON_RELEASE &&
868 CLEAN(e->state) == GDK_CONTROL_MASK)
869 t->ctrl_click = 1;
870 else
871 t->ctrl_click = 0;
873 return (XT_CB_PASSTHROUGH);
877 webview_mimetype_cb(WebKitWebView *wv, WebKitWebFrame *frame,
878 WebKitNetworkRequest *request, char *mime_type,
879 WebKitWebPolicyDecision *decision, struct tab *t)
881 if (t == NULL)
882 errx(1, "webview_mimetype_cb");
884 DNPRINTF(XT_D_DOWNLOAD, "webview_mimetype_cb: tab %d mime %s\n",
885 t->tab_id, mime_type);
887 if (webkit_web_view_can_show_mime_type(wv, mime_type) == FALSE) {
888 webkit_web_policy_decision_download(decision);
889 return (TRUE);
892 return (FALSE);
896 webview_download_cb(WebKitWebView *wv, WebKitDownload *download, struct tab *t)
898 const gchar *filename;
899 char *uri = NULL;
901 if (download == NULL || t == NULL)
902 errx(1, "webview_download_cb: invalid pointers");
904 filename = webkit_download_get_suggested_filename(download);
905 if (filename == NULL)
906 return (FALSE); /* abort download */
908 if (asprintf(&uri, "file://%s/%s", download_dir, filename) == -1)
909 err(1, "aprintf uri");
911 DNPRINTF(XT_D_DOWNLOAD, "webview_download_cb: tab %d filename %s "
912 "local %s\n",
913 t->tab_id, filename, uri);
915 webkit_download_set_destination_uri(download, uri);
917 if (uri)
918 free(uri);
920 webkit_download_start(download);
922 return (TRUE); /* start download */
925 /* XXX currently unused */
926 void
927 webview_hover_cb(WebKitWebView *wv, gchar *title, gchar *uri, struct tab *t)
929 DNPRINTF(XT_D_KEY, "webview_hover_cb: %s %s\n", title, uri);
931 if (t == NULL)
932 errx(1, "webview_hover_cb");
934 if (uri) {
935 if (t->hover) {
936 free(t->hover);
937 t->hover = NULL;
939 t->hover = strdup(uri);
940 } else if (t->hover) {
941 free(t->hover);
942 t->hover = NULL;
947 webview_keypress_cb(GtkWidget *w, GdkEventKey *e, struct tab *t)
949 int i;
951 /* don't use w directly; use t->whatever instead */
953 if (t == NULL)
954 errx(1, "webview_keypress_cb");
956 DNPRINTF(XT_D_KEY, "webview_keypress_cb: keyval 0x%x mask 0x%x t %p\n",
957 e->keyval, e->state, t);
959 for (i = 0; i < LENGTH(keys); i++)
960 if (e->keyval == keys[i].key && CLEAN(e->state) ==
961 keys[i].mask) {
962 keys[i].func(t, &keys[i].arg);
963 return (XT_CB_HANDLED);
966 return (XT_CB_PASSTHROUGH);
970 cmd_keyrelease_cb(GtkEntry *w, GdkEventKey *e, struct tab *t)
972 const gchar *c = gtk_entry_get_text(w);
973 GdkColor color;
975 DNPRINTF(XT_D_CMD, "cmd_keyrelease_cb: keyval 0x%x mask 0x%x t %p\n",
976 e->keyval, e->state, t);
978 if (t == NULL)
979 errx(1, "cmd_keyrelease_cb");
981 DNPRINTF(XT_D_CMD, "cmd_keyrelease_cb: keyval 0x%x mask 0x%x t %p\n",
982 e->keyval, e->state, t);
984 if (c[0] == ':')
985 goto done;
986 if (!strcmp(c, "/"))
987 goto done;
989 /* search */
990 if (webkit_web_view_search_text(t->wv, &c[1], FALSE, TRUE, TRUE) == FALSE) {
991 /* not found, mark red */
992 gdk_color_parse("red", &color);
993 gtk_widget_modify_base(t->cmd, GTK_STATE_NORMAL, &color);
994 /* unmark and remove selection */
995 webkit_web_view_unmark_text_matches(t->wv);
996 /* my kingdom for a way to unselect text in webview */
997 } else {
998 /* found, highlight all */
999 webkit_web_view_unmark_text_matches(t->wv);
1000 webkit_web_view_mark_text_matches(t->wv, &c[1], FALSE, 0);
1001 webkit_web_view_set_highlight_text_matches(t->wv, TRUE);
1002 gdk_color_parse("white", &color);
1003 gtk_widget_modify_base(t->cmd, GTK_STATE_NORMAL, &color);
1005 done:
1006 return (XT_CB_PASSTHROUGH);
1010 cmd_keypress_cb(GtkEntry *w, GdkEventKey *e, struct tab *t)
1012 int rv = XT_CB_HANDLED;
1013 const gchar *c = gtk_entry_get_text(w);
1015 if (t == NULL)
1016 errx(1, "cmd_keypress_cb");
1018 DNPRINTF(XT_D_CMD, "cmd_keypress_cb: keyval 0x%x mask 0x%x t %p\n",
1019 e->keyval, e->state, t);
1021 /* sanity */
1022 if (c == NULL)
1023 e->keyval = GDK_Escape;
1024 else if (!(c[0] == ':' || c[0] == '/'))
1025 e->keyval = GDK_Escape;
1027 switch (e->keyval) {
1028 case GDK_BackSpace:
1029 if (!(!strcmp(c, ":") || !strcmp(c, "/")))
1030 break;
1031 /* FALLTHROUGH */
1032 case GDK_Escape:
1033 gtk_widget_hide(t->cmd);
1034 gtk_widget_grab_focus(GTK_WIDGET(t->wv));
1035 goto done;
1038 rv = XT_CB_PASSTHROUGH;
1039 done:
1040 return (rv);
1044 cmd_focusout_cb(GtkWidget *w, GdkEventFocus *e, struct tab *t)
1046 if (t == NULL)
1047 errx(1, "cmd_focusout_cb");
1049 DNPRINTF(XT_D_CMD, "cmd_focusout_cb: tab %d focus_wv %d\n",
1050 t->tab_id, t->focus_wv);
1052 /* abort command when losing focus */
1053 gtk_widget_hide(t->cmd);
1054 if (t->focus_wv)
1055 gtk_widget_grab_focus(GTK_WIDGET(t->wv));
1056 else
1057 gtk_widget_grab_focus(GTK_WIDGET(t->uri_entry));
1059 return (XT_CB_PASSTHROUGH);
1062 void
1063 cmd_activate_cb(GtkEntry *entry, struct tab *t)
1065 int i;
1066 char *s;
1067 const gchar *c = gtk_entry_get_text(entry);
1069 if (t == NULL)
1070 errx(1, "cmd_activate_cb");
1072 DNPRINTF(XT_D_CMD, "cmd_activate_cb: tab %d %s\n", t->tab_id, c);
1074 /* sanity */
1075 if (c == NULL)
1076 goto done;
1077 else if (!(c[0] == ':' || c[0] == '/'))
1078 goto done;
1079 if (strlen(c) < 2)
1080 goto done;
1081 s = (char *)&c[1];
1083 if (c[0] == '/') {
1084 if (t->search_text) {
1085 free(t->search_text);
1086 t->search_text = NULL;
1089 t->search_text = strdup(s);
1090 if (t->search_text == NULL)
1091 err(1, "search_text");
1093 goto done;
1096 for (i = 0; i < LENGTH(cmds); i++)
1097 if (cmds[i].params) {
1098 if (!strncmp(s, cmds[i].cmd, strlen(cmds[i].cmd))) {
1099 cmds[i].arg.s = strdup(s);
1100 cmds[i].func(t, &cmds[i].arg);
1102 } else {
1103 if (!strcmp(s, cmds[i].cmd))
1104 cmds[i].func(t, &cmds[i].arg);
1107 done:
1108 gtk_widget_hide(t->cmd);
1111 void
1112 backward_cb(GtkWidget *w, struct tab *t)
1114 if (t == NULL)
1115 errx(1, "backward_cb");
1117 DNPRINTF(XT_D_NAV, "backward_cb: tab %d\n", t->tab_id);
1119 webkit_web_view_go_back(t->wv);
1122 void
1123 forward_cb(GtkWidget *w, struct tab *t)
1125 if (t == NULL)
1126 errx(1, "forward_cb");
1128 DNPRINTF(XT_D_NAV, "forward_cb: tab %d\n", t->tab_id);
1130 webkit_web_view_go_forward(t->wv);
1133 void
1134 stop_cb(GtkWidget *w, struct tab *t)
1136 WebKitWebFrame *frame;
1138 if (t == NULL)
1139 errx(1, "stop_cb");
1141 DNPRINTF(XT_D_NAV, "stop_cb: tab %d\n", t->tab_id);
1143 frame = webkit_web_view_get_main_frame(t->wv);
1144 if (frame == NULL) {
1145 warnx("stop_cb: no frame");
1146 return;
1149 webkit_web_frame_stop_loading(frame);
1152 GtkWidget *
1153 create_browser(struct tab *t)
1155 GtkWidget *w;
1157 if (t == NULL)
1158 errx(1, "create_browser");
1160 t->sb_h = GTK_SCROLLBAR(gtk_hscrollbar_new(NULL));
1161 t->sb_v = GTK_SCROLLBAR(gtk_vscrollbar_new(NULL));
1162 t->adjust_h = gtk_range_get_adjustment(GTK_RANGE(t->sb_h));
1163 t->adjust_v = gtk_range_get_adjustment(GTK_RANGE(t->sb_v));
1165 w = gtk_scrolled_window_new(t->adjust_h, t->adjust_v);
1166 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(w),
1167 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1169 t->wv = WEBKIT_WEB_VIEW(webkit_web_view_new());
1170 gtk_container_add(GTK_CONTAINER(w), GTK_WIDGET(t->wv));
1172 g_signal_connect(t->wv, "notify::load-status",
1173 G_CALLBACK(notify_load_status_cb), t);
1175 return (w);
1178 GtkWidget *
1179 create_window(void)
1181 GtkWidget *w;
1183 w = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1184 gtk_window_set_default_size(GTK_WINDOW(w), 800, 600);
1185 gtk_widget_set_name(w, "xxxterm");
1186 gtk_window_set_wmclass(GTK_WINDOW(w), "xxxterm", "XXXTerm");
1188 return (w);
1191 GtkWidget *
1192 create_toolbar(struct tab *t)
1194 GtkWidget *toolbar = gtk_toolbar_new();
1195 GtkToolItem *i;
1197 #if GTK_CHECK_VERSION(2,15,0)
1198 gtk_orientable_set_orientation(GTK_ORIENTABLE(toolbar),
1199 GTK_ORIENTATION_HORIZONTAL);
1200 #else
1201 gtk_toolbar_set_orientation(GTK_TOOLBAR(toolbar),
1202 GTK_ORIENTATION_HORIZONTAL);
1203 #endif
1204 gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_BOTH_HORIZ);
1206 if (fancy_bar) {
1207 /* backward button */
1208 t->backward = gtk_tool_button_new_from_stock(GTK_STOCK_GO_BACK);
1209 gtk_widget_set_sensitive(GTK_WIDGET(t->backward), FALSE);
1210 g_signal_connect(G_OBJECT(t->backward), "clicked",
1211 G_CALLBACK(backward_cb), t);
1212 gtk_toolbar_insert(GTK_TOOLBAR(toolbar), t->backward, -1);
1214 /* forward button */
1215 t->forward =
1216 gtk_tool_button_new_from_stock(GTK_STOCK_GO_FORWARD);
1217 gtk_widget_set_sensitive(GTK_WIDGET(t->forward), FALSE);
1218 g_signal_connect(G_OBJECT(t->forward), "clicked",
1219 G_CALLBACK(forward_cb), t);
1220 gtk_toolbar_insert(GTK_TOOLBAR(toolbar), t->forward, -1);
1222 /* stop button */
1223 t->stop = gtk_tool_button_new_from_stock(GTK_STOCK_STOP);
1224 gtk_widget_set_sensitive(GTK_WIDGET(t->stop), FALSE);
1225 g_signal_connect(G_OBJECT(t->stop), "clicked",
1226 G_CALLBACK(stop_cb), t);
1227 gtk_toolbar_insert(GTK_TOOLBAR(toolbar), t->stop, -1);
1230 /* uri entry */
1231 i = gtk_tool_item_new();
1232 gtk_tool_item_set_expand(i, TRUE);
1233 t->uri_entry = gtk_entry_new();
1234 gtk_container_add(GTK_CONTAINER(i), t->uri_entry);
1235 g_signal_connect(G_OBJECT(t->uri_entry), "activate",
1236 G_CALLBACK(activate_uri_entry_cb), t);
1237 gtk_toolbar_insert(GTK_TOOLBAR(toolbar), i, -1);
1239 /* search entry */
1240 if (fancy_bar && search_string) {
1241 i = gtk_tool_item_new();
1242 t->search_entry = gtk_entry_new();
1243 gtk_entry_set_width_chars(GTK_ENTRY(t->search_entry), 30);
1244 gtk_container_add(GTK_CONTAINER(i), t->search_entry);
1245 g_signal_connect(G_OBJECT(t->search_entry), "activate",
1246 G_CALLBACK(activate_search_entry_cb), t);
1247 gtk_toolbar_insert(GTK_TOOLBAR(toolbar), i, -1);
1250 return (toolbar);
1253 void
1254 delete_tab(struct tab *t)
1256 DNPRINTF(XT_D_TAB, "delete_tab: %p\n", t);
1258 if (t == NULL)
1259 return;
1261 TAILQ_REMOVE(&tabs, t, entry);
1262 if (TAILQ_EMPTY(&tabs))
1263 create_new_tab(NULL, 1);
1265 gtk_widget_destroy(t->vbox);
1266 g_free(t);
1269 void
1270 setup_webkit(struct tab *t)
1272 gchar *strval;
1273 gchar *ua;
1275 /* XXX this can't be called over and over; fix it */
1276 t->settings = webkit_web_settings_new();
1277 g_object_get((GObject *)t->settings, "user-agent", &strval, NULL);
1278 if (strval == NULL) {
1279 warnx("setup_webkit: can't get user-agent property");
1280 return;
1283 if (asprintf(&ua, "%s %s+", strval, version) == -1)
1284 err(1, "aprintf user-agent");
1286 g_object_set((GObject *)t->settings,
1287 "user-agent", ua, NULL);
1288 g_object_set((GObject *)t->settings,
1289 "enable-scripts", enable_scripts, NULL);
1290 g_object_set((GObject *)t->settings,
1291 "enable-plugins", enable_plugins, NULL);
1292 g_object_set((GObject *)t->settings,
1293 "default-font-size", default_font_size, NULL);
1295 webkit_web_view_set_settings(t->wv, t->settings);
1297 g_free (strval);
1298 free(ua);
1301 void
1302 create_new_tab(char *title, int focus)
1304 struct tab *t;
1305 int load = 1;
1306 char *newuri = NULL;
1308 DNPRINTF(XT_D_TAB, "create_new_tab: title %s focus %d\n", title, focus);
1310 if (tabless && !TAILQ_EMPTY(&tabs)) {
1311 DNPRINTF(XT_D_TAB, "create_new_tab: new tab rejected\n");
1312 return;
1315 t = g_malloc0(sizeof *t);
1316 TAILQ_INSERT_TAIL(&tabs, t, entry);
1318 if (title == NULL) {
1319 title = "(untitled)";
1320 load = 0;
1321 } else {
1322 if (valid_url_type(title)) {
1323 newuri = guess_url_type(title);
1324 title = newuri;
1328 t->vbox = gtk_vbox_new(FALSE, 0);
1330 /* label for tab */
1331 t->label = gtk_label_new(title);
1332 gtk_widget_set_size_request(t->label, 100, -1);
1334 /* toolbar */
1335 t->toolbar = create_toolbar(t);
1336 gtk_box_pack_start(GTK_BOX(t->vbox), t->toolbar, FALSE, FALSE, 0);
1338 /* browser */
1339 t->browser_win = create_browser(t);
1340 gtk_box_pack_start(GTK_BOX(t->vbox), t->browser_win, TRUE, TRUE, 0);
1341 setup_webkit(t);
1343 /* command entry */
1344 t->cmd = gtk_entry_new();
1345 gtk_entry_set_inner_border(GTK_ENTRY(t->cmd), NULL);
1346 gtk_entry_set_has_frame(GTK_ENTRY(t->cmd), FALSE);
1347 gtk_box_pack_end(GTK_BOX(t->vbox), t->cmd, FALSE, FALSE, 0);
1349 /* and show it all */
1350 gtk_widget_show_all(t->vbox);
1351 t->tab_id = gtk_notebook_append_page(notebook, t->vbox,
1352 t->label);
1354 g_object_connect((GObject*)t->cmd,
1355 "signal::key-press-event", (GCallback)cmd_keypress_cb, t,
1356 "signal::key-release-event", (GCallback)cmd_keyrelease_cb, t,
1357 "signal::focus-out-event", (GCallback)cmd_focusout_cb, t,
1358 "signal::activate", (GCallback)cmd_activate_cb, t,
1359 NULL);
1361 g_object_connect((GObject*)t->wv,
1362 "signal-after::key-press-event", (GCallback)webview_keypress_cb, t,
1363 /* "signal::hovering-over-link", (GCallback)webview_hover_cb, t, */
1364 "signal::download-requested", (GCallback)webview_download_cb, t,
1365 "signal::mime-type-policy-decision-requested", (GCallback)webview_mimetype_cb, t,
1366 "signal::navigation-policy-decision-requested", (GCallback)webview_npd_cb, t,
1367 "signal::event", (GCallback)webview_event_cb, t,
1368 NULL);
1370 /* hijack the unused keys as if we were the browser */
1371 g_object_connect((GObject*)t->toolbar,
1372 "signal-after::key-press-event", (GCallback)webview_keypress_cb, t,
1373 NULL);
1375 g_signal_connect(G_OBJECT(t->uri_entry), "focus",
1376 G_CALLBACK(focus_uri_entry_cb), t);
1378 /* hide stuff */
1379 gtk_widget_hide(t->cmd);
1380 if (showurl == 0)
1381 gtk_widget_hide(t->toolbar);
1383 if (focus) {
1384 gtk_notebook_set_current_page(notebook, t->tab_id);
1385 DNPRINTF(XT_D_TAB, "create_new_tab: going to tab: %d\n",
1386 t->tab_id);
1389 if (load)
1390 webkit_web_view_load_uri(t->wv, title);
1391 else
1392 gtk_widget_grab_focus(GTK_WIDGET(t->uri_entry));
1394 if (newuri)
1395 free(newuri);
1398 void
1399 notebook_switchpage_cb(GtkNotebook *nb, GtkNotebookPage *nbp, guint pn,
1400 gpointer *udata)
1402 struct tab *t;
1404 DNPRINTF(XT_D_TAB, "notebook_switchpage_cb: tab: %d\n", pn);
1406 TAILQ_FOREACH(t, &tabs, entry) {
1407 if (t->tab_id == pn) {
1408 DNPRINTF(XT_D_TAB, "notebook_switchpage_cb: going to "
1409 "%d\n", pn);
1410 gtk_widget_hide(t->cmd);
1415 void
1416 create_canvas(void)
1418 GtkWidget *vbox;
1420 vbox = gtk_vbox_new(FALSE, 0);
1421 notebook = GTK_NOTEBOOK(gtk_notebook_new());
1422 if (showtabs == 0)
1423 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(notebook), FALSE);
1424 gtk_notebook_set_scrollable(notebook, TRUE);
1426 gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(notebook), TRUE, TRUE, 0);
1428 g_object_connect((GObject*)notebook,
1429 "signal::switch-page", (GCallback)notebook_switchpage_cb, NULL,
1430 NULL);
1432 main_window = create_window();
1433 gtk_container_add(GTK_CONTAINER(main_window), vbox);
1434 gtk_widget_show_all(main_window);
1437 void
1438 setup_cookies(void)
1440 if (cookiejar) {
1441 soup_session_remove_feature(session,
1442 (SoupSessionFeature*)cookiejar);
1443 g_object_unref(cookiejar);
1444 cookiejar = NULL;
1447 if (cookies_enabled == 0)
1448 return;
1450 cookiejar = soup_cookie_jar_text_new(cookie_file, read_only_cookies);
1451 soup_session_add_feature(session, (SoupSessionFeature*)cookiejar);
1454 void
1455 setup_proxy(char *uri)
1457 if (proxy_uri) {
1458 g_object_set(session, "proxy_uri", NULL, NULL);
1459 soup_uri_free(proxy_uri);
1460 proxy_uri = NULL;
1462 if (http_proxy) {
1463 free(http_proxy);
1464 http_proxy = strdup(uri);
1465 if (http_proxy == NULL)
1466 err(1, "http_proxy");
1469 if (uri == NULL)
1470 return;
1472 DNPRINTF(XT_D_CONFIG, "setup_proxy: %s\n", uri);
1473 proxy_uri = soup_uri_new(uri);
1474 if (proxy_uri)
1475 g_object_set(session, "proxy-uri", proxy_uri, NULL);
1478 void
1479 usage(void)
1481 fprintf(stderr,
1482 "%s [-STVt][-f file] url ...\n", __progname);
1483 exit(0);
1487 main(int argc, char *argv[])
1489 struct stat sb;
1490 int c, focus = 1;
1491 char conf[PATH_MAX] = { '\0' };
1492 char *env_proxy = NULL;
1494 while ((c = getopt(argc, argv, "STVf:t")) != -1) {
1495 switch (c) {
1496 case 'S':
1497 showurl = 0;
1498 break;
1499 case 'T':
1500 showtabs = 0;
1501 break;
1502 case 'V':
1503 errx(0 , "Version: %s", version);
1504 break;
1505 case 'f':
1506 strlcpy(conf, optarg, sizeof(conf));
1507 break;
1508 case 't':
1509 tabless = 1;
1510 break;
1511 default:
1512 usage();
1513 /* NOTREACHED */
1516 argc -= optind;
1517 argv += optind;
1519 TAILQ_INIT(&tabs);
1521 /* prepare gtk */
1522 gtk_init(&argc, &argv);
1523 if (!g_thread_supported())
1524 g_thread_init(NULL);
1526 pwd = getpwuid(getuid());
1527 if (pwd == NULL)
1528 errx(1, "invalid user %d", getuid());
1530 /* set download dir */
1531 strlcpy(download_dir, pwd->pw_dir, sizeof download_dir);
1533 /* read config file */
1534 if (strlen(conf) == 0)
1535 snprintf(conf, sizeof conf, "%s/.%s",
1536 pwd->pw_dir, XT_CONF_FILE);
1537 config_parse(conf);
1539 if (stat(download_dir, &sb))
1540 errx(1, "must specify a valid download_dir");
1542 /* working directory */
1543 snprintf(work_dir, sizeof work_dir, "%s/%s", pwd->pw_dir, XT_DIR);
1544 if (stat(work_dir, &sb)) {
1545 if (mkdir(work_dir, S_IRWXU) == -1)
1546 err(1, "mkdir");
1547 } else if (((sb.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO))) != S_IRWXU) {
1548 warnx("fixing invalid permissions on %s", work_dir);
1549 if (chmod(work_dir, S_IRWXU) == -1)
1550 err(1, "chmod");
1553 /* cookies */
1554 session = webkit_get_default_session();
1555 snprintf(cookie_file, sizeof cookie_file, "%s/cookies.txt", work_dir);
1556 setup_cookies();
1558 /* proxy */
1559 env_proxy = getenv("http_proxy");
1560 if (env_proxy) {
1561 http_proxy = strdup(env_proxy);
1562 if (http_proxy == NULL)
1563 err(1, "http_proxy");
1565 setup_proxy(http_proxy);
1567 create_canvas();
1569 while (argc) {
1570 create_new_tab(argv[0], focus);
1571 focus = 0;
1573 argc--;
1574 argv++;
1576 if (focus == 1)
1577 create_new_tab(home, 1);
1579 gtk_main();
1581 return (0);