Intial commit
[profile/ivi/w3m.git] / image.c
1 /* $Id: image.c,v 1.36 2003/07/07 15:49:03 ukai Exp $ */
2
3 #include "fm.h"
4 #include <sys/types.h>
5 #include <sys/stat.h>
6 #include <signal.h>
7 #include <errno.h>
8 #include <unistd.h>
9 #ifdef HAVE_WAITPID
10 #include <sys/wait.h>
11 #endif
12
13 #ifdef USE_IMAGE
14
15 static int image_index = 0;
16
17 /* display image */
18
19 typedef struct _termialImage {
20     ImageCache *cache;
21     short x;
22     short y;
23     short sx;
24     short sy;
25     short width;
26     short height;
27 } TerminalImage;
28
29 static TerminalImage *terminal_image = NULL;
30 static int n_terminal_image = 0;
31 static int max_terminal_image = 0;
32 static FILE *Imgdisplay_rf = NULL, *Imgdisplay_wf = NULL;
33 static pid_t Imgdisplay_pid = 0;
34 static int openImgdisplay();
35 static void closeImgdisplay();
36 int getCharSize();
37
38 void
39 initImage()
40 {
41     if (activeImage)
42         return;
43     if (getCharSize())
44         activeImage = TRUE;
45 }
46
47 int
48 getCharSize()
49 {
50     FILE *f;
51     Str tmp;
52     int w = 0, h = 0;
53
54     set_environ("W3M_TTY", ttyname_tty());
55     tmp = Strnew();
56     if (!strchr(Imgdisplay, '/'))
57         Strcat_m_charp(tmp, w3m_auxbin_dir(), "/", NULL);
58     Strcat_m_charp(tmp, Imgdisplay, " -test 2>/dev/null", NULL);
59     f = popen(tmp->ptr, "r");
60     if (!f)
61         return FALSE;
62     while (fscanf(f, "%d %d", &w, &h) < 0) {
63         if (feof(f))
64             break;
65     }
66     pclose(f);
67
68     if (!(w > 0 && h > 0))
69         return FALSE;
70     if (!set_pixel_per_char)
71         pixel_per_char = (int)(1.0 * w / COLS + 0.5);
72     if (!set_pixel_per_line)
73         pixel_per_line = (int)(1.0 * h / LINES + 0.5);
74     return TRUE;
75 }
76
77 void
78 termImage()
79 {
80     if (!activeImage)
81         return;
82     clearImage();
83     if (Imgdisplay_wf) {
84         fputs("2;\n", Imgdisplay_wf);   /* ClearImage() */
85         fflush(Imgdisplay_wf);
86     }
87     closeImgdisplay();
88 }
89
90 static int
91 openImgdisplay()
92 {
93     Imgdisplay_pid = open_pipe_rw(&Imgdisplay_rf, &Imgdisplay_wf);
94     if (Imgdisplay_pid < 0)
95         goto err0;
96     if (Imgdisplay_pid == 0) {
97         /* child */
98         char *cmd;
99         setup_child(FALSE, 2, -1);
100         if (!strchr(Imgdisplay, '/'))
101             cmd = Strnew_m_charp(w3m_auxbin_dir(), "/", Imgdisplay, NULL)->ptr;
102         else
103             cmd = Imgdisplay;
104         myExec(cmd);
105         /* XXX: ifdef __EMX__, use start /f ? */
106     }
107     activeImage = TRUE;
108     return TRUE;
109   err0:
110     Imgdisplay_pid = 0;
111     activeImage = FALSE;
112     return FALSE;
113 }
114
115 static void
116 closeImgdisplay()
117 {
118     if (Imgdisplay_rf)
119         fclose(Imgdisplay_rf);
120     if (Imgdisplay_wf)
121         fclose(Imgdisplay_wf);
122     if (Imgdisplay_pid)
123         kill(Imgdisplay_pid, SIGKILL);
124     Imgdisplay_rf = NULL;
125     Imgdisplay_wf = NULL;
126     Imgdisplay_pid = 0;
127 }
128
129 void
130 addImage(ImageCache * cache, int x, int y, int sx, int sy, int w, int h)
131 {
132     TerminalImage *i;
133
134     if (!activeImage)
135         return;
136     if (n_terminal_image >= max_terminal_image) {
137         max_terminal_image = max_terminal_image ? (2 * max_terminal_image) : 8;
138         terminal_image = New_Reuse(TerminalImage, terminal_image,
139                                    max_terminal_image);
140     }
141     i = &terminal_image[n_terminal_image];
142     i->cache = cache;
143     i->x = x;
144     i->y = y;
145     i->sx = sx;
146     i->sy = sy;
147     i->width = w;
148     i->height = h;
149     n_terminal_image++;
150 }
151
152 static void
153 syncImage(void)
154 {
155     fputs("3;\n", Imgdisplay_wf);       /* XSync() */
156     fputs("4;\n", Imgdisplay_wf);       /* put '\n' */
157     while (fflush(Imgdisplay_wf) != 0) {
158         if (ferror(Imgdisplay_wf))
159             goto err;
160     }
161     if (!fgetc(Imgdisplay_rf))
162         goto err;
163     return;
164   err:
165     closeImgdisplay();
166     image_index += MAX_IMAGE;
167     n_terminal_image = 0;
168 }
169
170 void
171 drawImage()
172 {
173     static char buf[64];
174     int j, draw = FALSE;
175     TerminalImage *i;
176
177     if (!activeImage)
178         return;
179     if (!n_terminal_image)
180         return;
181     for (j = 0; j < n_terminal_image; j++) {
182         i = &terminal_image[j];
183         if (!(i->cache->loaded & IMG_FLAG_LOADED &&
184               i->width > 0 && i->height > 0))
185             continue;
186         if (!(Imgdisplay_rf && Imgdisplay_wf)) {
187             if (!openImgdisplay())
188                 return;
189         }
190         if (i->cache->index > 0) {
191             i->cache->index *= -1;
192             fputs("0;", Imgdisplay_wf); /* DrawImage() */
193         }
194         else
195             fputs("1;", Imgdisplay_wf); /* DrawImage(redraw) */
196         sprintf(buf, "%d;%d;%d;%d;%d;%d;%d;%d;%d;",
197                 (-i->cache->index - 1) % MAX_IMAGE + 1, i->x, i->y,
198                 (i->cache->width > 0) ? i->cache->width : 0,
199                 (i->cache->height > 0) ? i->cache->height : 0,
200                 i->sx, i->sy, i->width, i->height);
201         fputs(buf, Imgdisplay_wf);
202         fputs(i->cache->file, Imgdisplay_wf);
203         fputs("\n", Imgdisplay_wf);
204         draw = TRUE;
205     }
206     if (!draw)
207         return;
208     syncImage();
209     touch_cursor();
210     refresh();
211 }
212
213 void
214 clearImage()
215 {
216     static char buf[64];
217     int j;
218     TerminalImage *i;
219
220     if (!activeImage)
221         return;
222     if (!n_terminal_image)
223         return;
224     if (!Imgdisplay_wf) {
225         n_terminal_image = 0;
226         return;
227     }
228     for (j = 0; j < n_terminal_image; j++) {
229         i = &terminal_image[j];
230         if (!(i->cache->loaded & IMG_FLAG_LOADED &&
231               i->width > 0 && i->height > 0))
232             continue;
233         sprintf(buf, "6;%d;%d;%d;%d\n", i->x, i->y, i->width, i->height);
234         fputs(buf, Imgdisplay_wf);
235     }
236     syncImage();
237     n_terminal_image = 0;
238 }
239
240 /* load image */
241
242 #ifndef MAX_LOAD_IMAGE
243 #define MAX_LOAD_IMAGE 8
244 #endif
245 static int n_load_image = 0;
246 static Hash_sv *image_hash = NULL;
247 static Hash_sv *image_file = NULL;
248 static GeneralList *image_list = NULL;
249 static ImageCache **image_cache = NULL;
250 static Buffer *image_buffer = NULL;
251
252 void
253 deleteImage(Buffer *buf)
254 {
255     AnchorList *al;
256     Anchor *a;
257     int i;
258
259     if (!buf)
260         return;
261     al = buf->img;
262     if (!al)
263         return;
264     for (i = 0, a = al->anchors; i < al->nanchor; i++, a++) {
265         if (a->image && a->image->cache &&
266             a->image->cache->loaded != IMG_FLAG_UNLOADED &&
267             !(a->image->cache->loaded & IMG_FLAG_DONT_REMOVE) &&
268             a->image->cache->index < 0)
269             unlink(a->image->cache->file);
270     }
271     loadImage(NULL, IMG_FLAG_STOP);
272 }
273
274 void
275 getAllImage(Buffer *buf)
276 {
277     AnchorList *al;
278     Anchor *a;
279     ParsedURL *current;
280     int i;
281
282     image_buffer = buf;
283     if (!buf)
284         return;
285     buf->image_loaded = TRUE;
286     al = buf->img;
287     if (!al)
288         return;
289     current = baseURL(buf);
290     for (i = 0, a = al->anchors; i < al->nanchor; i++, a++) {
291         if (a->image) {
292             a->image->cache = getImage(a->image, current, buf->image_flag);
293             if (a->image->cache &&
294                 a->image->cache->loaded == IMG_FLAG_UNLOADED)
295                 buf->image_loaded = FALSE;
296         }
297     }
298 }
299
300 void
301 showImageProgress(Buffer *buf)
302 {
303     AnchorList *al;
304     Anchor *a;
305     int i, l, n;
306
307     if (!buf)
308         return;
309     al = buf->img;
310     if (!al)
311         return;
312     for (i = 0, l = 0, n = 0, a = al->anchors; i < al->nanchor; i++, a++) {
313         if (a->image && a->hseq >= 0) {
314             n++;
315             if (a->image->cache && a->image->cache->loaded & IMG_FLAG_LOADED)
316                 l++;
317         }
318     }
319     if (n) {
320         message(Sprintf("%d/%d images loaded", l, n)->ptr,
321                 buf->cursorX + buf->rootX, buf->cursorY + buf->rootY);
322         refresh();
323     }
324 }
325
326 void
327 loadImage(Buffer *buf, int flag)
328 {
329     ImageCache *cache;
330     struct stat st;
331     int i, draw = FALSE;
332     /* int wait_st; */
333
334     if (maxLoadImage > MAX_LOAD_IMAGE)
335         maxLoadImage = MAX_LOAD_IMAGE;
336     else if (maxLoadImage < 1)
337         maxLoadImage = 1;
338     if (n_load_image == 0)
339         n_load_image = maxLoadImage;
340     if (!image_cache) {
341         image_cache = New_N(ImageCache *, MAX_LOAD_IMAGE);
342         bzero(image_cache, sizeof(ImageCache *) * MAX_LOAD_IMAGE);
343     }
344     for (i = 0; i < n_load_image; i++) {
345         cache = image_cache[i];
346         if (!cache)
347             continue;
348         if (lstat(cache->touch, &st))
349             continue;
350         if (cache->pid) {
351             kill(cache->pid, SIGKILL);
352             /*
353              * #ifdef HAVE_WAITPID
354              * waitpid(cache->pid, &wait_st, 0);
355              * #else
356              * wait(&wait_st);
357              * #endif
358              */
359             cache->pid = 0;
360         }
361         if (!stat(cache->file, &st)) {
362             cache->loaded = IMG_FLAG_LOADED;
363             if (getImageSize(cache)) {
364                 if (image_buffer)
365                     image_buffer->need_reshape = TRUE;
366             }
367             draw = TRUE;
368         }
369         else
370             cache->loaded = IMG_FLAG_ERROR;
371         unlink(cache->touch);
372         image_cache[i] = NULL;
373     }
374
375     for (i = (buf != image_buffer) ? 0 : maxLoadImage; i < n_load_image; i++) {
376         cache = image_cache[i];
377         if (!cache)
378             continue;
379         if (cache->pid) {
380             kill(cache->pid, SIGKILL);
381             /*
382              * #ifdef HAVE_WAITPID
383              * waitpid(cache->pid, &wait_st, 0);
384              * #else
385              * wait(&wait_st);
386              * #endif
387              */
388             cache->pid = 0;
389         }
390         unlink(cache->touch);
391         image_cache[i] = NULL;
392     }
393
394     if (flag == IMG_FLAG_STOP) {
395         image_list = NULL;
396         image_file = NULL;
397         n_load_image = maxLoadImage;
398         image_buffer = NULL;
399         return;
400     }
401
402     if (draw && image_buffer) {
403         drawImage();
404         showImageProgress(image_buffer);
405     }
406
407     image_buffer = buf;
408
409     if (!image_list)
410         return;
411     for (i = 0; i < n_load_image; i++) {
412         if (image_cache[i])
413             continue;
414         while (1) {
415             cache = (ImageCache *) popValue(image_list);
416             if (!cache) {
417                 for (i = 0; i < n_load_image; i++) {
418                     if (image_cache[i])
419                         return;
420                 }
421                 image_list = NULL;
422                 image_file = NULL;
423                 if (image_buffer)
424                     displayBuffer(image_buffer, B_NORMAL);
425                 return;
426             }
427             if (cache->loaded == IMG_FLAG_UNLOADED)
428                 break;
429         }
430         image_cache[i] = cache;
431
432         flush_tty();
433         if ((cache->pid = fork()) == 0) {
434             Buffer *b;
435             /*
436              * setup_child(TRUE, 0, -1);
437              */
438             setup_child(FALSE, 0, -1);
439             image_source = cache->file;
440             b = loadGeneralFile(cache->url, cache->current, NULL, 0, NULL);
441             if (!b || !b->real_type || strncasecmp(b->real_type, "image/", 6))
442                 unlink(cache->file);
443 #if defined(HAVE_SYMLINK) && defined(HAVE_LSTAT)
444             symlink(cache->file, cache->touch);
445 #else
446             {
447                 FILE *f = fopen(cache->touch, "w");
448                 if (f)
449                     fclose(f);
450             }
451 #endif
452             exit(0);
453         }
454         else if (cache->pid < 0) {
455             cache->pid = 0;
456             return;
457         }
458     }
459 }
460
461 ImageCache *
462 getImage(Image * image, ParsedURL *current, int flag)
463 {
464     Str key = NULL;
465     ImageCache *cache;
466
467     if (!activeImage)
468         return NULL;
469     if (!image_hash)
470         image_hash = newHash_sv(100);
471     if (image->cache)
472         cache = image->cache;
473     else {
474         key = Sprintf("%d;%d;%s", image->width, image->height, image->url);
475         cache = (ImageCache *) getHash_sv(image_hash, key->ptr, NULL);
476     }
477     if (cache && cache->index && abs(cache->index) <= image_index - MAX_IMAGE) {
478         struct stat st;
479         if (stat(cache->file, &st))
480             cache->loaded = IMG_FLAG_UNLOADED;
481         cache->index = 0;
482     }
483
484     if (!cache) {
485         if (flag == IMG_FLAG_SKIP)
486             return NULL;
487
488         cache = New(ImageCache);
489         cache->url = image->url;
490         cache->current = current;
491         cache->file = tmpfname(TMPF_DFL, image->ext)->ptr;
492         cache->touch = tmpfname(TMPF_DFL, NULL)->ptr;
493         cache->pid = 0;
494         cache->index = 0;
495         cache->loaded = IMG_FLAG_UNLOADED;
496         cache->width = image->width;
497         cache->height = image->height;
498         putHash_sv(image_hash, key->ptr, (void *)cache);
499     }
500     if (flag != IMG_FLAG_SKIP) {
501         if (cache->loaded == IMG_FLAG_UNLOADED) {
502             if (!image_file)
503                 image_file = newHash_sv(100);
504             if (!getHash_sv(image_file, cache->file, NULL)) {
505                 putHash_sv(image_file, cache->file, (void *)cache);
506                 if (!image_list)
507                     image_list = newGeneralList();
508                 pushValue(image_list, (void *)cache);
509             }
510         }
511         if (!cache->index)
512             cache->index = ++image_index;
513     }
514     if (cache->loaded & IMG_FLAG_LOADED)
515         getImageSize(cache);
516     return cache;
517 }
518
519 int
520 getImageSize(ImageCache * cache)
521 {
522     Str tmp;
523     FILE *f;
524     int w = 0, h = 0;
525
526     if (!activeImage)
527         return FALSE;
528     if (!cache || !(cache->loaded & IMG_FLAG_LOADED) ||
529         (cache->width > 0 && cache->height > 0))
530         return FALSE;
531     tmp = Strnew();
532     if (!strchr(Imgdisplay, '/'))
533         Strcat_m_charp(tmp, w3m_auxbin_dir(), "/", NULL);
534     Strcat_m_charp(tmp, Imgdisplay, " -size ", shell_quote(cache->file), NULL);
535     f = popen(tmp->ptr, "r");
536     if (!f)
537         return FALSE;
538     while (fscanf(f, "%d %d", &w, &h) < 0) {
539         if (feof(f))
540             break;
541     }
542     pclose(f);
543
544     if (!(w > 0 && h > 0))
545         return FALSE;
546     w = (int)(w * image_scale / 100 + 0.5);
547     if (w == 0)
548         w = 1;
549     h = (int)(h * image_scale / 100 + 0.5);
550     if (h == 0)
551         h = 1;
552     if (cache->width < 0 && cache->height < 0) {
553         cache->width = (w > MAX_IMAGE_SIZE) ? MAX_IMAGE_SIZE : w;
554         cache->height = (h > MAX_IMAGE_SIZE) ? MAX_IMAGE_SIZE : h;
555     }
556     else if (cache->width < 0) {
557         int tmp = (int)((double)cache->height * w / h + 0.5);
558         cache->width = (tmp > MAX_IMAGE_SIZE) ? MAX_IMAGE_SIZE : tmp;
559     }
560     else if (cache->height < 0) {
561         int tmp = (int)((double)cache->width * h / w + 0.5);
562         cache->height = (tmp > MAX_IMAGE_SIZE) ? MAX_IMAGE_SIZE : tmp;
563     }
564     if (cache->width == 0)
565         cache->width = 1;
566     if (cache->height == 0)
567         cache->height = 1;
568     tmp = Sprintf("%d;%d;%s", cache->width, cache->height, cache->url);
569     putHash_sv(image_hash, tmp->ptr, (void *)cache);
570     return TRUE;
571 }
572 #endif