]> git.armaanb.net Git - chorizo.git/blob - zea.c
29a78411fdcad7618458f594cf778a6a2a70a4c2
[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_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,
19                                     gpointer data);
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);
31
32
33 static Window embed = 0;
34 static gint clients = 0;
35 static gdouble global_zoom = 1.0;
36
37
38 struct Client
39 {
40         GtkWidget *win;
41         GtkWidget *vbox;
42         GtkWidget *location;
43         GtkWidget *status;
44         GtkWidget *scroll;
45         GtkWidget *web_view;
46 };
47
48
49 void
50 zea_destroy_client(GtkWidget *obj, gpointer data)
51 {
52         struct Client *c = (struct Client *)data;
53
54         (void)obj;
55
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);
63         free(c);
64
65         clients--;
66         if (clients == 0)
67                 gtk_main_quit();
68 }
69
70 gboolean
71 zea_do_download(WebKitWebView *web_view, WebKitDownload *download, gpointer data)
72 {
73         const gchar *uri;
74         char id[16] = "";
75         gint ret;
76
77         (void)web_view;
78         (void)data;
79
80         uri = webkit_download_get_uri(download);
81         if (fork() == 0)
82         {
83                 chdir(DOWNLOAD_DIR);
84                 if (embed == 0)
85                         ret = execlp("xterm", "xterm", "-hold", "-e", "wget", uri, NULL);
86                 else
87                 {
88                         if (snprintf(id, 16, "%ld", embed) >= 16)
89                         {
90                                 fprintf(stderr, "zea: id for xterm embed truncated!\n");
91                                 exit(EXIT_FAILURE);
92                         }
93                         ret = execlp("xterm", "xterm", "-hold", "-into", id, "-e", "wget",
94                                      uri, NULL);
95                 }
96
97                 if (ret == -1)
98                 {
99                         fprintf(stderr, "zea: exec'ing xterm for download");
100                         perror(" failed");
101                         exit(EXIT_FAILURE);
102                 }
103         }
104
105         return FALSE;
106 }
107
108 gboolean
109 zea_download_request(WebKitWebView *web_view, WebKitWebFrame *frame,
110                      WebKitNetworkRequest *request, gchar *mime_type,
111                      WebKitWebPolicyDecision *policy_decision,
112                      gpointer data)
113 {
114         (void)frame;
115         (void)request;
116         (void)data;
117
118         if (!webkit_web_view_can_show_mime_type(web_view, mime_type))
119         {
120                 webkit_web_policy_decision_download(policy_decision);
121                 return TRUE;
122         }
123         return FALSE;
124 }
125
126 gboolean
127 zea_location_key(GtkWidget *widget, GdkEvent *event, gpointer data)
128 {
129         struct Client *c = (struct Client *)data;
130
131         (void)widget;
132
133         if (event->type == GDK_KEY_PRESS)
134         {
135                 if (((GdkEventKey *)event)->keyval == GDK_KEY_Return)
136                 {
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)));
140                         return TRUE;
141                 }
142         }
143
144         return FALSE;
145 }
146
147 void
148 zea_new_client(const gchar *uri)
149 {
150         struct Client *c = malloc(sizeof(struct Client));
151         if (!c)
152         {
153                 fprintf(stderr, "zea: fatal: malloc failed\n");
154                 exit(EXIT_FAILURE);
155         }
156
157         if (embed == 0)
158         {
159                 c->win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
160         }
161         else
162         {
163                 c->win = gtk_plug_new(embed);
164         }
165
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);
170
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");
174
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);
196
197         c->scroll = gtk_scrolled_window_new(NULL, NULL);
198
199         gtk_container_add(GTK_CONTAINER(c->scroll), c->web_view);
200
201         c->location = gtk_entry_new();
202         g_signal_connect(G_OBJECT(c->location), "key-press-event",
203                          G_CALLBACK(zea_location_key), c);
204
205         c->status = gtk_statusbar_new();
206
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);
211
212         gtk_container_add(GTK_CONTAINER(c->win), c->vbox);
213
214         gtk_widget_grab_focus(c->web_view);
215         gtk_widget_show_all(c->win);
216
217         webkit_web_view_load_uri(WEBKIT_WEB_VIEW(c->web_view), uri);
218
219         clients++;
220 }
221
222 gboolean
223 zea_new_client_request(WebKitWebView *web_view, WebKitWebFrame *frame,
224                        WebKitNetworkRequest *request,
225                        WebKitWebNavigationAction *navigation_action,
226                        WebKitWebPolicyDecision *policy_decision,
227                        gpointer user_data)
228 {
229         (void)web_view;
230         (void)frame;
231         (void)navigation_action;
232         (void)user_data;
233
234         webkit_web_policy_decision_ignore(policy_decision);
235         zea_new_client(webkit_network_request_get_uri(request));
236
237         return TRUE;
238 }
239
240 void
241 zea_scroll(GtkAdjustment *a, gint step_type, gdouble factor)
242 {
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;
246         if (step_type == 0)
247                 step = gtk_adjustment_get_step_increment(a);
248         else
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);
254 }
255
256 void
257 zea_title_changed(GObject *obj, GParamSpec *pspec, gpointer data)
258 {
259         const gchar *t;
260         struct Client *c = (struct Client *)data;
261
262         (void)obj;
263         (void)pspec;
264
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));
267 }
268
269 void
270 zea_uri_changed(GObject *obj, GParamSpec *pspec, gpointer data)
271 {
272         const gchar *t;
273         struct Client *c = (struct Client *)data;
274
275         (void)obj;
276         (void)pspec;
277
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));
280 }
281
282 void
283 zea_load_status_changed(GObject *obj, GParamSpec *pspec, gpointer data)
284 {
285         struct Client *c = (struct Client *)data;
286
287         (void)obj;
288         (void)pspec;
289
290         if (webkit_web_view_get_load_status(WEBKIT_WEB_VIEW(c->web_view))
291             == WEBKIT_LOAD_FINISHED)
292         {
293                 gtk_statusbar_pop(GTK_STATUSBAR(c->status), 1);
294                 gtk_statusbar_push(GTK_STATUSBAR(c->status), 1, "Finished.");
295         }
296         else
297         {
298                 gtk_statusbar_pop(GTK_STATUSBAR(c->status), 1);
299                 gtk_statusbar_push(GTK_STATUSBAR(c->status), 1, "Loading...");
300         }
301 }
302
303 void
304 zea_web_view_hover(WebKitWebView *web_view, gchar *title, gchar *uri,
305                    gpointer data)
306 {
307         struct Client *c = (struct Client *)data;
308
309         (void)web_view;
310         (void)title;
311
312         gtk_statusbar_pop(GTK_STATUSBAR(c->status), 0);
313         if (uri != NULL)
314                 gtk_statusbar_push(GTK_STATUSBAR(c->status), 0, uri);
315 }
316
317 gboolean
318 zea_web_view_key(GtkWidget *widget, GdkEvent *event, gpointer data)
319 {
320         struct Client *c = (struct Client *)data;
321
322         (void)widget;
323
324         if (event->type == GDK_KEY_PRESS)
325         {
326                 if (((GdkEventKey *)event)->state & GDK_CONTROL_MASK)
327                 {
328                         if (((GdkEventKey *)event)->keyval == GDK_KEY_o)
329                         {
330                                 gtk_widget_grab_focus(c->location);
331                                 return TRUE;
332                         }
333                         else if (((GdkEventKey *)event)->keyval == GDK_KEY_h)
334                         {
335                                 zea_scroll(gtk_scrolled_window_get_hadjustment(
336                                            GTK_SCROLLED_WINDOW(c->scroll)), 0, -1);
337                                 return TRUE;
338                         }
339                         else if (((GdkEventKey *)event)->keyval == GDK_KEY_j)
340                         {
341                                 zea_scroll(gtk_scrolled_window_get_vadjustment(
342                                            GTK_SCROLLED_WINDOW(c->scroll)), 0, 1);
343                                 return TRUE;
344                         }
345                         else if (((GdkEventKey *)event)->keyval == GDK_KEY_k)
346                         {
347                                 zea_scroll(gtk_scrolled_window_get_vadjustment(
348                                            GTK_SCROLLED_WINDOW(c->scroll)), 0, -1);
349                                 return TRUE;
350                         }
351                         else if (((GdkEventKey *)event)->keyval == GDK_KEY_l)
352                         {
353                                 zea_scroll(gtk_scrolled_window_get_hadjustment(
354                                            GTK_SCROLLED_WINDOW(c->scroll)), 0, 1);
355                                 return TRUE;
356                         }
357                         else if (((GdkEventKey *)event)->keyval == GDK_KEY_f)
358                         {
359                                 zea_scroll(gtk_scrolled_window_get_vadjustment(
360                                            GTK_SCROLLED_WINDOW(c->scroll)), 1, 0.5);
361                                 return TRUE;
362                         }
363                         if (((GdkEventKey *)event)->keyval == GDK_KEY_b)
364                         {
365                                 zea_scroll(gtk_scrolled_window_get_vadjustment(
366                                            GTK_SCROLLED_WINDOW(c->scroll)), 1, -0.5);
367                                 return TRUE;
368                         }
369                 }
370                 else if (((GdkEventKey *)event)->keyval == GDK_KEY_Escape)
371                 {
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.");
375                 }
376         }
377
378         return FALSE;
379 }
380
381 int
382 main(int argc, char **argv)
383 {
384         int opt;
385
386         gtk_init(&argc, &argv);
387
388         while ((opt = getopt(argc, argv, "z:e:")) != -1)
389         {
390                 switch (opt)
391                 {
392                         case 'z':
393                                 global_zoom = atof(optarg);
394                                 break;
395                         case 'e':
396                                 embed = atol(optarg);
397                                 break;
398                 }
399         }
400
401         if (optind >= argc)
402         {
403                 fprintf(stderr, "Usage: zea [OPTIONS] <URI>\n");
404                 exit(EXIT_FAILURE);
405         }
406
407         zea_new_client(argv[optind]);
408         gtk_main();
409         exit(EXIT_SUCCESS);
410 }