view src/video/windows/SDL_gapirender.c @ 5067:61d53410eb41

Fixed bug #859 CREATE_SUBDIRS helps a lot if browsing HTML documentation in a file browser. ALWAYS_DETAILED_SEC makes sure everything has at least the automatic documentation like function prototype and source references. STRIP_FROM_PATH allows you to include only the relevant portions of the files' paths, cleaning up both the file list and directory tree, though you need to change the path listed here to match wherever you put SDL. ALIASES avoids some warnings generated by C:\source\svn.libsdl.org\trunk\SDL\src\joystick\darwin\10.3.9-FIX\IOHIDLib.h. It seems Apple uses a few commands which are not normally supported by Doxygen. BUILTIN_STL_SUPPORT adds support for parsing code which makes use of the standard template library. There isn't a lot of C++ in SDL (some in bwindow at least), but this still seems like a good idea. TYPEDEF_HIDES_STRUCT means that for code like this: typedef struct A {int B;} C; C is documented as a structure containing B instead of a typedef mapped to A. EXTRACT_ALL, EXTRACT_PRIVATE, EXTRACT_STATIC, EXTRACT_LOCAL_METHODS, EXTRACT_ANON_NSPACES and INTERNAL_DOCS make sure that _everything_ is documented. CASE_SENSE_NAMES = NO avoids potential conflicts when building documentation on case insensitive file systems like NTFS and FAT32. WARN_NO_PARAMDOC lets you know when you have documented some, but not all, of the parameters of a function. This is useful when you're working on adding such documentation since it makes partially documented functions easier to spot. WARN_LOGFILE writes warnings to a seperate file instead of mixing them in with stdout. When not running in quiet mode, these warnings can be hard to spot without this flag. I added *.h.in and *.h.default to FILE_PATTERNS to generate documentation for config.h.in and config.h.default. RECURSIVE tells doxygen to look not only in the input directory, but also in subfolders. EXCLUDE avoids documenting things like test programs, examples and templates which need to be documented separately. I've used EXCLUDE_PATTERNS to exclude non-source subdirectories that often find their way into source folders (such as obj or .svn). EXAMPLE_PATH lists directories doxygen will search to find included example code. So far, SDL doesn't really use this feature, but I've listed some likely locations. SOURCE_BROWSER adds syntax highlighted source code to the HTML output. USE_HTAGS is nice, but not available on Windows. INLINE_SOURCES adds the body of a function to it's documentation so you can quickly see exactly what it does. ALPHABETICAL_INDEX generates an alphabetical list of all structures, functions, etc., which makes it much easier to find what you're looking for. IGNORE_PREFIX skips the SDL_ prefix when deciding which index page to place an item on so you don't have everything show up under "S". HTML_DYNAMIC_SECTIONS hides the includes/included by diagrams by default and adds JavaScript to allow the user to show and hide them by clicking a link. ENUM_VALUES_PER_LINE = 1 makes enums easier to read by placing each value on it's own line. GENERATE_TREEVIEW produces a two frame index page with a navigation tree on the left. I have LaTeX and man pages turned off to speed up doxygen, you may want to turn them back on yourself. I added _WIN32=1 to PREDEFINED to cause SDL to output documentation related to Win32 builds of SDL. Normally, doxygen gets confused since there are multiple definitions for various structures and formats that vary by platform. Without this doxygen can produce broken documentation or, if you're lucky, output documentation only for the dummy drivers, which isn't very useful. You need to pick a platform. GENERATE_TAGFILE produces a file which can be used to link other doxygen documentation to the SDL documentation. CLASS_DIAGRAMS turns on class diagrams even when dot is not available. HAVE_DOT tells doxygen to try to use dot to generate diagrams. TEMPLATE_RELATIONS and INCLUDE_GRAPH add additional diagrams to the documentation. DOT_MULTI_TARGETS speeds up dot. OUTPUT_DIRECTORY, INPUT and other paths reflect the fact that this Doxyfile is intended to process src as well as include and is being run from a separate subdirectory. Doxygen produces several temporary files while it's running and if interrupted, can leave those files behind. It's easier to clean up if there aren't a hundred or so files in the same folder. I typically run doxygen in SDL/doxy and set the output directory to '.'. Since doxygen puts it's output in subfolders by type, this keeps things pretty well organised. You could use '../doc' instead and get the same results.
author Sam Lantinga <slouken@libsdl.org>
date Fri, 21 Jan 2011 12:57:01 -0800
parents e8916fe9cfc8
children c2539ff054c8
line wrap: on
line source

