983 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			983 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/* $Id: win_w3mimg.cpp,v 1.2 2010/12/24 09:52:06 htrb Exp $ */
 | 
						|
#include <stdio.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include <string.h>
 | 
						|
#include <ctype.h>
 | 
						|
#include "config.h"
 | 
						|
#include <assert.h>
 | 
						|
#include <locale.h>
 | 
						|
 | 
						|
#include <new>
 | 
						|
#include <algorithm>
 | 
						|
 | 
						|
#include "w3mimg/w3mimg.h"
 | 
						|
#include <windows.h>
 | 
						|
#include <gdiplus.h>
 | 
						|
#include <unistd.h>
 | 
						|
#include <sys/cygwin.h>
 | 
						|
/* GDI+ can handle BMP, GIF, JPEG, PNG and TIFF by itself. */
 | 
						|
 | 
						|
#define OFFSET_X	2
 | 
						|
#define OFFSET_Y	2
 | 
						|
#define DEBUG
 | 
						|
 | 
						|
#ifdef DEBUG
 | 
						|
#define THROW_NONE throw()
 | 
						|
#else
 | 
						|
#define THROW_NONE
 | 
						|
#endif
 | 
						|
 | 
						|
struct win_info {
 | 
						|
    HWND window;
 | 
						|
    Gdiplus::ARGB background_pixel;
 | 
						|
    ULONG_PTR gdiplus_token;
 | 
						|
    FILE *logfile;
 | 
						|
};
 | 
						|
 | 
						|
struct window_list {
 | 
						|
    HWND *wnd;
 | 
						|
    size_t nwnd;
 | 
						|
    size_t capacity;
 | 
						|
};
 | 
						|
 | 
						|
typedef Gdiplus::CachedBitmap *cache_handle;
 | 
						|
class win_image {
 | 
						|
private:
 | 
						|
    win_image(const win_image &); // decl only
 | 
						|
    win_image &operator=(const win_image &); // decl only
 | 
						|
 | 
						|
    Gdiplus::Bitmap *gpbitmap;
 | 
						|
    unsigned int nframe;
 | 
						|
    unsigned int current;
 | 
						|
    unsigned long tick;
 | 
						|
    unsigned long loopcount; // zero = infinite
 | 
						|
    unsigned long *delay; // unit: millisecond
 | 
						|
    cache_handle *cache;
 | 
						|
 | 
						|
public:
 | 
						|
    win_image() THROW_NONE;
 | 
						|
    ~win_image() THROW_NONE;
 | 
						|
    int load(w3mimg_op *wop, Gdiplus::Bitmap **p_gpbitmap,
 | 
						|
	    int *wreturn, int *hreturn) THROW_NONE;
 | 
						|
    int show(w3mimg_op *wop, int sx, int sy, int sw, int sh, int x, int y) THROW_NONE;
 | 
						|
    int animate(w3mimg_op *wop) THROW_NONE;
 | 
						|
};
 | 
						|
 | 
						|
static int win_init(w3mimg_op * wop) THROW_NONE;
 | 
						|
static int win_finish(w3mimg_op * wop) THROW_NONE;
 | 
						|
static int win_active(w3mimg_op * wop) THROW_NONE;
 | 
						|
static void win_set_background(w3mimg_op * wop, char *background) THROW_NONE;
 | 
						|
static void win_sync(w3mimg_op * wop) THROW_NONE;
 | 
						|
static void win_close(w3mimg_op * wop) THROW_NONE;
 | 
						|
 | 
						|
static int win_load_image(w3mimg_op * wop, W3MImage * img, char *fname,
 | 
						|
	int w, int h) THROW_NONE;
 | 
						|
static int win_show_image(w3mimg_op * wop, W3MImage * img,
 | 
						|
	int sx, int sy, int sw, int sh, int x, int y) THROW_NONE;
 | 
						|
static void win_free_image(w3mimg_op * wop, W3MImage * img) THROW_NONE;
 | 
						|
static int win_get_image_size(w3mimg_op * wop, W3MImage * img,
 | 
						|
	char *fname, int *w, int *h) THROW_NONE;
 | 
						|
static int win_clear(w3mimg_op * wop, int x, int y, int w, int h) THROW_NONE;
 | 
						|
 | 
						|
static int window_alive(w3mimg_op *wop) THROW_NONE;
 | 
						|
static Gdiplus::Bitmap *read_image_file(w3mimg_op *wop, const char *fname) THROW_NONE;
 | 
						|
static BOOL CALLBACK store_to_window_list(HWND hWnd, LPARAM wndlist) THROW_NONE;
 | 
						|
static void clear_window_list(struct window_list *wl) THROW_NONE;
 | 
						|
static const char *gdip_strerror(Gdiplus::Status status) THROW_NONE;
 | 
						|
static void gdip_perror(w3mimg_op *wop, Gdiplus::Status status, const char *func) THROW_NONE;
 | 
						|
static char *win32_strerror_alloc(DWORD status) THROW_NONE;
 | 
						|
static void win32_perror(w3mimg_op *wop, DWORD status, const char *func) THROW_NONE;
 | 
						|
#if 0 /* unused */
 | 
						|
static WCHAR *mb2wstr_alloc(const char *) THROW_NONE;
 | 
						|
static char *wstr2mb_alloc(const WCHAR *) THROW_NONE;
 | 
						|
#endif
 | 
						|
 | 
						|
#define PRELUDE(wop, xi) \
 | 
						|
    assert(wop); \
 | 
						|
    struct win_info *xi = static_cast<struct win_info *>(wop->priv); \
 | 
						|
    assert(xi)
 | 
						|
 | 
						|
win_image::win_image() THROW_NONE
 | 
						|
    : gpbitmap(NULL), nframe(0)
 | 
						|
{}
 | 
						|
 | 
						|
