6 #include <gdk/gdkkeysyms.h>
7 #include <webkit/webkit.h>
10 #define DOWNLOAD_DIR "/tmp/tmp"
13 static void zea_destroy_client(GtkWidget *, gpointer);
14 static gboolean zea_do_download(WebKitWebView *, WebKitDownload *, gpointer);
15 static gboolean zea_download_request(WebKitWebView *, WebKitWebFrame *,
16 WebKitNetworkRequest *, gchar *,
17 WebKitWebPolicyDecision *, gpointer);
18 static void zea_load_status_changed(GObject *obj, GParamSpec *pspec,
20 static gboolean zea_location_key(GtkWidget *, GdkEvent *, gpointer);
21 static void zea_new_client(const gchar *uri);
22 static gboolean zea_new_client_request(WebKitWebView *, WebKitWebFrame *,
23 WebKitNetworkRequest *,
24 WebKitWebNavigationAction *,
25 WebKitWebPolicyDecision *, gpointer);
26 static void zea_scroll(GtkAdjustment *, gint, gdouble);
27 static void zea_title_changed(GObject *, GParamSpec *, gpointer);
28 static void zea_uri_changed(GObject *, GParamSpec *, gpointer);
29 static void zea_web_view_hover(WebKitWebView *, gchar *, gchar *, gpointer);
30 static gboolean zea_web_view_key(GtkWidget *, GdkEvent *, gpointer);
33 static Window embed = 0;
34 static gint clients = 0;
35 static gdouble global_zoom = 1.0;
50 zea_destroy_client(GtkWidget *obj, gpointer data)
52 struct Client *c = (struct Client *)data;
56 webkit_web_view_stop_loading(WEBKIT_WEB_VIEW(c->web_view));
57 gtk_widget_destroy(c->web_view);
58 gtk_widget_destroy(c->scroll);
59 gtk_widget_destroy(c->status);
60 gtk_widget_destroy(c->location);
61 gtk_widget_destroy(c->vbox);
62 gtk_widget_destroy(c->win);
71 zea_do_download(WebKitWebView *web_view, WebKitDownload *download, gpointer data)
80 uri = webkit_download_get_uri(download);
85 ret = execlp("xterm", "xterm", "-hold", "-e", "wget", uri, NULL);
88 if (snprintf(id, 16, "%ld", embed) >= 16)
90 fprintf(stderr, "zea: id for xterm embed truncated!\n");
93 ret = execlp("xterm", "xterm", "-hold", "-into", id, "-e", "wget",
99 fprintf(stderr, "zea: exec'ing xterm for download");
109 zea_download_request(WebKitWebView *web_view, WebKitWebFrame *frame,
110 WebKitNetworkRequest *request, gchar *mime_type,
111 WebKitWebPolicyDecision *policy_decision,
118 if (!webkit_web_view_can_show_mime_type(web_view, mime_type))
120 webkit_web_policy_decision_download(policy_decision);
127 zea_location_key(GtkWidget *widget, GdkEvent *event, gpointer data)
129 struct Client *c = (struct Client *)data;
133 if (event->type == GDK_KEY_PRESS)
135 if (((GdkEventKey *)event)->keyval == GDK_KEY_Return)
137 gtk_widget_grab_focus(c->web_view);
138 webkit_web_view_load_uri(WEBKIT_WEB_VIEW(c->web_view),
139 gtk_entry_get_text(GTK_ENTRY(c->location)));
148 zea_new_client(const gchar *uri)
150 struct Client *c = malloc(sizeof(struct Client));
153 fprintf(stderr, "zea: fatal: malloc failed\n");
159 c->win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
163 c->win = gtk_plug_new(embed);
166 /* When using Gtk2, zea only shows a white area when run in
167 * suckless' tabbed. It appears we need to set a default window size
168 * for this to work. This is not needed when using Gtk3. */
169 gtk_window_set_default_size(GTK_WINDOW(c->win), 1024, 768);
171 g_signal_connect(G_OBJECT(c->win), "destroy",
172 G_CALLBACK(zea_destroy_client), c);
173 gtk_window_set_title(GTK_WINDOW(c->win), "zea");
175 c->web_view = webkit_web_view_new();
176 webkit_web_view_set_full_content_zoom(WEBKIT_WEB_VIEW(c->web_view), TRUE);
177 webkit_web_view_set_zoom_level(WEBKIT_WEB_VIEW(c->web_view), global_zoom);
178 g_signal_connect(G_OBJECT(c->web_view), "notify::title",
179 G_CALLBACK(zea_title_changed), c);
180 g_signal_connect(G_OBJECT(c->web_view), "notify::uri",
181 G_CALLBACK(zea_uri_changed), c);
182 g_signal_connect(G_OBJECT(c->web_view), "notify::load-status",
183 G_CALLBACK(zea_load_status_changed), c);
184 g_signal_connect(G_OBJECT(c->web_view),
185 "new-window-policy-decision-requested",
186 G_CALLBACK(zea_new_client_request), NULL);
187 g_signal_connect(G_OBJECT(c->web_view),
188 "mime-type-policy-decision-requested",
189 G_CALLBACK(zea_download_request), NULL);
190 g_signal_connect(G_OBJECT(c->web_view), "download-requested",
191 G_CALLBACK(zea_do_download), NULL);
192 g_signal_connect(G_OBJECT(c->web_view), "key-press-event",
193 G_CALLBACK(zea_web_view_key), c);
194 g_signal_connect(G_OBJECT(c->web_view), "hovering-over-link",
195 G_CALLBACK(zea_web_view_hover), c);
197 c->scroll = gtk_scrolled_window_new(NULL, NULL);
199 gtk_container_add(GTK_CONTAINER(c->scroll), c->web_view);
201 c->location = gtk_entry_new();
202 g_signal_connect(G_OBJECT(c->location), "key-press-event",
203 G_CALLBACK(zea_location_key), c);
205 c->status = gtk_statusbar_new();
207 c->vbox = gtk_vbox_new(FALSE, 0);
208 gtk_box_pack_start(GTK_BOX(c->vbox), c->location, FALSE, FALSE, 0);
209 gtk_container_add(GTK_CONTAINER(c->vbox), c->scroll);
210 gtk_box_pack_end(GTK_BOX(c->vbox), c->status, FALSE, FALSE, 0);
212 gtk_container_add(GTK_CONTAINER(c->win), c->vbox);
214 gtk_widget_grab_focus(c->web_view);
215 gtk_widget_show_all(c->win);
217 webkit_web_view_load_uri(WEBKIT_WEB_VIEW(c->web_view), uri);
223 zea_new_client_request(WebKitWebView *web_view, WebKitWebFrame *frame,
224 WebKitNetworkRequest *request,
225 WebKitWebNavigationAction *navigation_action,
226 WebKitWebPolicyDecision *policy_decision,
231 (void)navigation_action;
234 webkit_web_policy_decision_ignore(policy_decision);
235 zea_new_client(webkit_network_request_get_uri(request));
241 zea_scroll(GtkAdjustment *a, gint step_type, gdouble factor)
243 gdouble new, lower, upper, step;
244 lower = gtk_adjustment_get_lower(a);
245 upper = gtk_adjustment_get_upper(a) - gtk_adjustment_get_page_size(a) + lower;
247 step = gtk_adjustment_get_step_increment(a);
249 step = gtk_adjustment_get_page_increment(a);
250 new = gtk_adjustment_get_value(a) + factor * step;
251 new = new < lower ? lower : new;
252 new = new > upper ? upper : new;
253 gtk_adjustment_set_value(a, new);
257 zea_title_changed(GObject *obj, GParamSpec *pspec, gpointer data)
260 struct Client *c = (struct Client *)data;
265 t = webkit_web_view_get_title(WEBKIT_WEB_VIEW(c->web_view));
266 gtk_window_set_title(GTK_WINDOW(c->win), (t == NULL ? "zea" : t));
270 zea_uri_changed(GObject *obj, GParamSpec *pspec, gpointer data)
273 struct Client *c = (struct Client *)data;
278 t = webkit_web_view_get_uri(WEBKIT_WEB_VIEW(c->web_view));
279 gtk_entry_set_text(GTK_ENTRY(c->location), (t == NULL ? "zea" : t));
283 zea_load_status_changed(GObject *obj, GParamSpec *pspec, gpointer data)
285 struct Client *c = (struct Client *)data;
290 if (webkit_web_view_get_load_status(WEBKIT_WEB_VIEW(c->web_view))
291 == WEBKIT_LOAD_FINISHED)
293 gtk_statusbar_pop(GTK_STATUSBAR(c->status), 1);
294 gtk_statusbar_push(GTK_STATUSBAR(c->status), 1, "Finished.");
298 gtk_statusbar_pop(GTK_STATUSBAR(c->status), 1);
299 gtk_statusbar_push(GTK_STATUSBAR(c->status), 1, "Loading...");
304 zea_web_view_hover(WebKitWebView *web_view, gchar *title, gchar *uri,
307 struct Client *c = (struct Client *)data;
312 gtk_statusbar_pop(GTK_STATUSBAR(c->status), 0);
314 gtk_statusbar_push(GTK_STATUSBAR(c->status), 0, uri);
318 zea_web_view_key(GtkWidget *widget, GdkEvent *event, gpointer data)
320 struct Client *c = (struct Client *)data;
324 if (event->type == GDK_KEY_PRESS)
326 if (((GdkEventKey *)event)->state & GDK_CONTROL_MASK)
328 if (((GdkEventKey *)event)->keyval == GDK_KEY_o)
330 gtk_widget_grab_focus(c->location);
333 else if (((GdkEventKey *)event)->keyval == GDK_KEY_h)
335 zea_scroll(gtk_scrolled_window_get_hadjustment(
336 GTK_SCROLLED_WINDOW(c->scroll)), 0, -1);
339 else if (((GdkEventKey *)event)->keyval == GDK_KEY_j)
341 zea_scroll(gtk_scrolled_window_get_vadjustment(
342 GTK_SCROLLED_WINDOW(c->scroll)), 0, 1);
345 else if (((GdkEventKey *)event)->keyval == GDK_KEY_k)
347 zea_scroll(gtk_scrolled_window_get_vadjustment(
348 GTK_SCROLLED_WINDOW(c->scroll)), 0, -1);
351 else if (((GdkEventKey *)event)->keyval == GDK_KEY_l)
353 zea_scroll(gtk_scrolled_window_get_hadjustment(
354 GTK_SCROLLED_WINDOW(c->scroll)), 0, 1);
357 else if (((GdkEventKey *)event)->keyval == GDK_KEY_f)
359 zea_scroll(gtk_scrolled_window_get_vadjustment(
360 GTK_SCROLLED_WINDOW(c->scroll)), 1, 0.5);
363 if (((GdkEventKey *)event)->keyval == GDK_KEY_b)
365 zea_scroll(gtk_scrolled_window_get_vadjustment(
366 GTK_SCROLLED_WINDOW(c->scroll)), 1, -0.5);
370 else if (((GdkEventKey *)event)->keyval == GDK_KEY_Escape)
372 webkit_web_view_stop_loading(WEBKIT_WEB_VIEW(c->web_view));
373 gtk_statusbar_pop(GTK_STATUSBAR(c->status), 1);
374 gtk_statusbar_push(GTK_STATUSBAR(c->status), 1, "Aborted.");
382 main(int argc, char **argv)
386 gtk_init(&argc, &argv);
388 while ((opt = getopt(argc, argv, "z:e:")) != -1)
393 global_zoom = atof(optarg);
396 embed = atol(optarg);
403 fprintf(stderr, "Usage: zea [OPTIONS] <URI>\n");
407 zea_new_client(argv[optind]);