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.
20 * inverse color browsing
23 * download files status
24 * multi letter commands
25 * pre and post counts for commands
28 * autocompletion on various inputs
29 * create privacy browsing
30 * - encrypted local data
41 #include <sys/queue.h>
42 #include <sys/types.h>
46 #include <gdk/gdkkeysyms.h>
47 #include <webkit/webkit.h>
48 #include <libsoup/soup.h>
49 #include <JavaScriptCore/JavaScript.h>
51 static char *version
= "$xxxterm$";
54 /* #define XT_DEBUG */
56 #define DPRINTF(x...) do { if (swm_debug) fprintf(stderr, x); } while (0)
57 #define DNPRINTF(n,x...) do { if (swm_debug & n) fprintf(stderr, x); } while (0)
58 #define XT_D_MOVE 0x0001
59 #define XT_D_KEY 0x0002
60 #define XT_D_TAB 0x0004
61 #define XT_D_URL 0x0008
62 #define XT_D_CMD 0x0010
63 #define XT_D_NAV 0x0020
64 #define XT_D_DOWNLOAD 0x0040
65 #define XT_D_CONFIG 0x0080
66 u_int32_t swm_debug
= 0
78 #define DNPRINTF(n,x...)
81 #define LENGTH(x) (sizeof x / sizeof x[0])
82 #define CLEAN(mask) (mask & ~(GDK_MOD2_MASK) & \
83 ~(GDK_BUTTON1_MASK) & \
84 ~(GDK_BUTTON2_MASK) & \
85 ~(GDK_BUTTON3_MASK) & \
86 ~(GDK_BUTTON4_MASK) & \
90 TAILQ_ENTRY(tab
) entry
;
94 GtkWidget
*search_entry
;
96 GtkWidget
*browser_win
;
98 GtkToolItem
*backward
;
104 /* adjustments for browser */
107 GtkAdjustment
*adjust_h
;
108 GtkAdjustment
*adjust_v
;
120 WebKitWebSettings
*settings
;
124 TAILQ_HEAD(tab_list
, tab
);
132 #define XT_NAME ("XXXTerm")
133 #define XT_DIR (".xxxterm")
134 #define XT_CONF_FILE ("xxxterm.conf")
135 #define XT_FAVS_FILE ("favorites")
136 #define XT_CB_HANDLED (TRUE)
137 #define XT_CB_PASSTHROUGH (FALSE)
140 #define XT_MOVE_INVALID (0)
141 #define XT_MOVE_DOWN (1)
142 #define XT_MOVE_UP (2)
143 #define XT_MOVE_BOTTOM (3)
144 #define XT_MOVE_TOP (4)
145 #define XT_MOVE_PAGEDOWN (5)
146 #define XT_MOVE_PAGEUP (6)
147 #define XT_MOVE_LEFT (7)
148 #define XT_MOVE_FARLEFT (8)
149 #define XT_MOVE_RIGHT (9)
150 #define XT_MOVE_FARRIGHT (10)
152 #define XT_TAB_LAST (-4)
153 #define XT_TAB_FIRST (-3)
154 #define XT_TAB_PREV (-2)
155 #define XT_TAB_NEXT (-1)
156 #define XT_TAB_INVALID (0)
157 #define XT_TAB_NEW (1)
158 #define XT_TAB_DELETE (2)
159 #define XT_TAB_DELQUIT (3)
160 #define XT_TAB_OPEN (4)
162 #define XT_NAV_INVALID (0)
163 #define XT_NAV_BACK (1)
164 #define XT_NAV_FORWARD (2)
165 #define XT_NAV_RELOAD (3)
167 #define XT_FOCUS_INVALID (0)
168 #define XT_FOCUS_URI (1)
169 #define XT_FOCUS_SEARCH (2)
171 #define XT_SEARCH_INVALID (0)
172 #define XT_SEARCH_NEXT (1)
173 #define XT_SEARCH_PREV (2)
175 #define XT_FONT_SET (0)
178 extern char *__progname
;
180 GtkWidget
*main_window
;
181 GtkNotebook
*notebook
;
182 struct tab_list tabs
;
189 TAILQ_ENTRY(mime_type
) entry
;
191 TAILQ_HEAD(mime_type_list
, mime_type
);
194 int showtabs
= 1; /* show tabs on notebook */
195 int showurl
= 1; /* show url toolbar on notebook */
196 int tabless
= 0; /* allow only 1 tab */
197 int ctrl_click_focus
= 0; /* ctrl click gets focus */
198 int cookies_enabled
= 1; /* enable cookies */
199 int read_only_cookies
= 0; /* enable to not write cookies */
200 int enable_scripts
= 0;
201 int enable_plugins
= 0;
202 int default_font_size
= 12;
203 int fancy_bar
= 1; /* fancy toolbar */
205 char *home
= "http://www.peereboom.us";
206 char *search_string
= NULL
;
207 char *http_proxy
= NULL
;
208 SoupURI
*proxy_uri
= NULL
;
209 char work_dir
[PATH_MAX
];
210 char cookie_file
[PATH_MAX
];
211 char download_dir
[PATH_MAX
];
212 SoupSession
*session
;
213 SoupCookieJar
*cookiejar
;
215 struct mime_type_list mtl
;
218 void create_new_tab(char *, int);
219 void delete_tab(struct tab
*);
220 void adjustfont_webkit(struct tab
*, int);
222 struct valid_url_types
{
232 valid_url_type(char *url
)
236 for (i
= 0; i
< LENGTH(vut
); i
++)
237 if (!strncasecmp(vut
[i
].type
, url
, strlen(vut
[i
].type
)))
244 guess_url_type(char *url_in
)
247 char *url_out
= NULL
;
249 /* XXX not sure about this heuristic */
250 if (stat(url_in
, &sb
) == 0) {
251 if (asprintf(&url_out
, "file://%s", url_in
) == -1)
252 err(1, "aprintf file");
255 if (asprintf(&url_out
, "http://%s", url_in
) == -1)
256 err(1, "aprintf http");
260 err(1, "asprintf pointer");
262 DNPRINTF(XT_D_URL
, "guess_url_type: guessed %s\n", url_out
);
268 add_mime_type(char *line
)
274 /* XXX this could be smarter */
277 errx(1, "add_mime_type");
280 m
= malloc(sizeof(*m
));
282 err(1, "add_mime_type: malloc");
284 if ((mime_type
= strsep(&l
, " \t,")) == NULL
|| l
== NULL
)
285 errx(1, "add_mime_type: invalid mime_type");
287 if (mime_type
[strlen(mime_type
) - 1] == '*') {
288 mime_type
[strlen(mime_type
) - 1] = '\0';
293 if (strlen(mime_type
) == 0 || strlen(l
) == 0)
294 errx(1, "add_mime_type: invalid mime_type");
296 m
->mt_type
= strdup(mime_type
);
297 if (m
->mt_type
== NULL
)
298 err(1, "add_mime_type: malloc type");
300 m
->mt_action
= strdup(l
);
301 if (m
->mt_action
== NULL
)
302 err(1, "add_mime_type: malloc action");
304 DNPRINTF(XT_D_CONFIG
, "add_mime_type: type %s action %s default %d\n",
305 m
->mt_type
, m
->mt_action
, m
->mt_default
);
307 TAILQ_INSERT_TAIL(&mtl
, m
, entry
);
311 find_mime_type(char *mime_type
)
313 struct mime_type
*m
, *def
= NULL
, *rv
= NULL
;
315 TAILQ_FOREACH(m
, &mtl
, entry
) {
317 !strncmp(mime_type
, m
->mt_type
, strlen(m
->mt_type
)))
320 if (m
->mt_default
== 0 && !strcmp(mime_type
, m
->mt_type
)) {
334 config_parse(char *filename
)
337 char *line
, *cp
, *var
, *val
;
338 size_t len
, lineno
= 0;
340 DNPRINTF(XT_D_CONFIG
, "config_parse: filename %s\n", filename
);
344 if (filename
== NULL
)
347 if ((config
= fopen(filename
, "r")) == NULL
) {
348 warn("config_parse: cannot open %s", filename
);
353 if ((line
= fparseln(config
, &len
, &lineno
, NULL
, 0)) == NULL
)
358 cp
+= (long)strspn(cp
, WS
);
365 if ((var
= strsep(&cp
, WS
)) == NULL
|| cp
== NULL
)
368 cp
+= (long)strspn(cp
, WS
);
370 if ((val
= strsep(&cp
, "\0")) == NULL
)
373 DNPRINTF(XT_D_CONFIG
, "config_parse: %s=%s\n",var
,val
);
376 if (!strcmp(var
, "home"))
378 else if (!strcmp(var
, "ctrl_click_focus"))
379 ctrl_click_focus
= atoi(val
);
380 else if (!strcmp(var
, "read_only_cookies"))
381 read_only_cookies
= atoi(val
);
382 else if (!strcmp(var
, "cookies_enabled"))
383 cookies_enabled
= atoi(val
);
384 else if (!strcmp(var
, "enable_scripts"))
385 enable_scripts
= atoi(val
);
386 else if (!strcmp(var
, "enable_plugins"))
387 enable_plugins
= atoi(val
);
388 else if (!strcmp(var
, "default_font_size"))
389 default_font_size
= atoi(val
);
390 else if (!strcmp(var
, "fancy_bar"))
391 fancy_bar
= atoi(val
);
392 else if (!strcmp(var
, "mime_type"))
394 else if (!strcmp(var
, "http_proxy")) {
397 http_proxy
= strdup(val
);
398 if (http_proxy
== NULL
)
399 err(1, "http_proxy");
400 } else if (!strcmp(var
, "search_string")) {
403 search_string
= strdup(val
);
404 if (search_string
== NULL
)
405 err(1, "search_string");
406 } else if (!strcmp(var
, "download_dir")) {
408 snprintf(download_dir
, sizeof download_dir
,
409 "%s/%s", pwd
->pw_dir
, &val
[1]);
411 strlcpy(download_dir
, val
, sizeof download_dir
);
413 errx(1, "invalid conf file entry: %s=%s", var
, val
);
421 quit(struct tab
*t
, struct karg
*args
)
429 focus(struct tab
*t
, struct karg
*args
)
431 if (t
== NULL
|| args
== NULL
)
434 if (args
->i
== XT_FOCUS_URI
)
435 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
436 else if (args
->i
== XT_FOCUS_SEARCH
)
437 gtk_widget_grab_focus(GTK_WIDGET(t
->search_entry
));
443 help(struct tab
*t
, struct karg
*args
)
448 webkit_web_view_load_string(t
->wv
,
449 "<html><body><h1>XXXTerm</h1></body></html>",
458 favorites(struct tab
*t
, struct karg
*args
)
462 char *uri
= NULL
, *title
= NULL
;
463 size_t len
, lineno
= 0;
467 errx(1, "favorites");
469 /* XXX run a digest over the favorites file instead of always generating it */
472 snprintf(file
, sizeof file
, "%s/%s/%s",
473 pwd
->pw_dir
, XT_DIR
, XT_FAVS_FILE
);
474 if ((f
= fopen(file
, "r")) == NULL
) {
479 /* open favorites html */
480 snprintf(file
, sizeof file
, "%s/%s/%s.html",
481 pwd
->pw_dir
, XT_DIR
, XT_FAVS_FILE
);
482 if ((h
= fopen(file
, "w+")) == NULL
) {
483 warn("favorites.html");
487 fprintf(h
, "<html><body>Favorites:<p>\n<ol>\n");
490 if ((title
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
)
493 if (strlen(title
) == 0)
496 if ((uri
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
)
502 fprintf(h
, "<li><a href=\"%s\">%s</a><br>\n", uri
, title
);
516 fprintf(h
, "</ol></body></html>");
521 webkit_web_view_load_string(t
->wv
,
522 "<html><body>Invalid favorites file</body></html>",
527 snprintf(file
, sizeof file
, "file://%s/%s/%s.html",
528 pwd
->pw_dir
, XT_DIR
, XT_FAVS_FILE
);
529 webkit_web_view_load_uri(t
->wv
, file
);
536 favadd(struct tab
*t
, struct karg
*args
)
540 WebKitWebFrame
*frame
;
541 const gchar
*uri
, *title
;
546 snprintf(file
, sizeof file
, "%s/%s/%s",
547 pwd
->pw_dir
, XT_DIR
, XT_FAVS_FILE
);
548 if ((f
= fopen(file
, "r+")) == NULL
) {
552 if (fseeko(f
, 0, SEEK_END
) == -1)
555 title
= webkit_web_view_get_title(t
->wv
);
556 frame
= webkit_web_view_get_main_frame(t
->wv
);
557 uri
= webkit_web_frame_get_uri(frame
);
561 if (title
== NULL
|| uri
== NULL
) {
562 webkit_web_view_load_string(t
->wv
,
563 "<html><body>can't add page to favorites</body></html>",
570 fprintf(f
, "\n%s\n%s", title
, uri
);
578 navaction(struct tab
*t
, struct karg
*args
)
580 DNPRINTF(XT_D_NAV
, "navaction: tab %d opcode %d\n",
585 webkit_web_view_go_back(t
->wv
);
588 webkit_web_view_go_forward(t
->wv
);
591 webkit_web_view_reload(t
->wv
);
594 return (XT_CB_PASSTHROUGH
);
598 move(struct tab
*t
, struct karg
*args
)
600 GtkAdjustment
*adjust
;
601 double pi
, si
, pos
, ps
, upper
, lower
, max
;
608 case XT_MOVE_PAGEDOWN
:
610 adjust
= t
->adjust_v
;
613 adjust
= t
->adjust_h
;
617 pos
= gtk_adjustment_get_value(adjust
);
618 ps
= gtk_adjustment_get_page_size(adjust
);
619 upper
= gtk_adjustment_get_upper(adjust
);
620 lower
= gtk_adjustment_get_lower(adjust
);
621 si
= gtk_adjustment_get_step_increment(adjust
);
622 pi
= gtk_adjustment_get_page_increment(adjust
);
625 DNPRINTF(XT_D_MOVE
, "move: opcode %d %s pos %f ps %f upper %f lower %f "
626 "max %f si %f pi %f\n",
627 args
->i
, adjust
== t
->adjust_h
? "horizontal" : "vertical",
628 pos
, ps
, upper
, lower
, max
, si
, pi
);
634 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
639 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
642 case XT_MOVE_FARRIGHT
:
643 gtk_adjustment_set_value(adjust
, max
);
646 case XT_MOVE_FARLEFT
:
647 gtk_adjustment_set_value(adjust
, lower
);
649 case XT_MOVE_PAGEDOWN
:
651 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
655 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
658 return (XT_CB_PASSTHROUGH
);
661 DNPRINTF(XT_D_MOVE
, "move: new pos %f %f\n", pos
, MIN(pos
, max
));
663 return (XT_CB_HANDLED
);
667 getparams(char *cmd
, char *cmp
)
672 if (!strncmp(cmd
, cmp
, strlen(cmp
))) {
673 rv
= cmd
+ strlen(cmp
);
685 tabaction(struct tab
*t
, struct karg
*args
)
687 int rv
= XT_CB_HANDLED
;
688 char *url
= NULL
, *newuri
= NULL
;
690 DNPRINTF(XT_D_TAB
, "tabaction: %p %d %d\n", t
, args
->i
, t
->focus_wv
);
693 return (XT_CB_PASSTHROUGH
);
697 if ((url
= getparams(args
->s
, "tabnew")))
698 create_new_tab(url
, 1);
700 create_new_tab(NULL
, 1);
706 if (gtk_notebook_get_n_pages(notebook
) > 1)
712 if ((url
= getparams(args
->s
, "open")) ||
713 ((url
= getparams(args
->s
, "op"))) ||
714 ((url
= getparams(args
->s
, "o"))))
717 rv
= XT_CB_PASSTHROUGH
;
721 if (valid_url_type(url
)) {
722 newuri
= guess_url_type(url
);
725 webkit_web_view_load_uri(t
->wv
, url
);
730 rv
= XT_CB_PASSTHROUGH
;
744 resizetab(struct tab
*t
, struct karg
*args
)
746 if (t
== NULL
|| args
== NULL
)
747 errx(1, "resizetab");
749 DNPRINTF(XT_D_TAB
, "resizetab: tab %d %d\n",
752 adjustfont_webkit(t
, args
->i
);
754 return (XT_CB_HANDLED
);
758 movetab(struct tab
*t
, struct karg
*args
)
763 if (t
== NULL
|| args
== NULL
)
766 DNPRINTF(XT_D_TAB
, "movetab: tab %d opcode %d\n",
769 if (args
->i
== XT_TAB_INVALID
)
770 return (XT_CB_PASSTHROUGH
);
772 if (args
->i
< XT_TAB_INVALID
) {
773 /* next or previous tab */
774 if (TAILQ_EMPTY(&tabs
))
775 return (XT_CB_PASSTHROUGH
);
779 gtk_notebook_next_page(notebook
);
782 gtk_notebook_prev_page(notebook
);
785 gtk_notebook_set_current_page(notebook
, 0);
788 gtk_notebook_set_current_page(notebook
, -1);
791 return (XT_CB_PASSTHROUGH
);
794 return (XT_CB_HANDLED
);
799 if (t
->tab_id
== x
) {
800 DNPRINTF(XT_D_TAB
, "movetab: do nothing\n");
801 return (XT_CB_HANDLED
);
804 TAILQ_FOREACH(tt
, &tabs
, entry
) {
805 if (tt
->tab_id
== x
) {
806 gtk_notebook_set_current_page(notebook
, x
);
807 DNPRINTF(XT_D_TAB
, "movetab: going to %d\n", x
);
809 gtk_widget_grab_focus(GTK_WIDGET(tt
->wv
));
813 return (XT_CB_HANDLED
);
817 command(struct tab
*t
, struct karg
*args
)
822 if (t
== NULL
|| args
== NULL
)
827 else if (args
->i
== '?')
829 else if (args
->i
== ':')
832 warnx("invalid command %c\n", args
->i
);
833 return (XT_CB_PASSTHROUGH
);
836 DNPRINTF(XT_D_CMD
, "command: type %s\n", s
);
838 gtk_entry_set_text(GTK_ENTRY(t
->cmd
), s
);
839 gdk_color_parse("white", &color
);
840 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
841 gtk_widget_show(t
->cmd
);
842 gtk_widget_grab_focus(GTK_WIDGET(t
->cmd
));
843 gtk_editable_set_position(GTK_EDITABLE(t
->cmd
), -1);
845 return (XT_CB_HANDLED
);
849 search(struct tab
*t
, struct karg
*args
)
853 if (t
== NULL
|| args
== NULL
)
855 if (t
->search_text
== NULL
)
856 return (XT_CB_PASSTHROUGH
);
858 DNPRINTF(XT_D_CMD
, "search: tab %d opc %d forw %d text %s\n",
859 t
->tab_id
, args
->i
, t
->search_forward
, t
->search_text
);
863 d
= t
->search_forward
;
866 d
= !t
->search_forward
;
869 return (XT_CB_PASSTHROUGH
);
872 webkit_web_view_search_text(t
->wv
, t
->search_text
, FALSE
, d
, TRUE
);
874 return (XT_CB_HANDLED
);
878 mnprintf(char **buf
, int *len
, char *fmt
, ...)
886 x
= vsnprintf(*buf
, *len
, fmt
, ap
);
890 errx(1, "mnprintf: buffer overflow");
901 set(struct tab
*t
, struct karg
*args
)
904 char b
[16 * 1024], *s
, *pars
;
907 if (t
== NULL
|| args
== NULL
)
910 DNPRINTF(XT_D_CMD
, "set: tab %d\n",
916 if ((pars
= getparams(args
->s
, "set")) == NULL
) {
917 mnprintf(&s
, &l
, "<html><body><pre>");
918 mnprintf(&s
, &l
, "ctrl_click_focus\t= %d<br>", ctrl_click_focus
);
919 mnprintf(&s
, &l
, "cookies_enabled\t\t= %d<br>", cookies_enabled
);
920 mnprintf(&s
, &l
, "default_font_size\t= %d<br>", default_font_size
);
921 mnprintf(&s
, &l
, "enable_plugins\t\t= %d<br>", enable_plugins
);
922 mnprintf(&s
, &l
, "enable_scripts\t\t= %d<br>", enable_scripts
);
923 mnprintf(&s
, &l
, "fancy_bar\t\t= %d<br>", fancy_bar
);
924 mnprintf(&s
, &l
, "home\t\t\t= %s<br>", home
);
925 TAILQ_FOREACH(m
, &mtl
, entry
) {
926 mnprintf(&s
, &l
, "mime_type\t\t= %s%s,%s<br>",
927 m
->mt_type
, m
->mt_default
? "*" : "", m
->mt_action
);
929 mnprintf(&s
, &l
, "proxy_uri\t\t= %s<br>", proxy_uri
);
930 mnprintf(&s
, &l
, "read_only_cookies\t= %d<br>", read_only_cookies
);
931 mnprintf(&s
, &l
, "search_string\t\t= %s<br>", search_string
);
932 mnprintf(&s
, &l
, "showurl\t\t\t= %d<br>", showurl
);
933 mnprintf(&s
, &l
, "showtabs\t\t= %d<br>", showtabs
);
934 mnprintf(&s
, &l
, "tabless\t\t\t= %d<br>", tabless
);
935 mnprintf(&s
, &l
, "download_dir\t\t= %s<br>", download_dir
);
936 mnprintf(&s
, &l
, "</pre></body></html>");
938 webkit_web_view_load_string(t
->wv
,
946 /* XXX this sucks donkey balls and is a POC only */
949 if (!strncmp(pars
, "enable_scripts ", strlen("enable_scripts"))) {
950 s
= pars
+ strlen("enable_scripts");
951 x
= strtol(s
, &e
, 10);
952 if (s
[0] == '\0' || *e
!= '\0')
953 webkit_web_view_load_string(t
->wv
,
954 "<html><body>invalid value</body></html>",
960 g_object_set((GObject
*)t
->settings
,
961 "enable-scripts", enable_scripts
, NULL
);
962 webkit_web_view_set_settings(t
->wv
, t
->settings
);
971 return (XT_CB_PASSTHROUGH
);
974 /* inherent to GTK not all keys will be caught at all times */
979 int (*func
)(struct tab
*, struct karg
*);
982 { 0, 0, GDK_slash
, command
, {.i
= '/'} },
983 { GDK_SHIFT_MASK
, 0, GDK_question
, command
, {.i
= '?'} },
984 { GDK_SHIFT_MASK
, 0, GDK_colon
, command
, {.i
= ':'} },
985 { GDK_CONTROL_MASK
, 0, GDK_q
, quit
, {0} },
988 { 0, 0, GDK_n
, search
, {.i
= XT_SEARCH_NEXT
} },
989 { GDK_SHIFT_MASK
, 0, GDK_N
, search
, {.i
= XT_SEARCH_PREV
} },
992 { 0, 0, GDK_F6
, focus
, {.i
= XT_FOCUS_URI
} },
993 { 0, 0, GDK_F7
, focus
, {.i
= XT_FOCUS_SEARCH
} },
996 { 0, 0, GDK_BackSpace
, navaction
, {.i
= XT_NAV_BACK
} },
997 { GDK_MOD1_MASK
, 0, GDK_Left
, navaction
, {.i
= XT_NAV_BACK
} },
998 { GDK_SHIFT_MASK
, 0, GDK_BackSpace
, navaction
, {.i
= XT_NAV_FORWARD
} },
999 { GDK_MOD1_MASK
, 0, GDK_Right
, navaction
, {.i
= XT_NAV_FORWARD
} },
1000 { 0, 0, GDK_F5
, navaction
, {.i
= XT_NAV_RELOAD
} },
1001 { GDK_CONTROL_MASK
, 0, GDK_r
, navaction
, {.i
= XT_NAV_RELOAD
} },
1002 { GDK_CONTROL_MASK
, 0, GDK_l
, navaction
, {.i
= XT_NAV_RELOAD
} },
1004 /* vertical movement */
1005 { 0, 0, GDK_j
, move
, {.i
= XT_MOVE_DOWN
} },
1006 { 0, 0, GDK_Down
, move
, {.i
= XT_MOVE_DOWN
} },
1007 { 0, 0, GDK_Up
, move
, {.i
= XT_MOVE_UP
} },
1008 { 0, 0, GDK_k
, move
, {.i
= XT_MOVE_UP
} },
1009 { GDK_SHIFT_MASK
, 0, GDK_G
, move
, {.i
= XT_MOVE_BOTTOM
} },
1010 { 0, 0, GDK_End
, move
, {.i
= XT_MOVE_BOTTOM
} },
1011 { 0, 0, GDK_Home
, move
, {.i
= XT_MOVE_TOP
} },
1012 { 0, GDK_g
, GDK_g
, move
, {.i
= XT_MOVE_TOP
} }, /* XXX make this work */
1013 { 0, 0, GDK_space
, move
, {.i
= XT_MOVE_PAGEDOWN
} },
1014 { GDK_CONTROL_MASK
, 0, GDK_f
, move
, {.i
= XT_MOVE_PAGEDOWN
} },
1015 { 0, 0, GDK_Page_Down
, move
, {.i
= XT_MOVE_PAGEDOWN
} },
1016 { 0, 0, GDK_Page_Up
, move
, {.i
= XT_MOVE_PAGEUP
} },
1017 { GDK_CONTROL_MASK
, 0, GDK_b
, move
, {.i
= XT_MOVE_PAGEUP
} },
1018 /* horizontal movement */
1019 { 0, 0, GDK_l
, move
, {.i
= XT_MOVE_RIGHT
} },
1020 { 0, 0, GDK_Right
, move
, {.i
= XT_MOVE_RIGHT
} },
1021 { 0, 0, GDK_Left
, move
, {.i
= XT_MOVE_LEFT
} },
1022 { 0, 0, GDK_h
, move
, {.i
= XT_MOVE_LEFT
} },
1023 { GDK_SHIFT_MASK
, 0, GDK_dollar
, move
, {.i
= XT_MOVE_FARRIGHT
} },
1024 { 0, 0, GDK_0
, move
, {.i
= XT_MOVE_FARLEFT
} },
1027 { GDK_CONTROL_MASK
, 0, GDK_t
, tabaction
, {.i
= XT_TAB_NEW
} },
1028 { GDK_CONTROL_MASK
, 0, GDK_w
, tabaction
, {.i
= XT_TAB_DELETE
} },
1029 { GDK_CONTROL_MASK
, 0, GDK_1
, movetab
, {.i
= 1} },
1030 { GDK_CONTROL_MASK
, 0, GDK_2
, movetab
, {.i
= 2} },
1031 { GDK_CONTROL_MASK
, 0, GDK_3
, movetab
, {.i
= 3} },
1032 { GDK_CONTROL_MASK
, 0, GDK_4
, movetab
, {.i
= 4} },
1033 { GDK_CONTROL_MASK
, 0, GDK_5
, movetab
, {.i
= 5} },
1034 { GDK_CONTROL_MASK
, 0, GDK_6
, movetab
, {.i
= 6} },
1035 { GDK_CONTROL_MASK
, 0, GDK_7
, movetab
, {.i
= 7} },
1036 { GDK_CONTROL_MASK
, 0, GDK_8
, movetab
, {.i
= 8} },
1037 { GDK_CONTROL_MASK
, 0, GDK_9
, movetab
, {.i
= 9} },
1038 { GDK_CONTROL_MASK
, 0, GDK_0
, movetab
, {.i
= 10} },
1039 { GDK_CONTROL_MASK
|GDK_SHIFT_MASK
, 0, GDK_less
, movetab
, {.i
= XT_TAB_FIRST
} },
1040 { GDK_CONTROL_MASK
|GDK_SHIFT_MASK
, 0, GDK_greater
, movetab
, {.i
= XT_TAB_LAST
} },
1041 { GDK_CONTROL_MASK
, 0, GDK_minus
, resizetab
, {.i
= -1} },
1042 { GDK_CONTROL_MASK
|GDK_SHIFT_MASK
, 0, GDK_plus
, resizetab
, {.i
= 1} },
1043 { GDK_CONTROL_MASK
, 0, GDK_equal
, resizetab
, {.i
= 1} },
1049 int (*func
)(struct tab
*, struct karg
*);
1052 { "q!", 0, quit
, {0} },
1053 { "qa", 0, quit
, {0} },
1054 { "qa!", 0, quit
, {0} },
1055 { "help", 0, help
, {0} },
1058 { "fav", 0, favorites
, {0} },
1059 { "favadd", 0, favadd
, {0} },
1062 { "o", 1, tabaction
, {.i
= XT_TAB_OPEN
} },
1063 { "op", 1, tabaction
, {.i
= XT_TAB_OPEN
} },
1064 { "open", 1, tabaction
, {.i
= XT_TAB_OPEN
} },
1065 { "tabnew", 1, tabaction
, {.i
= XT_TAB_NEW
} },
1066 { "tabedit", 1, tabaction
, {.i
= XT_TAB_NEW
} },
1067 { "tabe", 1, tabaction
, {.i
= XT_TAB_NEW
} },
1068 { "tabclose", 0, tabaction
, {.i
= XT_TAB_DELETE
} },
1069 { "tabc", 0, tabaction
, {.i
= XT_TAB_DELETE
} },
1070 { "quit", 0, tabaction
, {.i
= XT_TAB_DELQUIT
} },
1071 { "q", 0, tabaction
, {.i
= XT_TAB_DELQUIT
} },
1072 /* XXX add count to these commands */
1073 { "tabfirst", 0, movetab
, {.i
= XT_TAB_FIRST
} },
1074 { "tabfir", 0, movetab
, {.i
= XT_TAB_FIRST
} },
1075 { "tabrewind", 0, movetab
, {.i
= XT_TAB_FIRST
} },
1076 { "tabr", 0, movetab
, {.i
= XT_TAB_FIRST
} },
1077 { "tablast", 0, movetab
, {.i
= XT_TAB_LAST
} },
1078 { "tabl", 0, movetab
, {.i
= XT_TAB_LAST
} },
1079 { "tabprevious", 0, movetab
, {.i
= XT_TAB_PREV
} },
1080 { "tabp", 0, movetab
, {.i
= XT_TAB_PREV
} },
1081 { "tabnext", 0, movetab
, {.i
= XT_TAB_NEXT
} },
1082 { "tabn", 0, movetab
, {.i
= XT_TAB_NEXT
} },
1085 { "set", 1, set
, {0} },
1089 focus_uri_entry_cb(GtkWidget
* w
, GtkDirectionType direction
, struct tab
*t
)
1091 DNPRINTF(XT_D_URL
, "focus_uri_entry_cb: tab %d focus_wv %d\n",
1092 t
->tab_id
, t
->focus_wv
);
1095 errx(1, "focus_uri_entry_cb");
1097 /* focus on wv instead */
1099 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
1103 activate_uri_entry_cb(GtkWidget
* entry
, struct tab
*t
)
1105 const gchar
*uri
= gtk_entry_get_text(GTK_ENTRY(entry
));
1106 char *newuri
= NULL
;
1108 DNPRINTF(XT_D_URL
, "activate_uri_entry_cb: %s\n", uri
);
1111 errx(1, "activate_uri_entry_cb");
1116 if (valid_url_type((char *)uri
)) {
1117 newuri
= guess_url_type((char *)uri
);
1121 webkit_web_view_load_uri(t
->wv
, uri
);
1122 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
1129 activate_search_entry_cb(GtkWidget
* entry
, struct tab
*t
)
1131 const gchar
*search
= gtk_entry_get_text(GTK_ENTRY(entry
));
1132 char *newuri
= NULL
;
1134 DNPRINTF(XT_D_URL
, "activate_search_entry_cb: %s\n", search
);
1137 errx(1, "activate_search_entry_cb");
1139 if (search_string
== NULL
) {
1140 warnx("no search_string");
1144 if (asprintf(&newuri
, search_string
, search
) == -1)
1145 err(1, "activate_search_entry_cb");
1147 webkit_web_view_load_uri(t
->wv
, newuri
);
1148 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
1155 notify_load_status_cb(WebKitWebView
* wview
, GParamSpec
* pspec
, struct tab
*t
)
1158 WebKitWebFrame
*frame
;
1162 errx(1, "notify_load_status_cb");
1164 switch (webkit_web_view_get_load_status(wview
)) {
1165 case WEBKIT_LOAD_COMMITTED
:
1166 frame
= webkit_web_view_get_main_frame(wview
);
1167 uri
= webkit_web_frame_get_uri(frame
);
1169 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), uri
);
1171 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), TRUE
);
1174 /* take focus if we are visible */
1175 if (gtk_notebook_get_current_page(notebook
) == t
->tab_id
)
1176 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
1178 /* color uri_entry */
1179 if (uri
&& !strncmp(uri
, "https://", strlen("https://")))
1180 gdk_color_parse("green", &color
);
1182 gdk_color_parse("white", &color
);
1183 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
1187 case WEBKIT_LOAD_FIRST_VISUALLY_NON_EMPTY_LAYOUT
:
1188 uri
= webkit_web_view_get_title(wview
);
1190 frame
= webkit_web_view_get_main_frame(wview
);
1191 uri
= webkit_web_frame_get_uri(frame
);
1193 gtk_label_set_text(GTK_LABEL(t
->label
), uri
);
1194 gtk_window_set_title(GTK_WINDOW(main_window
), uri
);
1198 case WEBKIT_LOAD_PROVISIONAL
:
1199 case WEBKIT_LOAD_FINISHED
:
1200 #if WEBKIT_CHECK_VERSION(1, 1, 18)
1201 case WEBKIT_LOAD_FAILED
:
1204 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), FALSE
);
1208 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
),
1209 webkit_web_view_can_go_back(wview
));
1211 gtk_widget_set_sensitive(GTK_WIDGET(t
->forward
),
1212 webkit_web_view_can_go_forward(wview
));
1216 webview_nw_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
,
1217 WebKitNetworkRequest
*request
, WebKitWebNavigationAction
*na
,
1218 WebKitWebPolicyDecision
*pd
, struct tab
*t
)
1223 errx(1, "webview_nw_cb");
1225 DNPRINTF(XT_D_NAV
, "webview_nw_cb: %s\n",
1226 webkit_network_request_get_uri(request
));
1228 /* open in current tab */
1229 uri
= (char *)webkit_network_request_get_uri(request
);
1230 webkit_web_view_load_uri(t
->wv
, uri
);
1231 webkit_web_policy_decision_ignore(pd
);
1233 return (TRUE
); /* we made the decission */
1237 webview_npd_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
,
1238 WebKitNetworkRequest
*request
, WebKitWebNavigationAction
*na
,
1239 WebKitWebPolicyDecision
*pd
, struct tab
*t
)
1244 errx(1, "webview_npd_cb");
1246 DNPRINTF(XT_D_NAV
, "webview_npd_cb: %s\n",
1247 webkit_network_request_get_uri(request
));
1249 uri
= (char *)webkit_network_request_get_uri(request
);
1250 if (t
->ctrl_click
) {
1252 create_new_tab(uri
, ctrl_click_focus
);
1253 webkit_web_policy_decision_ignore(pd
);
1254 return (TRUE
); /* we made the decission */
1257 webkit_web_policy_decision_use(pd
);
1258 return (TRUE
); /* we made the decission */
1262 webview_cwv_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, struct tab
*t
)
1265 errx(1, "webview_cwv_cb");
1267 DNPRINTF(XT_D_NAV
, "webview_cwv_cb: %s\n",
1268 webkit_web_view_get_uri(wv
));
1274 webview_event_cb(GtkWidget
*w
, GdkEventButton
*e
, struct tab
*t
)
1276 /* we can not eat the event without throwing gtk off so defer it */
1278 /* catch ctrl click */
1279 if (e
->type
== GDK_BUTTON_RELEASE
&&
1280 CLEAN(e
->state
) == GDK_CONTROL_MASK
)
1285 return (XT_CB_PASSTHROUGH
);
1289 run_mimehandler(struct tab
*t
, char *mime_type
, WebKitNetworkRequest
*request
)
1291 struct mime_type
*m
;
1293 m
= find_mime_type(mime_type
);
1308 execlp(m
->mt_action
, m
->mt_action
,
1309 webkit_network_request_get_uri(request
), (void *)NULL
);
1318 webview_mimetype_cb(WebKitWebView
*wv
, WebKitWebFrame
*frame
,
1319 WebKitNetworkRequest
*request
, char *mime_type
,
1320 WebKitWebPolicyDecision
*decision
, struct tab
*t
)
1323 errx(1, "webview_mimetype_cb");
1325 DNPRINTF(XT_D_DOWNLOAD
, "webview_mimetype_cb: tab %d mime %s\n",
1326 t
->tab_id
, mime_type
);
1328 if (run_mimehandler(t
, mime_type
, request
) == 0) {
1329 webkit_web_policy_decision_ignore(decision
);
1330 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
1334 if (webkit_web_view_can_show_mime_type(wv
, mime_type
) == FALSE
) {
1335 webkit_web_policy_decision_download(decision
);
1343 webview_download_cb(WebKitWebView
*wv
, WebKitDownload
*download
, struct tab
*t
)
1345 const gchar
*filename
;
1348 if (download
== NULL
|| t
== NULL
)
1349 errx(1, "webview_download_cb: invalid pointers");
1351 filename
= webkit_download_get_suggested_filename(download
);
1352 if (filename
== NULL
)
1353 return (FALSE
); /* abort download */
1355 if (asprintf(&uri
, "file://%s/%s", download_dir
, filename
) == -1)
1356 err(1, "aprintf uri");
1358 DNPRINTF(XT_D_DOWNLOAD
, "webview_download_cb: tab %d filename %s "
1360 t
->tab_id
, filename
, uri
);
1362 webkit_download_set_destination_uri(download
, uri
);
1367 webkit_download_start(download
);
1369 return (TRUE
); /* start download */
1372 /* XXX currently unused */
1374 webview_hover_cb(WebKitWebView
*wv
, gchar
*title
, gchar
*uri
, struct tab
*t
)
1376 DNPRINTF(XT_D_KEY
, "webview_hover_cb: %s %s\n", title
, uri
);
1379 errx(1, "webview_hover_cb");
1386 t
->hover
= strdup(uri
);
1387 } else if (t
->hover
) {
1394 webview_keypress_cb(GtkWidget
*w
, GdkEventKey
*e
, struct tab
*t
)
1398 /* don't use w directly; use t->whatever instead */
1401 errx(1, "webview_keypress_cb");
1403 DNPRINTF(XT_D_KEY
, "webview_keypress_cb: keyval 0x%x mask 0x%x t %p\n",
1404 e
->keyval
, e
->state
, t
);
1406 for (i
= 0; i
< LENGTH(keys
); i
++)
1407 if (e
->keyval
== keys
[i
].key
&& CLEAN(e
->state
) ==
1409 keys
[i
].func(t
, &keys
[i
].arg
);
1410 return (XT_CB_HANDLED
);
1413 return (XT_CB_PASSTHROUGH
);
1417 cmd_keyrelease_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
1419 const gchar
*c
= gtk_entry_get_text(w
);
1423 DNPRINTF(XT_D_CMD
, "cmd_keyrelease_cb: keyval 0x%x mask 0x%x t %p\n",
1424 e
->keyval
, e
->state
, t
);
1427 errx(1, "cmd_keyrelease_cb");
1429 DNPRINTF(XT_D_CMD
, "cmd_keyrelease_cb: keyval 0x%x mask 0x%x t %p\n",
1430 e
->keyval
, e
->state
, t
);
1439 else if (c
[0] == '?')
1445 if (webkit_web_view_search_text(t
->wv
, &c
[1], FALSE
, forward
, TRUE
) ==
1447 /* not found, mark red */
1448 gdk_color_parse("red", &color
);
1449 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
1450 /* unmark and remove selection */
1451 webkit_web_view_unmark_text_matches(t
->wv
);
1452 /* my kingdom for a way to unselect text in webview */
1454 /* found, highlight all */
1455 webkit_web_view_unmark_text_matches(t
->wv
);
1456 webkit_web_view_mark_text_matches(t
->wv
, &c
[1], FALSE
, 0);
1457 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
1458 gdk_color_parse("white", &color
);
1459 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
1462 return (XT_CB_PASSTHROUGH
);
1467 cmd_complete(struct tab
*t
, char *s
)
1470 GtkEntry
*w
= GTK_ENTRY(t
->cmd
);
1472 DNPRINTF(XT_D_CMD
, "cmd_keypress_cb: complete %s\n", s
);
1474 for (i
= 0; i
< LENGTH(cmds
); i
++) {
1475 if (!strncasecmp(cmds
[i
].cmd
, s
, strlen(s
))) {
1476 fprintf(stderr
, "match %s %d\n", cmds
[i
].cmd
, strcasecmp(cmds
[i
].cmd
, s
));
1478 gtk_entry_set_text(w
, ":");
1479 gtk_entry_append_text(w
, cmds
[i
].cmd
);
1480 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
1490 cmd_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
1492 int rv
= XT_CB_HANDLED
;
1493 const gchar
*c
= gtk_entry_get_text(w
);
1496 errx(1, "cmd_keypress_cb");
1498 DNPRINTF(XT_D_CMD
, "cmd_keypress_cb: keyval 0x%x mask 0x%x t %p\n",
1499 e
->keyval
, e
->state
, t
);
1503 e
->keyval
= GDK_Escape
;
1504 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?'))
1505 e
->keyval
= GDK_Escape
;
1507 switch (e
->keyval
) {
1513 if (strchr (c
, ' ')) {
1514 /* par completion */
1515 fprintf(stderr
, "completeme par\n");
1519 cmd_complete(t
, (char *)&c
[1]);
1524 if (!(!strcmp(c
, ":") || !strcmp(c
, "/") || !strcmp(c
, "?")))
1528 gtk_widget_hide(t
->cmd
);
1529 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
1533 rv
= XT_CB_PASSTHROUGH
;
1539 cmd_focusout_cb(GtkWidget
*w
, GdkEventFocus
*e
, struct tab
*t
)
1542 errx(1, "cmd_focusout_cb");
1544 DNPRINTF(XT_D_CMD
, "cmd_focusout_cb: tab %d focus_wv %d\n",
1545 t
->tab_id
, t
->focus_wv
);
1547 /* abort command when losing focus */
1548 gtk_widget_hide(t
->cmd
);
1550 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
1552 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
1554 return (XT_CB_PASSTHROUGH
);
1558 cmd_activate_cb(GtkEntry
*entry
, struct tab
*t
)
1562 const gchar
*c
= gtk_entry_get_text(entry
);
1565 errx(1, "cmd_activate_cb");
1567 DNPRINTF(XT_D_CMD
, "cmd_activate_cb: tab %d %s\n", t
->tab_id
, c
);
1572 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?'))
1578 if (c
[0] == '/' || c
[0] == '?') {
1579 if (t
->search_text
) {
1580 free(t
->search_text
);
1581 t
->search_text
= NULL
;
1584 t
->search_text
= strdup(s
);
1585 if (t
->search_text
== NULL
)
1586 err(1, "search_text");
1588 t
->search_forward
= c
[0] == '/';
1593 for (i
= 0; i
< LENGTH(cmds
); i
++)
1594 if (cmds
[i
].params
) {
1595 if (!strncmp(s
, cmds
[i
].cmd
, strlen(cmds
[i
].cmd
))) {
1596 cmds
[i
].arg
.s
= strdup(s
);
1597 cmds
[i
].func(t
, &cmds
[i
].arg
);
1600 if (!strcmp(s
, cmds
[i
].cmd
))
1601 cmds
[i
].func(t
, &cmds
[i
].arg
);
1605 gtk_widget_hide(t
->cmd
);
1609 backward_cb(GtkWidget
*w
, struct tab
*t
)
1612 errx(1, "backward_cb");
1614 DNPRINTF(XT_D_NAV
, "backward_cb: tab %d\n", t
->tab_id
);
1616 webkit_web_view_go_back(t
->wv
);
1620 forward_cb(GtkWidget
*w
, struct tab
*t
)
1623 errx(1, "forward_cb");
1625 DNPRINTF(XT_D_NAV
, "forward_cb: tab %d\n", t
->tab_id
);
1627 webkit_web_view_go_forward(t
->wv
);
1631 stop_cb(GtkWidget
*w
, struct tab
*t
)
1633 WebKitWebFrame
*frame
;
1638 DNPRINTF(XT_D_NAV
, "stop_cb: tab %d\n", t
->tab_id
);
1640 frame
= webkit_web_view_get_main_frame(t
->wv
);
1641 if (frame
== NULL
) {
1642 warnx("stop_cb: no frame");
1646 webkit_web_frame_stop_loading(frame
);
1650 setup_webkit(struct tab
*t
)
1652 g_object_set((GObject
*)t
->settings
,
1653 "user-agent", t
->user_agent
, NULL
);
1654 g_object_set((GObject
*)t
->settings
,
1655 "enable-scripts", enable_scripts
, NULL
);
1656 g_object_set((GObject
*)t
->settings
,
1657 "enable-plugins", enable_plugins
, NULL
);
1658 adjustfont_webkit(t
, XT_FONT_SET
);
1660 webkit_web_view_set_settings(t
->wv
, t
->settings
);
1664 create_browser(struct tab
*t
)
1670 errx(1, "create_browser");
1672 t
->sb_h
= GTK_SCROLLBAR(gtk_hscrollbar_new(NULL
));
1673 t
->sb_v
= GTK_SCROLLBAR(gtk_vscrollbar_new(NULL
));
1674 t
->adjust_h
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_h
));
1675 t
->adjust_v
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_v
));
1677 w
= gtk_scrolled_window_new(t
->adjust_h
, t
->adjust_v
);
1678 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(w
),
1679 GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
1681 t
->wv
= WEBKIT_WEB_VIEW(webkit_web_view_new());
1682 gtk_container_add(GTK_CONTAINER(w
), GTK_WIDGET(t
->wv
));
1684 g_signal_connect(t
->wv
, "notify::load-status",
1685 G_CALLBACK(notify_load_status_cb
), t
);
1688 t
->settings
= webkit_web_settings_new();
1690 g_object_get((GObject
*)t
->settings
, "user-agent", &strval
, NULL
);
1692 errx(1, "setup_webkit: can't get user-agent property");
1694 if (asprintf(&t
->user_agent
, "%s %s+", strval
, version
) == -1)
1695 err(1, "aprintf user-agent");
1708 w
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
1709 gtk_window_set_default_size(GTK_WINDOW(w
), 1024, 768);
1710 gtk_widget_set_name(w
, "xxxterm");
1711 gtk_window_set_wmclass(GTK_WINDOW(w
), "xxxterm", "XXXTerm");
1712 g_signal_connect(G_OBJECT(w
), "delete_event",
1713 G_CALLBACK (gtk_main_quit
), NULL
);
1719 create_toolbar(struct tab
*t
)
1721 GtkWidget
*toolbar
= gtk_toolbar_new();
1724 #if GTK_CHECK_VERSION(2,15,0)
1725 gtk_orientable_set_orientation(GTK_ORIENTABLE(toolbar
),
1726 GTK_ORIENTATION_HORIZONTAL
);
1728 gtk_toolbar_set_orientation(GTK_TOOLBAR(toolbar
),
1729 GTK_ORIENTATION_HORIZONTAL
);
1731 gtk_toolbar_set_style(GTK_TOOLBAR(toolbar
), GTK_TOOLBAR_BOTH_HORIZ
);
1734 /* backward button */
1735 t
->backward
= gtk_tool_button_new_from_stock(GTK_STOCK_GO_BACK
);
1736 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
), FALSE
);
1737 g_signal_connect(G_OBJECT(t
->backward
), "clicked",
1738 G_CALLBACK(backward_cb
), t
);
1739 gtk_toolbar_insert(GTK_TOOLBAR(toolbar
), t
->backward
, -1);
1741 /* forward button */
1743 gtk_tool_button_new_from_stock(GTK_STOCK_GO_FORWARD
);
1744 gtk_widget_set_sensitive(GTK_WIDGET(t
->forward
), FALSE
);
1745 g_signal_connect(G_OBJECT(t
->forward
), "clicked",
1746 G_CALLBACK(forward_cb
), t
);
1747 gtk_toolbar_insert(GTK_TOOLBAR(toolbar
), t
->forward
, -1);
1750 t
->stop
= gtk_tool_button_new_from_stock(GTK_STOCK_STOP
);
1751 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), FALSE
);
1752 g_signal_connect(G_OBJECT(t
->stop
), "clicked",
1753 G_CALLBACK(stop_cb
), t
);
1754 gtk_toolbar_insert(GTK_TOOLBAR(toolbar
), t
->stop
, -1);
1758 i
= gtk_tool_item_new();
1759 gtk_tool_item_set_expand(i
, TRUE
);
1760 t
->uri_entry
= gtk_entry_new();
1761 gtk_container_add(GTK_CONTAINER(i
), t
->uri_entry
);
1762 g_signal_connect(G_OBJECT(t
->uri_entry
), "activate",
1763 G_CALLBACK(activate_uri_entry_cb
), t
);
1764 gtk_toolbar_insert(GTK_TOOLBAR(toolbar
), i
, -1);
1767 if (fancy_bar
&& search_string
) {
1768 i
= gtk_tool_item_new();
1769 t
->search_entry
= gtk_entry_new();
1770 gtk_entry_set_width_chars(GTK_ENTRY(t
->search_entry
), 30);
1771 gtk_container_add(GTK_CONTAINER(i
), t
->search_entry
);
1772 g_signal_connect(G_OBJECT(t
->search_entry
), "activate",
1773 G_CALLBACK(activate_search_entry_cb
), t
);
1774 gtk_toolbar_insert(GTK_TOOLBAR(toolbar
), i
, -1);
1781 delete_tab(struct tab
*t
)
1783 DNPRINTF(XT_D_TAB
, "delete_tab: %p\n", t
);
1788 TAILQ_REMOVE(&tabs
, t
, entry
);
1789 if (TAILQ_EMPTY(&tabs
))
1790 create_new_tab(NULL
, 1);
1792 gtk_widget_destroy(t
->vbox
);
1794 free(t
->user_agent
);
1799 adjustfont_webkit(struct tab
*t
, int adjust
)
1802 errx(1, "adjustfont_webkit");
1804 if (adjust
== XT_FONT_SET
)
1805 t
->font_size
= default_font_size
;
1807 t
->font_size
+= adjust
;
1808 g_object_set((GObject
*)t
->settings
, "default-font-size",
1809 t
->font_size
, NULL
);
1810 g_object_get((GObject
*)t
->settings
, "default-font-size",
1811 &t
->font_size
, NULL
);
1815 create_new_tab(char *title
, int focus
)
1819 char *newuri
= NULL
;
1821 DNPRINTF(XT_D_TAB
, "create_new_tab: title %s focus %d\n", title
, focus
);
1823 if (tabless
&& !TAILQ_EMPTY(&tabs
)) {
1824 DNPRINTF(XT_D_TAB
, "create_new_tab: new tab rejected\n");
1828 t
= g_malloc0(sizeof *t
);
1829 TAILQ_INSERT_TAIL(&tabs
, t
, entry
);
1831 if (title
== NULL
) {
1832 title
= "(untitled)";
1835 if (valid_url_type(title
)) {
1836 newuri
= guess_url_type(title
);
1841 t
->vbox
= gtk_vbox_new(FALSE
, 0);
1844 t
->label
= gtk_label_new(title
);
1845 gtk_widget_set_size_request(t
->label
, 100, -1);
1848 t
->toolbar
= create_toolbar(t
);
1849 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->toolbar
, FALSE
, FALSE
, 0);
1852 t
->browser_win
= create_browser(t
);
1853 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->browser_win
, TRUE
, TRUE
, 0);
1856 t
->cmd
= gtk_entry_new();
1857 gtk_entry_set_inner_border(GTK_ENTRY(t
->cmd
), NULL
);
1858 gtk_entry_set_has_frame(GTK_ENTRY(t
->cmd
), FALSE
);
1859 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->cmd
, FALSE
, FALSE
, 0);
1861 /* and show it all */
1862 gtk_widget_show_all(t
->vbox
);
1863 t
->tab_id
= gtk_notebook_append_page(notebook
, t
->vbox
,
1866 g_object_connect((GObject
*)t
->cmd
,
1867 "signal::key-press-event", (GCallback
)cmd_keypress_cb
, t
,
1868 "signal::key-release-event", (GCallback
)cmd_keyrelease_cb
, t
,
1869 "signal::focus-out-event", (GCallback
)cmd_focusout_cb
, t
,
1870 "signal::activate", (GCallback
)cmd_activate_cb
, t
,
1873 g_object_connect((GObject
*)t
->wv
,
1874 "signal-after::key-press-event", (GCallback
)webview_keypress_cb
, t
,
1875 /* "signal::hovering-over-link", (GCallback)webview_hover_cb, t, */
1876 "signal::download-requested", (GCallback
)webview_download_cb
, t
,
1877 "signal::mime-type-policy-decision-requested", (GCallback
)webview_mimetype_cb
, t
,
1878 "signal::navigation-policy-decision-requested", (GCallback
)webview_npd_cb
, t
,
1879 "signal::new-window-policy-decision-requested", (GCallback
)webview_nw_cb
, t
,
1880 "signal::create-web-view", (GCallback
)webview_cwv_cb
, t
,
1881 "signal::event", (GCallback
)webview_event_cb
, t
,
1884 /* hijack the unused keys as if we were the browser */
1885 g_object_connect((GObject
*)t
->toolbar
,
1886 "signal-after::key-press-event", (GCallback
)webview_keypress_cb
, t
,
1889 g_signal_connect(G_OBJECT(t
->uri_entry
), "focus",
1890 G_CALLBACK(focus_uri_entry_cb
), t
);
1893 gtk_widget_hide(t
->cmd
);
1895 gtk_widget_hide(t
->toolbar
);
1898 gtk_notebook_set_current_page(notebook
, t
->tab_id
);
1899 DNPRINTF(XT_D_TAB
, "create_new_tab: going to tab: %d\n",
1904 webkit_web_view_load_uri(t
->wv
, title
);
1906 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
1913 notebook_switchpage_cb(GtkNotebook
*nb
, GtkNotebookPage
*nbp
, guint pn
,
1919 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: tab: %d\n", pn
);
1921 TAILQ_FOREACH(t
, &tabs
, entry
) {
1922 if (t
->tab_id
== pn
) {
1923 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: going to "
1926 uri
= webkit_web_view_get_title(t
->wv
);
1929 gtk_window_set_title(GTK_WINDOW(main_window
), uri
);
1931 gtk_widget_hide(t
->cmd
);
1941 vbox
= gtk_vbox_new(FALSE
, 0);
1942 notebook
= GTK_NOTEBOOK(gtk_notebook_new());
1944 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(notebook
), FALSE
);
1945 gtk_notebook_set_scrollable(notebook
, TRUE
);
1947 gtk_box_pack_start(GTK_BOX(vbox
), GTK_WIDGET(notebook
), TRUE
, TRUE
, 0);
1949 g_object_connect((GObject
*)notebook
,
1950 "signal::switch-page", (GCallback
)notebook_switchpage_cb
, NULL
,
1953 main_window
= create_window();
1954 gtk_container_add(GTK_CONTAINER(main_window
), vbox
);
1955 gtk_window_set_title(GTK_WINDOW(main_window
), XT_NAME
);
1956 gtk_widget_show_all(main_window
);
1963 soup_session_remove_feature(session
,
1964 (SoupSessionFeature
*)cookiejar
);
1965 g_object_unref(cookiejar
);
1969 if (cookies_enabled
== 0)
1972 cookiejar
= soup_cookie_jar_text_new(cookie_file
, read_only_cookies
);
1973 soup_session_add_feature(session
, (SoupSessionFeature
*)cookiejar
);
1977 setup_proxy(char *uri
)
1980 g_object_set(session
, "proxy_uri", NULL
, NULL
);
1981 soup_uri_free(proxy_uri
);
1985 if (http_proxy
!= uri
) {
1992 http_proxy
= strdup(uri
);
1993 if (http_proxy
== NULL
)
1994 err(1, "setup_proxy: strdup");
1996 DNPRINTF(XT_D_CONFIG
, "setup_proxy: %s\n", uri
);
1997 proxy_uri
= soup_uri_new(http_proxy
);
1998 g_object_set(session
, "proxy-uri", proxy_uri
, NULL
);
2006 "%s [-STVt][-f file] url ...\n", __progname
);
2011 main(int argc
, char *argv
[])
2015 char conf
[PATH_MAX
] = { '\0' };
2016 char *env_proxy
= NULL
;
2019 while ((c
= getopt(argc
, argv
, "STVf:t")) != -1) {
2028 errx(0 , "Version: %s", version
);
2031 strlcpy(conf
, optarg
, sizeof(conf
));
2047 gtk_init(&argc
, &argv
);
2048 if (!g_thread_supported())
2049 g_thread_init(NULL
);
2051 pwd
= getpwuid(getuid());
2053 errx(1, "invalid user %d", getuid());
2055 /* set download dir */
2056 strlcpy(download_dir
, pwd
->pw_dir
, sizeof download_dir
);
2058 /* read config file */
2059 if (strlen(conf
) == 0)
2060 snprintf(conf
, sizeof conf
, "%s/.%s",
2061 pwd
->pw_dir
, XT_CONF_FILE
);
2065 if (stat(download_dir
, &sb
))
2066 errx(1, "must specify a valid download_dir");
2067 if (S_ISDIR(sb
.st_mode
) == 0)
2068 errx(1, "%s not a dir", download_dir
);
2069 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
2070 warnx("fixing invalid permissions on %s", download_dir
);
2071 if (chmod(download_dir
, S_IRWXU
) == -1)
2075 /* working directory */
2076 snprintf(work_dir
, sizeof work_dir
, "%s/%s", pwd
->pw_dir
, XT_DIR
);
2077 if (stat(work_dir
, &sb
)) {
2078 if (mkdir(work_dir
, S_IRWXU
) == -1)
2081 if (S_ISDIR(sb
.st_mode
) == 0)
2082 errx(1, "%s not a dir", work_dir
);
2083 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
2084 warnx("fixing invalid permissions on %s", work_dir
);
2085 if (chmod(work_dir
, S_IRWXU
) == -1)
2089 /* favorites file */
2090 snprintf(work_dir
, sizeof work_dir
, "%s/%s/%s",
2091 pwd
->pw_dir
, XT_DIR
, XT_FAVS_FILE
);
2092 if (stat(work_dir
, &sb
)) {
2093 warnx("favorites file doesn't exist, creating it");
2094 if ((f
= fopen(work_dir
, "w")) == NULL
)
2095 err(1, "favorites");
2100 session
= webkit_get_default_session();
2101 snprintf(cookie_file
, sizeof cookie_file
, "%s/cookies.txt", work_dir
);
2105 env_proxy
= getenv("http_proxy");
2107 setup_proxy(env_proxy
);
2109 setup_proxy(http_proxy
);
2114 create_new_tab(argv
[0], focus
);
2121 create_new_tab(home
, 1);