w3m

Unnamed repository; edit this file to name it for gitweb.
git clone https://logand.com/git/w3m.git/
Log | Files | Refs | README

image.c (12558B)


      1 /* $Id$ */
      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_wf)
    119 	fclose(Imgdisplay_wf);
    120     if (Imgdisplay_rf) {
    121 	/* sync with the child */
    122 	getc(Imgdisplay_rf); /* EOF expected */
    123 	fclose(Imgdisplay_rf);
    124     }
    125     if (Imgdisplay_pid)
    126 	kill(Imgdisplay_pid, SIGKILL);
    127     Imgdisplay_rf = NULL;
    128     Imgdisplay_wf = NULL;
    129     Imgdisplay_pid = 0;
    130 }
    131 
    132 void
    133 addImage(ImageCache * cache, int x, int y, int sx, int sy, int w, int h)
    134 {
    135     TerminalImage *i;
    136 
    137     if (!activeImage)
    138 	return;
    139     if (n_terminal_image >= max_terminal_image) {
    140 	max_terminal_image = max_terminal_image ? (2 * max_terminal_image) : 8;
    141 	terminal_image = New_Reuse(TerminalImage, terminal_image,
    142 				   max_terminal_image);
    143     }
    144     i = &terminal_image[n_terminal_image];
    145     i->cache = cache;
    146     i->x = x;
    147     i->y = y;
    148     i->sx = sx;
    149     i->sy = sy;
    150     i->width = w;
    151     i->height = h;
    152     n_terminal_image++;
    153 }
    154 
    155 static void
    156 syncImage(void)
    157 {
    158     fputs("3;\n", Imgdisplay_wf);	/* XSync() */
    159     fputs("4;\n", Imgdisplay_wf);	/* put '\n' */
    160     while (fflush(Imgdisplay_wf) != 0) {
    161 	if (ferror(Imgdisplay_wf))
    162 	    goto err;
    163     }
    164     if (!fgetc(Imgdisplay_rf))
    165 	goto err;
    166     return;
    167   err:
    168     closeImgdisplay();
    169     image_index += MAX_IMAGE;
    170     n_terminal_image = 0;
    171 }
    172 
    173 void
    174 drawImage()
    175 {
    176     static char buf[64];
    177     int j, draw = FALSE;
    178     TerminalImage *i;
    179 
    180     if (!activeImage)
    181 	return;
    182     if (!n_terminal_image)
    183 	return;
    184     for (j = 0; j < n_terminal_image; j++) {
    185 	i = &terminal_image[j];
    186 	if (!(i->cache->loaded & IMG_FLAG_LOADED &&
    187 	      i->width > 0 && i->height > 0))
    188 	    continue;
    189 	if (!(Imgdisplay_rf && Imgdisplay_wf)) {
    190 	    if (!openImgdisplay())
    191 		return;
    192 	}
    193 	if (i->cache->index > 0) {
    194 	    i->cache->index *= -1;
    195 	    fputs("0;", Imgdisplay_wf);	/* DrawImage() */
    196 	}
    197 	else
    198 	    fputs("1;", Imgdisplay_wf);	/* DrawImage(redraw) */
    199 	sprintf(buf, "%d;%d;%d;%d;%d;%d;%d;%d;%d;",
    200 		(-i->cache->index - 1) % MAX_IMAGE + 1, i->x, i->y,
    201 		(i->cache->width > 0) ? i->cache->width : 0,
    202 		(i->cache->height > 0) ? i->cache->height : 0,
    203 		i->sx, i->sy, i->width, i->height);
    204 	fputs(buf, Imgdisplay_wf);
    205 	fputs(i->cache->file, Imgdisplay_wf);
    206 	fputs("\n", Imgdisplay_wf);
    207 	draw = TRUE;
    208     }
    209     if (!draw)
    210 	return;
    211     syncImage();
    212     touch_cursor();
    213     refresh();
    214 }
    215 
    216 void
    217 clearImage()
    218 {
    219     static char buf[64];
    220     int j;
    221     TerminalImage *i;
    222 
    223     if (!activeImage)
    224 	return;
    225     if (!n_terminal_image)
    226 	return;
    227     if (!Imgdisplay_wf) {
    228 	n_terminal_image = 0;
    229 	return;
    230     }
    231     for (j = 0; j < n_terminal_image; j++) {
    232 	i = &terminal_image[j];
    233 	if (!(i->cache->loaded & IMG_FLAG_LOADED &&
    234 	      i->width > 0 && i->height > 0))
    235 	    continue;
    236 	sprintf(buf, "6;%d;%d;%d;%d\n", i->x, i->y, i->width, i->height);
    237 	fputs(buf, Imgdisplay_wf);
    238     }
    239     syncImage();
    240     n_terminal_image = 0;
    241 }
    242 
    243 /* load image */
    244 
    245 #ifndef MAX_LOAD_IMAGE
    246 #define MAX_LOAD_IMAGE 8
    247 #endif
    248 static int n_load_image = 0;
    249 static Hash_sv *image_hash = NULL;
    250 static Hash_sv *image_file = NULL;
    251 static GeneralList *image_list = NULL;
    252 static ImageCache **image_cache = NULL;
    253 static Buffer *image_buffer = NULL;
    254 
    255 void
    256 deleteImage(Buffer *buf)
    257 {
    258     AnchorList *al;
    259     Anchor *a;
    260     int i;
    261 
    262     if (!buf)
    263 	return;
    264     al = buf->img;
    265     if (!al)
    266 	return;
    267     for (i = 0, a = al->anchors; i < al->nanchor; i++, a++) {
    268 	if (a->image && a->image->cache &&
    269 	    a->image->cache->loaded != IMG_FLAG_UNLOADED &&
    270 	    !(a->image->cache->loaded & IMG_FLAG_DONT_REMOVE) &&
    271 	    a->image->cache->index < 0)
    272 	    unlink(a->image->cache->file);
    273     }
    274     loadImage(NULL, IMG_FLAG_STOP);
    275 }
    276 
    277 void
    278 getAllImage(Buffer *buf)
    279 {
    280     AnchorList *al;
    281     Anchor *a;
    282     ParsedURL *current;
    283     int i;
    284 
    285     image_buffer = buf;
    286     if (!buf)
    287 	return;
    288     buf->image_loaded = TRUE;
    289     al = buf->img;
    290     if (!al)
    291 	return;
    292     current = baseURL(buf);
    293     for (i = 0, a = al->anchors; i < al->nanchor; i++, a++) {
    294 	if (a->image) {
    295 	    a->image->cache = getImage(a->image, current, buf->image_flag);
    296 	    if (a->image->cache &&
    297 		a->image->cache->loaded == IMG_FLAG_UNLOADED)
    298 		buf->image_loaded = FALSE;
    299 	}
    300     }
    301 }
    302 
    303 void
    304 showImageProgress(Buffer *buf)
    305 {
    306     AnchorList *al;
    307     Anchor *a;
    308     int i, l, n;
    309 
    310     if (!buf)
    311 	return;
    312     al = buf->img;
    313     if (!al)
    314 	return;
    315     for (i = 0, l = 0, n = 0, a = al->anchors; i < al->nanchor; i++, a++) {
    316 	if (a->image && a->hseq >= 0) {
    317 	    n++;
    318 	    if (a->image->cache && a->image->cache->loaded & IMG_FLAG_LOADED)
    319 		l++;
    320 	}
    321     }
    322     if (n) {
    323 	message(Sprintf("%d/%d images loaded", l, n)->ptr,
    324 		buf->cursorX + buf->rootX, buf->cursorY + buf->rootY);
    325 	refresh();
    326     }
    327 }
    328 
    329 void
    330 loadImage(Buffer *buf, int flag)
    331 {
    332     ImageCache *cache;
    333     struct stat st;
    334     int i, draw = FALSE;
    335     /* int wait_st; */
    336 
    337     if (maxLoadImage > MAX_LOAD_IMAGE)
    338 	maxLoadImage = MAX_LOAD_IMAGE;
    339     else if (maxLoadImage < 1)
    340 	maxLoadImage = 1;
    341     if (n_load_image == 0)
    342 	n_load_image = maxLoadImage;
    343     if (!image_cache) {
    344 	image_cache = New_N(ImageCache *, MAX_LOAD_IMAGE);
    345 	bzero(image_cache, sizeof(ImageCache *) * MAX_LOAD_IMAGE);
    346     }
    347     for (i = 0; i < n_load_image; i++) {
    348 	cache = image_cache[i];
    349 	if (!cache)
    350 	    continue;
    351 	if (lstat(cache->touch, &st))
    352 	    continue;
    353 	if (cache->pid) {
    354 	    kill(cache->pid, SIGKILL);
    355 	    /*
    356 	     * #ifdef HAVE_WAITPID
    357 	     * waitpid(cache->pid, &wait_st, 0);
    358 	     * #else
    359 	     * wait(&wait_st);
    360 	     * #endif
    361 	     */
    362 	    cache->pid = 0;
    363 	}
    364 	if (!stat(cache->file, &st)) {
    365 	    cache->loaded = IMG_FLAG_LOADED;
    366 	    if (getImageSize(cache)) {
    367 		if (image_buffer)
    368 		    image_buffer->need_reshape = TRUE;
    369 	    }
    370 	    draw = TRUE;
    371 	}
    372 	else
    373 	    cache->loaded = IMG_FLAG_ERROR;
    374 	unlink(cache->touch);
    375 	image_cache[i] = NULL;
    376     }
    377 
    378     for (i = (buf != image_buffer) ? 0 : maxLoadImage; i < n_load_image; i++) {
    379 	cache = image_cache[i];
    380 	if (!cache)
    381 	    continue;
    382 	if (cache->pid) {
    383 	    kill(cache->pid, SIGKILL);
    384 	    /*
    385 	     * #ifdef HAVE_WAITPID
    386 	     * waitpid(cache->pid, &wait_st, 0);
    387 	     * #else
    388 	     * wait(&wait_st);
    389 	     * #endif
    390 	     */
    391 	    cache->pid = 0;
    392 	}
    393 	unlink(cache->touch);
    394 	image_cache[i] = NULL;
    395     }
    396 
    397     if (flag == IMG_FLAG_STOP) {
    398 	image_list = NULL;
    399 	image_file = NULL;
    400 	n_load_image = maxLoadImage;
    401 	image_buffer = NULL;
    402 	return;
    403     }
    404 
    405     if (draw && image_buffer) {
    406 	drawImage();
    407 	showImageProgress(image_buffer);
    408     }
    409 
    410     image_buffer = buf;
    411 
    412     if (!image_list)
    413 	return;
    414     for (i = 0; i < n_load_image; i++) {
    415 	if (image_cache[i])
    416 	    continue;
    417 	while (1) {
    418 	    cache = (ImageCache *) popValue(image_list);
    419 	    if (!cache) {
    420 		for (i = 0; i < n_load_image; i++) {
    421 		    if (image_cache[i])
    422 			return;
    423 		}
    424 		image_list = NULL;
    425 		image_file = NULL;
    426 		if (image_buffer)
    427 		    displayBuffer(image_buffer, B_NORMAL);
    428 		return;
    429 	    }
    430 	    if (cache->loaded == IMG_FLAG_UNLOADED)
    431 		break;
    432 	}
    433 	image_cache[i] = cache;
    434 
    435 	flush_tty();
    436 	if ((cache->pid = fork()) == 0) {
    437 	    Buffer *b;
    438 	    /*
    439 	     * setup_child(TRUE, 0, -1);
    440 	     */
    441 	    setup_child(FALSE, 0, -1);
    442 	    image_source = cache->file;
    443 	    b = loadGeneralFile(cache->url, cache->current, NULL, 0, NULL);
    444 	    if (!b || !b->real_type || strncasecmp(b->real_type, "image/", 6))
    445 		unlink(cache->file);
    446 #if defined(HAVE_SYMLINK) && defined(HAVE_LSTAT)
    447 	    symlink(cache->file, cache->touch);
    448 #else
    449 	    {
    450 		FILE *f = fopen(cache->touch, "w");
    451 		if (f)
    452 		    fclose(f);
    453 	    }
    454 #endif
    455 	    exit(0);
    456 	}
    457 	else if (cache->pid < 0) {
    458 	    cache->pid = 0;
    459 	    return;
    460 	}
    461     }
    462 }
    463 
    464 ImageCache *
    465 getImage(Image * image, ParsedURL *current, int flag)
    466 {
    467     Str key = NULL;
    468     ImageCache *cache;
    469 
    470     if (!activeImage)
    471 	return NULL;
    472     if (!image_hash)
    473 	image_hash = newHash_sv(100);
    474     if (image->cache)
    475 	cache = image->cache;
    476     else {
    477 	key = Sprintf("%d;%d;%s", image->width, image->height, image->url);
    478 	cache = (ImageCache *) getHash_sv(image_hash, key->ptr, NULL);
    479     }
    480     if (cache && cache->index && abs(cache->index) <= image_index - MAX_IMAGE) {
    481 	struct stat st;
    482 	if (stat(cache->file, &st))
    483 	    cache->loaded = IMG_FLAG_UNLOADED;
    484 	cache->index = 0;
    485     }
    486 
    487     if (!cache) {
    488 	if (flag == IMG_FLAG_SKIP)
    489 	    return NULL;
    490 
    491 	cache = New(ImageCache);
    492 	cache->url = image->url;
    493 	cache->current = current;
    494 	cache->file = tmpfname(TMPF_DFL, image->ext)->ptr;
    495 	cache->touch = tmpfname(TMPF_DFL, NULL)->ptr;
    496 	cache->pid = 0;
    497 	cache->index = 0;
    498 	cache->loaded = IMG_FLAG_UNLOADED;
    499 	cache->width = image->width;
    500 	cache->height = image->height;
    501 	putHash_sv(image_hash, key->ptr, (void *)cache);
    502     }
    503     if (flag != IMG_FLAG_SKIP) {
    504 	if (cache->loaded == IMG_FLAG_UNLOADED) {
    505 	    if (!image_file)
    506 		image_file = newHash_sv(100);
    507 	    if (!getHash_sv(image_file, cache->file, NULL)) {
    508 		putHash_sv(image_file, cache->file, (void *)cache);
    509 		if (!image_list)
    510 		    image_list = newGeneralList();
    511 		pushValue(image_list, (void *)cache);
    512 	    }
    513 	}
    514 	if (!cache->index)
    515 	    cache->index = ++image_index;
    516     }
    517     if (cache->loaded & IMG_FLAG_LOADED)
    518 	getImageSize(cache);
    519     return cache;
    520 }
    521 
    522 int
    523 getImageSize(ImageCache * cache)
    524 {
    525     Str tmp;
    526     FILE *f;
    527     int w = 0, h = 0;
    528 
    529     if (!activeImage)
    530 	return FALSE;
    531     if (!cache || !(cache->loaded & IMG_FLAG_LOADED) ||
    532 	(cache->width > 0 && cache->height > 0))
    533 	return FALSE;
    534     tmp = Strnew();
    535     if (!strchr(Imgdisplay, '/'))
    536 	Strcat_m_charp(tmp, w3m_auxbin_dir(), "/", NULL);
    537     Strcat_m_charp(tmp, Imgdisplay, " -size ", shell_quote(cache->file), NULL);
    538     f = popen(tmp->ptr, "r");
    539     if (!f)
    540 	return FALSE;
    541     while (fscanf(f, "%d %d", &w, &h) < 0) {
    542 	if (feof(f))
    543 	    break;
    544     }
    545     pclose(f);
    546 
    547     if (!(w > 0 && h > 0))
    548 	return FALSE;
    549     w = (int)(w * image_scale / 100 + 0.5);
    550     if (w == 0)
    551 	w = 1;
    552     h = (int)(h * image_scale / 100 + 0.5);
    553     if (h == 0)
    554 	h = 1;
    555     if (cache->width < 0 && cache->height < 0) {
    556 	cache->width = (w > MAX_IMAGE_SIZE) ? MAX_IMAGE_SIZE : w;
    557 	cache->height = (h > MAX_IMAGE_SIZE) ? MAX_IMAGE_SIZE : h;
    558     }
    559     else if (cache->width < 0) {
    560 	int tmp = (int)((double)cache->height * w / h + 0.5);
    561 	cache->width = (tmp > MAX_IMAGE_SIZE) ? MAX_IMAGE_SIZE : tmp;
    562     }
    563     else if (cache->height < 0) {
    564 	int tmp = (int)((double)cache->width * h / w + 0.5);
    565 	cache->height = (tmp > MAX_IMAGE_SIZE) ? MAX_IMAGE_SIZE : tmp;
    566     }
    567     if (cache->width == 0)
    568 	cache->width = 1;
    569     if (cache->height == 0)
    570 	cache->height = 1;
    571     tmp = Sprintf("%d;%d;%s", cache->width, cache->height, cache->url);
    572     putHash_sv(image_hash, tmp->ptr, (void *)cache);
    573     return TRUE;
    574 }
    575 #endif