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