win_image::~win_image() THROW_NONE
 | 
						|
{
 | 
						|
    if (this->cache) {
 | 
						|
	for (size_t i = 0; i != this->nframe; ++i) {
 | 
						|
	    delete this->cache[i];
 | 
						|
	}
 | 
						|
	delete[] this->cache;
 | 
						|
    }
 | 
						|
    delete[] this->delay;
 | 
						|
    delete this->gpbitmap;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
win_image::load(w3mimg_op *wop, Gdiplus::Bitmap **p_gpbitmap, int *wreturn, int *hreturn) THROW_NONE
 | 
						|
{
 | 
						|
    PRELUDE(wop, xi);
 | 
						|
    Gdiplus::Bitmap *gpbitmap = *p_gpbitmap;
 | 
						|
    assert(gpbitmap);
 | 
						|
    Gdiplus::Status status = Gdiplus::Ok;
 | 
						|
    int retval = 0;
 | 
						|
 | 
						|
    Gdiplus::PropertyItem *loopcountbuf = NULL;
 | 
						|
    Gdiplus::PropertyItem *delaybuf = NULL;
 | 
						|
    unsigned long *delay = NULL;
 | 
						|
    cache_handle *cache = NULL;
 | 
						|
 | 
						|
    if (xi->logfile) {
 | 
						|
	fprintf(xi->logfile, "win_image::load(%p, %p, %p, %p) start\n",
 | 
						|
		wop, gpbitmap, wreturn, hreturn);
 | 
						|
    }
 | 
						|
    {
 | 
						|
	unsigned int width = gpbitmap->GetWidth();
 | 
						|
	unsigned int height = gpbitmap->GetHeight();
 | 
						|
	unsigned int nframe = gpbitmap->GetFrameCount(&Gdiplus::FrameDimensionTime);
 | 
						|
	unsigned long loopcount = 0;
 | 
						|
	unsigned int first_frame = 0;
 | 
						|
 | 
						|
	if (xi->logfile)
 | 
						|
	    fprintf(xi->logfile, "win_image::load(): size[0]=%ux%u\n", width, height);
 | 
						|
	if (nframe == 0) {
 | 
						|
	    // Not an animated picture
 | 
						|
	    if (xi->logfile)
 | 
						|
		fprintf(xi->logfile, "win_image::load(): zero frame count\n");
 | 
						|
	    nframe = 1;
 | 
						|
	    delay = new(std::nothrow) unsigned long[1];
 | 
						|
	    if (delay == NULL)
 | 
						|
		goto last;
 | 
						|
	    delay[0] = 0;
 | 
						|
	} else {
 | 
						|
	    unsigned int loopcountsize = gpbitmap->GetPropertyItemSize(PropertyTagLoopCount);
 | 
						|
	    unsigned int delaysize = gpbitmap->GetPropertyItemSize(PropertyTagFrameDelay);
 | 
						|
 | 
						|
	    // Get loop count
 | 
						|
	    if (loopcountsize != 0) {
 | 
						|
		loopcountbuf = (Gdiplus::PropertyItem *)malloc(loopcountsize);
 | 
						|
		if (loopcountbuf == NULL)
 | 
						|
		    goto last;
 | 
						|
		status = gpbitmap->GetPropertyItem(PropertyTagLoopCount, loopcountsize, loopcountbuf);
 | 
						|
		if (status != Gdiplus::Ok)
 | 
						|
		    goto gdip_error;
 | 
						|
		if (loopcountbuf->type == PropertyTagTypeShort &&
 | 
						|
			loopcountbuf->length >= sizeof(unsigned short)) {
 | 
						|
		    loopcount = *(unsigned short *)loopcountbuf->value;
 | 
						|
		} else if (loopcountbuf->type == PropertyTagTypeLong &&
 | 
						|
			loopcountbuf->length >= sizeof(unsigned long)) {
 | 
						|
		    loopcount = *(unsigned long *)loopcountbuf->value;
 | 
						|
		}
 | 
						|
	    }
 | 
						|
	    if (xi->logfile)
 | 
						|
		fprintf(xi->logfile, "win_image::load(): loopcount=%lu\n", loopcount);
 | 
						|
	    // Get delay times
 | 
						|
	    if (delaysize != 0) {
 | 
						|
		delaybuf = (Gdiplus::PropertyItem *)malloc(delaysize);
 | 
						|
		if (delaybuf == NULL)
 | 
						|
		    goto last;
 | 
						|
		status = gpbitmap->GetPropertyItem(PropertyTagFrameDelay, delaysize, delaybuf);
 | 
						|
		if (status != Gdiplus::Ok)
 | 
						|
		    goto gdip_error;
 | 
						|
		delay = new(std::nothrow) unsigned long[nframe];
 | 
						|
		if (delay == NULL)
 | 
						|
		    goto last;
 | 
						|
		std::fill(delay, delay + nframe, 0);
 | 
						|
		if (delaybuf->type == PropertyTagTypeShort) {
 | 
						|
		    unsigned int count = delaybuf->length / sizeof(unsigned short);
 | 
						|
		    for (unsigned int i = 0; i != count; ++i)
 | 
						|
			delay[i] = ((unsigned short *)delaybuf->value)[i] * 10;
 | 
						|
		} else if (delaybuf->type == PropertyTagTypeLong) {
 | 
						|
		    unsigned int count = delaybuf->length / sizeof(unsigned long);
 | 
						|
		    for (unsigned int i = 0; i != count; ++i)
 | 
						|
			delay[i] = ((unsigned long *)delaybuf->value)[i] * 10;
 | 
						|
		}
 | 
						|
	    }
 | 
						|
	    if (xi->logfile) {
 | 
						|
		for (unsigned int i = 0; i != nframe; ++i)
 | 
						|
		    fprintf(xi->logfile, "win_image::load(): delay[%u]=%lu\n", i, delay[i]);
 | 
						|
	    }
 | 
						|
	    // Get dimensions
 | 
						|
	    for (unsigned int nextframe = 1; nextframe != nframe; ++nextframe) {
 | 
						|
		status = gpbitmap->SelectActiveFrame(&Gdiplus::FrameDimensionTime, nextframe);
 | 
						|
		if (status != Gdiplus::Ok) {
 | 
						|
		    if (xi->logfile)
 | 
						|
			fprintf(xi->logfile, "win_image::load(): SelectActiveFrame() to %u failed = %d: %s\n",
 | 
						|
				nextframe, (int)status, gdip_strerror(status));
 | 
						|
		    goto last;
 | 
						|
		}
 | 
						|
		unsigned int iw = gpbitmap->GetWidth();
 | 
						|
		unsigned int ih = gpbitmap->GetHeight();
 | 
						|
		if (iw > width)
 | 
						|
		    width = iw;
 | 
						|
		if (ih > height)
 | 
						|
		    height = ih;
 | 
						|
		if (xi->logfile)
 | 
						|
		    fprintf(xi->logfile, "win_image::load(): size[%u]=%ux%u\n", nextframe, iw, ih);
 | 
						|
	    }
 | 
						|
	    // Go to the first frame
 | 
						|
	    first_frame = (0 < -wop->max_anim && -wop->max_anim < nframe) ? (nframe + wop->max_anim) : 0;
 | 
						|
	    status = gpbitmap->SelectActiveFrame(&Gdiplus::FrameDimensionTime, first_frame);
 | 
						|
	    if (status != Gdiplus::Ok) {
 | 
						|
		if (xi->logfile)
 | 
						|
		    fprintf(xi->logfile, "win_image::load(): SelectActiveFrame() to %u frame = %d: %s\n",
 | 
						|
			    first_frame, (int)status, gdip_strerror(status));
 | 
						|
		goto last;
 | 
						|
	    }
 | 
						|
	}
 | 
						|
	// Allocate cache array
 | 
						|
	cache = new(std::nothrow) cache_handle[nframe];
 | 
						|
	if (cache == NULL)
 | 
						|
	    goto last;
 | 
						|
	std::fill(cache, cache + nframe, (cache_handle)NULL);
 | 
						|
	// Sanity check
 | 
						|
	if (width > SHRT_MAX || height > SHRT_MAX) {
 | 
						|
	    if (xi->logfile)
 | 
						|
		fprintf(xi->logfile, "win_image::load(): too big image: %ux%u\n", width, height);
 | 
						|
	    goto last;
 | 
						|
	}
 | 
						|
	// Store the results
 | 
						|
	if (wreturn)
 | 
						|
	    *wreturn = (int)width;
 | 
						|
	if (hreturn)
 | 
						|
	    *hreturn = (int)height;
 | 
						|
	this->gpbitmap = gpbitmap;
 | 
						|
	*p_gpbitmap = NULL; // ownership transfer
 | 
						|
	this->nframe = nframe;
 | 
						|
	this->current = first_frame;
 | 
						|
	this->tick = 0;
 | 
						|
	this->loopcount = loopcount;
 | 
						|
	this->delay = delay;
 | 
						|
	delay = NULL; // ownership transfer
 | 
						|
	this->cache = cache;
 | 
						|
	cache = NULL; // ownership transfer
 | 
						|
	retval = 1;
 | 
						|
    }
 | 
						|
    goto last;
 | 
						|
    
 | 
						|
gdip_error:
 | 
						|
    gdip_perror(wop, status, "win_image::load");
 | 
						|
    goto last;
 | 
						|
last:
 | 
						|
    delete[] cache;
 | 
						|
    delete[] delay;
 | 
						|
    free(delaybuf);
 | 
						|
    free(loopcountbuf);
 | 
						|
    if (xi->logfile)
 | 
						|
	fprintf(xi->logfile, "win_image::load() = %d\n", retval);
 | 
						|
    return retval;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
win_image::show(w3mimg_op *wop, int sx, int sy, int sw, int sh, int x, int y) THROW_NONE
 | 
						|
{
 | 
						|
    PRELUDE(wop, xi);
 | 
						|
    int retval = 0;
 | 
						|
    Gdiplus::Status status = Gdiplus::Ok;
 | 
						|
    cache_handle newcache = NULL;
 | 
						|
 | 
						|
    if (xi->logfile)
 | 
						|
	fprintf(xi->logfile, "win_image::show(%p, %d, %d, %d, %d, %d, %d) start current=%u\n",
 | 
						|
		wop, sx, sy, sw, sh, x, y, this->current);
 | 
						|
    if (!window_alive(wop))
 | 
						|
	goto last;
 | 
						|
    {
 | 
						|
	int xx = x + wop->offset_x;
 | 
						|
	int yy = y + wop->offset_y;
 | 
						|
 | 
						|
	// Prepare the Graphics object for painting
 | 
						|
	Gdiplus::Graphics graphics(xi->window);
 | 
						|
	if ((status = graphics.GetLastStatus()) != Gdiplus::Ok)
 | 
						|
	    goto gdip_error;
 | 
						|
	Gdiplus::Rect clip(xx, yy, sw, sh);
 | 
						|
	status = graphics.SetClip(clip);
 | 
						|
	if (status != Gdiplus::Ok)
 | 
						|
	    goto gdip_error;
 | 
						|
 | 
						|
	unsigned int retry_count = 2;
 | 
						|
	do {
 | 
						|
	    if (this->cache[this->current] == NULL) {
 | 
						|
		// Cache the image
 | 
						|
		Gdiplus::Bitmap tmp_bitmap(sw, sh, &graphics);
 | 
						|
		if ((status = tmp_bitmap.GetLastStatus()) != Gdiplus::Ok)
 | 
						|
		    goto gdip_error;
 | 
						|
		Gdiplus::Graphics tmp_graphics(&tmp_bitmap);
 | 
						|
		if ((status = tmp_graphics.GetLastStatus()) != Gdiplus::Ok)
 | 
						|
		    goto gdip_error;
 | 
						|
		status = tmp_graphics.Clear(Gdiplus::Color(xi->background_pixel));
 | 
						|
		if (status != Gdiplus::Ok)
 | 
						|
		    goto gdip_error;
 | 
						|
		status = tmp_graphics.DrawImage(this->gpbitmap, 0, 0, sw, sh);
 | 
						|
		if (status != Gdiplus::Ok)
 | 
						|
		    goto gdip_error;
 | 
						|
		Gdiplus::CachedBitmap *newcache = new Gdiplus::CachedBitmap(&tmp_bitmap, &graphics);
 | 
						|
		if (newcache == NULL)
 | 
						|
		    goto last;
 | 
						|
		if ((status = newcache->GetLastStatus()) != Gdiplus::Ok)
 | 
						|
		    goto gdip_error;
 | 
						|
		this->cache[this->current] = newcache;
 | 
						|
		newcache = NULL; // ownership transfer
 | 
						|
		--retry_count;
 | 
						|
	    }
 | 
						|
	    // Draw it
 | 
						|
	    status = graphics.DrawCachedBitmap(this->cache[this->current], xx - sx, yy - sy);
 | 
						|
	    if (status == Gdiplus::Ok)
 | 
						|
		break;
 | 
						|
	    // maybe the user altered the display configuration
 | 
						|
	    if (xi->logfile)
 | 
						|
		fprintf(xi->logfile, "win_image::show(): stale cache = %d: %s\n",
 | 
						|
			(int)status, gdip_strerror(status));
 | 
						|
	    delete this->cache[this->current];
 | 
						|
	    this->cache[this->current] = NULL;
 | 
						|
	    if (retry_count == 0)
 | 
						|
		goto last;
 | 
						|
	} while (1);
 | 
						|
 | 
						|
	retval = 1;
 | 
						|
    }
 | 
						|
    goto last;
 | 
						|
gdip_error:
 | 
						|
    gdip_perror(wop, status, "win_image::show");
 | 
						|
    goto last;
 | 
						|
last:
 | 
						|
    delete newcache;
 | 
						|
    if (xi->logfile)
 | 
						|
	fprintf(xi->logfile, "win_image::show() = %d\n", retval);
 | 
						|
    return retval;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
win_image::animate(w3mimg_op * wop) THROW_NONE
 | 
						|
{
 | 
						|
    PRELUDE(wop, xi);
 | 
						|
    int retval = 0;
 | 
						|
    Gdiplus::Status status = Gdiplus::Ok;
 | 
						|
 | 
						|
    if (xi->logfile)
 | 
						|
	fprintf(xi->logfile, "win_image::animate(%p) start\n", wop);
 | 
						|
    {
 | 
						|
	if (this->nframe <= 1)
 | 
						|
	    goto animation_end;
 | 
						|
#define UNIT_TICK 50
 | 
						|
#define MIN_DELAY (UNIT_TICK*2)
 | 
						|
	this->tick += UNIT_TICK;
 | 
						|
	if (this->tick >= MIN_DELAY && this->tick >= this->delay[this->current]) {
 | 
						|
	    this->tick = 0;
 | 
						|
	    unsigned int nextframe = this->current + 1;
 | 
						|
	    if (wop->max_anim == nextframe)
 | 
						|
		goto animation_end;
 | 
						|
	    if (nextframe >= this->nframe) {
 | 
						|
		if (this->loopcount == 1 || wop->max_anim < 0) // end of the loop
 | 
						|
		    goto animation_end;
 | 
						|
		nextframe = 0;
 | 
						|
	    }
 | 
						|
	    status = this->gpbitmap->SelectActiveFrame(&Gdiplus::FrameDimensionTime, nextframe);
 | 
						|
	    if (status != Gdiplus::Ok)
 | 
						|
		goto gdip_error;
 | 
						|
	    this->current = nextframe;
 | 
						|
	    if (nextframe == 0 && this->loopcount > 1)
 | 
						|
		--this->loopcount;
 | 
						|
	}
 | 
						|
animation_end:
 | 
						|
	retval = 1;
 | 
						|
    }
 | 
						|
    goto last;
 | 
						|
gdip_error:
 | 
						|
    gdip_perror(wop, status, "win_image::animate");
 | 
						|
    goto last;
 | 
						|
last:
 | 
						|
    if (xi->logfile)
 | 
						|
	fprintf(xi->logfile, "win_image::animate() = %d\n", retval);
 | 
						|
    return retval;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
window_alive(w3mimg_op *wop) THROW_NONE
 | 
						|
{
 | 
						|
    PRELUDE(wop, xi);
 | 
						|
    if (xi->window == NULL)
 | 
						|
	return 0;
 | 
						|
    if (IsWindow(xi->window))
 | 
						|
	return 1;
 | 
						|
    xi->window = NULL;
 | 
						|
    fputs("w3mimgdisplay: target window disappeared\n", stderr);
 | 
						|
    if (xi->logfile)
 | 
						|
	fputs("w3mimgdisplay: target window disappeared\n", xi->logfile);
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
win_init(w3mimg_op *) THROW_NONE
 | 
						|
{
 | 
						|
    // nothing to do
 | 
						|
    return 1;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
win_finish(w3mimg_op *) THROW_NONE
 | 
						|
{
 | 
						|
    // nothing to do
 | 
						|
    return 1;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
win_clear(w3mimg_op *wop, int x, int y, int w, int h) THROW_NONE
 | 
						|
{
 | 
						|
    PRELUDE(wop, xi);
 | 
						|
    Gdiplus::Status status = Gdiplus::Ok;
 | 
						|
    int retval = 0;
 | 
						|
 | 
						|
    if (xi->logfile)
 | 
						|
	fprintf(xi->logfile, "win_clear(%p, %d, %d, %d, %d) start\n",
 | 
						|
		wop, x, y, w, h);
 | 
						|
    if (!window_alive(wop))
 | 
						|
	goto last;
 | 
						|
    {
 | 
						|
	if (x < 0)
 | 
						|
	    x = 0;
 | 
						|
	if (y < 0)
 | 
						|
	    y = 0;
 | 
						|
	Gdiplus::SolidBrush brush(Gdiplus::Color(xi->background_pixel));
 | 
						|
	if ((status = brush.GetLastStatus()) != Gdiplus::Ok)
 | 
						|
	    goto gdip_error;
 | 
						|
	Gdiplus::Graphics graphics(xi->window);
 | 
						|
	if ((status = graphics.GetLastStatus()) != Gdiplus::Ok)
 | 
						|
	    goto gdip_error;
 | 
						|
	status = graphics.FillRectangle(&brush, x + wop->offset_x, y + wop->offset_y, w, h);
 | 
						|
	if (status != Gdiplus::Ok)
 | 
						|
	    goto gdip_error;
 | 
						|
	retval = 1;
 | 
						|
    }
 | 
						|
    goto last;
 | 
						|
gdip_error:
 | 
						|
    gdip_perror(wop, status, "win_clear");
 | 
						|
    goto last;
 | 
						|
last:
 | 
						|
    if (xi->logfile)
 | 
						|
	fprintf(xi->logfile, "win_clear() = %d\n", retval);
 | 
						|
    return retval;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
win_active(w3mimg_op * wop) THROW_NONE
 | 
						|
{
 | 
						|
    return window_alive(wop);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
win_set_background(w3mimg_op * wop, char *background) THROW_NONE
 | 
						|
{
 | 
						|
    PRELUDE(wop, xi);
 | 
						|
 | 
						|
    HDC windc = NULL;
 | 
						|
 | 
						|
    if (xi->logfile)
 | 
						|
	fprintf(xi->logfile, "win_set_background(%p, \"%s\")\n", wop, background ? background : "(auto)");
 | 
						|
    {
 | 
						|
	// Fallback value
 | 
						|
	// xi->background_pixel = Gdiplus::Color::White;
 | 
						|
	xi->background_pixel = Gdiplus::Color::Black;
 | 
						|
 | 
						|
	// Explicit
 | 
						|
	if (background) {
 | 
						|
	    unsigned int r, g, b;
 | 
						|
	    if (sscanf(background, "#%02x%02x%02x", &r, &g, &b) == 3) {
 | 
						|
		xi->background_pixel = Gdiplus::Color::MakeARGB((BYTE)255, (BYTE)r, (BYTE)g, (BYTE)b);
 | 
						|
		goto last;
 | 
						|
	    }
 | 
						|
	}
 | 
						|
 | 
						|
	// Auto detect
 | 
						|
	if (xi->window == NULL || !IsWindow(xi->window))
 | 
						|
	    goto last;
 | 
						|
	windc = GetDC(xi->window);
 | 
						|
	if (windc == NULL)
 | 
						|
	    goto win32_error;
 | 
						|
	COLORREF c = GetPixel(windc,
 | 
						|
		    (wop->offset_x >= 1) ? (wop->offset_x - 1) : 0,
 | 
						|
		    (wop->offset_y >= 1) ? (wop->offset_y - 1) : 0);
 | 
						|
	xi->background_pixel = Gdiplus::Color::MakeARGB(
 | 
						|
		(BYTE)255, GetRValue(c), GetGValue(c), GetBValue(c));
 | 
						|
    }
 | 
						|
    goto last;
 | 
						|
win32_error:
 | 
						|
    win32_perror(wop, GetLastError(), "win_set_background");
 | 
						|
    goto last;
 | 
						|
last:
 | 
						|
    if (xi->logfile)
 | 
						|
	fprintf(xi->logfile, "win_set_background() result = #%06x\n",
 | 
						|
		(unsigned int)xi->background_pixel);
 | 
						|
    if (windc)
 | 
						|
	ReleaseDC(xi->window, windc);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
win_sync(w3mimg_op *) THROW_NONE
 | 
						|
{
 | 
						|
    // nothing to do
 | 
						|
    return;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
win_close(w3mimg_op * wop) THROW_NONE
 | 
						|
{
 | 
						|
    PRELUDE(wop, xi);
 | 
						|
 | 
						|
    if (xi->gdiplus_token)
 | 
						|
	Gdiplus::GdiplusShutdown(xi->gdiplus_token);
 | 
						|
    if (xi->logfile) {
 | 
						|
	fprintf(xi->logfile, "win_close(%p)\n", wop);
 | 
						|
	fclose(xi->logfile);
 | 
						|
    }
 | 
						|
    delete xi;
 | 
						|
    delete wop;
 | 
						|
}
 | 
						|
 | 
						|
static Gdiplus::Bitmap *
 | 
						|
read_image_file(w3mimg_op *wop, const char *fname) THROW_NONE
 | 
						|
{
 | 
						|
    PRELUDE(wop, xi);
 | 
						|
    Gdiplus::Status status = Gdiplus::Ok;
 | 
						|
    Gdiplus::Bitmap *retval = NULL;
 | 
						|
 | 
						|
    WCHAR *wfname = NULL;
 | 
						|
    Gdiplus::Bitmap *gpbitmap = NULL;
 | 
						|
 | 
						|
    if (xi->logfile)
 | 
						|
	fprintf(xi->logfile, "read_image_file(%p, \"%s\") start\n", wop, fname);
 | 
						|
    {
 | 
						|
	wfname = (WCHAR *)cygwin_create_path(CCP_POSIX_TO_WIN_W, fname);
 | 
						|
	if (wfname == NULL)
 | 
						|
	    goto last;
 | 
						|
	gpbitmap = new Gdiplus::Bitmap(wfname);
 | 
						|
	if (gpbitmap == NULL)
 | 
						|
	    goto last;
 | 
						|
	status = gpbitmap->GetLastStatus();
 | 
						|
	switch (status) {
 | 
						|
	    case Gdiplus::Ok:
 | 
						|
		break;
 | 
						|
	    case Gdiplus::UnknownImageFormat:
 | 
						|
	    case Gdiplus::FileNotFound:
 | 
						|
		goto last; // fail silently
 | 
						|
	    default:
 | 
						|
		goto gdip_error;
 | 
						|
	}
 | 
						|
	retval = gpbitmap;
 | 
						|
	gpbitmap = NULL; // ownership transfer
 | 
						|
    }
 | 
						|
    goto last;
 | 
						|
gdip_error:
 | 
						|
    gdip_perror(wop, status, "read_image_file");
 | 
						|
last:
 | 
						|
    delete gpbitmap;
 | 
						|
    free(wfname);
 | 
						|
    if (xi->logfile)
 | 
						|
	fprintf(xi->logfile, "read_image_file() = %p\n", retval);
 | 
						|
    return retval;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
win_load_image(w3mimg_op * wop, W3MImage * img, char *fname, int w, int h) THROW_NONE
 | 
						|
{
 | 
						|
    PRELUDE(wop, xi);
 | 
						|
    int retval = 0;
 | 
						|
    Gdiplus::Bitmap *gpbitmap = NULL;
 | 
						|
    win_image *wimg = NULL;
 | 
						|
 | 
						|
    assert(img);
 | 
						|
    if (xi->logfile) {
 | 
						|
	fprintf(xi->logfile, "win_load_image(%p, %p, \"%s\", %d, %d) start\n",
 | 
						|
		wop, img, fname, w, h);
 | 
						|
    }
 | 
						|
    {
 | 
						|
	gpbitmap = read_image_file(wop, fname);
 | 
						|
	if (gpbitmap == NULL)
 | 
						|
	    goto last;
 | 
						|
	int iw, ih;
 | 
						|
	wimg = new(std::nothrow) win_image;
 | 
						|
	if (!wimg->load(wop, &gpbitmap, &iw, &ih))
 | 
						|
	    goto last;
 | 
						|
	img->pixmap = wimg;
 | 
						|
	wimg = NULL; // ownership transfer
 | 
						|
	img->width = (0 <= w && w < iw) ? w : iw;
 | 
						|
	img->height = (0 <= h && h < ih) ? h : ih;
 | 
						|
	retval = 1;
 | 
						|
    }
 | 
						|
    goto last;
 | 
						|
last:
 | 
						|
    delete wimg;
 | 
						|
    delete gpbitmap;
 | 
						|
    if (xi->logfile)
 | 
						|
	fprintf(xi->logfile, "win_load_image() = %d\n", retval);
 | 
						|
    return retval;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
win_show_image(w3mimg_op * wop, W3MImage * img, int sx, int sy, int sw,
 | 
						|
	       int sh, int x, int y) THROW_NONE
 | 
						|
{
 | 
						|
    PRELUDE(wop, xi);
 | 
						|
    int retval = 0;
 | 
						|
 | 
						|
    assert(img);
 | 
						|
    win_image *wimg = static_cast<win_image *>(img->pixmap);
 | 
						|
    assert(wimg);
 | 
						|
    
 | 
						|
    if (xi->logfile)
 | 
						|
	fprintf(xi->logfile, "win_show_image(%p, %p, %d, %d, %d, %d, %d, %d) start\n",
 | 
						|
		wop, img, sx, sy, sw, sh, x, y);
 | 
						|
    int sww = sw ? sw : img->width;
 | 
						|
    int shh = sh ? sh : img->height;
 | 
						|
    retval = wimg->show(wop, sx, sy, sww, shh, x, y)
 | 
						|
	&& wimg->animate(wop);
 | 
						|
    if (xi->logfile)
 | 
						|
	fprintf(xi->logfile, "win_show_image() = %d\n", retval);
 | 
						|
    return retval;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
win_free_image(w3mimg_op * wop, W3MImage * img) THROW_NONE
 | 
						|
{
 | 
						|
    PRELUDE(wop, xi);
 | 
						|
 | 
						|
    assert(img);
 | 
						|
    if (xi->logfile)
 | 
						|
	fprintf(xi->logfile, "win_free_image(%p, %p) pixmap=%p\n", wop, img, img->pixmap);
 | 
						|
    delete static_cast<win_image *>(img->pixmap);
 | 
						|
    img->pixmap = NULL;
 | 
						|
    img->width = 0;
 | 
						|
    img->height = 0;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
win_get_image_size(w3mimg_op * wop, W3MImage *img_unused, char *fname, int *w, int *h) THROW_NONE
 | 
						|
{
 | 
						|
    PRELUDE(wop, xi);
 | 
						|
    int retval = 0;
 | 
						|
    Gdiplus::Bitmap *gpbitmap = NULL;
 | 
						|
    win_image *wimg = NULL;
 | 
						|
 | 
						|
    if (xi->logfile) {
 | 
						|
	fprintf(xi->logfile, "win_get_image_size(%p, %p, \"%s\", %p, %p) start\n",
 | 
						|
		wop, img_unused, fname, w, h);
 | 
						|
    }
 | 
						|
    {
 | 
						|
	gpbitmap = read_image_file(wop, fname);
 | 
						|
	if (gpbitmap == NULL)
 | 
						|
	    goto last;
 | 
						|
	wimg = new(std::nothrow) win_image;
 | 
						|
	if (wimg == NULL)
 | 
						|
	    goto last;
 | 
						|
	retval = wimg->load(wop, &gpbitmap, w, h);;
 | 
						|
    }
 | 
						|
    goto last;
 | 
						|
last:
 | 
						|
    delete wimg;
 | 
						|
    delete gpbitmap;
 | 
						|
    if (xi->logfile)
 | 
						|
	fprintf(xi->logfile, "win_get_image_size() = %d\n", retval);
 | 
						|
    return retval;
 | 
						|
}
 | 
						|
 | 
						|
extern "C" w3mimg_op *
 | 
						|
w3mimg_winopen()
 | 
						|
{
 | 
						|
    w3mimg_op *retval = NULL;
 | 
						|
    Gdiplus::Status status = Gdiplus::Ok;
 | 
						|
 | 
						|
    w3mimg_op *wop = NULL;
 | 
						|
    struct win_info *xi = NULL;
 | 
						|
    struct window_list children = { NULL, 0, 0 };
 | 
						|
 | 
						|
    {
 | 
						|
	// Quit if running on X
 | 
						|
	const char *display_name;
 | 
						|
	if ((display_name = getenv("DISPLAY")) != NULL &&
 | 
						|
		display_name[0] && strcmp(display_name, ":0") != 0)
 | 
						|
	    return NULL;
 | 
						|
 | 
						|
	// Allocate the context objects
 | 
						|
	wop = new(std::nothrow) w3mimg_op(); // call the default ctor instead of "new w3mimg_op;"
 | 
						|
	if (wop == NULL)
 | 
						|
	    return NULL;
 | 
						|
	wop->priv = xi = new(std::nothrow) win_info();
 | 
						|
	if (xi == NULL)
 | 
						|
	    goto last;
 | 
						|
 | 
						|
	// Debug logging
 | 
						|
	const char *logging_dir;
 | 
						|
	if ((logging_dir = getenv("W3MIMG_LOGDIR")) != NULL &&
 | 
						|
		logging_dir[0]) {
 | 
						|
	    size_t l = strlen(logging_dir) + sizeof "/w3mimgXXXXXXXXXX.log";
 | 
						|
	    char *fname = (char *)malloc(l);
 | 
						|
	    snprintf(fname, l, "%s/w3mimg%d.log", logging_dir, (int)getpid());
 | 
						|
	    xi->logfile = fopen(fname, "a");
 | 
						|
	    if (xi->logfile) {
 | 
						|
		setvbuf(xi->logfile, NULL, _IONBF, 0);
 | 
						|
		fprintf(xi->logfile, "\nw3mimg_winopen() start pid=%d\n", (int)getpid());
 | 
						|
	    }
 | 
						|
	}
 | 
						|
 | 
						|
	// Look for the window to draw the image
 | 
						|
	xi->window = NULL;
 | 
						|
	const char *windowid;
 | 
						|
	if ((windowid = getenv("WINDOWID")) != NULL)
 | 
						|
	    xi->window = FindWindowA(windowid, NULL);
 | 
						|
	if (!xi->window)
 | 
						|
	    xi->window = GetForegroundWindow();
 | 
						|
	if (!xi->window)
 | 
						|
	    goto win32_error;
 | 
						|
 | 
						|
	WINDOWINFO winfo = WINDOWINFO();
 | 
						|
	winfo.cbSize = sizeof winfo;
 | 
						|
	GetWindowInfo(xi->window, &winfo);
 | 
						|
	wop->width = (int)(winfo.rcClient.right - winfo.rcClient.left);
 | 
						|
	wop->height = (int)(winfo.rcClient.bottom - winfo.rcClient.top);
 | 
						|
 | 
						|
	// Search decendant windows and find out which is the text window
 | 
						|
	while (1) {
 | 
						|
	    HWND p_window = xi->window;
 | 
						|
 | 
						|
	    clear_window_list(&children);
 | 
						|
	    EnumChildWindows(xi->window, &store_to_window_list, (LPARAM)&children);
 | 
						|
	    for (unsigned int i = 0; i < children.nwnd; i++) {
 | 
						|
		int width, height;
 | 
						|
 | 
						|
		GetWindowInfo(children.wnd[i], &winfo);
 | 
						|
		width = (int)(winfo.rcClient.right - winfo.rcClient.left);
 | 
						|
		height = (int)(winfo.rcClient.bottom - winfo.rcClient.top);
 | 
						|
		if (width > wop->width * 0.7 &&
 | 
						|
			height > wop->height * 0.7) {
 | 
						|
		    /* maybe text window */
 | 
						|
		    wop->width = width;
 | 
						|
		    wop->height = height;
 | 
						|
		    xi->window = children.wnd[i];
 | 
						|
		}
 | 
						|
	    }
 | 
						|
	    if (p_window == xi->window)
 | 
						|
		break;
 | 
						|
	}
 | 
						|
 | 
						|
	// Terminal may leave some border pixels
 | 
						|
	wop->offset_x = OFFSET_X;
 | 
						|
	wop->offset_y = OFFSET_Y;
 | 
						|
 | 
						|
	// Start up the GDI+
 | 
						|
	Gdiplus::GdiplusStartupInput startup_input; /// default ctor
 | 
						|
	status = Gdiplus::GdiplusStartup(&xi->gdiplus_token, &startup_input, NULL);
 | 
						|
	if (status != Gdiplus::Ok)
 | 
						|
	    goto gdip_error;
 | 
						|
 | 
						|
	// Fill the context object
 | 
						|
	wop->init = win_init;
 | 
						|
	wop->finish = win_finish;
 | 
						|
	wop->active = win_active;
 | 
						|
	wop->set_background = win_set_background;
 | 
						|
	wop->sync = win_sync;
 | 
						|
	wop->close = win_close;
 | 
						|
	wop->clear = win_clear;
 | 
						|
 | 
						|
	wop->load_image = win_load_image;
 | 
						|
	wop->show_image = win_show_image;
 | 
						|
	wop->free_image = win_free_image;
 | 
						|
	wop->get_image_size = win_get_image_size;
 | 
						|
 | 
						|
	retval = wop; // take care of the object lifetime
 | 
						|
    }
 | 
						|
    goto last;
 | 
						|
win32_error:
 | 
						|
    win32_perror(wop, GetLastError(), "w3mimg_winopen");
 | 
						|
    goto last;
 | 
						|
gdip_error:
 | 
						|
    gdip_perror(wop, status, "w3mimg_winopen");
 | 
						|
    goto last;
 | 
						|
last:
 | 
						|
    if (xi && xi->logfile)
 | 
						|
	fprintf(xi->logfile, "w3mimg_winopen() = %p\n", retval);
 | 
						|
    clear_window_list(&children);
 | 
						|
    if (!retval) {
 | 
						|
	if (xi) {
 | 
						|
	    if (xi->gdiplus_token)
 | 
						|
		Gdiplus::GdiplusShutdown(xi->gdiplus_token);
 | 
						|
	    if (xi->logfile)
 | 
						|
		fclose(xi->logfile);
 | 
						|
	    delete xi;
 | 
						|
	}
 | 
						|
	delete wop;
 | 
						|
    }
 | 
						|
    return retval;
 | 
						|
}
 | 
						|
 | 
						|
static BOOL CALLBACK
 | 
						|
store_to_window_list(HWND hWnd, LPARAM wndlist) THROW_NONE
 | 
						|
{
 | 
						|
    struct window_list *wl = (struct window_list *)wndlist;
 | 
						|
 | 
						|
    if (wl->nwnd >= wl->capacity) {
 | 
						|
	size_t newsize = (wl->capacity < 4 ) ? 4 : (wl->capacity * 2);
 | 
						|
	HWND *newbuf = (HWND *)realloc(wl->wnd, newsize * sizeof newbuf[0]);
 | 
						|
	if (newbuf == NULL) {
 | 
						|
	    clear_window_list(wl);
 | 
						|
	    return FALSE;
 | 
						|
	}
 | 
						|
	wl->wnd = newbuf;
 | 
						|
	wl->capacity = newsize;
 | 
						|
    }
 | 
						|
    wl->wnd[wl->nwnd++] = hWnd;
 | 
						|
    return TRUE;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
clear_window_list(struct window_list *wl) THROW_NONE
 | 
						|
{
 | 
						|
    free(wl->wnd);
 | 
						|
    wl->wnd = NULL;
 | 
						|
    wl->nwnd = 0;
 | 
						|
    wl->capacity = 0;
 | 
						|
}
 | 
						|
 | 
						|
static const char *
 | 
						|
gdip_strerror(Gdiplus::Status status) THROW_NONE
 | 
						|
{
 | 
						|
    size_t i;
 | 
						|
    struct status_rec {
 | 
						|
	Gdiplus::Status code;
 | 
						|
	const char *str;
 | 
						|
    };
 | 
						|
    static const struct status_rec table[] = {
 | 
						|
#define ERRITEM(s) { Gdiplus::s, #s }
 | 
						|
	ERRITEM(Ok),
 | 
						|
	ERRITEM(GenericError),
 | 
						|
	ERRITEM(InvalidParameter),
 | 
						|
	ERRITEM(OutOfMemory),
 | 
						|
	ERRITEM(ObjectBusy),
 | 
						|
	ERRITEM(InsufficientBuffer),
 | 
						|
	ERRITEM(NotImplemented),
 | 
						|
	ERRITEM(Win32Error),
 | 
						|
	ERRITEM(WrongState),
 | 
						|
	ERRITEM(Aborted),
 | 
						|
	ERRITEM(FileNotFound),
 | 
						|
	ERRITEM(ValueOverflow),
 | 
						|
	ERRITEM(AccessDenied),
 | 
						|
	ERRITEM(UnknownImageFormat),
 | 
						|
	ERRITEM(FontFamilyNotFound),
 | 
						|
	ERRITEM(FontStyleNotFound),
 | 
						|
	ERRITEM(NotTrueTypeFont),
 | 
						|
	ERRITEM(UnsupportedGdiplusVersion),
 | 
						|
	ERRITEM(GdiplusNotInitialized),
 | 
						|
	ERRITEM(PropertyNotFound),
 | 
						|
	ERRITEM(PropertyNotSupported),
 | 
						|
	ERRITEM(ProfileNotFound),
 | 
						|
#undef ERRITEM
 | 
						|
    };
 | 
						|
    for (i = 0; i != sizeof table / sizeof table[0]; ++i)
 | 
						|
	if (table[i].code == status)
 | 
						|
	    return table[i].str;
 | 
						|
    return "unknown";
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
gdip_perror(w3mimg_op *wop, Gdiplus::Status status, const char *func) THROW_NONE
 | 
						|
{
 | 
						|
    const char *s = gdip_strerror(status);
 | 
						|
    fprintf(stderr, "w3mimgdisplay: GDI+ error %d: %s\n", (int)status, s);
 | 
						|
    if (wop && wop->priv) {
 | 
						|
	struct win_info *xi = (struct win_info *)wop->priv;
 | 
						|
	if (xi->logfile) {
 | 
						|
	    fprintf(xi->logfile, "%s(): GDI+ error %d: %s\n", func, (int)status, s);
 | 
						|
	}
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
// Don't free() the result; use LocalFree() instead
 | 
						|
static char *
 | 
						|
win32_strerror_alloc(DWORD status) THROW_NONE
 | 
						|
{
 | 
						|
    char *errbuf = NULL;
 | 
						|
 | 
						|
    FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER |
 | 
						|
	    FORMAT_MESSAGE_FROM_SYSTEM |
 | 
						|
	    FORMAT_MESSAGE_IGNORE_INSERTS,
 | 
						|
	    NULL, status, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
 | 
						|
	    (LPSTR)&errbuf, 0, NULL);
 | 
						|
    if (errbuf) {
 | 
						|
	size_t len = strlen(errbuf);
 | 
						|
	if (len > 0 && errbuf[len - 1] == '\n')
 | 
						|
	    errbuf[len - 1] = '\0';
 | 
						|
    }
 | 
						|
    return errbuf;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
win32_perror(w3mimg_op *wop, DWORD status, const char *func) THROW_NONE
 | 
						|
{
 | 
						|
    char *errbuf = win32_strerror_alloc(status);
 | 
						|
    const char *s = errbuf ? errbuf : "(unknown)";
 | 
						|
 | 
						|
    fprintf(stderr, "w3mimgdisplay: Win32 error %u: %s\n", (unsigned int)status, s);
 | 
						|
    if (wop && wop->priv) {
 | 
						|
	struct win_info *xi = (struct win_info *)wop->priv;
 | 
						|
	if (xi->logfile) {
 | 
						|
	    fprintf(xi->logfile, "%s(): Win32 error %u: %s\n",
 | 
						|
		    func, (unsigned int)status, s);
 | 
						|
	}
 | 
						|
    }
 | 
						|
    LocalFree(errbuf);
 | 
						|
}
 | 
						|
 | 
						|
#if 0 /* unused */
 | 
						|
static WCHAR *
 | 
						|
mb2wstr_alloc(const char *s) THROW_NONE
 | 
						|
{
 | 
						|
    int len;
 | 
						|
    WCHAR *buf = NULL;
 | 
						|
 | 
						|
    len = MultiByteToWideChar(CP_OEMCP, MB_PRECOMPOSED, s, -1, NULL, 0);
 | 
						|
    if (len <= 0) {
 | 
						|
	fprintf(stderr, "w3mimgdisplay: unable to convert string ecode=%u\n",
 | 
						|
		(unsigned int)GetLastError());
 | 
						|
	goto error;
 | 
						|
    }
 | 
						|
    buf = (WCHAR *)malloc(len * sizeof(WCHAR)); /* including L'\0' */
 | 
						|
    if (!buf)
 | 
						|
	goto error;
 | 
						|
    len = MultiByteToWideChar(CP_OEMCP, MB_PRECOMPOSED, s, -1, buf, len);
 | 
						|
    if (len <= 0) {
 | 
						|
	fprintf(stderr, "w3mimgdisplay: unable to convert string ecode=%u\n",
 | 
						|
		(unsigned int)GetLastError());
 | 
						|
	goto error;
 | 
						|
    }
 | 
						|
    return buf;
 | 
						|
error:
 | 
						|
    free(buf);
 | 
						|
    return NULL;
 | 
						|
}
 | 
						|
 | 
						|
static char *
 | 
						|
wstr2mb_alloc(const WCHAR *ws) THROW_NONE
 | 
						|
{
 | 
						|
    int len;
 | 
						|
    char *buf = NULL;
 | 
						|
 | 
						|
    len = WideCharToMultiByte(CP_OEMCP, WC_COMPOSITECHECK, ws, -1, NULL, 0, NULL, NULL);
 | 
						|
    if (len <= 0) {
 | 
						|
	fprintf(stderr, "w3mimgdisplay: unable to convert string ecode=%u\n",
 | 
						|
		(unsigned int)GetLastError());
 | 
						|
	goto error;
 | 
						|
    }
 | 
						|
    buf = (char *)malloc(len); /* including '\0' */
 | 
						|
    if (!buf)
 | 
						|
	goto error;
 | 
						|
    len = WideCharToMultiByte(CP_OEMCP, WC_COMPOSITECHECK, ws, -1, buf, len, NULL, NULL);
 | 
						|
    if (len <= 0) {
 | 
						|
	fprintf(stderr, "w3mimgdisplay: unable to convert string ecode=%u\n",
 | 
						|
		(unsigned int)GetLastError());
 | 
						|
	goto error;
 | 
						|
    }
 | 
						|
    return buf;
 | 
						|
error:
 | 
						|
    free(buf);
 | 
						|
    return NULL;
 | 
						|
}
 | 
						|
#endif /* unused */
 |