win_w3mimg.cpp (27128B)
1 /* $Id$ */ 2 #include <stdio.h> 3 #include <stdlib.h> 4 #include <string.h> 5 #include <ctype.h> 6 #include "config.h" 7 #include <assert.h> 8 #include <locale.h> 9 10 #include <new> 11 #include <algorithm> 12 13 #include "w3mimg/w3mimg.h" 14 #include <windows.h> 15 #include <gdiplus.h> 16 #include <unistd.h> 17 #include <sys/cygwin.h> 18 /* GDI+ can handle BMP, GIF, JPEG, PNG and TIFF by itself. */ 19 20 #define OFFSET_X 2 21 #define OFFSET_Y 2 22 #define DEBUG 23 24 #ifdef DEBUG 25 #define THROW_NONE throw() 26 #else 27 #define THROW_NONE 28 #endif 29 30 struct win_info { 31 HWND window; 32 Gdiplus::ARGB background_pixel; 33 ULONG_PTR gdiplus_token; 34 FILE *logfile; 35 }; 36 37 struct window_list { 38 HWND *wnd; 39 size_t nwnd; 40 size_t capacity; 41 }; 42 43 typedef Gdiplus::CachedBitmap *cache_handle; 44 class win_image { 45 private: 46 win_image(const win_image &); // decl only 47 win_image &operator=(const win_image &); // decl only 48 49 Gdiplus::Bitmap *gpbitmap; 50 unsigned int nframe; 51 unsigned int current; 52 unsigned long tick; 53 unsigned long loopcount; // zero = infinite 54 unsigned long *delay; // unit: millisecond 55 cache_handle *cache; 56 57 public: 58 win_image() THROW_NONE; 59 ~win_image() THROW_NONE; 60 int load(w3mimg_op *wop, Gdiplus::Bitmap **p_gpbitmap, 61 int *wreturn, int *hreturn) THROW_NONE; 62 int show(w3mimg_op *wop, int sx, int sy, int sw, int sh, int x, int y) THROW_NONE; 63 int animate(w3mimg_op *wop) THROW_NONE; 64 }; 65 66 static int win_init(w3mimg_op * wop) THROW_NONE; 67 static int win_finish(w3mimg_op * wop) THROW_NONE; 68 static int win_active(w3mimg_op * wop) THROW_NONE; 69 static void win_set_background(w3mimg_op * wop, char *background) THROW_NONE; 70 static void win_sync(w3mimg_op * wop) THROW_NONE; 71 static void win_close(w3mimg_op * wop) THROW_NONE; 72 73 static int win_load_image(w3mimg_op * wop, W3MImage * img, char *fname, 74 int w, int h) THROW_NONE; 75 static int win_show_image(w3mimg_op * wop, W3MImage * img, 76 int sx, int sy, int sw, int sh, int x, int y) THROW_NONE; 77 static void win_free_image(w3mimg_op * wop, W3MImage * img) THROW_NONE; 78 static int win_get_image_size(w3mimg_op * wop, W3MImage * img, 79 char *fname, int *w, int *h) THROW_NONE; 80 static int win_clear(w3mimg_op * wop, int x, int y, int w, int h) THROW_NONE; 81 82 static int window_alive(w3mimg_op *wop) THROW_NONE; 83 static Gdiplus::Bitmap *read_image_file(w3mimg_op *wop, const char *fname) THROW_NONE; 84 static BOOL CALLBACK store_to_window_list(HWND hWnd, LPARAM wndlist) THROW_NONE; 85 static void clear_window_list(struct window_list *wl) THROW_NONE; 86 static const char *gdip_strerror(Gdiplus::Status status) THROW_NONE; 87 static void gdip_perror(w3mimg_op *wop, Gdiplus::Status status, const char *func) THROW_NONE; 88 static char *win32_strerror_alloc(DWORD status) THROW_NONE; 89 static void win32_perror(w3mimg_op *wop, DWORD status, const char *func) THROW_NONE; 90 #if 0 /* unused */ 91 static WCHAR *mb2wstr_alloc(const char *) THROW_NONE; 92 static char *wstr2mb_alloc(const WCHAR *) THROW_NONE; 93 #endif 94 95 #define PRELUDE(wop, xi) \ 96 assert(wop); \ 97 struct win_info *xi = static_cast<struct win_info *>(wop->priv); \ 98 assert(xi) 99 100 win_image::win_image() THROW_NONE 101 : gpbitmap(NULL), nframe(0) 102 {} 103 104 win_image::~win_image() THROW_NONE 105 { 106 if (this->cache) { 107 for (size_t i = 0; i != this->nframe; ++i) { 108 delete this->cache[i]; 109 } 110 delete[] this->cache; 111 } 112 delete[] this->delay; 113 delete this->gpbitmap; 114 } 115 116 int 117 win_image::load(w3mimg_op *wop, Gdiplus::Bitmap **p_gpbitmap, int *wreturn, int *hreturn) THROW_NONE 118 { 119 PRELUDE(wop, xi); 120 Gdiplus::Bitmap *gpbitmap = *p_gpbitmap; 121 assert(gpbitmap); 122 Gdiplus::Status status = Gdiplus::Ok; 123 int retval = 0; 124 125 Gdiplus::PropertyItem *loopcountbuf = NULL; 126 Gdiplus::PropertyItem *delaybuf = NULL; 127 unsigned long *delay = NULL; 128 cache_handle *cache = NULL; 129 130 if (xi->logfile) { 131 fprintf(xi->logfile, "win_image::load(%p, %p, %p, %p) start\n", 132 wop, gpbitmap, wreturn, hreturn); 133 } 134 { 135 unsigned int width = gpbitmap->GetWidth(); 136 unsigned int height = gpbitmap->GetHeight(); 137 unsigned int nframe = gpbitmap->GetFrameCount(&Gdiplus::FrameDimensionTime); 138 unsigned long loopcount = 0; 139 unsigned int first_frame = 0; 140 141 if (xi->logfile) 142 fprintf(xi->logfile, "win_image::load(): size[0]=%ux%u\n", width, height); 143 if (nframe == 0) { 144 // Not an animated picture 145 if (xi->logfile) 146 fprintf(xi->logfile, "win_image::load(): zero frame count\n"); 147 nframe = 1; 148 delay = new(std::nothrow) unsigned long[1]; 149 if (delay == NULL) 150 goto last; 151 delay[0] = 0; 152 } else { 153 unsigned int loopcountsize = gpbitmap->GetPropertyItemSize(PropertyTagLoopCount); 154 unsigned int delaysize = gpbitmap->GetPropertyItemSize(PropertyTagFrameDelay); 155 156 // Get loop count 157 if (loopcountsize != 0) { 158 loopcountbuf = (Gdiplus::PropertyItem *)malloc(loopcountsize); 159 if (loopcountbuf == NULL) 160 goto last; 161 status = gpbitmap->GetPropertyItem(PropertyTagLoopCount, loopcountsize, loopcountbuf); 162 if (status != Gdiplus::Ok) 163 goto gdip_error; 164 if (loopcountbuf->type == PropertyTagTypeShort && 165 loopcountbuf->length >= sizeof(unsigned short)) { 166 loopcount = *(unsigned short *)loopcountbuf->value; 167 } else if (loopcountbuf->type == PropertyTagTypeLong && 168 loopcountbuf->length >= sizeof(unsigned long)) { 169 loopcount = *(unsigned long *)loopcountbuf->value; 170 } 171 } 172 if (xi->logfile) 173 fprintf(xi->logfile, "win_image::load(): loopcount=%lu\n", loopcount); 174 // Get delay times 175 if (delaysize != 0) { 176 delaybuf = (Gdiplus::PropertyItem *)malloc(delaysize); 177 if (delaybuf == NULL) 178 goto last; 179 status = gpbitmap->GetPropertyItem(PropertyTagFrameDelay, delaysize, delaybuf); 180 if (status != Gdiplus::Ok) 181 goto gdip_error; 182 delay = new(std::nothrow) unsigned long[nframe]; 183 if (delay == NULL) 184 goto last; 185 std::fill(delay, delay + nframe, 0); 186 if (delaybuf->type == PropertyTagTypeShort) { 187 unsigned int count = delaybuf->length / sizeof(unsigned short); 188 for (unsigned int i = 0; i != count; ++i) 189 delay[i] = ((unsigned short *)delaybuf->value)[i] * 10; 190 } else if (delaybuf->type == PropertyTagTypeLong) { 191 unsigned int count = delaybuf->length / sizeof(unsigned long); 192 for (unsigned int i = 0; i != count; ++i) 193 delay[i] = ((unsigned long *)delaybuf->value)[i] * 10; 194 } 195 } 196 if (xi->logfile) { 197 for (unsigned int i = 0; i != nframe; ++i) 198 fprintf(xi->logfile, "win_image::load(): delay[%u]=%lu\n", i, delay[i]); 199 } 200 // Get dimensions 201 for (unsigned int nextframe = 1; nextframe != nframe; ++nextframe) { 202 status = gpbitmap->SelectActiveFrame(&Gdiplus::FrameDimensionTime, nextframe); 203 if (status != Gdiplus::Ok) { 204 if (xi->logfile) 205 fprintf(xi->logfile, "win_image::load(): SelectActiveFrame() to %u failed = %d: %s\n", 206 nextframe, (int)status, gdip_strerror(status)); 207 goto last; 208 } 209 unsigned int iw = gpbitmap->GetWidth(); 210 unsigned int ih = gpbitmap->GetHeight(); 211 if (iw > width) 212 width = iw; 213 if (ih > height) 214 height = ih; 215 if (xi->logfile) 216 fprintf(xi->logfile, "win_image::load(): size[%u]=%ux%u\n", nextframe, iw, ih); 217 } 218 // Go to the first frame 219 first_frame = (0 < -wop->max_anim && -wop->max_anim < nframe) ? (nframe + wop->max_anim) : 0; 220 status = gpbitmap->SelectActiveFrame(&Gdiplus::FrameDimensionTime, first_frame); 221 if (status != Gdiplus::Ok) { 222 if (xi->logfile) 223 fprintf(xi->logfile, "win_image::load(): SelectActiveFrame() to %u frame = %d: %s\n", 224 first_frame, (int)status, gdip_strerror(status)); 225 goto last; 226 } 227 } 228 // Allocate cache array 229 cache = new(std::nothrow) cache_handle[nframe]; 230 if (cache == NULL) 231 goto last; 232 std::fill(cache, cache + nframe, (cache_handle)NULL); 233 // Sanity check 234 if (width > SHRT_MAX || height > SHRT_MAX) { 235 if (xi->logfile) 236 fprintf(xi->logfile, "win_image::load(): too big image: %ux%u\n", width, height); 237 goto last; 238 } 239 // Store the results 240 if (wreturn) 241 *wreturn = (int)width; 242 if (hreturn) 243 *hreturn = (int)height; 244 this->gpbitmap = gpbitmap; 245 *p_gpbitmap = NULL; // ownership transfer 246 this->nframe = nframe; 247 this->current = first_frame; 248 this->tick = 0; 249 this->loopcount = loopcount; 250 this->delay = delay; 251 delay = NULL; // ownership transfer 252 this->cache = cache; 253 cache = NULL; // ownership transfer 254 retval = 1; 255 } 256 goto last; 257 258 gdip_error: 259 gdip_perror(wop, status, "win_image::load"); 260 goto last; 261 last: 262 delete[] cache; 263 delete[] delay; 264 free(delaybuf); 265 free(loopcountbuf); 266 if (xi->logfile) 267 fprintf(xi->logfile, "win_image::load() = %d\n", retval); 268 return retval; 269 } 270 271 int 272 win_image::show(w3mimg_op *wop, int sx, int sy, int sw, int sh, int x, int y) THROW_NONE 273 { 274 PRELUDE(wop, xi); 275 int retval = 0; 276 Gdiplus::Status status = Gdiplus::Ok; 277 cache_handle newcache = NULL; 278 279 if (xi->logfile) 280 fprintf(xi->logfile, "win_image::show(%p, %d, %d, %d, %d, %d, %d) start current=%u\n", 281 wop, sx, sy, sw, sh, x, y, this->current); 282 if (!window_alive(wop)) 283 goto last; 284 { 285 int xx = x + wop->offset_x; 286 int yy = y + wop->offset_y; 287 288 // Prepare the Graphics object for painting 289 Gdiplus::Graphics graphics(xi->window); 290 if ((status = graphics.GetLastStatus()) != Gdiplus::Ok) 291 goto gdip_error; 292 Gdiplus::Rect clip(xx, yy, sw, sh); 293 status = graphics.SetClip(clip); 294 if (status != Gdiplus::Ok) 295 goto gdip_error; 296 297 unsigned int retry_count = 2; 298 do { 299 if (this->cache[this->current] == NULL) { 300 // Cache the image 301 Gdiplus::Bitmap tmp_bitmap(sw, sh, &graphics); 302 if ((status = tmp_bitmap.GetLastStatus()) != Gdiplus::Ok) 303 goto gdip_error; 304 Gdiplus::Graphics tmp_graphics(&tmp_bitmap); 305 if ((status = tmp_graphics.GetLastStatus()) != Gdiplus::Ok) 306 goto gdip_error; 307 status = tmp_graphics.Clear(Gdiplus::Color(xi->background_pixel)); 308 if (status != Gdiplus::Ok) 309 goto gdip_error; 310 status = tmp_graphics.DrawImage(this->gpbitmap, 0, 0, sw, sh); 311 if (status != Gdiplus::Ok) 312 goto gdip_error; 313 Gdiplus::CachedBitmap *newcache = new Gdiplus::CachedBitmap(&tmp_bitmap, &graphics); 314 if (newcache == NULL) 315 goto last; 316 if ((status = newcache->GetLastStatus()) != Gdiplus::Ok) 317 goto gdip_error; 318 this->cache[this->current] = newcache; 319 newcache = NULL; // ownership transfer 320 --retry_count; 321 } 322 // Draw it 323 status = graphics.DrawCachedBitmap(this->cache[this->current], xx - sx, yy - sy); 324 if (status == Gdiplus::Ok) 325 break; 326 // maybe the user altered the display configuration 327 if (xi->logfile) 328 fprintf(xi->logfile, "win_image::show(): stale cache = %d: %s\n", 329 (int)status, gdip_strerror(status)); 330 delete this->cache[this->current]; 331 this->cache[this->current] = NULL; 332 if (retry_count == 0) 333 goto last; 334 } while (1); 335 336 retval = 1; 337 } 338 goto last; 339 gdip_error: 340 gdip_perror(wop, status, "win_image::show"); 341 goto last; 342 last: 343 delete newcache; 344 if (xi->logfile) 345 fprintf(xi->logfile, "win_image::show() = %d\n", retval); 346 return retval; 347 } 348 349 int 350 win_image::animate(w3mimg_op * wop) THROW_NONE 351 { 352 PRELUDE(wop, xi); 353 int retval = 0; 354 Gdiplus::Status status = Gdiplus::Ok; 355 356 if (xi->logfile) 357 fprintf(xi->logfile, "win_image::animate(%p) start\n", wop); 358 { 359 if (this->nframe <= 1) 360 goto animation_end; 361 #define UNIT_TICK 50 362 #define MIN_DELAY (UNIT_TICK*2) 363 this->tick += UNIT_TICK; 364 if (this->tick >= MIN_DELAY && this->tick >= this->delay[this->current]) { 365 this->tick = 0; 366 unsigned int nextframe = this->current + 1; 367 if (wop->max_anim == nextframe) 368 goto animation_end; 369 if (nextframe >= this->nframe) { 370 if (this->loopcount == 1 || wop->max_anim < 0) // end of the loop 371 goto animation_end; 372 nextframe = 0; 373 } 374 status = this->gpbitmap->SelectActiveFrame(&Gdiplus::FrameDimensionTime, nextframe); 375 if (status != Gdiplus::Ok) 376 goto gdip_error; 377 this->current = nextframe; 378 if (nextframe == 0 && this->loopcount > 1) 379 --this->loopcount; 380 } 381 animation_end: 382 retval = 1; 383 } 384 goto last; 385 gdip_error: 386 gdip_perror(wop, status, "win_image::animate"); 387 goto last; 388 last: 389 if (xi->logfile) 390 fprintf(xi->logfile, "win_image::animate() = %d\n", retval); 391 return retval; 392 } 393 394 static int 395 window_alive(w3mimg_op *wop) THROW_NONE 396 { 397 PRELUDE(wop, xi); 398 if (xi->window == NULL) 399 return 0; 400 if (IsWindow(xi->window)) 401 return 1; 402 xi->window = NULL; 403 fputs("w3mimgdisplay: target window disappeared\n", stderr); 404 if (xi->logfile) 405 fputs("w3mimgdisplay: target window disappeared\n", xi->logfile); 406 return 0; 407 } 408 409 static int 410 win_init(w3mimg_op *) THROW_NONE 411 { 412 // nothing to do 413 return 1; 414 } 415 416 static int 417 win_finish(w3mimg_op *) THROW_NONE 418 { 419 // nothing to do 420 return 1; 421 } 422 423 static int 424 win_clear(w3mimg_op *wop, int x, int y, int w, int h) THROW_NONE 425 { 426 PRELUDE(wop, xi); 427 Gdiplus::Status status = Gdiplus::Ok; 428 int retval = 0; 429 430 if (xi->logfile) 431 fprintf(xi->logfile, "win_clear(%p, %d, %d, %d, %d) start\n", 432 wop, x, y, w, h); 433 if (!window_alive(wop)) 434 goto last; 435 { 436 if (x < 0) 437 x = 0; 438 if (y < 0) 439 y = 0; 440 Gdiplus::SolidBrush brush(Gdiplus::Color(xi->background_pixel)); 441 if ((status = brush.GetLastStatus()) != Gdiplus::Ok) 442 goto gdip_error; 443 Gdiplus::Graphics graphics(xi->window); 444 if ((status = graphics.GetLastStatus()) != Gdiplus::Ok) 445 goto gdip_error; 446 status = graphics.FillRectangle(&brush, x + wop->offset_x, y + wop->offset_y, w, h); 447 if (status != Gdiplus::Ok) 448 goto gdip_error; 449 retval = 1; 450 } 451 goto last; 452 gdip_error: 453 gdip_perror(wop, status, "win_clear"); 454 goto last; 455 last: 456 if (xi->logfile) 457 fprintf(xi->logfile, "win_clear() = %d\n", retval); 458 return retval; 459 } 460 461 static int 462 win_active(w3mimg_op * wop) THROW_NONE 463 { 464 return window_alive(wop); 465 } 466 467 static void 468 win_set_background(w3mimg_op * wop, char *background) THROW_NONE 469 { 470 PRELUDE(wop, xi); 471 472 HDC windc = NULL; 473 474 if (xi->logfile) 475 fprintf(xi->logfile, "win_set_background(%p, \"%s\")\n", wop, background ? background : "(auto)"); 476 { 477 // Fallback value 478 // xi->background_pixel = Gdiplus::Color::White; 479 xi->background_pixel = Gdiplus::Color::Black; 480 481 // Explicit 482 if (background) { 483 unsigned int r, g, b; 484 if (sscanf(background, "#%02x%02x%02x", &r, &g, &b) == 3) { 485 xi->background_pixel = Gdiplus::Color::MakeARGB((BYTE)255, (BYTE)r, (BYTE)g, (BYTE)b); 486 goto last; 487 } 488 } 489 490 // Auto detect 491 if (xi->window == NULL || !IsWindow(xi->window)) 492 goto last; 493 windc = GetDC(xi->window); 494 if (windc == NULL) 495 goto win32_error; 496 COLORREF c = GetPixel(windc, 497 (wop->offset_x >= 1) ? (wop->offset_x - 1) : 0, 498 (wop->offset_y >= 1) ? (wop->offset_y - 1) : 0); 499 xi->background_pixel = Gdiplus::Color::MakeARGB( 500 (BYTE)255, GetRValue(c), GetGValue(c), GetBValue(c)); 501 } 502 goto last; 503 win32_error: 504 win32_perror(wop, GetLastError(), "win_set_background"); 505 goto last; 506 last: 507 if (xi->logfile) 508 fprintf(xi->logfile, "win_set_background() result = #%06x\n", 509 (unsigned int)xi->background_pixel); 510 if (windc) 511 ReleaseDC(xi->window, windc); 512 } 513 514 static void 515 win_sync(w3mimg_op *) THROW_NONE 516 { 517 // nothing to do 518 return; 519 } 520 521 static void 522 win_close(w3mimg_op * wop) THROW_NONE 523 { 524 PRELUDE(wop, xi); 525 526 if (xi->gdiplus_token) 527 Gdiplus::GdiplusShutdown(xi->gdiplus_token); 528 if (xi->logfile) { 529 fprintf(xi->logfile, "win_close(%p)\n", wop); 530 fclose(xi->logfile); 531 } 532 delete xi; 533 delete wop; 534 } 535 536 static Gdiplus::Bitmap * 537 read_image_file(w3mimg_op *wop, const char *fname) THROW_NONE 538 { 539 PRELUDE(wop, xi); 540 Gdiplus::Status status = Gdiplus::Ok; 541 Gdiplus::Bitmap *retval = NULL; 542 543 WCHAR *wfname = NULL; 544 Gdiplus::Bitmap *gpbitmap = NULL; 545 546 if (xi->logfile) 547 fprintf(xi->logfile, "read_image_file(%p, \"%s\") start\n", wop, fname); 548 { 549 wfname = (WCHAR *)cygwin_create_path(CCP_POSIX_TO_WIN_W, fname); 550 if (wfname == NULL) 551 goto last; 552 gpbitmap = new Gdiplus::Bitmap(wfname); 553 if (gpbitmap == NULL) 554 goto last; 555 status = gpbitmap->GetLastStatus(); 556 switch (status) { 557 case Gdiplus::Ok: 558 break; 559 case Gdiplus::UnknownImageFormat: 560 case Gdiplus::FileNotFound: 561 goto last; // fail silently 562 default: 563 goto gdip_error; 564 } 565 retval = gpbitmap; 566 gpbitmap = NULL; // ownership transfer 567 } 568 goto last; 569 gdip_error: 570 gdip_perror(wop, status, "read_image_file"); 571 last: 572 delete gpbitmap; 573 free(wfname); 574 if (xi->logfile) 575 fprintf(xi->logfile, "read_image_file() = %p\n", retval); 576 return retval; 577 } 578 579 static int 580 win_load_image(w3mimg_op * wop, W3MImage * img, char *fname, int w, int h) THROW_NONE 581 { 582 PRELUDE(wop, xi); 583 int retval = 0; 584 Gdiplus::Bitmap *gpbitmap = NULL; 585 win_image *wimg = NULL; 586 587 assert(img); 588 if (xi->logfile) { 589 fprintf(xi->logfile, "win_load_image(%p, %p, \"%s\", %d, %d) start\n", 590 wop, img, fname, w, h); 591 } 592 { 593 gpbitmap = read_image_file(wop, fname); 594 if (gpbitmap == NULL) 595 goto last; 596 int iw, ih; 597 wimg = new(std::nothrow) win_image; 598 if (!wimg->load(wop, &gpbitmap, &iw, &ih)) 599 goto last; 600 img->pixmap = wimg; 601 wimg = NULL; // ownership transfer 602 img->width = (0 <= w && w < iw) ? w : iw; 603 img->height = (0 <= h && h < ih) ? h : ih; 604 retval = 1; 605 } 606 goto last; 607 last: 608 delete wimg; 609 delete gpbitmap; 610 if (xi->logfile) 611 fprintf(xi->logfile, "win_load_image() = %d\n", retval); 612 return retval; 613 } 614 615 static int 616 win_show_image(w3mimg_op * wop, W3MImage * img, int sx, int sy, int sw, 617 int sh, int x, int y) THROW_NONE 618 { 619 PRELUDE(wop, xi); 620 int retval = 0; 621 622 assert(img); 623 win_image *wimg = static_cast<win_image *>(img->pixmap); 624 assert(wimg); 625 626 if (xi->logfile) 627 fprintf(xi->logfile, "win_show_image(%p, %p, %d, %d, %d, %d, %d, %d) start\n", 628 wop, img, sx, sy, sw, sh, x, y); 629 int sww = sw ? sw : img->width; 630 int shh = sh ? sh : img->height; 631 retval = wimg->show(wop, sx, sy, sww, shh, x, y) 632 && wimg->animate(wop); 633 if (xi->logfile) 634 fprintf(xi->logfile, "win_show_image() = %d\n", retval); 635 return retval; 636 } 637 638 static void 639 win_free_image(w3mimg_op * wop, W3MImage * img) THROW_NONE 640 { 641 PRELUDE(wop, xi); 642 643 assert(img); 644 if (xi->logfile) 645 fprintf(xi->logfile, "win_free_image(%p, %p) pixmap=%p\n", wop, img, img->pixmap); 646 delete static_cast<win_image *>(img->pixmap); 647 img->pixmap = NULL; 648 img->width = 0; 649 img->height = 0; 650 } 651 652 static int 653 win_get_image_size(w3mimg_op * wop, W3MImage *img_unused, char *fname, int *w, int *h) THROW_NONE 654 { 655 PRELUDE(wop, xi); 656 int retval = 0; 657 Gdiplus::Bitmap *gpbitmap = NULL; 658 win_image *wimg = NULL; 659 660 if (xi->logfile) { 661 fprintf(xi->logfile, "win_get_image_size(%p, %p, \"%s\", %p, %p) start\n", 662 wop, img_unused, fname, w, h); 663 } 664 { 665 gpbitmap = read_image_file(wop, fname); 666 if (gpbitmap == NULL) 667 goto last; 668 wimg = new(std::nothrow) win_image; 669 if (wimg == NULL) 670 goto last; 671 retval = wimg->load(wop, &gpbitmap, w, h);; 672 } 673 goto last; 674 last: 675 delete wimg; 676 delete gpbitmap; 677 if (xi->logfile) 678 fprintf(xi->logfile, "win_get_image_size() = %d\n", retval); 679 return retval; 680 } 681 682 extern "C" w3mimg_op * 683 w3mimg_winopen() 684 { 685 w3mimg_op *retval = NULL; 686 Gdiplus::Status status = Gdiplus::Ok; 687 688 w3mimg_op *wop = NULL; 689 struct win_info *xi = NULL; 690 struct window_list children = { NULL, 0, 0 }; 691 692 { 693 // Quit if running on X 694 const char *display_name; 695 if ((display_name = getenv("DISPLAY")) != NULL && 696 display_name[0] && strcmp(display_name, ":0") != 0) 697 return NULL; 698 699 // Allocate the context objects 700 wop = new(std::nothrow) w3mimg_op(); // call the default ctor instead of "new w3mimg_op;" 701 if (wop == NULL) 702 return NULL; 703 wop->priv = xi = new(std::nothrow) win_info(); 704 if (xi == NULL) 705 goto last; 706 707 // Debug logging 708 const char *logging_dir; 709 if ((logging_dir = getenv("W3MIMG_LOGDIR")) != NULL && 710 logging_dir[0]) { 711 size_t l = strlen(logging_dir) + sizeof "/w3mimgXXXXXXXXXX.log"; 712 char *fname = (char *)malloc(l); 713 snprintf(fname, l, "%s/w3mimg%d.log", logging_dir, (int)getpid()); 714 xi->logfile = fopen(fname, "a"); 715 if (xi->logfile) { 716 setvbuf(xi->logfile, NULL, _IONBF, 0); 717 fprintf(xi->logfile, "\nw3mimg_winopen() start pid=%d\n", (int)getpid()); 718 } 719 } 720 721 // Look for the window to draw the image 722 xi->window = NULL; 723 const char *windowid; 724 if ((windowid = getenv("WINDOWID")) != NULL) 725 xi->window = FindWindowA(windowid, NULL); 726 if (!xi->window) 727 xi->window = GetForegroundWindow(); 728 if (!xi->window) 729 goto win32_error; 730 731 WINDOWINFO winfo = WINDOWINFO(); 732 winfo.cbSize = sizeof winfo; 733 GetWindowInfo(xi->window, &winfo); 734 wop->width = (int)(winfo.rcClient.right - winfo.rcClient.left); 735 wop->height = (int)(winfo.rcClient.bottom - winfo.rcClient.top); 736 737 // Search decendant windows and find out which is the text window 738 while (1) { 739 HWND p_window = xi->window; 740 741 clear_window_list(&children); 742 EnumChildWindows(xi->window, &store_to_window_list, (LPARAM)&children); 743 for (unsigned int i = 0; i < children.nwnd; i++) { 744 int width, height; 745 746 GetWindowInfo(children.wnd[i], &winfo); 747 width = (int)(winfo.rcClient.right - winfo.rcClient.left); 748 height = (int)(winfo.rcClient.bottom - winfo.rcClient.top); 749 if (width > wop->width * 0.7 && 750 height > wop->height * 0.7) { 751 /* maybe text window */ 752 wop->width = width; 753 wop->height = height; 754 xi->window = children.wnd[i]; 755 } 756 } 757 if (p_window == xi->window) 758 break; 759 } 760 761 // Terminal may leave some border pixels 762 wop->offset_x = OFFSET_X; 763 wop->offset_y = OFFSET_Y; 764 765 // Start up the GDI+ 766 Gdiplus::GdiplusStartupInput startup_input; /// default ctor 767 status = Gdiplus::GdiplusStartup(&xi->gdiplus_token, &startup_input, NULL); 768 if (status != Gdiplus::Ok) 769 goto gdip_error; 770 771 // Fill the context object 772 wop->init = win_init; 773 wop->finish = win_finish; 774 wop->active = win_active; 775 wop->set_background = win_set_background; 776 wop->sync = win_sync; 777 wop->close = win_close; 778 wop->clear = win_clear; 779 780 wop->load_image = win_load_image; 781 wop->show_image = win_show_image; 782 wop->free_image = win_free_image; 783 wop->get_image_size = win_get_image_size; 784 785 retval = wop; // take care of the object lifetime 786 } 787 goto last; 788 win32_error: 789 win32_perror(wop, GetLastError(), "w3mimg_winopen"); 790 goto last; 791 gdip_error: 792 gdip_perror(wop, status, "w3mimg_winopen"); 793 goto last; 794 last: 795 if (xi && xi->logfile) 796 fprintf(xi->logfile, "w3mimg_winopen() = %p\n", retval); 797 clear_window_list(&children); 798 if (!retval) { 799 if (xi) { 800 if (xi->gdiplus_token) 801 Gdiplus::GdiplusShutdown(xi->gdiplus_token); 802 if (xi->logfile) 803 fclose(xi->logfile); 804 delete xi; 805 } 806 delete wop; 807 } 808 return retval; 809 } 810 811 static BOOL CALLBACK 812 store_to_window_list(HWND hWnd, LPARAM wndlist) THROW_NONE 813 { 814 struct window_list *wl = (struct window_list *)wndlist; 815 816 if (wl->nwnd >= wl->capacity) { 817 size_t newsize = (wl->capacity < 4 ) ? 4 : (wl->capacity * 2); 818 HWND *newbuf = (HWND *)realloc(wl->wnd, newsize * sizeof newbuf[0]); 819 if (newbuf == NULL) { 820 clear_window_list(wl); 821 return FALSE; 822 } 823 wl->wnd = newbuf; 824 wl->capacity = newsize; 825 } 826 wl->wnd[wl->nwnd++] = hWnd; 827 return TRUE; 828 } 829 830 static void 831 clear_window_list(struct window_list *wl) THROW_NONE 832 { 833 free(wl->wnd); 834 wl->wnd = NULL; 835 wl->nwnd = 0; 836 wl->capacity = 0; 837 } 838 839 static const char * 840 gdip_strerror(Gdiplus::Status status) THROW_NONE 841 { 842 size_t i; 843 struct status_rec { 844 Gdiplus::Status code; 845 const char *str; 846 }; 847 static const struct status_rec table[] = { 848 #define ERRITEM(s) { Gdiplus::s, #s } 849 ERRITEM(Ok), 850 ERRITEM(GenericError), 851 ERRITEM(InvalidParameter), 852 ERRITEM(OutOfMemory), 853 ERRITEM(ObjectBusy), 854 ERRITEM(InsufficientBuffer), 855 ERRITEM(NotImplemented), 856 ERRITEM(Win32Error), 857 ERRITEM(WrongState), 858 ERRITEM(Aborted), 859 ERRITEM(FileNotFound), 860 ERRITEM(ValueOverflow), 861 ERRITEM(AccessDenied), 862 ERRITEM(UnknownImageFormat), 863 ERRITEM(FontFamilyNotFound), 864 ERRITEM(FontStyleNotFound), 865 ERRITEM(NotTrueTypeFont), 866 ERRITEM(UnsupportedGdiplusVersion), 867 ERRITEM(GdiplusNotInitialized), 868 ERRITEM(PropertyNotFound), 869 ERRITEM(PropertyNotSupported), 870 ERRITEM(ProfileNotFound), 871 #undef ERRITEM 872 }; 873 for (i = 0; i != sizeof table / sizeof table[0]; ++i) 874 if (table[i].code == status) 875 return table[i].str; 876 return "unknown"; 877 } 878 879 static void 880 gdip_perror(w3mimg_op *wop, Gdiplus::Status status, const char *func) THROW_NONE 881 { 882 const char *s = gdip_strerror(status); 883 fprintf(stderr, "w3mimgdisplay: GDI+ error %d: %s\n", (int)status, s); 884 if (wop && wop->priv) { 885 struct win_info *xi = (struct win_info *)wop->priv; 886 if (xi->logfile) { 887 fprintf(xi->logfile, "%s(): GDI+ error %d: %s\n", func, (int)status, s); 888 } 889 } 890 } 891 892 // Don't free() the result; use LocalFree() instead 893 static char * 894 win32_strerror_alloc(DWORD status) THROW_NONE 895 { 896 char *errbuf = NULL; 897 898 FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | 899 FORMAT_MESSAGE_FROM_SYSTEM | 900 FORMAT_MESSAGE_IGNORE_INSERTS, 901 NULL, status, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), 902 (LPSTR)&errbuf, 0, NULL); 903 if (errbuf) { 904 size_t len = strlen(errbuf); 905 if (len > 0 && errbuf[len - 1] == '\n') 906 errbuf[len - 1] = '\0'; 907 } 908 return errbuf; 909 } 910 911 static void 912 win32_perror(w3mimg_op *wop, DWORD status, const char *func) THROW_NONE 913 { 914 char *errbuf = win32_strerror_alloc(status); 915 const char *s = errbuf ? errbuf : "(unknown)"; 916 917 fprintf(stderr, "w3mimgdisplay: Win32 error %u: %s\n", (unsigned int)status, s); 918 if (wop && wop->priv) { 919 struct win_info *xi = (struct win_info *)wop->priv; 920 if (xi->logfile) { 921 fprintf(xi->logfile, "%s(): Win32 error %u: %s\n", 922 func, (unsigned int)status, s); 923 } 924 } 925 LocalFree(errbuf); 926 } 927 928 #if 0 /* unused */ 929 static WCHAR * 930 mb2wstr_alloc(const char *s) THROW_NONE 931 { 932 int len; 933 WCHAR *buf = NULL; 934 935 len = MultiByteToWideChar(CP_OEMCP, MB_PRECOMPOSED, s, -1, NULL, 0); 936 if (len <= 0) { 937 fprintf(stderr, "w3mimgdisplay: unable to convert string ecode=%u\n", 938 (unsigned int)GetLastError()); 939 goto error; 940 } 941 buf = (WCHAR *)malloc(len * sizeof(WCHAR)); /* including L'\0' */ 942 if (!buf) 943 goto error; 944 len = MultiByteToWideChar(CP_OEMCP, MB_PRECOMPOSED, s, -1, buf, len); 945 if (len <= 0) { 946 fprintf(stderr, "w3mimgdisplay: unable to convert string ecode=%u\n", 947 (unsigned int)GetLastError()); 948 goto error; 949 } 950 return buf; 951 error: 952 free(buf); 953 return NULL; 954 } 955 956 static char * 957 wstr2mb_alloc(const WCHAR *ws) THROW_NONE 958 { 959 int len; 960 char *buf = NULL; 961 962 len = WideCharToMultiByte(CP_OEMCP, WC_COMPOSITECHECK, ws, -1, NULL, 0, NULL, NULL); 963 if (len <= 0) { 964 fprintf(stderr, "w3mimgdisplay: unable to convert string ecode=%u\n", 965 (unsigned int)GetLastError()); 966 goto error; 967 } 968 buf = (char *)malloc(len); /* including '\0' */ 969 if (!buf) 970 goto error; 971 len = WideCharToMultiByte(CP_OEMCP, WC_COMPOSITECHECK, ws, -1, buf, len, NULL, NULL); 972 if (len <= 0) { 973 fprintf(stderr, "w3mimgdisplay: unable to convert string ecode=%u\n", 974 (unsigned int)GetLastError()); 975 goto error; 976 } 977 return buf; 978 error: 979 free(buf); 980 return NULL; 981 } 982 #endif /* unused */