Intial commit
[profile/ivi/w3m.git] / w3mimg / x11 / x11_w3mimg.c
1 /* $Id: x11_w3mimg.c,v 1.29 2004/11/08 17:14:06 ukai Exp $ */
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include <ctype.h>
6 #include "config.h"
7
8 #if defined(USE_IMLIB)
9 #include <Imlib.h>
10 #elif defined(USE_IMLIB2)
11 #include <X11/Xlib.h>
12 #include <X11/Xutil.h>
13 #include <Imlib2.h>
14 #elif defined(USE_GDKPIXBUF)
15 #if defined(USE_GTK2)
16 #include <glib-object.h>
17 #include <gdk/gdk.h>
18 #include <gdk-pixbuf-xlib/gdk-pixbuf-xlib.h>
19 #else
20 #include <gdk-pixbuf/gdk-pixbuf-xlib.h>
21 #endif
22 #else
23 #error no Imlib and GdkPixbuf support
24 #endif
25
26 #include "w3mimg/w3mimg.h"
27
28 #define OFFSET_X        2
29 #define OFFSET_Y        2
30
31 struct x11_info {
32     Display *display;
33     Window window, parent;
34     unsigned long background_pixel;
35     GC imageGC;
36 #if defined(USE_IMLIB)
37     ImlibData *id;
38 #elif defined(USE_GDKPIXBUF)
39     int init_flag;
40 #endif
41 };
42
43 #if defined(USE_GDKPIXBUF)
44 struct x11_image {
45     int total;
46     int no;
47     int wait;
48     int delay;
49     Pixmap *pixmap;
50 };
51
52 #if defined(USE_GTK2)
53 static int
54 get_animation_size(GdkPixbufAnimation * animation, int *w, int *h, int *delay)
55 {
56     GdkPixbufAnimationIter *iter;
57     int n, i, d = -1;
58     GTimeVal time;
59
60     g_get_current_time(&time);
61     iter = gdk_pixbuf_animation_get_iter(animation, &time);
62     *w = gdk_pixbuf_animation_get_width(animation);
63     *h = gdk_pixbuf_animation_get_height(animation);
64     for (i = 1;
65          gdk_pixbuf_animation_iter_on_currently_loading_frame(iter) != TRUE;
66          i++) {
67         int tmp;
68         tmp = gdk_pixbuf_animation_iter_get_delay_time(iter);
69         g_time_val_add(&time, tmp * 1000);
70         if (tmp > d)
71             d = tmp;
72         gdk_pixbuf_animation_iter_advance(iter, &time);
73     }
74     if (delay)
75         *delay = d;
76     g_object_unref(G_OBJECT(iter));
77     n = i;
78     return n;
79 }
80 #else
81 static int
82 get_animation_size(GdkPixbufAnimation * animation, int *w, int *h, int *delay)
83 {
84     GList *frames;
85     int iw, ih, n, i, d = -1;
86
87     frames = gdk_pixbuf_animation_get_frames(animation);
88     n = gdk_pixbuf_animation_get_num_frames(animation);
89     *w = gdk_pixbuf_animation_get_width(animation);
90     *h = gdk_pixbuf_animation_get_height(animation);
91     for (i = 0; i < n; i++) {
92         GdkPixbufFrame *frame;
93         GdkPixbuf *pixbuf;
94         int tmp;
95
96         frame = (GdkPixbufFrame *) g_list_nth_data(frames, i);
97         tmp = gdk_pixbuf_frame_get_delay_time(frame);
98         if (tmp > d)
99             d = tmp;
100         pixbuf = gdk_pixbuf_frame_get_pixbuf(frame);
101         iw = gdk_pixbuf_frame_get_x_offset(frame)
102             + gdk_pixbuf_get_width(pixbuf);
103         ih = gdk_pixbuf_frame_get_y_offset(frame)
104             + gdk_pixbuf_get_height(pixbuf);
105         if (iw > *w)
106             *w = iw;
107         if (ih > *h)
108             *h = ih;
109     }
110     if (delay)
111         *delay = d;
112     return n;
113 }
114 #endif
115 #endif
116
117 static int
118 x11_init(w3mimg_op * self)
119 {
120     struct x11_info *xi;
121     if (self == NULL)
122         return 0;
123     xi = (struct x11_info *)self->priv;
124     if (xi == NULL)
125         return 0;
126 #if defined(USE_IMLIB)
127     if (!xi->id) {
128         xi->id = Imlib_init(xi->display);
129         if (!xi->id)
130             return 0;
131     }
132 #elif defined(USE_GDKPIXBUF)
133     if (!xi->init_flag) {
134 #if defined(USE_GTK2)
135         g_type_init();
136 #endif
137         gdk_pixbuf_xlib_init(xi->display, 0);
138         xi->init_flag = TRUE;
139     }
140 #endif
141     if (!xi->imageGC) {
142         xi->imageGC = XCreateGC(xi->display, xi->parent, 0, NULL);
143         if (!xi->imageGC)
144             return 0;
145     }
146     return 1;
147 }
148
149 static int
150 x11_finish(w3mimg_op * self)
151 {
152     struct x11_info *xi;
153     if (self == NULL)
154         return 0;
155     xi = (struct x11_info *)self->priv;
156     if (xi == NULL)
157         return 0;
158     if (xi->imageGC) {
159         XFreeGC(xi->display, xi->imageGC);
160         xi->imageGC = NULL;
161     }
162     return 1;
163 }
164
165 static int
166 x11_clear(w3mimg_op * self, int x, int y, int w, int h)
167 {
168     struct x11_info *xi;
169     if (self == NULL)
170         return 0;
171     xi = (struct x11_info *)self->priv;
172     if (xi == NULL)
173         return 0;
174
175     if (x < 0)
176         x = 0;
177     if (y < 0)
178         y = 0;
179
180     XClearArea(xi->display, xi->window, x, y, w, h, False);
181     return 1;
182 }
183
184 static int
185 x11_active(w3mimg_op * self)
186 {
187     struct x11_info *xi;
188     if (self == NULL)
189         return 0;
190     xi = (struct x11_info *)self->priv;
191     if (xi == NULL)
192         return 0;
193     if (!xi->imageGC)
194         return 0;
195     return 1;
196 }
197
198 static void
199 x11_set_background(w3mimg_op * self, char *background)
200 {
201     XColor screen_def, exact_def;
202     struct x11_info *xi;
203     if (self == NULL)
204         return;
205     xi = (struct x11_info *)self->priv;
206     if (xi == NULL)
207         return;
208
209     if (background &&
210         XAllocNamedColor(xi->display, DefaultColormap(xi->display, 0),
211                          background, &screen_def, &exact_def))
212         xi->background_pixel = screen_def.pixel;
213     else {
214         Pixmap p;
215         GC gc;
216         XImage *i;
217
218         p = XCreatePixmap(xi->display, xi->window, 1, 1,
219                           DefaultDepth(xi->display, 0));
220         gc = XCreateGC(xi->display, xi->window, 0, NULL);
221         if (!p || !gc)
222             exit(1);            /* XXX */
223         XCopyArea(xi->display, xi->window, p, gc,
224                   (self->offset_x >= 1) ? (self->offset_x - 1) : 0,
225                   (self->offset_y >= 1) ? (self->offset_y - 1) : 0,
226                   1, 1, 0, 0);
227         i = XGetImage(xi->display, p, 0, 0, 1, 1, -1, ZPixmap);
228         if (!i)
229             exit(1);
230         xi->background_pixel = XGetPixel(i, 0, 0);
231         XDestroyImage(i);
232         XFreeGC(xi->display, gc);
233         XFreePixmap(xi->display, p);
234     }
235 }
236
237 static void
238 x11_sync(w3mimg_op * self)
239 {
240     struct x11_info *xi;
241     if (self == NULL)
242         return;
243     xi = (struct x11_info *)self->priv;
244     if (xi == NULL)
245         return;
246     XSync(xi->display, False);
247 }
248
249 static void
250 x11_close(w3mimg_op * self)
251 {
252     /* XCloseDisplay(xi->display); */
253 }
254
255 #if defined(USE_GDKPIXBUF)
256 static struct x11_image *
257 x11_img_new(struct x11_info *xi, int w, int h, int n)
258 {
259     struct x11_image *img = NULL;
260     int i;
261
262     img = malloc(sizeof(*img));
263     if (!img)
264         goto ERROR;
265
266     img->pixmap = calloc(n, sizeof(*(img->pixmap)));
267     if (!img->pixmap)
268         goto ERROR;
269
270     for (i = 0; i < n; i++) {
271         img->pixmap[i] = XCreatePixmap(xi->display, xi->parent, w, h,
272                                        DefaultDepth(xi->display, 0));
273         if (!img->pixmap[i])
274             goto ERROR;
275
276         XSetForeground(xi->display, xi->imageGC, xi->background_pixel);
277         XFillRectangle(xi->display, (Pixmap) img->pixmap[i], xi->imageGC, 0, 0,
278                        w, h);
279     }
280
281     img->no = 0;
282     img->total = n;
283     img->wait = 0;
284     img->delay = -1;
285
286     return img;
287   ERROR:
288     if (img) {
289         if (img->pixmap) {
290             for (i = 0; i < n; i++) {
291                 if (img->pixmap[i])
292                     XFreePixmap(xi->display, (Pixmap) img->pixmap[i]);
293             }
294             free(img->pixmap);
295         }
296         free(img);
297     }
298     return NULL;
299 }
300
301 static GdkPixbuf *
302 resize_image(GdkPixbuf * pixbuf, int width, int height)
303 {
304     GdkPixbuf *resized_pixbuf;
305     int w, h;
306
307     if (pixbuf == NULL)
308         return NULL;
309     w = gdk_pixbuf_get_width(pixbuf);
310     h = gdk_pixbuf_get_height(pixbuf);
311     if (width < 1 || height < 1)
312         return pixbuf;
313     if (w == width && h == height)
314         return pixbuf;
315     resized_pixbuf =
316         gdk_pixbuf_scale_simple(pixbuf, width, height, GDK_INTERP_BILINEAR);
317     if (resized_pixbuf == NULL)
318         return NULL;
319     return resized_pixbuf;
320 }
321 #endif
322
323 static int
324 x11_load_image(w3mimg_op * self, W3MImage * img, char *fname, int w, int h)
325 {
326     struct x11_info *xi;
327 #if defined(USE_IMLIB)
328     ImlibImage *im;
329 #elif defined(USE_IMLIB2)
330     Imlib_Image im;
331 #elif defined(USE_GDKPIXBUF)
332     GdkPixbufAnimation *animation;
333     int j, iw, ih, n, frame_num, delay = -1, max_anim;
334     double ratio_w, ratio_h;
335     struct x11_image *ximg;
336     Pixmap tmp_pixmap;
337 #if defined(USE_GTK2)
338     GdkPixbufAnimationIter *iter;
339     GTimeVal time;
340 #else
341     int i;
342     GList *frames;
343 #endif
344 #endif
345
346     if (self == NULL)
347         return 0;
348     xi = (struct x11_info *)self->priv;
349     if (xi == NULL)
350         return 0;
351
352 #if defined(USE_IMLIB)
353     im = Imlib_load_image(xi->id, fname);
354     if (!im)
355         return 0;
356     if (w <= 0)
357         w = im->rgb_width;
358     if (h <= 0)
359         h = im->rgb_height;
360     img->pixmap = (void *)XCreatePixmap(xi->display, xi->parent, w, h,
361                                         DefaultDepth(xi->display, 0));
362     if (!img->pixmap)
363         return 0;
364     XSetForeground(xi->display, xi->imageGC, xi->background_pixel);
365     XFillRectangle(xi->display, (Pixmap) img->pixmap, xi->imageGC, 0, 0, w, h);
366     Imlib_paste_image(xi->id, im, (Pixmap) img->pixmap, 0, 0, w, h);
367     Imlib_kill_image(xi->id, im);
368 #elif defined(USE_IMLIB2)
369     im = imlib_load_image(fname);
370     if (!im)
371         return 0;
372     imlib_context_set_image(im);
373     if (w <= 0)
374         w = imlib_image_get_width();
375     if (h <= 0)
376         h = imlib_image_get_height();
377     img->pixmap = (void *)XCreatePixmap(xi->display, xi->parent, w, h,
378                                         DefaultDepth(xi->display, 0));
379     if (!img->pixmap)
380         return 0;
381     XSetForeground(xi->display, xi->imageGC, xi->background_pixel);
382     XFillRectangle(xi->display, (Pixmap) img->pixmap, xi->imageGC, 0, 0, w, h);
383     imlib_context_set_display(xi->display);
384     imlib_context_set_visual(DefaultVisual(xi->display, 0));
385     imlib_context_set_colormap(DefaultColormap(xi->display, 0));
386     imlib_context_set_drawable((Drawable) img->pixmap);
387     imlib_render_image_on_drawable_at_size(0, 0, w, h);
388     imlib_free_image();
389 #elif defined(USE_GDKPIXBUF)
390     max_anim = self->max_anim;
391 #if defined(USE_GTK2)
392     animation = gdk_pixbuf_animation_new_from_file(fname, NULL);
393 #else
394     animation = gdk_pixbuf_animation_new_from_file(fname);
395 #endif
396     if (!animation)
397         return 0;
398     frame_num = n = get_animation_size(animation, &iw, &ih, &delay);
399     if (delay <= 0)
400         max_anim = -1;
401
402     if (max_anim < 0) {
403         frame_num = (-max_anim > n) ? n : -max_anim;
404     }
405     else if (max_anim > 0) {
406         frame_num = n = (max_anim > n) ? n : max_anim;
407     }
408
409     if (w < 1 || h < 1) {
410         w = iw;
411         h = ih;
412         ratio_w = ratio_h = 1;
413     }
414     else {
415         ratio_w = 1.0 * w / iw;
416         ratio_h = 1.0 * h / ih;
417     }
418     tmp_pixmap = XCreatePixmap(xi->display, xi->parent, w, h,
419                                DefaultDepth(xi->display, 0));
420     XSetForeground(xi->display, xi->imageGC, xi->background_pixel);
421     XFillRectangle(xi->display, (Pixmap) tmp_pixmap, xi->imageGC, 0, 0, w, h);
422     if (!tmp_pixmap) {
423 #if defined(USE_GTK2)
424         g_object_unref(G_OBJECT(animation));
425 #else
426         gdk_pixbuf_animation_unref(animation);
427 #endif
428         return 0;
429     }
430     ximg = x11_img_new(xi, w, h, frame_num);
431     if (!ximg) {
432         XFreePixmap(xi->display, tmp_pixmap);
433 #if defined(USE_GTK2)
434         g_object_unref(G_OBJECT(animation));
435 #else
436         gdk_pixbuf_animation_unref(animation);
437 #endif
438         return 0;
439     }
440 #if defined(USE_GTK2)
441     g_get_current_time(&time);
442     iter = gdk_pixbuf_animation_get_iter(animation, &time);
443
444    if (max_anim < 0 && n > -max_anim) {
445         max_anim = n + max_anim;
446         for (j = 0; j < max_anim; j++) {
447             delay = gdk_pixbuf_animation_iter_get_delay_time(iter);
448             g_time_val_add(&time, delay * 1000);
449             gdk_pixbuf_animation_iter_advance(iter, &time);
450         }
451     }
452     for (j = 0; j < frame_num; j++) {
453         GdkPixbuf *org_pixbuf, *pixbuf;
454
455         org_pixbuf = gdk_pixbuf_animation_iter_get_pixbuf(iter);
456         delay = gdk_pixbuf_animation_iter_get_delay_time(iter);
457         pixbuf = resize_image(org_pixbuf, w, h);
458
459         if (delay > ximg->delay)
460             ximg->delay = delay;
461
462         gdk_pixbuf_xlib_render_to_drawable_alpha(pixbuf,
463                                                  (Drawable) ximg->pixmap[j], 0,
464                                                  0, 0, 0, w, h,
465                                                  GDK_PIXBUF_ALPHA_BILEVEL, 1,
466                                                  XLIB_RGB_DITHER_NORMAL, 0, 0);
467         if (org_pixbuf != pixbuf)
468             g_object_unref(G_OBJECT(pixbuf));
469         g_time_val_add(&time, delay * 1000);
470         gdk_pixbuf_animation_iter_advance(iter, &time);
471     }
472     XFreePixmap(xi->display, tmp_pixmap);
473     g_object_unref(G_OBJECT(animation));
474
475 #else
476     frames = gdk_pixbuf_animation_get_frames(animation);
477
478     for (j = 0; j < n; j++) {
479         GdkPixbufFrame *frame;
480         GdkPixbuf *org_pixbuf, *pixbuf;
481         int width, height, ofstx, ofsty;
482
483         if (max_anim < 0) {
484             i = (j - n + frame_num > 0) ? (j - n + frame_num) : 0;
485         }
486         else {
487             i = j;
488         }
489         frame = (GdkPixbufFrame *) g_list_nth_data(frames, j);
490         org_pixbuf = gdk_pixbuf_frame_get_pixbuf(frame);
491         ofstx = gdk_pixbuf_frame_get_x_offset(frame);
492         ofsty = gdk_pixbuf_frame_get_y_offset(frame);
493         delay = gdk_pixbuf_frame_get_delay_time(frame);
494         width = gdk_pixbuf_get_width(org_pixbuf);
495         height = gdk_pixbuf_get_height(org_pixbuf);
496
497         if (ofstx == 0 && ofsty == 0 && width == w && height == h) {
498             pixbuf = resize_image(org_pixbuf, w, h);
499         }
500         else {
501             pixbuf =
502                 resize_image(org_pixbuf, width * ratio_w, height * ratio_h);
503             ofstx *= ratio_w;
504             ofsty *= ratio_h;
505         }
506         width = gdk_pixbuf_get_width(pixbuf);
507         height = gdk_pixbuf_get_height(pixbuf);
508
509         if (delay > ximg->delay)
510             ximg->delay = delay;
511
512         XCopyArea(xi->display, tmp_pixmap, ximg->pixmap[i],
513                   xi->imageGC, 0, 0, w, h, 0, 0);
514         gdk_pixbuf_xlib_render_to_drawable_alpha(pixbuf,
515                                                  (Drawable) ximg->pixmap[i], 0,
516                                                  0, ofstx, ofsty, width,
517                                                  height,
518                                                  GDK_PIXBUF_ALPHA_BILEVEL, 1,
519                                                  XLIB_RGB_DITHER_NORMAL, 0, 0);
520
521         switch (gdk_pixbuf_frame_get_action(frame)) {
522         case GDK_PIXBUF_FRAME_RETAIN:
523             XCopyArea(xi->display, ximg->pixmap[i], tmp_pixmap,
524                       xi->imageGC, 0, 0, w, h, 0, 0);
525             break;
526         case GDK_PIXBUF_FRAME_DISPOSE:
527             XSetForeground(xi->display, xi->imageGC, xi->background_pixel);
528             XFillRectangle(xi->display, tmp_pixmap, xi->imageGC,
529                            0, 0, w, h);
530             break;
531         case GDK_PIXBUF_FRAME_REVERT:
532             XCopyArea(xi->display, ximg->pixmap[0], tmp_pixmap,
533                       xi->imageGC, 0, 0, w, h, 0, 0);
534             break;
535         default:
536             XCopyArea(xi->display, ximg->pixmap[0], tmp_pixmap,
537                       xi->imageGC, 0, 0, w, h, 0, 0);
538             break;
539         }
540
541
542         if (org_pixbuf != pixbuf)
543             gdk_pixbuf_finalize(pixbuf);
544
545     }
546     XFreePixmap(xi->display, tmp_pixmap);
547     gdk_pixbuf_animation_unref(animation);
548 #endif
549     img->pixmap = ximg;
550 #endif
551
552     img->width = w;
553     img->height = h;
554     return 1;
555 }
556
557 static int
558 x11_show_image(w3mimg_op * self, W3MImage * img, int sx, int sy, int sw,
559                int sh, int x, int y)
560 {
561     struct x11_info *xi;
562 #if defined(USE_GDKPIXBUF)
563     struct x11_image *ximg = img->pixmap;
564     int i;
565 #endif
566     if (self == NULL)
567         return 0;
568
569     if (img->pixmap == NULL)
570         return 0;
571
572     xi = (struct x11_info *)self->priv;
573     if (xi == NULL)
574         return 0;
575
576 #if defined(USE_IMLIB) || defined(USE_IMLIB2)
577     XCopyArea(xi->display, (Pixmap) img->pixmap, xi->window, xi->imageGC,
578               sx, sy,
579               (sw ? sw : img->width),
580               (sh ? sh : img->height), x + self->offset_x, y + self->offset_y);
581 #elif defined(USE_GDKPIXBUF)
582 #define WAIT_CNT 4
583     if (ximg->delay <= 0)
584         i = ximg->total - 1;
585     else
586         i = ximg->no;
587     XCopyArea(xi->display, ximg->pixmap[i], xi->window, xi->imageGC,
588               sx, sy,
589               (sw ? sw : img->width),
590               (sh ? sh : img->height), x + self->offset_x, y + self->offset_y);
591     if (ximg->total > 1) {
592         if (ximg->wait > WAIT_CNT) {
593             ximg->wait = 0;
594             if (i < ximg->total - 1)
595                 ximg->no = i + 1;
596             else
597                 ximg->no = 0;
598         }
599         ximg->wait += 1;
600     }
601 #endif
602     return 1;
603 }
604
605 static void
606 x11_free_image(w3mimg_op * self, W3MImage * img)
607 {
608     struct x11_info *xi;
609     if (self == NULL)
610         return;
611     xi = (struct x11_info *)self->priv;
612     if (xi == NULL)
613         return;
614 #if defined(USE_IMLIB) || defined(USE_IMLIB2)
615     if (img && img->pixmap) {
616         XFreePixmap(xi->display, (Pixmap) img->pixmap);
617         img->pixmap = NULL;
618         img->width = 0;
619         img->height = 0;
620     }
621 #elif defined(USE_GDKPIXBUF)
622     if (img && img->pixmap) {
623         struct x11_image *ximg = img->pixmap;
624         int i, n;
625         if (ximg->pixmap) {
626             n = ximg->total;
627             for (i = 0; i < n; i++) {
628                 if (ximg->pixmap[i])
629                     XFreePixmap(xi->display, (Pixmap) ximg->pixmap[i]);
630             }
631             free(ximg->pixmap);
632         }
633         free(ximg);
634         img->pixmap = NULL;
635         img->width = 0;
636         img->height = 0;
637     }
638 #endif
639 }
640
641 static int
642 x11_get_image_size(w3mimg_op * self, W3MImage * img, char *fname, int *w,
643                    int *h)
644 {
645     struct x11_info *xi;
646 #if defined(USE_IMLIB)
647     ImlibImage *im;
648 #elif defined(USE_IMLIB2)
649     Imlib_Image im;
650 #elif defined(USE_GDKPIXBUF)
651     GdkPixbufAnimation *animation;
652 #endif
653
654     if (self == NULL)
655         return 0;
656     xi = (struct x11_info *)self->priv;
657     if (xi == NULL)
658         return 0;
659
660 #if defined(USE_IMLIB)
661     im = Imlib_load_image(xi->id, fname);
662     if (!im)
663         return 0;
664
665     *w = im->rgb_width;
666     *h = im->rgb_height;
667     Imlib_kill_image(xi->id, im);
668 #elif defined(USE_IMLIB2)
669     im = imlib_load_image(fname);
670     if (im == NULL)
671         return 0;
672
673     imlib_context_set_image(im);
674     *w = imlib_image_get_width();
675     *h = imlib_image_get_height();
676     imlib_free_image();
677 #elif defined(USE_GDKPIXBUF)
678 #if defined(USE_GTK2)
679     animation = gdk_pixbuf_animation_new_from_file(fname, NULL);
680 #else
681     animation = gdk_pixbuf_animation_new_from_file(fname);
682 #endif
683     if (!animation)
684         return 0;
685
686     get_animation_size(animation, w, h, NULL);
687 #if defined(USE_GTK2)
688     g_object_unref(G_OBJECT(animation));
689 #else
690     gdk_pixbuf_animation_unref(animation);
691 #endif
692 #endif
693     return 1;
694 }
695
696 /* *INDENT-OFF* */
697 /*
698   xterm/kterm/hanterm/cxterm
699     top window (WINDOWID)
700       +- text window
701            +- scrollbar
702   rxvt/aterm/Eterm/wterm
703     top window (WINDOWID)
704       +- text window
705       +- scrollbar
706       +- menubar (etc.)
707   gnome-terminal
708     top window
709       +- text window (WINDOWID)
710       +- scrollbar
711       +- menubar
712   mlterm (-s)
713     top window
714       +- text window (WINDOWID)
715       +- scrollbar
716   mlterm
717     top window = text window (WINDOWID)
718
719   powershell
720     top window
721       +- window
722       |    +- text window
723       |    +- scrollbar
724       +- menubar (etc.)
725   dtterm
726     top window
727       +- window
728            +- window
729            |    +- window
730            |         +- text window
731            |         +- scrollbar
732            +- menubar
733   hpterm
734     top window
735       +- window
736            +- text window
737            +- scrollbar
738            +- (etc.)
739 */
740 /* *INDENT-ON* */
741
742 w3mimg_op *
743 w3mimg_x11open()
744 {
745     w3mimg_op *wop = NULL;
746     struct x11_info *xi = NULL;
747     char *id;
748     int revert, i;
749     unsigned int nchildren;
750     XWindowAttributes attr;
751     Window root, *children;
752
753     wop = (w3mimg_op *) malloc(sizeof(w3mimg_op));
754     if (wop == NULL)
755         return NULL;
756     memset(wop, 0, sizeof(w3mimg_op));
757
758     xi = (struct x11_info *)malloc(sizeof(struct x11_info));
759     if (xi == NULL)
760         goto error;
761     memset(xi, 0, sizeof(struct x11_info));
762
763     xi->display = XOpenDisplay(NULL);
764     if (xi->display == NULL) {
765         goto error;
766     }
767     if ((id = getenv("WINDOWID")) != NULL)
768         xi->window = (Window) atoi(id);
769     else
770         XGetInputFocus(xi->display, &xi->window, &revert);
771     if (!xi->window)
772         exit(1);
773
774     XGetWindowAttributes(xi->display, xi->window, &attr);
775     wop->width = attr.width;
776     wop->height = attr.height;
777
778     while (1) {
779         Window p_window;
780
781         XQueryTree(xi->display, xi->window, &root, &xi->parent,
782                    &children, &nchildren);
783         p_window = xi->window;
784         for (i = 0; i < nchildren; i++) {
785             XGetWindowAttributes(xi->display, children[i], &attr);
786             if (attr.width > wop->width * 0.7 &&
787                 attr.height > wop->height * 0.7) {
788                 /* maybe text window */
789                 wop->width = attr.width;
790                 wop->height = attr.height;
791                 xi->window = children[i];
792             }
793         }
794         if (p_window == xi->window)
795             break;
796     }
797     wop->offset_x = OFFSET_X;
798     for (i = 0; i < nchildren; i++) {
799         XGetWindowAttributes(xi->display, children[i], &attr);
800         if (attr.x <= 0 && attr.width < 30 && attr.height > wop->height * 0.7) {
801             /* scrollbar of xterm/kterm ? */
802             wop->offset_x += attr.x + attr.width + attr.border_width * 2;
803             break;
804         }
805     }
806     wop->offset_y = OFFSET_Y;
807
808     wop->priv = xi;
809
810     wop->init = x11_init;
811     wop->finish = x11_finish;
812     wop->active = x11_active;
813     wop->set_background = x11_set_background;
814     wop->sync = x11_sync;
815     wop->close = x11_close;
816     wop->clear = x11_clear;
817
818     wop->load_image = x11_load_image;
819     wop->show_image = x11_show_image;
820     wop->free_image = x11_free_image;
821     wop->get_image_size = x11_get_image_size;
822
823     return wop;
824   error:
825     if (xi)
826         free(xi);
827     free(wop);
828     return NULL;
829 }