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