]> git.armaanb.net Git - chorizo.git/blob - zea.c
9fc7d542bb33fb028378cf75e0b6091e3720d2e1
[chorizo.git] / zea.c
1 #include <stdio.h>
2 #include <stdlib.h>
3
4 #include <gtk/gtk.h>
5 #include <gdk/gdkx.h>
6 #include <gdk/gdkkeysyms.h>
7 #include <webkit/webkit.h>
8
9
10 #define DOWNLOAD_DIR "/tmp/tmp"
11
12
13 static void zea_adblock(WebKitWebView *, WebKitWebFrame *, WebKitWebResource *,
14                         WebKitNetworkRequest *, WebKitNetworkResponse *, gpointer);
15 static void zea_destroy_client(GtkWidget *, gpointer);
16 static gboolean zea_do_download(WebKitWebView *, WebKitDownload *, gpointer);
17 static gboolean zea_download_request(WebKitWebView *, WebKitWebFrame *,
18                                      WebKitNetworkRequest *, gchar *,
19                                      WebKitWebPolicyDecision *, gpointer);
20 static void zea_load_status_changed(GObject *obj, GParamSpec *pspec,
21                                     gpointer data);
22 static gboolean zea_location_key(GtkWidget *, GdkEvent *, gpointer);
23 static void zea_new_client(const gchar *uri);
24 static gboolean zea_new_client_request(WebKitWebView *, WebKitWebFrame *,
25                                        WebKitNetworkRequest *,
26                                        WebKitWebNavigationAction *,
27                                        WebKitWebPolicyDecision *, gpointer);
28 static void zea_search(gpointer, gint);
29 static void zea_scroll(GtkAdjustment *, gint, gdouble);
30 static void zea_title_changed(GObject *, GParamSpec *, gpointer);
31 static void zea_uri_changed(GObject *, GParamSpec *, gpointer);
32 static void zea_web_view_hover(WebKitWebView *, gchar *, gchar *, gpointer);
33 static gboolean zea_web_view_key(GtkWidget *, GdkEvent *, gpointer);
34
35
36 static Window embed = 0;
37 static gint clients = 0;
38 static gdouble global_zoom = 1.0;
39 static gchar *search_text = NULL;
40 static gchar *first_uri = NULL;
41 static gboolean show_all_requests = FALSE;
42
43
44 struct Client
45 {
46         GtkWidget *win;
47         GtkWidget *vbox;
48         GtkWidget *location;
49         GtkWidget *status;
50         GtkWidget *scroll;
51         GtkWidget *web_view;
52 };
53
54
55 void
56 zea_adblock(WebKitWebView *web_view, WebKitWebFrame *frame,
57             WebKitWebResource *resource, WebKitNetworkRequest *request,
58             WebKitNetworkResponse *response, gpointer data)
59 {
60         (void)web_view;
61         (void)frame;
62         (void)resource;
63         (void)response;
64         (void)data;
65
66         if (show_all_requests)
67                 fprintf(stderr, "-> %s\n", webkit_network_request_get_uri(request));
68
69         /* XXX Changing the URI here using webkit_network_request_set_uri()
70          * effectively blocks the request. */
71 }
72
73 void
74 zea_destroy_client(GtkWidget *obj, gpointer data)
75 {
76         struct Client *c = (struct Client *)data;
77
78         (void)obj;
79
80         webkit_web_view_stop_loading(WEBKIT_WEB_VIEW(c->web_view));
81         gtk_widget_destroy(c->web_view);
82         gtk_widget_destroy(c->scroll);
83         gtk_widget_destroy(c->status);
84         gtk_widget_destroy(c->location);
85         gtk_widget_destroy(c->vbox);
86         gtk_widget_destroy(c->win);
87         free(c);
88
89         clients--;
90         if (clients == 0)
91                 gtk_main_quit();
92 }
93
94 gboolean
95 zea_do_download(WebKitWebView *web_view, WebKitDownload *download, gpointer data)
96 {
97         const gchar *uri;
98         char id[16] = "";
99         gint ret;
100
101         (void)web_view;
102         (void)data;
103
104         uri = webkit_download_get_uri(download);
105         if (fork() == 0)
106         {
107                 chdir(DOWNLOAD_DIR);
108                 if (embed == 0)
109                         ret = execlp("xterm", "xterm", "-hold", "-e", "wget", uri, NULL);
110                 else
111                 {
112                         if (snprintf(id, 16, "%ld", embed) >= 16)
113                         {
114                                 fprintf(stderr, "zea: id for xterm embed truncated!\n");
115                                 exit(EXIT_FAILURE);
116                         }
117                         ret = execlp("xterm", "xterm", "-hold", "-into", id, "-e", "wget",
118                                      uri, NULL);
119                 }
120
121                 if (ret == -1)
122                 {
123                         fprintf(stderr, "zea: exec'ing xterm for download");
124                         perror(" failed");
125                         exit(EXIT_FAILURE);
126                 }
127         }
128
129         return FALSE;
130 }
131
132 gboolean
133 zea_download_request(WebKitWebView *web_view, WebKitWebFrame *frame,
134                      WebKitNetworkRequest *request, gchar *mime_type,
135                      WebKitWebPolicyDecision *policy_decision,
136                      gpointer data)
137 {
138         (void)frame;
139         (void)request;
140         (void)data;
141
142         if (!webkit_web_view_can_show_mime_type(web_view, mime_type))
143         {
144                 webkit_web_policy_decision_download(policy_decision);
145                 return TRUE;
146         }
147         return FALSE;
148 }
149
150 gboolean
151 zea_location_key(GtkWidget *widget, GdkEvent *event, gpointer data)
152 {
153         struct Client *c = (struct Client *)data;
154         const gchar *t;
155
156         (void)widget;
157
158         if (event->type == GDK_KEY_PRESS)
159         {
160                 if (((GdkEventKey *)event)->keyval == GDK_KEY_Return)
161                 {
162                         gtk_widget_grab_focus(c->web_view);
163                         t = gtk_entry_get_text(GTK_ENTRY(c->location));
164                         if (t != NULL && t[0] == '/')
165                         {
166                                 if (search_text != NULL)
167                                         g_free(search_text);
168                                 search_text = g_strdup(t + 1);  /* XXX whacky */
169                                 zea_search(c, 1);
170                         }
171                         else
172                                 webkit_web_view_load_uri(WEBKIT_WEB_VIEW(c->web_view), t);
173                         return TRUE;
174                 }
175         }
176
177         return FALSE;
178 }
179
180 void
181 zea_new_client(const gchar *uri)
182 {
183         struct Client *c = malloc(sizeof(struct Client));
184         if (!c)
185         {
186                 fprintf(stderr, "zea: fatal: malloc failed\n");
187                 exit(EXIT_FAILURE);
188         }
189
190         if (embed == 0)
191         {
192                 c->win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
193         }
194         else
195         {
196                 c->win = gtk_plug_new(embed);
197         }
198
199         /* When using Gtk2, zea only shows a white area when run in
200          * suckless' tabbed. It appears we need to set a default window size
201          * for this to work. This is not needed when using Gtk3. */
202         gtk_window_set_default_size(GTK_WINDOW(c->win), 1024, 768);
203
204         g_signal_connect(G_OBJECT(c->win), "destroy",
205                          G_CALLBACK(zea_destroy_client), c);
206         gtk_window_set_title(GTK_WINDOW(c->win), "zea");
207
208         c->web_view = webkit_web_view_new();
209         webkit_web_view_set_full_content_zoom(WEBKIT_WEB_VIEW(c->web_view), TRUE);
210         webkit_web_view_set_zoom_level(WEBKIT_WEB_VIEW(c->web_view), global_zoom);
211         g_signal_connect(G_OBJECT(c->web_view), "notify::title",
212                          G_CALLBACK(zea_title_changed), c);
213         g_signal_connect(G_OBJECT(c->web_view), "notify::uri",
214                          G_CALLBACK(zea_uri_changed), c);
215         g_signal_connect(G_OBJECT(c->web_view), "notify::load-status",
216                          G_CALLBACK(zea_load_status_changed), c);
217         g_signal_connect(G_OBJECT(c->web_view),
218                          "new-window-policy-decision-requested",
219                          G_CALLBACK(zea_new_client_request), NULL);
220         g_signal_connect(G_OBJECT(c->web_view),
221                          "mime-type-policy-decision-requested",
222                          G_CALLBACK(zea_download_request), NULL);
223         g_signal_connect(G_OBJECT(c->web_view), "download-requested",
224                          G_CALLBACK(zea_do_download), NULL);
225         g_signal_connect(G_OBJECT(c->web_view), "key-press-event",
226                          G_CALLBACK(zea_web_view_key), c);
227         g_signal_connect(G_OBJECT(c->web_view), "hovering-over-link",
228                          G_CALLBACK(zea_web_view_hover), c);
229         g_signal_connect(G_OBJECT(c->web_view), "resource-request-starting",
230                          G_CALLBACK(zea_adblock), NULL);
231
232         c->scroll = gtk_scrolled_window_new(NULL, NULL);
233
234         gtk_container_add(GTK_CONTAINER(c->scroll), c->web_view);
235
236         c->location = gtk_entry_new();
237         g_signal_connect(G_OBJECT(c->location), "key-press-event",
238                          G_CALLBACK(zea_location_key), c);
239
240         c->status = gtk_statusbar_new();
241
242         c->vbox = gtk_vbox_new(FALSE, 0);
243         gtk_box_pack_start(GTK_BOX(c->vbox), c->location, FALSE, FALSE, 0);
244         gtk_container_add(GTK_CONTAINER(c->vbox), c->scroll);
245         gtk_box_pack_end(GTK_BOX(c->vbox), c->status, FALSE, FALSE, 0);
246
247         gtk_container_add(GTK_CONTAINER(c->win), c->vbox);
248
249         gtk_widget_grab_focus(c->web_view);
250         gtk_widget_show_all(c->win);
251
252         webkit_web_view_load_uri(WEBKIT_WEB_VIEW(c->web_view), uri);
253
254         clients++;
255 }
256
257 gboolean
258 zea_new_client_request(WebKitWebView *web_view, WebKitWebFrame *frame,
259                        WebKitNetworkRequest *request,
260                        WebKitWebNavigationAction *navigation_action,
261                        WebKitWebPolicyDecision *policy_decision,
262                        gpointer user_data)
263 {
264         (void)web_view;
265         (void)frame;
266         (void)navigation_action;
267         (void)user_data;
268
269         webkit_web_policy_decision_ignore(policy_decision);
270         zea_new_client(webkit_network_request_get_uri(request));
271
272         return TRUE;
273 }
274
275 void
276 zea_search(gpointer data, gint direction)
277 {
278         struct Client *c = (struct Client *)data;
279
280         if (search_text == NULL)
281                 return;
282
283         webkit_web_view_search_text(WEBKIT_WEB_VIEW(c->web_view), search_text,
284                                     FALSE, direction == 1, TRUE);
285 }
286
287 void
288 zea_scroll(GtkAdjustment *a, gint step_type, gdouble factor)
289 {
290         gdouble new, lower, upper, step;
291         lower = gtk_adjustment_get_lower(a);
292         upper = gtk_adjustment_get_upper(a) - gtk_adjustment_get_page_size(a) + lower;
293         if (step_type == 0)
294                 step = gtk_adjustment_get_step_increment(a);
295         else
296                 step = gtk_adjustment_get_page_increment(a);
297         new = gtk_adjustment_get_value(a) + factor * step;
298         new = new < lower ? lower : new;
299         new = new > upper ? upper : new;
300         gtk_adjustment_set_value(a, new);
301 }
302
303 void
304 zea_title_changed(GObject *obj, GParamSpec *pspec, gpointer data)
305 {
306         const gchar *t;
307         struct Client *c = (struct Client *)data;
308
309         (void)obj;
310         (void)pspec;
311
312         t = webkit_web_view_get_title(WEBKIT_WEB_VIEW(c->web_view));
313         gtk_window_set_title(GTK_WINDOW(c->win), (t == NULL ? "zea" : t));
314 }
315
316 void
317 zea_uri_changed(GObject *obj, GParamSpec *pspec, gpointer data)
318 {
319         const gchar *t;
320         struct Client *c = (struct Client *)data;
321
322         (void)obj;
323         (void)pspec;
324
325         t = webkit_web_view_get_uri(WEBKIT_WEB_VIEW(c->web_view));
326         gtk_entry_set_text(GTK_ENTRY(c->location), (t == NULL ? "zea" : t));
327 }
328
329 void
330 zea_load_status_changed(GObject *obj, GParamSpec *pspec, gpointer data)
331 {
332         struct Client *c = (struct Client *)data;
333
334         (void)obj;
335         (void)pspec;
336
337         if (webkit_web_view_get_load_status(WEBKIT_WEB_VIEW(c->web_view))
338             == WEBKIT_LOAD_FINISHED)
339         {
340                 gtk_statusbar_pop(GTK_STATUSBAR(c->status), 1);
341                 gtk_statusbar_push(GTK_STATUSBAR(c->status), 1, "Finished.");
342         }
343         else
344         {
345                 gtk_statusbar_pop(GTK_STATUSBAR(c->status), 1);
346                 gtk_statusbar_push(GTK_STATUSBAR(c->status), 1, "Loading...");
347         }
348 }
349
350 void
351 zea_web_view_hover(WebKitWebView *web_view, gchar *title, gchar *uri,
352                    gpointer data)
353 {
354         struct Client *c = (struct Client *)data;
355
356         (void)web_view;
357         (void)title;
358
359         gtk_statusbar_pop(GTK_STATUSBAR(c->status), 0);
360         if (uri != NULL)
361                 gtk_statusbar_push(GTK_STATUSBAR(c->status), 0, uri);
362 }
363
364 gboolean
365 zea_web_view_key(GtkWidget *widget, GdkEvent *event, gpointer data)
366 {
367         struct Client *c = (struct Client *)data;
368
369         (void)widget;
370
371         if (event->type == GDK_KEY_PRESS)
372         {
373                 if (((GdkEventKey *)event)->state & GDK_CONTROL_MASK)
374                 {
375                         if (((GdkEventKey *)event)->keyval == GDK_KEY_o)
376                         {
377                                 gtk_widget_grab_focus(c->location);
378                                 return TRUE;
379                         }
380                         else if (((GdkEventKey *)event)->keyval == GDK_KEY_h)
381                         {
382                                 zea_scroll(gtk_scrolled_window_get_hadjustment(
383                                            GTK_SCROLLED_WINDOW(c->scroll)), 0, -1);
384                                 return TRUE;
385                         }
386                         else if (((GdkEventKey *)event)->keyval == GDK_KEY_j)
387                         {
388                                 zea_scroll(gtk_scrolled_window_get_vadjustment(
389                                            GTK_SCROLLED_WINDOW(c->scroll)), 0, 1);
390                                 return TRUE;
391                         }
392                         else if (((GdkEventKey *)event)->keyval == GDK_KEY_k)
393                         {
394                                 zea_scroll(gtk_scrolled_window_get_vadjustment(
395                                            GTK_SCROLLED_WINDOW(c->scroll)), 0, -1);
396                                 return TRUE;
397                         }
398                         else if (((GdkEventKey *)event)->keyval == GDK_KEY_l)
399                         {
400                                 zea_scroll(gtk_scrolled_window_get_hadjustment(
401                                            GTK_SCROLLED_WINDOW(c->scroll)), 0, 1);
402                                 return TRUE;
403                         }
404                         else if (((GdkEventKey *)event)->keyval == GDK_KEY_f)
405                         {
406                                 zea_scroll(gtk_scrolled_window_get_vadjustment(
407                                            GTK_SCROLLED_WINDOW(c->scroll)), 1, 0.5);
408                                 return TRUE;
409                         }
410                         else if (((GdkEventKey *)event)->keyval == GDK_KEY_b)
411                         {
412                                 zea_scroll(gtk_scrolled_window_get_vadjustment(
413                                            GTK_SCROLLED_WINDOW(c->scroll)), 1, -0.5);
414                                 return TRUE;
415                         }
416                         else if (((GdkEventKey *)event)->keyval == GDK_KEY_n)
417                         {
418                                 zea_search(c, 1);
419                                 return TRUE;
420                         }
421                         else if (((GdkEventKey *)event)->keyval == GDK_KEY_p)
422                         {
423                                 zea_search(c, -1);
424                                 return TRUE;
425                         }
426                         else if (((GdkEventKey *)event)->keyval == GDK_KEY_g)
427                         {
428                                 webkit_web_view_load_uri(WEBKIT_WEB_VIEW(c->web_view),
429                                                          first_uri);
430                                 return TRUE;
431                         }
432                 }
433                 else if (((GdkEventKey *)event)->keyval == GDK_KEY_Escape)
434                 {
435                         webkit_web_view_stop_loading(WEBKIT_WEB_VIEW(c->web_view));
436                         gtk_statusbar_pop(GTK_STATUSBAR(c->status), 1);
437                         gtk_statusbar_push(GTK_STATUSBAR(c->status), 1, "Aborted.");
438                 }
439         }
440
441         return FALSE;
442 }
443
444 int
445 main(int argc, char **argv)
446 {
447         int opt, i;
448
449         gtk_init(&argc, &argv);
450
451         while ((opt = getopt(argc, argv, "z:e:R")) != -1)
452         {
453                 switch (opt)
454                 {
455                         case 'z':
456                                 global_zoom = atof(optarg);
457                                 break;
458                         case 'e':
459                                 embed = atol(optarg);
460                                 break;
461                         case 'R':
462                                 show_all_requests = TRUE;
463                                 break;
464                 }
465         }
466
467         if (optind >= argc)
468         {
469                 fprintf(stderr, "Usage: zea [OPTIONS] <URI>\n");
470                 exit(EXIT_FAILURE);
471         }
472
473         first_uri = g_strdup(argv[optind]);
474         for (i = optind; i < argc; i++)
475                 zea_new_client(argv[i]);
476         gtk_main();
477         exit(EXIT_SUCCESS);
478 }