]> git.armaanb.net Git - chorizo.git/blob - browser.c
Built-in download feature
[chorizo.git] / browser.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <sys/types.h>
4 #include <sys/stat.h>
5 #include <fcntl.h>
6 #include <string.h>
7
8 #include <gtk/gtk.h>
9 #include <gdk/gdkx.h>
10 #include <gdk/gdkkeysyms.h>
11 #include <gio/gio.h>
12 #include <webkit/webkit.h>
13
14
15 static void adblock(WebKitWebView *, WebKitWebFrame *, WebKitWebResource *,
16                     WebKitNetworkRequest *, WebKitNetworkResponse *, gpointer);
17 static void adblock_load(void);
18 static void client_destroy(GtkWidget *obj, gpointer data);
19 static gboolean client_destroy_request(WebKitWebView *, gpointer);
20 static WebKitWebView *client_new(const gchar *uri);
21 static WebKitWebView *client_new_request(WebKitWebView *, WebKitWebFrame *,
22                                          gpointer);
23 static void cooperation_setup(void);
24 static void changed_load_status(GObject *obj, GParamSpec *pspec,
25                                 gpointer data);
26 static void changed_title(GObject *, GParamSpec *, gpointer);
27 static void changed_uri(GObject *, GParamSpec *, gpointer);
28 static gboolean download_handle(WebKitWebView *, WebKitDownload *, gpointer);
29 static gboolean download_request(WebKitWebView *, WebKitWebFrame *,
30                                  WebKitNetworkRequest *, gchar *,
31                                  WebKitWebPolicyDecision *, gpointer);
32 static gchar *ensure_url_scheme(const gchar *);
33 static void grab_environment_configuration(void);
34 static void hover_web_view(WebKitWebView *, gchar *, gchar *, gpointer);
35 static gboolean key_location(GtkWidget *, GdkEvent *, gpointer);
36 static gboolean key_web_view(GtkWidget *, GdkEvent *, gpointer);
37 static gboolean remote_msg(GIOChannel *, GIOCondition, gpointer);
38 static void search(gpointer, gint);
39 static void scroll(GtkAdjustment *, gint, gdouble);
40 static Window tabbed_launch(void);
41 static void usage(void);
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 static gchar *accepted_language = "en-US";
56 static GSList *adblock_patterns = NULL;
57 static gint clients = 0;
58 static gboolean cooperative_alone = TRUE;
59 static gboolean cooperative_instances = TRUE;
60 static int cooperative_pipe_fp = 0;
61 static gchar *download_dir = "/tmp";
62 static Window embed = 0;
63 static gchar *first_uri = NULL;
64 static gdouble global_zoom = 1.0;
65 static gboolean language_is_set = FALSE;
66 static gchar *search_text = NULL;
67 static gboolean show_all_requests = FALSE;
68 static gboolean tabbed_automagic = TRUE;
69
70
71 void
72 adblock(WebKitWebView *web_view, WebKitWebFrame *frame,
73         WebKitWebResource *resource, WebKitNetworkRequest *request,
74         WebKitNetworkResponse *response, gpointer data)
75 {
76         GSList *it = adblock_patterns;
77         const gchar *uri;
78
79         (void)web_view;
80         (void)frame;
81         (void)resource;
82         (void)response;
83         (void)data;
84
85         uri = webkit_network_request_get_uri(request);
86         if (show_all_requests)
87                 fprintf(stderr, "   -> %s\n", uri);
88
89         while (it)
90         {
91                 if (g_regex_match((GRegex *)(it->data), uri, 0, NULL))
92                 {
93                         webkit_network_request_set_uri(request, "about:blank");
94                         if (show_all_requests)
95                                 fprintf(stderr, "            BLOCKED!\n");
96                         return;
97                 }
98                 it = g_slist_next(it);
99         }
100 }
101
102 void
103 adblock_load(void)
104 {
105         GRegex *re = NULL;
106         GError *err = NULL;
107         GIOChannel *channel = NULL;
108         gchar *path = NULL;
109         gchar *buf = NULL;
110
111         path = g_build_filename(g_get_user_config_dir(), __NAME__, "adblock.black",
112                                 NULL);
113         channel = g_io_channel_new_file(path, "r", &err);
114         if (channel != NULL)
115         {
116                 while (g_io_channel_read_line(channel, &buf, NULL, NULL, NULL)
117                        == G_IO_STATUS_NORMAL)
118                 {
119                         g_strstrip(buf);
120                         re = g_regex_new(buf,
121                                          G_REGEX_CASELESS | G_REGEX_OPTIMIZE,
122                                          G_REGEX_MATCH_PARTIAL, &err);
123                         if (err != NULL)
124                         {
125                                 fprintf(stderr, __NAME__": Could not compile regex: %s\n", buf);
126                                 g_error_free(err);
127                                 err = NULL;
128                         }
129                         adblock_patterns = g_slist_append(adblock_patterns, re);
130
131                         g_free(buf);
132                 }
133         }
134         g_free(path);
135 }
136
137 void
138 client_destroy(GtkWidget *obj, gpointer data)
139 {
140         struct Client *c = (struct Client *)data;
141
142         (void)obj;
143         (void)data;
144
145         free(c);
146         clients--;
147
148         if (clients == 0)
149                 gtk_main_quit();
150 }
151
152 gboolean
153 client_destroy_request(WebKitWebView *web_view, gpointer data)
154 {
155         struct Client *c = (struct Client *)data;
156
157         (void)web_view;
158
159         gtk_widget_destroy(c->win);
160
161         return TRUE;
162 }
163
164 WebKitWebView *
165 client_new(const gchar *uri)
166 {
167         struct Client *c;
168         gchar *f;
169
170         if (uri != NULL && cooperative_instances && !cooperative_alone)
171         {
172                 write(cooperative_pipe_fp, uri, strlen(uri));
173                 write(cooperative_pipe_fp, "\n", 1);
174                 return NULL;
175         }
176
177         c = malloc(sizeof(struct Client));
178         if (!c)
179         {
180                 fprintf(stderr, __NAME__": fatal: malloc failed\n");
181                 exit(EXIT_FAILURE);
182         }
183
184         c->win = NULL;
185         if (embed != 0)
186         {
187                 c->win = gtk_plug_new(embed);
188                 if (!gtk_plug_get_embedded(GTK_PLUG(c->win)))
189                 {
190                         fprintf(stderr, __NAME__": Can't plug-in to XID %ld.\n", embed);
191                         gtk_widget_destroy(c->win);
192                         c->win = NULL;
193                         embed = 0;
194                 }
195         }
196
197         if (c->win == NULL)
198         {
199                 c->win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
200                 gtk_window_set_wmclass(GTK_WINDOW(c->win), __NAME__, __NAME_CAPITALIZED__);
201         }
202
203         /* When using Gtk2, it only shows a white area when run in suckless'
204          * tabbed. It appears we need to set a default window size for this
205          * to work. This is not needed when using Gtk3. */
206         gtk_window_set_default_size(GTK_WINDOW(c->win), 1024, 768);
207
208         g_signal_connect(G_OBJECT(c->win), "destroy", G_CALLBACK(client_destroy), c);
209         gtk_window_set_title(GTK_WINDOW(c->win), __NAME__);
210
211         c->web_view = webkit_web_view_new();
212
213         /* XXX I really do want to enable this option. However, I get
214          * reproducable crashes with it enabled. I've seen bug reports from
215          * 2010 about this... WebKit crashes in libpixman, so maybe it's not
216          * a WebKit issue.
217          * Yeah, well. I'll turn it off for now. */
218         /*webkit_web_view_set_full_content_zoom(WEBKIT_WEB_VIEW(c->web_view), TRUE);*/
219
220         webkit_web_view_set_zoom_level(WEBKIT_WEB_VIEW(c->web_view), global_zoom);
221         g_signal_connect(G_OBJECT(c->web_view), "notify::title",
222                          G_CALLBACK(changed_title), c);
223         g_signal_connect(G_OBJECT(c->web_view), "notify::uri",
224                          G_CALLBACK(changed_uri), c);
225         g_signal_connect(G_OBJECT(c->web_view), "notify::load-status",
226                          G_CALLBACK(changed_load_status), c);
227         g_signal_connect(G_OBJECT(c->web_view), "create-web-view",
228                          G_CALLBACK(client_new_request), NULL);
229         g_signal_connect(G_OBJECT(c->web_view), "close-web-view",
230                          G_CALLBACK(client_destroy_request), c);
231         g_signal_connect(G_OBJECT(c->web_view),
232                          "mime-type-policy-decision-requested",
233                          G_CALLBACK(download_request), NULL);
234         g_signal_connect(G_OBJECT(c->web_view), "download-requested",
235                          G_CALLBACK(download_handle), NULL);
236         g_signal_connect(G_OBJECT(c->web_view), "key-press-event",
237                          G_CALLBACK(key_web_view), c);
238         g_signal_connect(G_OBJECT(c->web_view), "button-press-event",
239                          G_CALLBACK(key_web_view), c);
240         g_signal_connect(G_OBJECT(c->web_view), "hovering-over-link",
241                          G_CALLBACK(hover_web_view), c);
242         g_signal_connect(G_OBJECT(c->web_view), "resource-request-starting",
243                          G_CALLBACK(adblock), NULL);
244
245         if (!language_is_set)
246         {
247                 g_object_set(webkit_get_default_session(), "accept-language",
248                              accepted_language, NULL);
249                 language_is_set = TRUE;
250         }
251
252         c->scroll = gtk_scrolled_window_new(NULL, NULL);
253
254         gtk_container_add(GTK_CONTAINER(c->scroll), c->web_view);
255
256         c->location = gtk_entry_new();
257         g_signal_connect(G_OBJECT(c->location), "key-press-event",
258                          G_CALLBACK(key_location), c);
259
260         c->status = gtk_statusbar_new();
261         gtk_statusbar_set_has_resize_grip(GTK_STATUSBAR(c->status), FALSE);
262
263         c->vbox = gtk_vbox_new(FALSE, 2);
264         gtk_box_pack_start(GTK_BOX(c->vbox), c->location, FALSE, FALSE, 0);
265         gtk_container_add(GTK_CONTAINER(c->vbox), c->scroll);
266         gtk_box_pack_end(GTK_BOX(c->vbox), c->status, FALSE, FALSE, 0);
267
268         gtk_container_add(GTK_CONTAINER(c->win), c->vbox);
269
270         gtk_widget_grab_focus(c->web_view);
271         gtk_widget_show_all(c->win);
272
273         if (uri != NULL)
274         {
275                 f = ensure_url_scheme(uri);
276                 if (show_all_requests)
277                         fprintf(stderr, "====> %s\n", uri);
278                 webkit_web_view_load_uri(WEBKIT_WEB_VIEW(c->web_view), f);
279                 g_free(f);
280         }
281
282         clients++;
283
284         return WEBKIT_WEB_VIEW(c->web_view);
285 }
286
287 WebKitWebView *
288 client_new_request(WebKitWebView *web_view, WebKitWebFrame *frame, gpointer data)
289 {
290         (void)web_view;
291         (void)frame;
292         (void)data;
293
294         return client_new(NULL);
295 }
296
297 void
298 cooperation_setup(void)
299 {
300         GIOChannel *towatch;
301         gchar *fifopath;
302
303         fifopath = g_build_filename(g_get_user_runtime_dir(), __NAME__".fifo", NULL);
304
305         if (!g_file_test(fifopath, G_FILE_TEST_EXISTS))
306                 mkfifo(fifopath, 0600);
307
308         cooperative_pipe_fp = open(fifopath, O_WRONLY | O_NONBLOCK);
309         if (!cooperative_pipe_fp)
310         {
311                 fprintf(stderr, __NAME__": Can't open FIFO at all.\n");
312         }
313         else
314         {
315                 if (write(cooperative_pipe_fp, "", 0) == -1)
316                 {
317                         /* Could not do an empty write to the FIFO which means there's
318                          * no one listening. */
319                         close(cooperative_pipe_fp);
320                         towatch = g_io_channel_new_file(fifopath, "r+", NULL);
321                         g_io_add_watch(towatch, G_IO_IN, (GIOFunc)remote_msg, NULL);
322                 }
323                 else
324                         cooperative_alone = FALSE;
325         }
326
327         g_free(fifopath);
328 }
329
330 void
331 changed_load_status(GObject *obj, GParamSpec *pspec, gpointer data)
332 {
333         struct Client *c = (struct Client *)data;
334
335         (void)obj;
336         (void)pspec;
337
338         if (webkit_web_view_get_load_status(WEBKIT_WEB_VIEW(c->web_view))
339             == WEBKIT_LOAD_FINISHED)
340         {
341                 gtk_statusbar_pop(GTK_STATUSBAR(c->status), 1);
342                 gtk_statusbar_push(GTK_STATUSBAR(c->status), 1, "Finished.");
343         }
344         else
345         {
346                 gtk_statusbar_pop(GTK_STATUSBAR(c->status), 1);
347                 gtk_statusbar_push(GTK_STATUSBAR(c->status), 1, "Loading...");
348         }
349 }
350
351 void
352 changed_title(GObject *obj, GParamSpec *pspec, gpointer data)
353 {
354         const gchar *t;
355         struct Client *c = (struct Client *)data;
356
357         (void)obj;
358         (void)pspec;
359
360         t = webkit_web_view_get_title(WEBKIT_WEB_VIEW(c->web_view));
361         gtk_window_set_title(GTK_WINDOW(c->win), (t == NULL ? __NAME__ : t));
362 }
363
364 void
365 changed_uri(GObject *obj, GParamSpec *pspec, gpointer data)
366 {
367         const gchar *t;
368         struct Client *c = (struct Client *)data;
369
370         (void)obj;
371         (void)pspec;
372
373         t = webkit_web_view_get_uri(WEBKIT_WEB_VIEW(c->web_view));
374         gtk_entry_set_text(GTK_ENTRY(c->location), (t == NULL ? __NAME__ : t));
375 }
376
377 gboolean
378 download_handle(WebKitWebView *web_view, WebKitDownload *download, gpointer data)
379 {
380         gchar *path, *path2 = NULL, *uri;
381         gboolean ret;
382         int suffix = 1;
383
384         (void)web_view;
385         (void)data;
386
387         path = g_build_filename(download_dir,
388                                 webkit_download_get_suggested_filename(download),
389                                 NULL);
390         path2 = g_strdup(path);
391         while (g_file_test(path2, G_FILE_TEST_EXISTS) && suffix < 1000)
392         {
393                 g_free(path2);
394
395                 path2 = g_strdup_printf("%s.%d", path, suffix);
396                 suffix++;
397         }
398
399         if (suffix == 1000)
400         {
401                 fprintf(stderr, __NAME__": Suffix reached limit for download.\n");
402                 ret = FALSE;
403         }
404         else
405         {
406                 uri = g_filename_to_uri(path2, NULL, NULL);
407                 webkit_download_set_destination_uri(download, uri);
408                 ret = TRUE;
409                 g_free(uri);
410         }
411
412         g_free(path);
413         g_free(path2);
414
415         return ret;
416 }
417
418 gboolean
419 download_request(WebKitWebView *web_view, WebKitWebFrame *frame,
420                  WebKitNetworkRequest *request, gchar *mime_type,
421                  WebKitWebPolicyDecision *policy_decision, gpointer data)
422 {
423         (void)frame;
424         (void)request;
425         (void)data;
426
427         if (!webkit_web_view_can_show_mime_type(web_view, mime_type))
428         {
429                 webkit_web_policy_decision_download(policy_decision);
430                 return TRUE;
431         }
432         return FALSE;
433 }
434
435 gchar *
436 ensure_url_scheme(const gchar *t)
437 {
438         gchar *f;
439
440         f = g_ascii_strdown(t, -1);
441         if (!g_str_has_prefix(f, "http:") &&
442             !g_str_has_prefix(f, "https:") &&
443             !g_str_has_prefix(f, "file:") &&
444             !g_str_has_prefix(f, "about:"))
445         {
446                 g_free(f);
447                 f = g_strdup_printf("http://%s", t);
448                 return f;
449         }
450         else
451                 return g_strdup(t);
452 }
453
454 void
455 grab_environment_configuration(void)
456 {
457         const gchar *e;
458
459         e = g_getenv(__NAME_UPPERCASE__"_ACCEPTED_LANGUAGE");
460         if (e != NULL)
461                 accepted_language = g_strdup(e);
462
463         e = g_getenv(__NAME_UPPERCASE__"_DOWNLOAD_DIR");
464         if (e != NULL)
465                 download_dir = g_strdup(e);
466
467         e = g_getenv(__NAME_UPPERCASE__"_ZOOM");
468         if (e != NULL)
469                 global_zoom = atof(e);
470 }
471
472 void
473 hover_web_view(WebKitWebView *web_view, gchar *title, gchar *uri, gpointer data)
474 {
475         struct Client *c = (struct Client *)data;
476
477         (void)web_view;
478         (void)title;
479
480         gtk_statusbar_pop(GTK_STATUSBAR(c->status), 0);
481         if (uri != NULL)
482                 gtk_statusbar_push(GTK_STATUSBAR(c->status), 0, uri);
483 }
484
485 gboolean
486 key_location(GtkWidget *widget, GdkEvent *event, gpointer data)
487 {
488         struct Client *c = (struct Client *)data;
489         const gchar *t;
490         gchar *f;
491
492         (void)widget;
493
494         if (event->type == GDK_KEY_PRESS)
495         {
496                 switch (((GdkEventKey *)event)->keyval)
497                 {
498                         case GDK_KEY_Return:
499                                 gtk_widget_grab_focus(c->web_view);
500                                 t = gtk_entry_get_text(GTK_ENTRY(c->location));
501                                 if (t != NULL && t[0] == '/')
502                                 {
503                                         if (search_text != NULL)
504                                                 g_free(search_text);
505                                         search_text = g_strdup(t + 1);  /* XXX whacky */
506                                         search(c, 1);
507                                 }
508                                 else
509                                 {
510                                         f = ensure_url_scheme(t);
511                                         if (show_all_requests)
512                                                 fprintf(stderr, "====> %s\n", f);
513                                         webkit_web_view_load_uri(WEBKIT_WEB_VIEW(c->web_view), f);
514                                         g_free(f);
515                                 }
516                                 return TRUE;
517                         case GDK_KEY_Escape:
518                                 t = webkit_web_view_get_uri(WEBKIT_WEB_VIEW(c->web_view));
519                                 gtk_entry_set_text(GTK_ENTRY(c->location),
520                                                    (t == NULL ? __NAME__ : t));
521                                 return TRUE;
522                 }
523         }
524
525         return FALSE;
526 }
527
528 gboolean
529 key_web_view(GtkWidget *widget, GdkEvent *event, gpointer data)
530 {
531         struct Client *c = (struct Client *)data;
532         WebKitHitTestResultContext ht_context;
533         WebKitHitTestResult *ht_result = NULL;
534         gchar *ht_uri = NULL, *f;
535
536         (void)widget;
537
538         if (event->type == GDK_KEY_PRESS)
539         {
540                 if (((GdkEventKey *)event)->state & GDK_CONTROL_MASK)
541                 {
542                         switch (((GdkEventKey *)event)->keyval)
543                         {
544                                 case GDK_KEY_o:
545                                         gtk_widget_grab_focus(c->location);
546                                         return TRUE;
547                                 case GDK_KEY_h:
548                                         scroll(gtk_scrolled_window_get_hadjustment(
549                                                GTK_SCROLLED_WINDOW(c->scroll)), 0, -1);
550                                         return TRUE;
551                                 case GDK_KEY_j:
552                                         scroll(gtk_scrolled_window_get_vadjustment(
553                                                GTK_SCROLLED_WINDOW(c->scroll)), 0, 1);
554                                         return TRUE;
555                                 case GDK_KEY_k:
556                                         scroll(gtk_scrolled_window_get_vadjustment(
557                                                GTK_SCROLLED_WINDOW(c->scroll)), 0, -1);
558                                         return TRUE;
559                                 case GDK_KEY_l:
560                                         scroll(gtk_scrolled_window_get_hadjustment(
561                                                GTK_SCROLLED_WINDOW(c->scroll)), 0, 1);
562                                         return TRUE;
563                                 case GDK_KEY_f:
564                                         scroll(gtk_scrolled_window_get_vadjustment(
565                                                GTK_SCROLLED_WINDOW(c->scroll)), 1, 0.5);
566                                         return TRUE;
567                                 case GDK_KEY_b:
568                                         scroll(gtk_scrolled_window_get_vadjustment(
569                                                GTK_SCROLLED_WINDOW(c->scroll)), 1, -0.5);
570                                         return TRUE;
571                                 case GDK_KEY_n:
572                                         search(c, 1);
573                                         return TRUE;
574                                 case GDK_KEY_p:
575                                         search(c, -1);
576                                         return TRUE;
577                                 case GDK_KEY_g:
578                                         f = ensure_url_scheme(first_uri);
579                                         if (show_all_requests)
580                                                 fprintf(stderr, "====> %s\n", f);
581                                         webkit_web_view_load_uri(WEBKIT_WEB_VIEW(c->web_view), f);
582                                         g_free(f);
583                                         return TRUE;
584                         }
585                 }
586                 else if (((GdkEventKey *)event)->keyval == GDK_KEY_Escape)
587                 {
588                         webkit_web_view_stop_loading(WEBKIT_WEB_VIEW(c->web_view));
589                         gtk_statusbar_pop(GTK_STATUSBAR(c->status), 1);
590                         gtk_statusbar_push(GTK_STATUSBAR(c->status), 1, "Aborted.");
591                 }
592         }
593         else if (event->type == GDK_BUTTON_PRESS)
594         {
595                 switch (((GdkEventButton *)event)->button)
596                 {
597                         case 2:
598                                 ht_result = webkit_web_view_get_hit_test_result(
599                                                                    WEBKIT_WEB_VIEW(c->web_view),
600                                                                        (GdkEventButton *)event);
601                                 g_object_get(ht_result, "context", &ht_context, NULL);
602                                 if (ht_context & WEBKIT_HIT_TEST_RESULT_CONTEXT_LINK)
603                                 {
604                                         g_object_get(ht_result, "link-uri", &ht_uri, NULL);
605                                         client_new(ht_uri);
606                                         g_object_unref(ht_result);
607                                         return TRUE;
608                                 }
609                                 g_object_unref(ht_result);
610                                 return FALSE;
611                         case 8:
612                                 webkit_web_view_go_back(WEBKIT_WEB_VIEW(c->web_view));
613                                 return TRUE;
614                         case 9:
615                                 webkit_web_view_go_forward(WEBKIT_WEB_VIEW(c->web_view));
616                                 return TRUE;
617                 }
618         }
619
620         return FALSE;
621 }
622
623 gboolean
624 remote_msg(GIOChannel *channel, GIOCondition condition, gpointer data)
625 {
626         gchar *uri = NULL;
627
628         (void)condition;
629         (void)data;
630
631         g_io_channel_read_line(channel, &uri, NULL, NULL, NULL);
632         if (uri)
633         {
634                 g_strstrip(uri);
635                 client_new(uri);
636                 g_free(uri);
637         }
638         return TRUE;
639 }
640
641 void
642 search(gpointer data, gint direction)
643 {
644         struct Client *c = (struct Client *)data;
645
646         if (search_text == NULL)
647                 return;
648
649         webkit_web_view_search_text(WEBKIT_WEB_VIEW(c->web_view), search_text,
650                                     FALSE, direction == 1, TRUE);
651 }
652
653 void
654 scroll(GtkAdjustment *a, gint step_type, gdouble factor)
655 {
656         gdouble new, lower, upper, step;
657         lower = gtk_adjustment_get_lower(a);
658         upper = gtk_adjustment_get_upper(a) - gtk_adjustment_get_page_size(a) + lower;
659         if (step_type == 0)
660                 step = gtk_adjustment_get_step_increment(a);
661         else
662                 step = gtk_adjustment_get_page_increment(a);
663         new = gtk_adjustment_get_value(a) + factor * step;
664         new = new < lower ? lower : new;
665         new = new > upper ? upper : new;
666         gtk_adjustment_set_value(a, new);
667 }
668
669 Window
670 tabbed_launch(void)
671 {
672         gint tabbed_stdout;
673         GIOChannel *tabbed_stdout_channel;
674         GError *err = NULL;
675         gchar *output = NULL;
676         char *argv[] = { "tabbed", "-c", "-d", "-p", "s1", "-n", __NAME__, NULL };
677         Window plug_into;
678
679         if (!g_spawn_async_with_pipes(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL,
680                                       NULL, NULL, NULL, &tabbed_stdout, NULL,
681                                       &err))
682         {
683                 fprintf(stderr, __NAME__": Could not launch tabbed: %s\n", err->message);
684                 g_error_free(err);
685                 return 0;
686         }
687
688         tabbed_stdout_channel = g_io_channel_unix_new(tabbed_stdout);
689         g_io_channel_read_line(tabbed_stdout_channel, &output, NULL, NULL, NULL);
690         if (output == NULL)
691         {
692                 fprintf(stderr, __NAME__": Could not read XID from tabbed\n");
693                 return 0;
694         }
695
696         g_io_channel_shutdown(tabbed_stdout_channel, FALSE, NULL);
697
698         g_strstrip(output);
699         plug_into = strtol(output, NULL, 16);
700         g_free(output);
701         return plug_into;
702 }
703
704 void
705 usage(void)
706 {
707         fprintf(stderr, "Usage: "__NAME__" [OPTION]... <URI>...\n");
708         exit(EXIT_FAILURE);
709 }
710
711
712 int
713 main(int argc, char **argv)
714 {
715         int opt, i;
716
717         gtk_init(&argc, &argv);
718
719         grab_environment_configuration();
720
721         while ((opt = getopt(argc, argv, "e:rCT")) != -1)
722         {
723                 switch (opt)
724                 {
725                         case 'e':
726                                 embed = atol(optarg);
727                                 tabbed_automagic = FALSE;
728                                 break;
729                         case 'r':
730                                 show_all_requests = TRUE;
731                                 break;
732                         case 'C':
733                                 cooperative_instances = FALSE;
734                                 break;
735                         case 'T':
736                                 tabbed_automagic = FALSE;
737                                 break;
738                         default:
739                                 usage();
740                 }
741         }
742
743         if (optind >= argc)
744                 usage();
745
746         adblock_load();
747         cooperation_setup();
748
749         if (tabbed_automagic && !(cooperative_instances && !cooperative_alone))
750                 embed = tabbed_launch();
751
752         first_uri = g_strdup(argv[optind]);
753         for (i = optind; i < argc; i++)
754                 client_new(argv[i]);
755         if (!cooperative_instances || cooperative_alone)
756                 gtk_main();
757         exit(EXIT_SUCCESS);
758 }