/***************************************************************************
 *   Copyright (C) 2010 by Andrey Afletdinov <afletdinov@gmail.com>        *
 *                                                                         *
 *   WinCE RAW/GAPI video driver                                           *
 *                                                                         *
 *   Part of the SDL - (Simple DirectMedia Layer)                          *
 *   http://www.libsdl.org                                                 *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

#include "SDL_config.h"

#if SDL_VIDEO_RENDER_GAPI

#include "SDL_windowsvideo.h"
#include "SDL_windowswindow.h"
#include "../SDL_yuv_sw_c.h"

// RawFrameBufferInfo
typedef struct
{
   WORD wFormat;
   WORD wBPP;
   VOID *pFramePointer;
   int  cxStride;
   int  cyStride;
   int  cxPixels;
   int  cyPixels;
} RawFrameBufferInfo;

// GXDeviceInfo
typedef struct
{
    long Version;
    void* pvFrameBuffer;
    unsigned long cbStride;
    unsigned long cxWidth;
    unsigned long cyHeight;
    unsigned long cBPP;
    unsigned long ffFormat;
    char unknown[0x84 - 7 * 4];
} GXDeviceInfo;

// wince: GXDisplayProperties
struct GXDisplayProperties
{
    DWORD cxWidth;
    DWORD cyHeight;
    long cbxPitch;
    long cbyPitch;
    long cBPP;
    DWORD ffFormat;
};

// gx.dll
typedef int   (*PFNGXOpenDisplay)(HWND hWnd, DWORD dwFlags);
typedef int   (*PFNGXCloseDisplay)();
typedef void* (*PFNGXBeginDraw)();
typedef int   (*PFNGXEndDraw)();
typedef struct GXDisplayProperties (*PFNGXGetDisplayProperties)();
typedef int   (*PFNGXSuspend)();
typedef int   (*PFNGXResume)();

typedef struct
{
    // gx.dll
    HMODULE                   hGapiLib;
    PFNGXOpenDisplay          GXOpenDisplay;
    PFNGXCloseDisplay         GXCloseDisplay;
    PFNGXBeginDraw            GXBeginDraw;
    PFNGXEndDraw              GXEndDraw;
    PFNGXGetDisplayProperties GXGetDisplayProperties;
    PFNGXSuspend              GXSuspend;
    PFNGXResume               GXResume;
} GapiInfo;

//#ifndef DM_DISPLAYORIENTATION
//#define DM_DISPLAYORIENTATION 0x00800000L
//#endif

#define FORMAT_565		1
#define FORMAT_555		2
#define FORMAT_OTHER		3

#define GETRAWFRAMEBUFFER	0x00020001
#define GETGXINFO		0x00020000

#define kfPalette		0x10
#define kfDirect		0x20
#define kfDirect555		0x40
#define kfDirect565		0x80

#define GX_FULLSCREEN		0x01

enum ScreenOrientation { ORIENTATION_UNKNOWN = -1, ORIENTATION_UP = DMDO_0, ORIENTATION_DOWN = DMDO_180, ORIENTATION_LEFT = DMDO_270, ORIENTATION_RIGHT = DMDO_90 };
enum ScreenGeometry { GEOMETRY_UNKNOWN, GEOMETRY_PORTRAIT, GEOMETRY_LANDSCAPE, GEOMETRY_SQUARE };
enum FrameBufferFlags { FB_SKIP_OFFSET = 0x0001, FB_RAW_MODE = 0x0002, FB_SUSPENDED = 0x0004 };

// private framebuffer info
typedef struct
{
    int width;
    int height;
    int xpitch;
    int ypitch;
    int offset;
} FrameBufferInfo;

// private display data
typedef struct
{
    unsigned char* pixels;	// video memory
    int format;			// video format
    FrameBufferInfo fb;		// framebuffer geometry
    GapiInfo* gapi;		// GAPI module
    int userOrientation;
    int systemOrientation;
    int hardwareGeometry;
    int flags;			// fb flags
    float scale;		// scale pointer position
    int debug;

} WINCE_RenderData;

typedef struct
{
    SDL_SW_YUVTexture *yuv;
    Uint32 format;
    void *pixels;
    int pitch;

} WINCE_TextureData;


// system func
SDL_Renderer*	WINCE_CreateRenderer(SDL_Window* window, Uint32 flags);
void		WINCE_DestroyRenderer(SDL_Renderer* renderer);

int		WINCE_CreateTexture(SDL_Renderer* renderer, SDL_Texture* texture);
void		WINCE_DestroyTexture(SDL_Renderer* renderer, SDL_Texture* texture);
int		WINCE_QueryTexturePixels(SDL_Renderer* renderer, SDL_Texture* texture, void** pixels, int* pitch);
int		WINCE_UpdateTexture(SDL_Renderer* renderer, SDL_Texture* texture, const SDL_Rect* rect, const void* pixels, int pitch);
int		WINCE_LockTexture(SDL_Renderer* renderer, SDL_Texture* texture, const SDL_Rect* rect, int dirty, void** pixels, int* pitch);
void		WINCE_UnlockTexture(SDL_Renderer* renderer, SDL_Texture* texture);

int		WINCE_Available(void);
void		WINCE_SetupOrientation(WINCE_RenderData* data, int width, int height);

int		WINCE_RenderCopy(SDL_Renderer* renderer, SDL_Texture* texture, const SDL_Rect* srect, const SDL_Rect* drect);
void		WINCE_ShowWindow(_THIS, SDL_Window* window, int visible);

void		WINCE_RenderPresent(SDL_Renderer* renderer);
int		WINCE_RenderDrawPoints(SDL_Renderer* renderer, const SDL_Point* points, int count);
int		WINCE_RenderDrawLines(SDL_Renderer* renderer, const SDL_Point* points, int count);
int		WINCE_RenderDrawRects(SDL_Renderer* renderer, const SDL_Rect ** rects, int count);
int		WINCE_RenderFillRects(SDL_Renderer* renderer, const SDL_Rect** rects, int count);

void		WINCE_PointerCoordinateTransform(SDL_Window* window, POINT* pt);
void		WINCE_DumpVideoInfo(WINCE_RenderData* data);
void		WINCE_PortraitTransform(WINCE_RenderData* data, int width, int height);
void		WINCE_LandscapeTransform(WINCE_RenderData* data, int width, int height);
void		WINCE_SquareTransform(WINCE_RenderData* data, int width, int height);
int		WINCE_FixedGeometry(FrameBufferInfo* fb, int bpp, int debug);
int		WINCE_GetDMOrientation(void);
int		WINCE_SetDMOrientation(int orientation);
void		WINCE_UpdateYUVTextureData(SDL_Texture* texture);

// gapi engine specific
int		GAPI_Init(WINCE_RenderData* data, HWND hwnd);
void		GAPI_Quit(WINCE_RenderData* data);

// raw engine specific
int		RAW_Init(WINCE_RenderData* data);
void		RAW_Quit(WINCE_RenderData* data);

// tools
void		FrameBufferRotate(FrameBufferInfo* src, int orientation);
int		GetFrameBufferOrientation(const FrameBufferInfo* src);
void		PointerRotate(POINT* pt, const FrameBufferInfo* fb, int orientation);
void		FrameBufferInitialize(FrameBufferInfo* fb);
void		FrameBufferDumpInfo(const FrameBufferInfo* fb, const char*);
const		char* GetOrientationName(int orientation);
void		UpdateLine16to16(const FrameBufferInfo* fb, const Uint16* src, Uint16* dst, Uint16 width);

// stdlib
inline int	__abs(int x){ return x < 0 ? -x : x; };
inline void	__swap(int* a, int* b){ int t = *a; *a = *b; *b = t; };

#define GAPI_RENDER_NAME	"gapi"
#define RAW_RENDER_NAME		"raw"
//
SDL_RenderDriver GAPI_RenderDriver = {
    WINCE_CreateRenderer,
    {
	GAPI_RENDER_NAME,
	(SDL_RENDERER_SINGLEBUFFER | SDL_RENDERER_PRESENTDISCARD),
	(SDL_TEXTUREMODULATE_NONE),
	(SDL_BLENDMODE_NONE),
	(SDL_SCALEMODE_NONE),
	7,
	{
	    SDL_PIXELFORMAT_RGB555,
	    SDL_PIXELFORMAT_RGB565,
	    SDL_PIXELFORMAT_YV12,
	    SDL_PIXELFORMAT_IYUV,
	    SDL_PIXELFORMAT_YUY2,
	    SDL_PIXELFORMAT_UYVY,
	    SDL_PIXELFORMAT_YVYU
	},
	0,
	0
    }
};

SDL_RenderDriver RAW_RenderDriver = {
    WINCE_CreateRenderer,
    {
	RAW_RENDER_NAME,
	(SDL_RENDERER_SINGLEBUFFER | SDL_RENDERER_PRESENTDISCARD),
	(SDL_TEXTUREMODULATE_NONE),
	(SDL_BLENDMODE_NONE),
	(SDL_SCALEMODE_NONE),
	7,
	{
	    SDL_PIXELFORMAT_RGB555,
	    SDL_PIXELFORMAT_RGB565,
	    SDL_PIXELFORMAT_YV12,
	    SDL_PIXELFORMAT_IYUV,
	    SDL_PIXELFORMAT_YUY2,
	    SDL_PIXELFORMAT_UYVY,
	    SDL_PIXELFORMAT_YVYU
	},
	0,
	0
    }
};

int WINCE_Available(void)
{
    const char* preferably = SDL_getenv("SDL_VIDEO_RENDERER");

    // raw check
    RawFrameBufferInfo rfbi = { 0 };
    HDC hdc = GetDC(NULL);
    int render_raw = ExtEscape(hdc, GETRAWFRAMEBUFFER, 0, NULL, sizeof(RawFrameBufferInfo), (char *) &rfbi);
    ReleaseDC(NULL, hdc);

    if(render_raw != 0 && rfbi.cxPixels != 0 && rfbi.cyPixels != 0 &&
        rfbi.pFramePointer != 0 && rfbi.cxStride != 0 && rfbi.cyStride != 0)
	    render_raw = 1;

    if(preferably && 0 == SDL_strcasecmp(preferably, RAW_RENDER_NAME)) return 0 != render_raw;

    // gapi check
    HMODULE render_gapi = LoadLibrary(TEXT("\\Windows\\gx.dll"));
    if(0 == render_gapi)
        render_gapi = LoadLibrary(TEXT("gx.dll"));
    FreeLibrary(render_gapi);

    if(preferably && 0 == SDL_strcasecmp(preferably, GAPI_RENDER_NAME)) return 0 != render_gapi;

    return 0 != render_raw || 0 != render_gapi;
}

void WINCE_AddRenderDriver(_THIS)
{
    HDC hdc;
    HMODULE render_gapi;
    int render_raw, ii;
    const char* preferably = SDL_getenv("SDL_VIDEO_RENDERER");

   // raw check
    RawFrameBufferInfo rfbi = { 0 };
    hdc = GetDC(NULL);
    render_raw = ExtEscape(hdc, GETRAWFRAMEBUFFER, 0, NULL, sizeof(RawFrameBufferInfo), (char *) &rfbi);
    ReleaseDC(NULL, hdc);

    if(render_raw != 0 && rfbi.cxPixels != 0 && rfbi.cyPixels != 0 &&
    	rfbi.pFramePointer != 0 && rfbi.cxStride != 0 && rfbi.cyStride != 0)
	    render_raw = 1;

    // gapi check
    render_gapi = LoadLibrary(TEXT("\\Windows\\gx.dll"));
    if(0 == render_gapi)
        render_gapi = LoadLibrary(TEXT("gx.dll"));

    if(render_gapi)
	FreeLibrary(render_gapi);

    for(ii = 0; ii < _this->num_displays; ++ii)
    {
	if(preferably)
	{
	    if(0 == SDL_strcasecmp(preferably, RAW_RENDER_NAME) && render_raw)
    		SDL_AddRenderDriver(&_this->displays[ii], &RAW_RenderDriver);
	    else
	    if(0 == SDL_strcasecmp(preferably, GAPI_RENDER_NAME) && render_gapi)
    		SDL_AddRenderDriver(&_this->displays[ii], &GAPI_RenderDriver);
	}
	else
	{
	    if(render_raw)
    		SDL_AddRenderDriver(&_this->displays[ii], &RAW_RenderDriver);
	    if(render_gapi)
    		SDL_AddRenderDriver(&_this->displays[ii], &GAPI_RenderDriver);
	}
    }
}

SDL_Renderer* WINCE_CreateRenderer(SDL_Window* window, Uint32 flags)
{
    SDL_VideoDisplay* display = window->display;
    SDL_DisplayMode* displayMode = &display->current_mode;
    SDL_WindowData* windowdata = (SDL_WindowData *) window->driverdata;
    SDL_Renderer* renderer;
    WINCE_RenderData* data;
    int bpp;
    Uint32 Rmask, Gmask, Bmask, Amask;

    if(!(window->flags & SDL_WINDOW_FULLSCREEN))
	window->flags |= SDL_WINDOW_FULLSCREEN;

    if(!SDL_PixelFormatEnumToMasks(displayMode->format, &bpp, &Rmask, &Gmask, &Bmask, &Amask))
    {
        SDL_SetError("Unknown display format");
        return NULL;
    }

    switch(window->fullscreen_mode.format)
    {
	case SDL_PIXELFORMAT_RGB555:
	case SDL_PIXELFORMAT_RGB565:
	    break;

	default:
    	    SDL_SetError("Support only 16 or 15 bpp");
	    return NULL;
    }

    renderer = (SDL_Renderer*) SDL_calloc(1, sizeof(SDL_Renderer));
    if(!renderer)
    {
        SDL_OutOfMemory();
        return NULL;
    }

    data = (WINCE_RenderData*) SDL_calloc(1, sizeof(WINCE_RenderData));
    if(!data)
    {
        WINCE_DestroyRenderer(renderer);
        SDL_OutOfMemory();
        return NULL;
    }

    // initialize internal engine
    if(!RAW_Init(data) && !GAPI_Init(data, windowdata->hwnd))
    {
        WINCE_DestroyRenderer(renderer);
        return NULL;
    }


    // set debug
    data->debug	= SDL_getenv("DEBUG_VIDEO_GAPI") || SDL_getenv("GAPI_RENDERER_DEBUG") ? 1 : 0;
#if defined(DEBUG_VIDEO_GAPI) || defined(GAPI_RENDERER_DEBUG)
    data->debug	= 1;
#endif

    windowdata->videodata->render = data->gapi ? RENDER_GAPI : RENDER_RAW;
    windowdata->videodata->CoordTransform = WINCE_PointerCoordinateTransform;

    window->display->device->MaximizeWindow = NULL;
    window->display->device->MinimizeWindow = NULL;

    WINCE_SetupOrientation(data, window->w, window->h);

    renderer->CreateTexture = WINCE_CreateTexture;
    renderer->DestroyTexture = WINCE_DestroyTexture;
    renderer->QueryTexturePixels = WINCE_QueryTexturePixels;
    renderer->UpdateTexture = WINCE_UpdateTexture;
    renderer->LockTexture = WINCE_LockTexture;
    renderer->UnlockTexture = WINCE_UnlockTexture;

    renderer->RenderCopy = WINCE_RenderCopy;
    renderer->DestroyRenderer = WINCE_DestroyRenderer;

    renderer->RenderPresent = WINCE_RenderPresent;
    renderer->RenderDrawPoints = WINCE_RenderDrawPoints;
    renderer->RenderDrawLines = WINCE_RenderDrawLines;
    renderer->RenderDrawRects = WINCE_RenderDrawRects;
    renderer->RenderFillRects = WINCE_RenderFillRects;

    renderer->info = data->gapi ? GAPI_RenderDriver.info : RAW_RenderDriver.info;

    renderer->window = window;
    renderer->driverdata = data;

    return renderer;
}

void WINCE_DestroyRenderer(SDL_Renderer* renderer)
{
    WINCE_RenderData *renderdata = (WINCE_RenderData*) renderer->driverdata;

    if(renderdata)
    {
	if(renderdata->gapi)
    	    GAPI_Quit(renderdata);
	else
	    RAW_Quit(renderdata);

        SDL_free(renderdata);
    }

    SDL_free(renderer);
}

int WINCE_CreateTexture(SDL_Renderer* renderer, SDL_Texture* texture)
{
    WINCE_TextureData* texturedata = (WINCE_TextureData*) SDL_calloc(1, sizeof(WINCE_TextureData));
    if(NULL == texturedata)
    {
        SDL_OutOfMemory();
        return -1;
    }

    texturedata->pitch = texture->w * SDL_BYTESPERPIXEL(texture->format);
    texturedata->pixels = SDL_malloc(texture->h * texturedata->pitch);
    if(NULL == texturedata->pixels)
    {
        SDL_OutOfMemory();
        return -1;
    }

    if(SDL_ISPIXELFORMAT_FOURCC(texture->format))
    {
	texturedata->yuv = SDL_SW_CreateYUVTexture(texture->format, texture->w, texture->h);
        if(NULL == texturedata->yuv)
	{
    	    SDL_OutOfMemory();
            return -1;
        }
	SDL_Window* window = renderer->window;
	SDL_VideoDisplay* display = window->display;
	texturedata->format = display->current_mode.format;
    }
    else
    {
	texturedata->yuv = NULL;
	texturedata->format = texture->format;
    }

    texture->driverdata = texturedata;

    return 0;
}

void WINCE_DestroyTexture(SDL_Renderer* renderer, SDL_Texture* texture)
{
    WINCE_TextureData *texturedata = (WINCE_TextureData*) texture->driverdata;

    if(texturedata)
    {
	if(texturedata->yuv) SDL_SW_DestroyYUVTexture(texturedata->yuv);
	if(texturedata->pixels) SDL_free(texturedata->pixels);
	SDL_free(texturedata);
	texture->driverdata = NULL;
    }
}

int WINCE_QueryTexturePixels(SDL_Renderer* renderer, SDL_Texture* texture, void** pixels, int* pitch)
{
    WINCE_TextureData* texturedata = (WINCE_TextureData*) texture->driverdata;

    if(texturedata->yuv)
        return SDL_SW_QueryYUVTexturePixels(texturedata->yuv, pixels, pitch);

    *pixels = texturedata->pixels;
    *pitch = texturedata->pitch;

    return 0;
}

int WINCE_UpdateTexture(SDL_Renderer* renderer, SDL_Texture* texture, const SDL_Rect* rect, const void* pixels, int pitch)
{
    WINCE_TextureData* texturedata = (WINCE_TextureData*) texture->driverdata;

    if(texturedata->yuv)
    {
        if(SDL_SW_UpdateYUVTexture(texturedata->yuv, rect, pixels, pitch) < 0)
            return -1;
        WINCE_UpdateYUVTextureData(texture);
        return 0;
    }

    if(0 < rect->w && 0 < rect->h)
    {
	const unsigned char *src = ((const unsigned char*) pixels);
	unsigned char *dst = ((unsigned char*) texturedata->pixels) +
				rect->y * texturedata->pitch +
				rect->x * SDL_BYTESPERPIXEL(texture->format);
        int length = rect->w * SDL_BYTESPERPIXEL(texture->format);
	int height = rect->h;

	while(height--)
	{
	    SDL_memcpy(dst, src, length);
	    dst += texturedata->pitch;
	    src += pitch;
	}
    }

    return 0;
}

int WINCE_LockTexture(SDL_Renderer* renderer, SDL_Texture* texture, const SDL_Rect* rect, int dirty, void** pixels, int* pitch)
{
    WINCE_TextureData *texturedata = (WINCE_TextureData*) texture->driverdata;

    if(texturedata->yuv)
        return SDL_SW_LockYUVTexture(texturedata->yuv, rect, dirty, pixels, pitch);

    *pixels = (void *) ((unsigned char*) texturedata->pixels +
		    rect->y * texturedata->pitch +
		    rect->x * SDL_BYTESPERPIXEL(texture->format));
    *pitch = texturedata->pitch;
}

void WINCE_UnlockTexture(SDL_Renderer* renderer, SDL_Texture* texture)
{
    WINCE_TextureData *texturedata = (WINCE_TextureData*) texture->driverdata;

    if(texturedata->yuv)
    {
        SDL_SW_UnlockYUVTexture(texturedata->yuv);
	WINCE_UpdateYUVTextureData(texture);
    }
}

int WINCE_RenderCopy(SDL_Renderer* renderer, SDL_Texture* texture, const SDL_Rect* srect, const SDL_Rect* drect)
{
    WINCE_RenderData* dstdata = (WINCE_RenderData*) renderer->driverdata;
    WINCE_TextureData* srcdata = (WINCE_TextureData*) texture->driverdata;

    if((dstdata->flags & FB_SUSPENDED) ||
       0 >= srect->w || 0 >= srect->h) return;

    // lock gapi
    if(dstdata->gapi) dstdata->gapi->GXBeginDraw();

    const unsigned char *src = ((const unsigned char*) srcdata->pixels);
    unsigned char *dst = dstdata->pixels + (dstdata->flags & FB_SKIP_OFFSET ? 0 : dstdata->fb.offset) +
				drect->y * dstdata->fb.ypitch +
				drect->x * dstdata->fb.xpitch;
    if(srcdata->yuv)
    {
	return SDL_SW_CopyYUVToRGB(srcdata->yuv,
                                   srect, srcdata->format,
                                   drect->w, drect->h, dst,
                                   dstdata->fb.ypitch);
    }
    else
    {
	int height = drect->h;
	int length = drect->w * SDL_BYTESPERPIXEL(texture->format); // in bytes

	while(height--)
	{
	    switch(SDL_BYTESPERPIXEL(texture->format))
	    {
		case 2: UpdateLine16to16(&dstdata->fb, (Uint16*) src, (Uint16*) dst, length >> 1); break;

		default: break;
	    }

	    dst += dstdata->fb.ypitch;
	    src += srcdata->pitch;
	}
    }

    // unlock gapi
    if(dstdata->gapi) dstdata->gapi->GXEndDraw();

    return 0;
}

void WINCE_RenderPresent(SDL_Renderer* renderer)
{
}

int WINCE_RenderDrawPoints(SDL_Renderer* renderer, const SDL_Point* points, int count)
{
    SDL_Unsupported();
    return -1;
}

int WINCE_RenderDrawLines(SDL_Renderer* renderer, const SDL_Point* points, int count)
{
    SDL_Unsupported();
    return -1;
}

int WINCE_RenderDrawRects(SDL_Renderer* renderer, const SDL_Rect ** rects, int count)
{
    SDL_Unsupported();
    return -1;
}

int WINCE_RenderFillRects(SDL_Renderer* renderer, const SDL_Rect** rects, int count)
{
    SDL_Unsupported();
    return -1;
}



void WINCE_SetupOrientation(WINCE_RenderData* data, int width, int height)
{
    const float maxW1 = GetSystemMetrics(SM_CXSCREEN) > GetSystemMetrics(SM_CYSCREEN) ? GetSystemMetrics(SM_CXSCREEN) : GetSystemMetrics(SM_CYSCREEN);
    const float maxW2 = data->fb.width > data->fb.height ? data->fb.width : data->fb.height;

    // scale define
    data->scale = maxW2 / maxW1;

    // init fb values
    FrameBufferInitialize(&data->fb);

    // orientation values
    data->userOrientation = ORIENTATION_UP;
    data->systemOrientation = WINCE_GetDMOrientation();
    data->hardwareGeometry = data->fb.width == data->fb.height ? GEOMETRY_SQUARE :
				(data->fb.width < data->fb.height ? GEOMETRY_PORTRAIT : GEOMETRY_LANDSCAPE);

    if(data->debug)
	WINCE_DumpVideoInfo(data);

    if(data->systemOrientation == ORIENTATION_UNKNOWN)
	data->systemOrientation == ORIENTATION_UP;

    data->userOrientation = ORIENTATION_UP;

    switch(data->hardwareGeometry)
    {
	case GEOMETRY_PORTRAIT:  WINCE_PortraitTransform(data, width, height); break;
	case GEOMETRY_LANDSCAPE: WINCE_LandscapeTransform(data, width, height); break;
	case GEOMETRY_SQUARE:    WINCE_SquareTransform(data, width, height); break;
	default: break;
    }

    // debug
    if(data->debug)
    {
	printf("\n");
	printf("user video width:          %d\n", width);
	printf("user video height:         %d\n", height);
	FrameBufferDumpInfo(&data->fb, "user");
    }
}

void WINCE_DumpVideoInfo(WINCE_RenderData* data)
{
    // get oem info
    WCHAR oemInfo[48];
    SDL_memset(oemInfo, 0, sizeof(oemInfo));
    SystemParametersInfo(SPI_GETOEMINFO, sizeof(oemInfo) - sizeof(WCHAR), oemInfo, 0);

    printf("hardware oem: ");
    wprintf(oemInfo);
    printf("\n");

    printf("video driver mode:             %s\n", (data->flags & FB_RAW_MODE ? RAW_RENDER_NAME : GAPI_RENDER_NAME));
    printf("GetSystemMetrics(SM_CXSCREEN): %d\n", GetSystemMetrics(SM_CXSCREEN));
    printf("GetSystemMetrics(SM_CYSCREEN): %d\n", GetSystemMetrics(SM_CYSCREEN));
    printf("scale coord:                   %f\n", data->scale);

    FrameBufferDumpInfo(&data->fb, "hardware");

    printf("display format:                %p\n", data->format);
    printf("display bits per pixel:        %d\n", SDL_BITSPERPIXEL(data->format));
    printf("display bytes per pixel:       %d\n", SDL_BYTESPERPIXEL(data->format));
    printf("display memory:                %p\n", data->pixels);
    printf("system orientation:            %d, %s\n", data->systemOrientation, GetOrientationName(data->systemOrientation));
    printf("hardware geometry:             %d\n", data->hardwareGeometry);
}

void WINCE_ShowWindow(_THIS, SDL_Window* window, int visible)
{
    SDL_WindowData* windowdata = (SDL_WindowData*) window->driverdata;
    SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata;
    SDL_Renderer* renderer = (SDL_Renderer*) window->renderer;

    if(visible)
    {
	if(window->flags & SDL_WINDOW_FULLSCREEN)
	{
	    if(videodata->SHFullScreen)
		videodata->SHFullScreen(windowdata->hwnd, SHFS_HIDETASKBAR | SHFS_HIDESTARTICON | SHFS_HIDESIPBUTTON);
	    ShowWindow(FindWindow(TEXT("HHTaskBar"), NULL), SW_HIDE);
	}

	ShowWindow(windowdata->hwnd, SW_SHOW);
        SetForegroundWindow(windowdata->hwnd);

	if(renderer &&
	    (videodata->render == RENDER_GAPI || videodata->render == RENDER_RAW))
	{
	    WINCE_RenderData* renderdata = (WINCE_RenderData*) renderer->driverdata;
	    renderdata->flags &= ~FB_SUSPENDED;
	    if(renderdata->gapi) renderdata->gapi->GXResume();
	}
    }
    else
    {
	if(renderer &&
	    (videodata->render == RENDER_GAPI || videodata->render == RENDER_RAW))
	{
	    WINCE_RenderData* renderdata = (WINCE_RenderData*) renderer->driverdata;
	    if(renderdata->gapi) renderdata->gapi->GXSuspend();
	    renderdata->flags |= FB_SUSPENDED;
	}

	ShowWindow(windowdata->hwnd, SW_HIDE);

	if(window->flags & SDL_WINDOW_FULLSCREEN)
	{
	    if(videodata->SHFullScreen)
		videodata->SHFullScreen(windowdata->hwnd, SHFS_SHOWTASKBAR | SHFS_SHOWSTARTICON | SHFS_SHOWSIPBUTTON);
	    ShowWindow(FindWindow(TEXT("HHTaskBar"), NULL), SW_SHOW);
	}
    }
}


void WINCE_PointerCoordinateTransform(SDL_Window* window, POINT* pt)
{
    WINCE_RenderData* data = (WINCE_RenderData*) window->renderer->driverdata;

    pt->x *= data->scale;
    pt->y *= data->scale;

    PointerRotate(pt, &data->fb, data->userOrientation);
}

void WINCE_PortraitTransform(WINCE_RenderData* data, int width, int height)
{
    if(data->systemOrientation != ORIENTATION_UP)
	FrameBufferRotate(&data->fb, data->systemOrientation);

    if(data->fb.width != width || data->fb.height != height)
    switch(data->systemOrientation)
    {
	case ORIENTATION_UP:
	case ORIENTATION_LEFT: data->userOrientation = ORIENTATION_RIGHT; break;
	case ORIENTATION_RIGHT:
	case ORIENTATION_DOWN: data->userOrientation = ORIENTATION_LEFT; break;
	default: break;
    }

    if(data->userOrientation != ORIENTATION_UP)
	FrameBufferRotate(&data->fb, data->userOrientation);
}

void WINCE_LandscapeTransform(WINCE_RenderData* data, int width, int height)
{
    switch(data->systemOrientation)
    {
	case ORIENTATION_UP:  FrameBufferRotate(&data->fb, ORIENTATION_LEFT); break;
	case ORIENTATION_LEFT:FrameBufferRotate(&data->fb, ORIENTATION_DOWN); break;
	case ORIENTATION_DOWN:FrameBufferRotate(&data->fb, ORIENTATION_RIGHT); break;
	default: break;
    }

    if(data->fb.width != width || data->fb.height != height)
    switch(data->systemOrientation)
    {
	case ORIENTATION_UP:
	case ORIENTATION_LEFT: data->userOrientation = ORIENTATION_RIGHT; break;
	case ORIENTATION_RIGHT:
	case ORIENTATION_DOWN: data->userOrientation = ORIENTATION_LEFT; break;
	default: break;
    }

    if(data->userOrientation != ORIENTATION_UP)
	FrameBufferRotate(&data->fb, data->userOrientation);
}

void WINCE_SquareTransform(WINCE_RenderData* data, int width, int height)
{
    WINCE_PortraitTransform(data, width, height);
}

int WINCE_FixedGeometry(FrameBufferInfo* fb, int bpp, int debug)
{
    // check square
    if(GetSystemMetrics(SM_CXSCREEN) == GetSystemMetrics(SM_CYSCREEN) &&
	fb->width != fb->height)
    {
	if(fb->width < fb->height)
	    fb->height = fb->width;
	else
	if(fb->height < fb->width)
	    fb->width = fb->height;
    }

    // check width
    if(__abs(fb->xpitch) == bpp &&
	fb->width  != __abs(fb->ypitch) / bpp)
    {
	if(fb->height == __abs(fb->ypitch) / bpp)
    	{
	    __swap(&fb->width, &fb->height);

	    if(debug)
		printf("WINCE_FixedGeometry: width: %d, height: %d\n", fb->width, fb->height);
	}
	else
	    return -1;
    }
    else
    // check height
    if(__abs(fb->ypitch) == bpp &&
	fb->height != __abs(fb->xpitch) / bpp)
    {
	if(fb->width  == __abs(fb->xpitch) / bpp)
    	{
	    __swap(&fb->width, &fb->height);

	    if(debug)
		printf("WINCE_FixedGeometry: width: %d, height: %d\n", fb->width, fb->height);
	}
	else
	    return -1;
    }

    return 0;
}

void WINCE_UpdateYUVTextureData(SDL_Texture* texture)
{
    WINCE_TextureData* texturedata = (WINCE_TextureData*) texture->driverdata;
    SDL_Rect rect;

    rect.x = 0;
    rect.y = 0;
    rect.w = texture->w;
    rect.h = texture->h;
    SDL_SW_CopyYUVToRGB(texturedata->yuv, &rect, texturedata->format, texture->w, texture->h, texturedata->pixels, texturedata->pitch);
}

int GAPI_Init(WINCE_RenderData* data, HWND hwnd)
{
    if(NULL == data->gapi)
    {
	const char* preferably = SDL_getenv("SDL_VIDEO_RENDERER");
	if(preferably && 0 != SDL_strcasecmp(preferably, GAPI_RENDER_NAME)) return 0;

        data->gapi = (GapiInfo *) SDL_calloc(1, sizeof(GapiInfo));
        if(NULL == data->gapi)
        {
            SDL_OutOfMemory();
            return 0;
        }

	data->gapi->hGapiLib = LoadLibrary(TEXT("\\Windows\\gx.dll"));
	if(0 == data->gapi->hGapiLib)
	{
	    data->gapi->hGapiLib = LoadLibrary(TEXT("gx.dll"));
	    if(0 == data->gapi->hGapiLib) return 0;
	}

	// load gapi library
#define LINK(type,name,import) name=(PFN##type)GetProcAddress(data->gapi->hGapiLib,TEXT(import))
	LINK(GXOpenDisplay,         data->gapi->GXOpenDisplay,         "?GXOpenDisplay@@YAHPAUHWND__@@K@Z");
	LINK(GXCloseDisplay,        data->gapi->GXCloseDisplay,        "?GXCloseDisplay@@YAHXZ");
	LINK(GXBeginDraw,           data->gapi->GXBeginDraw,           "?GXBeginDraw@@YAPAXXZ");
	LINK(GXEndDraw,             data->gapi->GXEndDraw,             "?GXEndDraw@@YAHXZ");
	LINK(GXGetDisplayProperties,data->gapi->GXGetDisplayProperties,"?GXGetDisplayProperties@@YA?AUGXDisplayProperties@@XZ");
	LINK(GXSuspend,             data->gapi->GXSuspend,             "?GXSuspend@@YAHXZ");
	LINK(GXResume,              data->gapi->GXResume,              "?GXResume@@YAHXZ");
#undef LINK

	int enable = data->gapi->GXGetDisplayProperties && data->gapi->GXCloseDisplay && data->gapi->GXOpenDisplay &&
	    data->gapi->GXBeginDraw && data->gapi->GXEndDraw && data->gapi->GXSuspend && data->gapi->GXResume;

	if(!enable)
	{
	    SDL_SetError("GAPI_Init: error gx.dll: internal error");
	    GAPI_Quit(data);
	    return 0;
	}

	if(0 == data->gapi->GXOpenDisplay(hwnd, GX_FULLSCREEN))
	{
	    SDL_SetError("GAPI_Init: couldn't initialize GAPI");
	    GAPI_Quit(data);
	    return 0;
	}

	struct GXDisplayProperties gxProperties = data->gapi->GXGetDisplayProperties();

	// fill FrameBufferInfo
	data->fb.xpitch = gxProperties.cbxPitch;
	data->fb.ypitch = gxProperties.cbyPitch;
	data->fb.width  = gxProperties.cxWidth;
	data->fb.height = gxProperties.cyHeight;
	data->fb.offset = 0;

        if((gxProperties.ffFormat & kfDirect565) || 16 == gxProperties.cBPP)
	    data->format = SDL_PIXELFORMAT_RGB565;
	else
	if((gxProperties.ffFormat & kfDirect555) || 15 == gxProperties.cBPP)
	    data->format = SDL_PIXELFORMAT_RGB555;
	else
	    data->format = 0;

	// get pixels
	GXDeviceInfo gxInfo = { 0 };
	HDC hdc = GetDC(NULL);

	gxInfo.Version = 100;
	int result = ExtEscape(hdc, GETGXINFO, 0, NULL, sizeof(gxInfo), (char *) &gxInfo);
	ReleaseDC(NULL, hdc);

	if(result > 0)
	{
	    // more debug
	    if(data->debug)
	    {
		printf("GXDeviceInfo.pvFrameBuffer:    %p\n", gxInfo.pvFrameBuffer);
    		printf("GXDeviceInfo.cxWidth:          %d\n", gxInfo.cxWidth);
    		printf("GXDeviceInfo.cyHeight:         %d\n", gxInfo.cyHeight);
    		printf("GXDeviceInfo.cbStride:         %d\n", gxInfo.cbStride);
    		printf("GXDeviceInfo.cBPP:             %d\n", gxInfo.cBPP);
    		printf("GXDeviceInfo.ffFormat:        0x%x\n", gxInfo.ffFormat);

    		printf("GXDeviceInfo.unk:\n");
		int ii; for(ii = 0; ii <  sizeof(gxInfo.unknown); ++ii)
		printf("0x%02hhX,", gxInfo.unknown[ii]);
		printf("\n");
	    }

    	    if(gxInfo.ffFormat && gxInfo.ffFormat != gxProperties.ffFormat)
	    {
		if((gxInfo.ffFormat & kfDirect565) || 16 == gxInfo.cBPP)
		    data->format = SDL_PIXELFORMAT_RGB565;
		else
		if((gxInfo.ffFormat & kfDirect555) || 15 == gxInfo.cBPP)
		    data->format = SDL_PIXELFORMAT_RGB555;
	    }

    	    data->pixels = gxInfo.pvFrameBuffer;
        }
	else
	{
	    data->flags |= FB_SKIP_OFFSET;
	    data->pixels = data->gapi->GXBeginDraw();
	    data->gapi->GXEndDraw();

	    if(data->debug)
	    {
		printf("GAPI_Init\n");
		printf("use GXBeginDraw:               %p\n", data->pixels);
		printf("use skip offset\n");
	    }
	}

	if(0 == data->format ||
	    0 > WINCE_FixedGeometry(&data->fb, SDL_BYTESPERPIXEL(data->format), data->debug))
	{
	    SDL_SetError("GAPI_Init: unknown hardware");
	    GAPI_Quit(data);
	    return 0;
	}
    }

    return data->gapi && data->pixels ? 1 : 0;
}

void GAPI_Quit(WINCE_RenderData* data)
{
    if(data->gapi)
    {
	if(data->gapi->GXCloseDisplay) data->gapi->GXCloseDisplay(); 
	if(data->gapi->hGapiLib)  FreeLibrary(data->gapi->hGapiLib);

	SDL_free(data->gapi);
        data->gapi = NULL;
    }
}

int RAW_Init(WINCE_RenderData* data)
{
    const char* preferably = SDL_getenv("SDL_VIDEO_RENDERER");
    if(preferably && 0 != SDL_strcasecmp(preferably, RAW_RENDER_NAME)) return 0;

    RawFrameBufferInfo rfbi = { 0 };
    HDC hdc = GetDC(NULL);
    int result = ExtEscape(hdc, GETRAWFRAMEBUFFER, 0, NULL, sizeof(RawFrameBufferInfo), (char *) &rfbi);
    ReleaseDC(NULL, hdc);

    //disable
    if(result == 0 || rfbi.pFramePointer == 0 ||
    	rfbi.cxPixels == 0 || rfbi.cyPixels == 0 ||
	rfbi.cxStride == 0 || rfbi.cyStride == 0) return 0;

    data->flags     = FB_RAW_MODE;

    // fill FrameBufferInfo
    SDL_memset(&data->fb, 0, sizeof(FrameBufferInfo));

    data->fb.xpitch = rfbi.cxStride;
    data->fb.ypitch = rfbi.cyStride;
    data->fb.width  = rfbi.cxPixels;
    data->fb.height = rfbi.cyPixels;
    data->fb.offset = 0;

    if((FORMAT_565 & rfbi.wFormat) || 16 == rfbi.wBPP)
	data->format = SDL_PIXELFORMAT_RGB565;
    else
    if((FORMAT_555 & rfbi.wFormat) || 15 == rfbi.wBPP)
	data->format = SDL_PIXELFORMAT_RGB555;
    else
	data->format = 0;

    if(0 == data->format ||
	0 > WINCE_FixedGeometry(&data->fb, SDL_BYTESPERPIXEL(data->format), data->debug))
    {
	SDL_SetError("RAW_Init: unknown hardware");
	RAW_Quit(data);
	return 0;
    }

    data->pixels = rfbi.pFramePointer;

    return data->pixels ? 1 : 0;
}

void RAW_Quit(WINCE_RenderData* data)
{
}

void FrameBufferInitialize(FrameBufferInfo* fb)
{
    int orientation = GetFrameBufferOrientation(fb);

    // set correct start offset
    switch(orientation)
    {
	case ORIENTATION_UP:
	    fb->offset = 0;
	    break;

	case ORIENTATION_LEFT:
	    fb->offset = __abs(fb->ypitch * (fb->height - 1));
	    break;

	case ORIENTATION_RIGHT:
	    fb->offset = __abs(fb->xpitch * (fb->width - 1));
	    break;

	case ORIENTATION_DOWN:
	    fb->offset = __abs(fb->xpitch * (fb->width - 1) +
				fb->ypitch * (fb->height - 1));
	    break;

	default: break;
    }

    //if(orientation != ORIENTATION_UP)
    switch(orientation)
    {
	case ORIENTATION_LEFT: FrameBufferRotate(fb, ORIENTATION_RIGHT); break;
	case ORIENTATION_RIGHT:FrameBufferRotate(fb, ORIENTATION_LEFT); break;
	case ORIENTATION_DOWN: FrameBufferRotate(fb, ORIENTATION_DOWN); break;

	default: break;
    }
}

int GetFrameBufferOrientation(const FrameBufferInfo* src)
{
    if(src->xpitch > 0 && src->ypitch > 0)
	return ORIENTATION_UP;
    else
    if(src->xpitch > 0 && src->ypitch < 0)
	return ORIENTATION_LEFT;
    else
    if(src->xpitch < 0 && src->ypitch > 0)
	return ORIENTATION_RIGHT;
    else
    if(src->xpitch < 0 && src->ypitch < 0)
	return ORIENTATION_DOWN;

    return ORIENTATION_UNKNOWN;
}

void FrameBufferRotate(FrameBufferInfo* dst, int orientation)
{
    FrameBufferInfo src;
    // copy dst -> src
    SDL_memcpy(&src, dst, sizeof(FrameBufferInfo));

    switch(orientation)
    {
        case ORIENTATION_LEFT:
	    dst->width  = src.height;
	    dst->height = src.width;
	    dst->xpitch = src.ypitch;
	    dst->ypitch = -src.xpitch;
	    dst->offset = src.offset + src.xpitch * (src.width - 1);
	    break;

        case ORIENTATION_RIGHT:
	    dst->width  = src.height;
	    dst->height = src.width;
	    dst->xpitch = -src.ypitch;
	    dst->ypitch = src.xpitch;
	    dst->offset = src.offset + src.ypitch * (src.height - 1);
	    break;

        case ORIENTATION_DOWN:
	    FrameBufferRotate(dst, ORIENTATION_LEFT);
	    FrameBufferRotate(dst, ORIENTATION_LEFT);
	    break;

        default:
	    break;
    }
}

void PointerRotate(POINT* pt, const FrameBufferInfo* fb, int orientation)
{
    switch(orientation)
    {
	case ORIENTATION_UP:
	    break;

	case ORIENTATION_LEFT:
	{
	    int temp = pt->y;
	    pt->y = fb->height - pt->x;
	    pt->x = temp;
	}
	    break;

	case ORIENTATION_RIGHT:
	{
	    int temp = pt->x;
	    pt->x = fb->width - pt->y;
	    pt->y = temp;
	}
	    break;

	case ORIENTATION_DOWN:
	    pt->x = fb->width  - pt->x;
	    pt->y = fb->height - pt->y;
	    break;

	default: break;
    }
}

const char* GetOrientationName(int orientation)
{
    switch(orientation)
    {
        case ORIENTATION_UP:        return "UP";
        case ORIENTATION_DOWN:      return "DOWN";
        case ORIENTATION_LEFT:      return "LEFT";
        case ORIENTATION_RIGHT:     return "RIGHT";
        default: break;
    }

    return "UNKNOWN";
}

int WINCE_GetDMOrientation(void)
{
    DEVMODE sDevMode = {0};
    sDevMode.dmSize = sizeof(DEVMODE);
    sDevMode.dmFields = DM_DISPLAYORIENTATION;

    // DMDO_0, DMDO_90, DMDO_180, DMDO_270
    if(DISP_CHANGE_BADMODE != ChangeDisplaySettingsEx(NULL, &sDevMode, 0, CDS_TEST, NULL))
	switch(sDevMode.dmDisplayOrientation)
	{
	    case DMDO_0:	return DMDO_0;
	    case DMDO_90:	return DMDO_90;
	    case DMDO_180:	return DMDO_180;
	    case DMDO_270:	return DMDO_270;
	    default: break;
	}

    SDL_SetError("WINCE_GetDMOrientation: ChangeDisplaySettingsEx return BADMODE");
    return -1;
}

int WINCE_SetDMOrientation(int orientation)
{
    DEVMODE sDevMode = {0};
    sDevMode.dmSize = sizeof(DEVMODE);
    sDevMode.dmFields = DM_DISPLAYORIENTATION;

    switch(orientation)
    {
	case DMDO_0:	sDevMode.dmDisplayOrientation = DMDO_0;   break;
	case DMDO_90:	sDevMode.dmDisplayOrientation = DMDO_90;  break;
	case DMDO_180:	sDevMode.dmDisplayOrientation = DMDO_180; break;
	case DMDO_270:	sDevMode.dmDisplayOrientation = DMDO_270; break;
	default: return 0;
    }

    if(DISP_CHANGE_BADMODE != ChangeDisplaySettingsEx(NULL, &sDevMode, 0, CDS_RESET, NULL))
	return 1;

    SDL_SetError("WINCE_SetDMOrientation: ChangeDisplaySettingsEx return BADMODE");
    return -1;
}

void FrameBufferDumpInfo(const FrameBufferInfo* fb, const char* name)
{
    printf("%s fb.width:       %d\n", name, fb->width);
    printf("%s fb.height:      %d\n", name, fb->height);
    printf("%s fb.xpitch:      %d\n", name, fb->xpitch);
    printf("%s fb.ypitch:      %d\n", name, fb->ypitch);
    printf("%s fb.offset:      %d\n", name, fb->offset);

    int orientation = GetFrameBufferOrientation(fb);
    printf("%s fb.orientation: %d, %s\n", name, orientation, GetOrientationName(orientation));
}

void UpdateLine16to16(const FrameBufferInfo* fb, const Uint16* src, Uint16* dst, Uint16 width)
{
    if(2 == fb->xpitch)
    {
	switch(width)
	{
	    case 1:
		*dst = *src;
		break;

	    case 2:
		*((Uint32*) dst) = *((Uint32*) src);
		break;

	    default:
		SDL_memcpy(dst, src, width * 2);
		break;
	}
    }
    else
    if(-2 == fb->xpitch)
    {
	while(width--)
	    *dst-- = *src++;
    }
    else
    {
	while(width--)
	{
	    *dst = *src++;
	    dst += fb->xpitch / 2;
	}
    }
}

#endif // SDL_VIDEO_RENDER_GAPI