1 /* See LICENSE file for copyright and license details.
3 * To understand surf, start reading main().
10 #include <gdk/gdkkeysyms.h>
15 #include <webkit/webkit.h>
17 #define LENGTH(x) (sizeof x / sizeof x[0])
18 /* Plan9-style Argument parsing */
19 /* Vars: _c -> count; _b -> break; _a -> argument */
20 #define ARG int _c, _b; char *_a; \
21 for(_c = 1; _c < argc && argv[_c][0] == '-' && argv[_c][1] && \
22 (strcmp(argv[_c], "--") != 0); _c++) \
23 for(_a = &argv[_c][1], _b = 0; !_b && *_a; _a++ ) \
25 #define ARGVAL() (!_b && _a[1] && (_b = 1) ? &_a[1] : _c + 1 == argc ? \
27 #define ARGCHR() (*_a)
32 typedef struct Client {
40 Client *clients = NULL;
41 gboolean embed = FALSE;
42 gboolean showxid = FALSE;
43 gboolean ignore_once = FALSE;
45 static Client *newclient();
46 static void die(char *str);
47 static void setup(void);
48 static void cleanup(void);
49 static void updatetitle(Client *c);
50 static void destroywin(GtkWidget* w, gpointer d);
51 static gboolean keypress(GtkWidget* w, GdkEventKey *ev, gpointer d);
52 static void titlechange(WebKitWebView* view, WebKitWebFrame* frame, const gchar* title, gpointer d);
53 static void progresschange(WebKitWebView *view, gint p, gpointer d);
54 static void loadcommit(WebKitWebView *view, WebKitWebFrame *f, gpointer d);
55 static void linkhover(WebKitWebView* page, const gchar* t, const gchar* l, gpointer d);
56 static void destroyclient(Client *c);
57 static gboolean newwindow(WebKitWebView *view, WebKitWebFrame *f,
58 WebKitNetworkRequest *r, WebKitWebNavigationAction *n,
59 WebKitWebPolicyDecision *p, gpointer d);
60 static gboolean download(WebKitWebView *view, GObject *o, gpointer d);
61 static void loaduri(const Client *c, const gchar *uri);
62 static void loadfile(const Client *c, const gchar *f);
63 GdkFilterReturn processx(GdkXEvent *xevent, GdkEvent *event, gpointer data);
68 destroyclient(clients);
72 processx(GdkXEvent *e, GdkEvent *event, gpointer d) {
74 Client *c = (Client *)d;
78 unsigned char *buf = NULL;
79 if(((XEvent *)e)->type == PropertyNotify) {
80 ev = &((XEvent *)e)->xproperty;
81 if(ignore_once == FALSE && ev->atom == urlprop && ev->state == PropertyNewValue) {
82 XGetWindowProperty(dpy, ev->window, urlprop, 0L, BUFSIZ, False, XA_STRING,
83 &adummy, &idummy, &ldummy, &ldummy, &buf);
84 loaduri(c, (gchar *)buf);
86 return GDK_FILTER_REMOVE;
90 return GDK_FILTER_CONTINUE;
94 loadfile(const Client *c, const gchar *f) {
95 GIOChannel *chan = NULL;
97 GString *code = g_string_new("");
98 GString *uri = g_string_new(f);
101 if(strcmp(f, "-") == 0) {
102 chan = g_io_channel_unix_new(STDIN_FILENO);
104 while(g_io_channel_read_line(chan, &line, NULL, NULL, &e) == G_IO_STATUS_NORMAL) {
105 g_string_append(code, line);
108 webkit_web_view_load_html_string(c->view, code->str, NULL);
109 g_io_channel_shutdown(chan, FALSE, NULL);
113 g_string_prepend(uri, "file://");
114 loaduri(c, uri->str);
119 static void loaduri(const Client *c, const gchar *uri) {
120 GString* u = g_string_new(uri);
121 if(g_strrstr(u->str, ":") == NULL)
122 g_string_prepend(u, "http://");
123 webkit_web_view_load_uri(c->view, u->str);
124 g_string_free(u, TRUE);
128 download(WebKitWebView *view, GObject *o, gpointer d) {
134 newwindow(WebKitWebView *view, WebKitWebFrame *f,
135 WebKitNetworkRequest *r, WebKitWebNavigationAction *n,
136 WebKitWebPolicyDecision *p, gpointer d) {
138 Client *c = newclient();
139 webkit_web_view_load_request(c->view, r);
143 linkhover(WebKitWebView* page, const gchar* t, const gchar* l, gpointer d) {
144 Client *c = (Client *)d;
147 gtk_window_set_title(GTK_WINDOW(c->win), l);
153 loadcommit(WebKitWebView *view, WebKitWebFrame *f, gpointer d) {
154 Client *c = (Client *)d;
157 if(!(uri = (gchar *)webkit_web_view_get_uri(view)))
160 XChangeProperty(dpy, GDK_WINDOW_XID(GTK_WIDGET(c->win)->window), urlprop,
161 XA_STRING, 8, PropModeReplace, (unsigned char *)uri,
166 progresschange(WebKitWebView* view, gint p, gpointer d) {
167 Client *c = (Client *)d;
174 updatetitle(Client *c) {
176 if(c->progress == 100)
177 snprintf(t, LENGTH(t), "%s", c->title);
179 snprintf(t, LENGTH(t), "%s [%i%%]", c->title, c->progress);
180 gtk_window_set_title(GTK_WINDOW(c->win), t);
184 titlechange(WebKitWebView *v, WebKitWebFrame *f, const gchar *t, gpointer d) {
185 Client *c = (Client *)d;
189 c->title = g_strdup(t);
194 destroywin(GtkWidget* w, gpointer d) {
195 Client *c = (Client *)d;
201 destroyclient(Client *c) {
203 gtk_widget_destroy(c->win);
204 if(clients == c && c->next == NULL)
206 for(p = clients; p && p->next != c; p = p->next);
215 keypress(GtkWidget* w, GdkEventKey *ev, gpointer d) {
216 Client *c = (Client *)d;
218 if(ev->type == GDK_KEY_PRESS && (ev->state == GDK_CONTROL_MASK
219 || ev->state == (GDK_CONTROL_MASK | GDK_SHIFT_MASK))) {
223 if((ev->state & GDK_SHIFT_MASK))
224 webkit_web_view_reload_bypass_cache(c->view);
226 webkit_web_view_reload(c->view);
235 webkit_web_view_go_back(c->view);
238 webkit_web_view_go_forward(c->view);
247 urlprop = XInternAtom(dpy, "_SURF_URL", False);
250 void die(char *str) {
258 if(!(c = calloc(1, sizeof(Client))))
259 die("Cannot malloc!\n");
261 c->win = gtk_plug_new(0);
264 c->win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
265 gtk_window_set_wmclass(GTK_WINDOW(c->win), "surf", "surf");
267 gtk_window_set_default_size(GTK_WINDOW(c->win), 800, 600);
268 c->browser = gtk_scrolled_window_new(NULL, NULL);
269 g_signal_connect (G_OBJECT(c->win), "destroy", G_CALLBACK(destroywin), c);
270 g_signal_connect (G_OBJECT(c->win), "key-press-event", G_CALLBACK(keypress), c);
272 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(c->browser),
273 GTK_POLICY_NEVER, GTK_POLICY_NEVER);
274 c->view = WEBKIT_WEB_VIEW(webkit_web_view_new());
275 gtk_container_add(GTK_CONTAINER(c->browser), GTK_WIDGET(c->view));
277 g_signal_connect(G_OBJECT(c->view), "title-changed", G_CALLBACK(titlechange), c);
278 g_signal_connect(G_OBJECT(c->view), "load-progress-changed", G_CALLBACK(progresschange), c);
279 g_signal_connect(G_OBJECT(c->view), "load-committed", G_CALLBACK(loadcommit), c);
280 g_signal_connect(G_OBJECT(c->view), "hovering-over-link", G_CALLBACK(linkhover), c);
281 g_signal_connect(G_OBJECT(c->view), "new-window", G_CALLBACK(newwindow), c);
282 g_signal_connect(G_OBJECT(c->view), "download-requested", G_CALLBACK(download), c);
284 gtk_container_add(GTK_CONTAINER(c->win), c->browser);
285 gtk_widget_grab_focus(GTK_WIDGET(c->view));
286 gtk_widget_show_all(c->win);
288 printf("%u\n", (unsigned int)GDK_WINDOW_XID(GTK_WIDGET(c->win)->window));
291 gdk_window_set_events(GTK_WIDGET(c->win)->window, GDK_ALL_EVENTS_MASK);
292 gdk_window_add_filter(GTK_WIDGET(c->win)->window, processx, c);
296 int main(int argc, char *argv[]) {
297 gchar *uri = NULL, *file = NULL;
300 gtk_init(NULL, NULL);
301 if (!g_thread_supported())
313 if(!(uri = ARGVAL()))
320 if(!(file = ARGVAL()))
328 puts("surf - simple browser");
329 printf("usage: %s [-e] [-x] [-u uri] [-f file]\n", argv[0]);