root/modules/gapi/gapi.cpp

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. GAPI_GetCoordinates
  2. w32_translate_key
  3. GAPI_WindowProc
  4. GAPI_WindowThread
  5. GAPI_SetupWindow
  6. GAPI_ShutdownWindow
  7. GAPI_Clear
  8. createPixmap
  9. GAPI_ReleaseOGL_ES
  10. GAPI_SetupOGL_ES
  11. GAPI_SetupOGL_ES_Offscreen
  12. GAPI_ReleaseObjects
  13. GAPI_Setup
  14. GAPI_Shutdown
  15. GAPI_SetFullScreen
  16. GAPI_ClearFS
  17. GAPI_FlipBackBuffer
  18. GAPI_Flush
  19. get_sys_col
  20. GAPI_ProcessEvent
  21. check_resolution_switch
  22. gapi_get_raw_fb
  23. GAPI_InitBackBuffer
  24. GAPI_AdjustLandscape
  25. GAPI_LockBackBuffer
  26. NewGAPIVideoOutput
  27. DeleteVideoOutput
  28. QueryInterfaces
  29. LoadInterface
  30. ShutdownInterface

/*
 *                      GPAC - Multimedia Framework C SDK
 *
 *                      Authors: Jean Le Feuvre
 *                      Copyright (c) Telecom ParisTech 2000-2012
 *                                      All rights reserved
 *
 *  This file is part of GPAC / GAPI WinCE-iPaq video render module
 *
 *  GPAC is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as published by
 *  the Free Software Foundation; either version 2, or (at your option)
 *  any later version.
 *
 *  GPAC 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 Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; see the file COPYING.  If not, write to
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */


#include <windows.h>
#include <aygshell.h>
#include <wingdi.h>
#include <gx.h>

#include "gapi.h"

#ifdef GPAC_USE_GLES1X

#if (defined(WIN32) || defined(_WIN32_WCE)) && !defined(__GNUC__)

#if 0
# pragma message("Using OpenGL-ES Common Lite Profile")
# pragma comment(lib, "libGLES_CL")

#define GLES_NO_PBUFFER
#define GLES_NO_PIXMAP

#else
# pragma message("Using OpenGL-ES Common Profile")
# pragma comment(lib, "libGLES_CM")

//#define GLES_NO_PIXMAP

#endif

#pragma comment(lib, "gx.lib")
#endif

#endif

static Bool landscape = GF_FALSE;


#define PRINT(__str) OutputDebugString(_T(__str))

#define GAPICTX(dr)     GAPIPriv *gctx = (GAPIPriv *) dr->opaque;

static GF_Err GAPI_InitBackBuffer(GF_VideoOutput *dr, u32 VideoWidth, u32 VideoHeight);

static GF_VideoOutput *the_video_driver = NULL;

static void GAPI_GetCoordinates(DWORD lParam, GF_Event *evt)
{
        GAPIPriv *ctx = (GAPIPriv *)the_video_driver->opaque;
        evt->mouse.x = LOWORD(lParam);
        evt->mouse.y = HIWORD(lParam);

        if (ctx->scale_coords) {
                evt->mouse.x = evt->mouse.x * the_video_driver->max_screen_width / ctx->sys_w;
                evt->mouse.y = evt->mouse.y * the_video_driver->max_screen_height / ctx->sys_h;
        }


        if (ctx->fullscreen) {
                POINT pt;
                pt.x = evt->mouse.x;
                pt.y = evt->mouse.y;
                ClientToScreen(ctx->hWnd, &pt);
                if (landscape) {
                        evt->mouse.x = ctx->fs_w - pt.y;
                        evt->mouse.y = pt.x;
                } else {
                        evt->mouse.x = pt.x;
                        evt->mouse.y = pt.y;
                }
        }
}

static void w32_translate_key(u32 wParam, u32 lParam, GF_EventKey *evt)
{
        evt->flags = 0;
        evt->hw_code = wParam;
        switch (wParam) {
        case VK_BACK:
                evt->key_code = GF_KEY_BACKSPACE;
                break;
        case VK_TAB:
                evt->key_code = GF_KEY_TAB;
                break;
        case VK_CLEAR:
                evt->key_code = GF_KEY_CLEAR;
                break;
        case VK_RETURN:
                evt->key_code = GF_KEY_ENTER;
                break;
        case VK_SHIFT:
                evt->key_code = GF_KEY_SHIFT;
                break;
        case VK_CONTROL:
                evt->key_code = GF_KEY_CONTROL;
                break;
        case VK_MENU:
                evt->key_code = GF_KEY_ALT;
                break;
        case VK_PAUSE:
                evt->key_code = GF_KEY_PAUSE;
                break;
        case VK_CAPITAL:
                evt->key_code = GF_KEY_CAPSLOCK;
                break;
        case VK_KANA:
                evt->key_code = GF_KEY_KANAMODE;
                break;
        case VK_JUNJA:
                evt->key_code = GF_KEY_JUNJAMODE;
                break;
        case VK_FINAL:
                evt->key_code = GF_KEY_FINALMODE;
                break;
        case VK_KANJI:
                evt->key_code = GF_KEY_KANJIMODE;
                break;
        case VK_ESCAPE:
                evt->key_code = GF_KEY_ESCAPE;
                break;
        case VK_CONVERT:
                evt->key_code = GF_KEY_CONVERT;
                break;
        case VK_SPACE:
                evt->key_code = GF_KEY_SPACE;
                break;
        case VK_PRIOR:
                evt->key_code = GF_KEY_PAGEUP;
                break;
        case VK_NEXT:
                evt->key_code = GF_KEY_PAGEDOWN;
                break;
        case VK_END:
                evt->key_code = GF_KEY_END;
                break;
        case VK_HOME:
                evt->key_code = GF_KEY_HOME;
                break;
        case VK_LEFT:
                evt->key_code = GF_KEY_LEFT;
                break;
        case VK_UP:
                evt->key_code = GF_KEY_UP;
                break;
        case VK_RIGHT:
                evt->key_code = GF_KEY_RIGHT;
                break;
        case VK_DOWN:
                evt->key_code = GF_KEY_DOWN;
                break;
        case VK_SELECT:
                evt->key_code = GF_KEY_SELECT;
                break;
        case VK_PRINT:
        case VK_SNAPSHOT:
                evt->key_code = GF_KEY_PRINTSCREEN;
                break;
        case VK_EXECUTE:
                evt->key_code = GF_KEY_EXECUTE;
                break;
        case VK_INSERT:
                evt->key_code = GF_KEY_INSERT;
                break;
        case VK_DELETE:
                evt->key_code = GF_KEY_DEL;
                break;
        case VK_HELP:
                evt->key_code = GF_KEY_HELP;
                break;

        /*      case VK_LWIN: return ;
                case VK_RWIN: return ;
                case VK_APPS: return ;
        */
        case VK_NUMPAD0:
                evt->key_code = GF_KEY_0;
                evt->flags = GF_KEY_EXT_NUMPAD;
                break;
        case VK_NUMPAD1:
                evt->key_code = GF_KEY_1;
                evt->flags = GF_KEY_EXT_NUMPAD;
                break;
        case VK_NUMPAD2:
                evt->key_code = GF_KEY_2;
                evt->flags = GF_KEY_EXT_NUMPAD;
                break;
        case VK_NUMPAD3:
                evt->key_code = GF_KEY_3;
                evt->flags = GF_KEY_EXT_NUMPAD;
                break;
        case VK_NUMPAD4:
                evt->key_code = GF_KEY_4;
                evt->flags = GF_KEY_EXT_NUMPAD;
                break;
        case VK_NUMPAD5:
                evt->key_code = GF_KEY_5;
                evt->flags = GF_KEY_EXT_NUMPAD;
                break;
        case VK_NUMPAD6:
                evt->key_code = GF_KEY_6;
                evt->flags = GF_KEY_EXT_NUMPAD;
                break;
        case VK_NUMPAD7:
                evt->key_code = GF_KEY_7;
                evt->flags = GF_KEY_EXT_NUMPAD;
                break;
        case VK_NUMPAD8:
                evt->key_code = GF_KEY_8;
                evt->flags = GF_KEY_EXT_NUMPAD;
                break;
        case VK_NUMPAD9:
                evt->key_code = GF_KEY_9;
                evt->flags = GF_KEY_EXT_NUMPAD;
                break;
        case VK_MULTIPLY:
                evt->key_code = GF_KEY_STAR;
                evt->flags = GF_KEY_EXT_NUMPAD;
                break;
        case VK_ADD:
                evt->key_code = GF_KEY_PLUS;
                evt->flags = GF_KEY_EXT_NUMPAD;
                break;
        case VK_SEPARATOR:
                evt->key_code = GF_KEY_FULLSTOP;
                evt->flags = GF_KEY_EXT_NUMPAD;
                break;
        case VK_SUBTRACT:
                evt->key_code = GF_KEY_HYPHEN;
                evt->flags = GF_KEY_EXT_NUMPAD;
                break;
        case VK_DECIMAL:
                evt->key_code = GF_KEY_COMMA;
                evt->flags = GF_KEY_EXT_NUMPAD;
                break;
        case VK_DIVIDE:
                evt->key_code = GF_KEY_SLASH;
                evt->flags = GF_KEY_EXT_NUMPAD;
                break;
        case VK_F1:
                evt->key_code = GF_KEY_F1;
                break;
        case VK_F2:
                evt->key_code = GF_KEY_F2;
                break;
        case VK_F3:
                evt->key_code = GF_KEY_F3;
                break;
        case VK_F4:
                evt->key_code = GF_KEY_F4;
                break;
        case VK_F5:
                evt->key_code = GF_KEY_F5;
                break;
//      case VK_F6: evt->key_code = GF_KEY_F6; break;
//      case VK_F7: evt->key_code = GF_KEY_F7; break;
        case VK_F6:
                evt->key_code = GF_KEY_VOLUMEUP;
                break;
        case VK_F7:
                evt->key_code = GF_KEY_VOLUMEDOWN;
                break;
        case VK_F8:
                evt->key_code = GF_KEY_F8;
                break;

        case VK_F9:
                evt->key_code = GF_KEY_F9;
                break;
        case VK_F10:
                evt->key_code = GF_KEY_F10;
                break;
        case VK_F11:
                evt->key_code = GF_KEY_F11;
                break;
        case VK_F12:
                evt->key_code = GF_KEY_F12;
                break;
        case VK_F13:
                evt->key_code = GF_KEY_F13;
                break;
        case VK_F14:
                evt->key_code = GF_KEY_F14;
                break;
        case VK_F15:
                evt->key_code = GF_KEY_F15;
                break;
        case VK_F16:
                evt->key_code = GF_KEY_F16;
                break;
        case VK_F17:
                evt->key_code = GF_KEY_F17;
                break;
        case VK_F18:
                evt->key_code = GF_KEY_F18;
                break;
        case VK_F19:
                evt->key_code = GF_KEY_F19;
                break;
        case VK_F20:
                evt->key_code = GF_KEY_F20;
                break;
        case VK_F21:
                evt->key_code = GF_KEY_F21;
                break;
        case VK_F22:
                evt->key_code = GF_KEY_F22;
                break;
        case VK_F23:
                evt->key_code = GF_KEY_F23;
                break;
        case VK_F24:
                evt->key_code = GF_KEY_F24;
                break;

        case VK_NUMLOCK:
                evt->key_code = GF_KEY_NUMLOCK;
                break;
        case VK_SCROLL:
                evt->key_code = GF_KEY_SCROLL;
                break;

        /*
         * VK_L* & VK_R* - left and right Alt, Ctrl and Shift virtual keys.
         * Used only as parameters to GetAsyncKeyState() and GetKeyState().
         * No other API or message will distinguish left and right keys in this way.
         */
        case VK_LSHIFT:
                evt->key_code = GF_KEY_SHIFT;
                evt->flags = GF_KEY_EXT_LEFT;
                break;
        case VK_RSHIFT:
                evt->key_code = GF_KEY_SHIFT;
                evt->flags = GF_KEY_EXT_RIGHT;
                break;
        case VK_LCONTROL:
                evt->key_code = GF_KEY_CONTROL;
                evt->flags = GF_KEY_EXT_LEFT;
                break;
        case VK_RCONTROL:
                evt->key_code = GF_KEY_CONTROL;
                evt->flags = GF_KEY_EXT_RIGHT;
                break;
        case VK_LMENU:
                evt->key_code = GF_KEY_ALT;
                evt->flags = GF_KEY_EXT_LEFT;
                break;
        case VK_RMENU:
                evt->key_code = GF_KEY_ALT;
                evt->flags = GF_KEY_EXT_RIGHT;
                break;

#if(WINVER >= 0x0400)
        case VK_PROCESSKEY:
                evt->key_code = GF_KEY_PROCESS;
                break;
#endif /* WINVER >= 0x0400 */

        case VK_ATTN:
                evt->key_code = GF_KEY_ATTN;
                break;
        case VK_CRSEL:
                evt->key_code = GF_KEY_CRSEL;
                break;
        case VK_EXSEL:
                evt->key_code = GF_KEY_EXSEL;
                break;
        case VK_EREOF:
                evt->key_code = GF_KEY_ERASEEOF;
                break;
        case VK_PLAY:
                evt->key_code = GF_KEY_PLAY;
                break;
        case VK_ZOOM:
                evt->key_code = GF_KEY_ZOOM;
                break;
        //case VK_NONAME: evt->key_code = GF_KEY_NONAME; break;
        //case VK_PA1: evt->key_code = GF_KEY_PA1; break;
        case VK_OEM_CLEAR:
                evt->key_code = GF_KEY_CLEAR;
                break;

        /*thru VK_9 are the same as ASCII '0' thru '9' (0x30 - 0x39) */
        /* VK_A thru VK_Z are the same as ASCII 'A' thru 'Z' (0x41 - 0x5A) */
        default:
                if ((wParam>=0x30) && (wParam<=0x39))  evt->key_code = GF_KEY_0 + wParam-0x30;
                else if ((wParam>=0x41) && (wParam<=0x5A))  evt->key_code = GF_KEY_A + wParam-0x41;
                else {
                        GAPIPriv *ctx = (GAPIPriv *)the_video_driver->opaque;
                        short res = (LOWORD(wParam) != 0x5b) ? LOWORD(wParam) : wParam;

                        if (res==ctx->keys.vkLeft) evt->key_code = landscape ? GF_KEY_UP : GF_KEY_LEFT;
                        else if (res==ctx->keys.vkRight) evt->key_code = landscape ? GF_KEY_DOWN : GF_KEY_RIGHT;
                        else if (res==ctx->keys.vkDown) evt->key_code = landscape ? GF_KEY_LEFT : GF_KEY_DOWN;
                        else if (res==ctx->keys.vkUp) evt->key_code = landscape ? GF_KEY_RIGHT : GF_KEY_UP;
                        else if (res==ctx->keys.vkStart) evt->key_code = GF_KEY_ENTER;
                        else if (res==ctx->keys.vkA)
                                evt->key_code = GF_KEY_MEDIAPREVIOUSTRACK;
                        else if (res==ctx->keys.vkB)
                                evt->key_code = GF_KEY_MEDIANEXTTRACK;
                        else if (res==ctx->keys.vkC)
                                evt->key_code = GF_KEY_SHIFT;
                        else if (res==0xc1)
                                evt->key_code = GF_KEY_ALT;
                        else if (res==0xc2)
                                evt->key_code = GF_KEY_CONTROL;
                        else if (res==0xc5)
                                evt->key_code = GF_KEY_VOLUMEDOWN;
                        else {
                                evt->key_code = GF_KEY_UNIDENTIFIED;
                        }
                }
                break;
        }
}

//#define DIRECT_BITBLT

LRESULT APIENTRY GAPI_WindowProc(HWND hWnd, UINT msg, UINT wParam, LONG lParam)
{
        GF_Event evt;
        switch (msg) {
        case WM_SIZE:
        {
                GAPIPriv *ctx = (GAPIPriv *)the_video_driver->opaque;
                evt.type = GF_EVENT_SIZE;
                evt.size.width = LOWORD(lParam);
                evt.size.height = HIWORD(lParam);
                the_video_driver->on_event(the_video_driver->evt_cbk_hdl, &evt);
        }
        break;
        case WM_CLOSE:
                memset(&evt, 0, sizeof(GF_Event));
                evt.type = GF_EVENT_QUIT;
                the_video_driver->on_event(the_video_driver->evt_cbk_hdl, &evt);
                return 1;
        case WM_DESTROY:
        {
                GAPIPriv *ctx = (GAPIPriv *)the_video_driver->opaque;
                if (ctx->owns_hwnd ) {
                        PostQuitMessage (0);
                } else if (ctx->orig_wnd_proc) {
                        /*restore window proc*/
                        SetWindowLong(ctx->hWnd, GWL_WNDPROC, ctx->orig_wnd_proc);
                        ctx->orig_wnd_proc = 0L;
                }
        }
        break;

        case WM_ERASEBKGND:
                evt.type = GF_EVENT_REFRESH;
                the_video_driver->on_event(the_video_driver->evt_cbk_hdl, &evt);
                break;
        case WM_PAINT:
#ifndef DIRECT_BITBLT
        {
                GAPIPriv *gctx = (GAPIPriv *)the_video_driver->opaque;
                if (gctx->gx_mode || !gctx->bitmap) break;
                HDC dc = GetDC(gctx->hWnd);
                BitBlt(dc, gctx->dst_blt.x, gctx->dst_blt.y, gctx->bb_width, gctx->bb_height, gctx->hdcBitmap, 0, 0, SRCCOPY);
                ReleaseDC(gctx->hWnd, dc);
        }
#endif
        break;

        case WM_MOUSEMOVE:
                GAPI_GetCoordinates(lParam, &evt);
                evt.type = GF_EVENT_MOUSEMOVE;
                the_video_driver->on_event(the_video_driver->evt_cbk_hdl, &evt);
                break;
        case WM_LBUTTONDOWN:
        case WM_LBUTTONDBLCLK:
                GAPI_GetCoordinates(lParam, &evt);
                evt.type = GF_EVENT_MOUSEDOWN;
                evt.mouse.button = GF_MOUSE_LEFT;
                the_video_driver->on_event(the_video_driver->evt_cbk_hdl, &evt);
                break;
        case WM_LBUTTONUP:
                GAPI_GetCoordinates(lParam, &evt);
                evt.type = GF_EVENT_MOUSEUP;
                evt.mouse.button = GF_MOUSE_LEFT;
                the_video_driver->on_event(the_video_driver->evt_cbk_hdl, &evt);
                break;

        /*FIXME - there's a bug on alt state (we miss one event)*/
        case WM_SYSKEYDOWN:
        case WM_SYSKEYUP:
        case WM_KEYDOWN:
        case WM_KEYUP:
                w32_translate_key(wParam, lParam, &evt.key);
                evt.type = ((msg==WM_SYSKEYDOWN) || (msg==WM_KEYDOWN))  ? GF_EVENT_KEYDOWN : GF_EVENT_KEYUP;
                the_video_driver->on_event(the_video_driver->evt_cbk_hdl, &evt);
                break;
        case WM_CHAR:
                evt.type = GF_EVENT_TEXTINPUT;
                evt.character.unicode_char = wParam;
                break;
        }
        return DefWindowProc (hWnd, msg, wParam, lParam);
}

void GAPI_WindowThread(void *par)
{
        MSG msg;
        WNDCLASS wc;
        GAPIPriv *ctx = (GAPIPriv *)the_video_driver->opaque;

        memset(&wc, 0, sizeof(WNDCLASS));
        wc.hInstance = GetModuleHandle(_T("gm_gapi.dll"));
        wc.lpfnWndProc = GAPI_WindowProc;
        wc.hCursor = LoadCursor (NULL, IDC_ARROW);
        wc.hbrBackground = (HBRUSH)GetStockObject (BLACK_BRUSH);
        wc.lpszClassName = _T("GPAC GAPI Output");
        RegisterClass (&wc);

        ctx->hWnd = CreateWindow(_T("GPAC GAPI Output"), NULL, WS_POPUP, 0, 0, 120, 100, NULL, NULL, wc.hInstance, NULL);
        if (ctx->hWnd == NULL) {
                ctx->ThreadID = 0;
                ExitThread(1);
        }
        ShowWindow(ctx->hWnd, SW_SHOWNORMAL);

        while (GetMessage (&(msg), NULL, 0, 0)) {
                TranslateMessage (&(msg));
                DispatchMessage (&(msg));
        }
        ctx->ThreadID = 0;
        ExitThread (0);
}


void GAPI_SetupWindow(GF_VideoOutput *dr)
{
#ifdef GPAC_USE_GLES1X
        GF_Err e;
#endif
        GAPIPriv *ctx = (GAPIPriv *)dr->opaque;
        if (the_video_driver) return;
        the_video_driver = dr;

        if (!ctx->hWnd) {
                ctx->hThread = CreateThread (NULL, 0, (LPTHREAD_START_ROUTINE) GAPI_WindowThread, (LPVOID) dr, 0, &(ctx->ThreadID) );
                while (!ctx->hWnd && ctx->hThread) gf_sleep(10);
                if (!ctx->hThread) return;
                ctx->owns_hwnd = GF_TRUE;
        } else {
                ctx->orig_wnd_proc = GetWindowLong(ctx->hWnd, GWL_WNDPROC);
                /*override window proc*/
                SetWindowLong(ctx->hWnd, GWL_WNDPROC, (DWORD) GAPI_WindowProc);
        }

        {
                HDC hdc;
                hdc = GetDC(ctx->hWnd);
                dr->dpi_x = GetDeviceCaps(hdc, LOGPIXELSX);
                dr->dpi_y = GetDeviceCaps(hdc, LOGPIXELSY);
                ReleaseDC(ctx->hWnd, hdc);
        }


#ifdef GPAC_USE_GLES1X
        ctx->use_pbuffer = GF_TRUE;
        dr->hw_caps |= GF_VIDEO_HW_OPENGL_OFFSCREEN_ALPHA;
        e = GAPI_SetupOGL_ES_Offscreen(dr, 20, 20);
        if (e!=GF_OK) {
                dr->hw_caps &= ~GF_VIDEO_HW_OPENGL_OFFSCREEN_ALPHA;
#ifndef GLES_NO_PIXMAP
                e = GAPI_SetupOGL_ES_Offscreen(dr, 20, 20);
#endif
        }
        if (!e) {
                dr->hw_caps |= GF_VIDEO_HW_OPENGL_OFFSCREEN;
                return;
        }
        ctx->use_pbuffer = GF_FALSE;

        WNDCLASS wc;
        HINSTANCE hInst;
        hInst = GetModuleHandle(_T("gm_gapi.dll") );
        memset(&wc, 0, sizeof(WNDCLASS));
        wc.hInstance = hInst;
        wc.lpfnWndProc = GAPI_WindowProc;
        wc.lpszClassName = _T("GPAC GAPI Offscreen");
        RegisterClass (&wc);

        ctx->gl_hwnd = CreateWindow(_T("GPAC GAPI Offscreen"), _T("GPAC GAPI Offscreen"), WS_POPUP, 0, 0, 120, 100, NULL, NULL, hInst, NULL);
        if (!ctx->gl_hwnd) return;
        ShowWindow(ctx->gl_hwnd, SW_HIDE);

        dr->hw_caps |= GF_VIDEO_HW_OPENGL_OFFSCREEN_ALPHA;
        e = GAPI_SetupOGL_ES_Offscreen(dr, 20, 20);

        if (e!=GF_OK) {
                dr->hw_caps &= ~GF_VIDEO_HW_OPENGL_OFFSCREEN_ALPHA;
                e = GAPI_SetupOGL_ES_Offscreen(dr, 20, 20);
        }
        if (e==GF_OK) dr->hw_caps |= GF_VIDEO_HW_OPENGL_OFFSCREEN;
#endif

}

void GAPI_ShutdownWindow(GF_VideoOutput *dr)
{
        GAPIPriv *ctx = (GAPIPriv *)dr->opaque;

        if (ctx->owns_hwnd) {
                PostMessage(ctx->hWnd, WM_DESTROY, 0, 0);
                while (ctx->ThreadID) gf_sleep(10);
                UnregisterClass(_T("GPAC GAPI Output"), GetModuleHandle(_T("gapi.dll")));
                CloseHandle(ctx->hThread);
                ctx->hThread = NULL;
        } else if (ctx->orig_wnd_proc) {
                /*restore window proc*/
                SetWindowLong(ctx->hWnd, GWL_WNDPROC, ctx->orig_wnd_proc);
                ctx->orig_wnd_proc = 0L;
        }
        ctx->hWnd = NULL;
#ifdef GPAC_USE_GLES1X
        PostMessage(ctx->gl_hwnd, WM_DESTROY, 0, 0);
        ctx->gl_hwnd = NULL;
        UnregisterClass(_T("GPAC GAPI Offscreen"), GetModuleHandle(_T("gm_gapi.dll") ));
#endif

        the_video_driver = NULL;
}


GF_Err GAPI_Clear(GF_VideoOutput *dr, u32 color)
{
        GAPICTX(dr);
        gctx->erase_dest = GF_TRUE;
        return GF_OK;
}


static void createPixmap(GAPIPriv *ctx, u32 pix_type)
{
        const size_t    bmiSize = sizeof(BITMAPINFO) + 256U*sizeof(RGBQUAD);
        BITMAPINFO*     bmi;
        DWORD*          p;
        u32 bpel = 0;

        if (ctx->bmi) gf_free(ctx->bmi);

        bmi = (BITMAPINFO*)gf_malloc(bmiSize);
        memset(bmi, 0, bmiSize);

        bmi->bmiHeader.biSize           = sizeof(BITMAPINFOHEADER);
        bmi->bmiHeader.biWidth          = ctx->bb_width;
        bmi->bmiHeader.biHeight         = -1 *  (s32) ctx->bb_height;   /*top-down image*/
        bmi->bmiHeader.biPlanes         = (short)1;
        bmi->bmiHeader.biBitCount       = (unsigned int) ctx->bits_per_pixel;
        bmi->bmiHeader.biCompression    = BI_BITFIELDS;
        bmi->bmiHeader.biClrUsed        = 3;

        p = (DWORD*)bmi->bmiColors;
        switch (ctx->pixel_format) {
        case GF_PIXEL_RGB_555:
                p[0] = 0x00007c00;
                p[1] = 0x000003e0;
                p[2] = 0x0000001f;
                bpel = 16;
                break;
        case GF_PIXEL_RGB_565:
                p[0] = 0x0000f800;
                p[1] = 0x000007e0;
                p[2] = 0x0000001f;
                bpel = 16;
                break;
        case GF_PIXEL_RGB_24:
                p[0] = 0x00ff0000;
                p[1] = 0x0000ff00;
                p[2] = 0x000000ff;
                bpel = 24;
                break;
        }
        ctx->hdc = GetDC(NULL/*ctx->hWnd*/);

        if (pix_type==2) {
#ifdef GPAC_USE_GLES1X
                ctx->gl_bitmap = CreateDIBSection(ctx->hdc, bmi, DIB_RGB_COLORS, (void **) &ctx->gl_bits, NULL, 0);
#endif
        } else if (pix_type==1) {
                ctx->hdcBitmap = CreateCompatibleDC(ctx->hdc);
                ctx->bitmap = CreateDIBSection(ctx->hdc, bmi, DIB_RGB_COLORS, (void **) &ctx->bits, NULL, 0);
                ctx->old_bitmap = (HBITMAP) SelectObject(ctx->hdcBitmap, ctx->bitmap);
        } else {
                ctx->hdcBitmap = CreateCompatibleDC(ctx->hdc);
                ctx->bitmap = CreateDIBSection(ctx->hdc, bmi, DIB_RGB_COLORS, (void **) &ctx->backbuffer, NULL, 0);
                ctx->old_bitmap = (HBITMAP) SelectObject(ctx->hdcBitmap, ctx->bitmap);

                /*watchout - win32 always create DWORD align memory, so align our pitch*/
                while ((ctx->bb_pitch % 4) != 0) ctx->bb_pitch ++;
        }
        ReleaseDC(NULL/*ctx->hWnd*/, ctx->hdc);

        ctx->bmi = bmi;
//      gf_free(bmi);
}


#ifdef GPAC_USE_GLES1X

void GAPI_ReleaseOGL_ES(GAPIPriv *ctx, Bool offscreen_only)
{
        if (ctx->egldpy) {
                eglMakeCurrent(ctx->egldpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);

                if (ctx->eglctx) eglDestroyContext(ctx->egldpy, ctx->eglctx);
                ctx->eglctx = 0;
                if (ctx->surface) eglDestroySurface(ctx->egldpy, ctx->surface);
                ctx->surface = 0;
                if (ctx->egldpy) eglTerminate(ctx->egldpy);
                ctx->egldpy = 0;
        }
        if (ctx->gl_bitmap) DeleteObject(ctx->gl_bitmap);
        ctx->gl_bitmap = NULL;

        if (offscreen_only) return;

        if (ctx->bitmap) DeleteObject(ctx->bitmap);
        ctx->bitmap = NULL;
}

GF_Err GAPI_SetupOGL_ES(GF_VideoOutput *dr)
{
        EGLint n, maj, min;
        u32 i;
        GF_Event evt;
        GAPICTX(dr)
        static int atts[32];
        const char *opt;

        i=0;
        atts[i++] = EGL_RED_SIZE;
        atts[i++] = (gctx->pixel_format==GF_PIXEL_RGB_24) ? 8 : 5;
        atts[i++] = EGL_GREEN_SIZE;
        atts[i++] = (gctx->pixel_format==GF_PIXEL_RGB_24) ? 8 : (gctx->pixel_format==GF_PIXEL_RGB_565) ? 6 : 5;
        atts[i++] = EGL_BLUE_SIZE;
        atts[i++] = (gctx->pixel_format==GF_PIXEL_RGB_24) ? 8 : 5;
        opt = gf_modules_get_option((GF_BaseInterface *)dr, "Video", "GLNbBitsDepth");
        atts[i++] = EGL_DEPTH_SIZE;
        atts[i++] = opt ? atoi(opt) : 16;
        atts[i++] = EGL_SURFACE_TYPE;

#ifdef GLES_NO_PIXMAP
        atts[i++] = EGL_WINDOW_BIT;
#else
        atts[i++] = EGL_PIXMAP_BIT;
//      atts[i++] = gctx->fullscreen ? EGL_WINDOW_BIT : EGL_PIXMAP_BIT;
#endif
        atts[i++] = EGL_ALPHA_SIZE;
        atts[i++] = EGL_DONT_CARE;
        atts[i++] = EGL_STENCIL_SIZE;
        atts[i++] = EGL_DONT_CARE;
        atts[i++] = EGL_NONE;

        /*whenever window is resized we must reinit OGL-ES*/
        GAPI_ReleaseOGL_ES(gctx, GF_FALSE);

        if (!gctx->fullscreen) {
                RECT rc;
                ::GetClientRect(gctx->hWnd, &rc);
                gctx->bb_width = rc.right-rc.left;
                gctx->bb_height = rc.bottom-rc.top;

#ifndef GLES_NO_PIXMAP
                createPixmap(gctx, 1);
#endif
        }

        gctx->egldpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
        if (!gctx->egldpy) {
                GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[GAPI] Cannot get OpenGL display\n"));
                return GF_IO_ERR;
        }
        if (!eglInitialize(gctx->egldpy, &maj, &min)) {
                gctx->egldpy = NULL;
                GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[GAPI] Cannot initialize OpenGL layer\n"));
                return GF_IO_ERR;
        }

        if (!eglChooseConfig(gctx->egldpy, atts, &gctx->eglconfig, 1, &n) || (eglGetError() != EGL_SUCCESS)) {
                GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[GAPI] Cannot choose OpenGL config\n"));
                return GF_IO_ERR;
        }

        if (gctx->fullscreen
#ifdef GLES_NO_PIXMAP
                || 1
#endif
           ) {
                gctx->surface = eglCreateWindowSurface(gctx->egldpy, gctx->eglconfig, gctx->hWnd, 0);
        } else {
                gctx->surface = eglCreatePixmapSurface(gctx->egldpy, gctx->eglconfig, gctx->bitmap, 0);
        }

        if (!gctx->surface) {
                GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[GAPI] Cannot create OpenGL surface - error %d\n", eglGetError()));
                return GF_IO_ERR;
        }
        gctx->eglctx = eglCreateContext(gctx->egldpy, gctx->eglconfig, NULL, NULL);
        if (!gctx->eglctx) {
                GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[GAPI] Cannot create OpenGL context\n"));
                eglDestroySurface(gctx->egldpy, gctx->surface);
                gctx->surface = 0L;
                return GF_IO_ERR;
        }
        if (!eglMakeCurrent(gctx->egldpy, gctx->surface, gctx->surface, gctx->eglctx)) {
                GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[GAPI] Cannot bind OpenGL context\n"));
                eglDestroyContext(gctx->egldpy, gctx->eglctx);
                gctx->eglctx = 0L;
                eglDestroySurface(gctx->egldpy, gctx->surface);
                gctx->surface = 0L;
                return GF_IO_ERR;
        }
        GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[GAPI] OpenGL initialize - %d x %d \n", gctx->bb_width, gctx->bb_height));
        memset(&evt, 0, sizeof(GF_Event));
        evt.type = GF_EVENT_VIDEO_SETUP;
        evt.hw_reset = 1;
        dr->on_event(dr->evt_cbk_hdl, &evt);
        return GF_OK;
}



GF_Err GAPI_SetupOGL_ES_Offscreen(GF_VideoOutput *dr, u32 width, u32 height)
{
        int atts[15];
        const char *opt;
        EGLint n, maj, min;

        GAPICTX(dr)

        GAPI_ReleaseOGL_ES(gctx, GF_TRUE);

        if (!gctx->use_pbuffer) {
                SetWindowPos(gctx->gl_hwnd, NULL, 0, 0, width, height, SWP_NOZORDER | SWP_NOMOVE);
                createPixmap(gctx, 2);
        }

        gctx->egldpy = eglGetDisplay(/*gctx->dpy*/EGL_DEFAULT_DISPLAY);
        if (!eglInitialize(gctx->egldpy, &maj, &min)) {
                gctx->egldpy = NULL;
                return GF_IO_ERR;
        }
        atts[0] = EGL_RED_SIZE;
        atts[1] = 8;
        atts[2] = EGL_GREEN_SIZE;
        atts[3] = 8;
        atts[4] = EGL_BLUE_SIZE;
        atts[5] = 8;
        atts[6] = EGL_ALPHA_SIZE;
        atts[7] = (dr->hw_caps & GF_VIDEO_HW_OPENGL_OFFSCREEN_ALPHA) ? 8 : EGL_DONT_CARE;
        opt = gf_modules_get_option((GF_BaseInterface *)dr, "Video", "GLNbBitsDepth");
        atts[8] = EGL_DEPTH_SIZE;
        atts[9] = opt ? atoi(opt) : 16;

        atts[10] = EGL_STENCIL_SIZE;
        atts[11] = EGL_DONT_CARE;
        atts[12] = EGL_SURFACE_TYPE;
        atts[13] = gctx->use_pbuffer ? EGL_PBUFFER_BIT : EGL_PIXMAP_BIT;
        atts[14] = EGL_NONE;

        eglGetConfigs(gctx->egldpy, NULL, 0, &n);
        if (!eglChooseConfig(gctx->egldpy, atts, &gctx->eglconfig, 1, &n)) {
                return GF_IO_ERR;
        }

        if (!gctx->use_pbuffer) {
                gctx->surface = eglCreatePixmapSurface(gctx->egldpy, gctx->eglconfig, gctx->gl_bitmap, 0);
        } else {
                atts[0] = EGL_WIDTH;
                atts[1] = width;
                atts[2] = EGL_HEIGHT;
                atts[3] = height;
                atts[4] = EGL_NONE;

                gctx->surface = eglCreatePbufferSurface(gctx->egldpy, gctx->eglconfig, atts);
        }

        if (!gctx->surface) {
                return GF_IO_ERR;
        }
        gctx->eglctx = eglCreateContext(gctx->egldpy, gctx->eglconfig, NULL, NULL);
        if (!gctx->eglctx) {
                eglDestroySurface(gctx->egldpy, gctx->surface);
                gctx->surface = 0L;
                return GF_IO_ERR;
        }
        if (!eglMakeCurrent(gctx->egldpy, gctx->surface, gctx->surface, gctx->eglctx)) {
                eglDestroyContext(gctx->egldpy, gctx->eglctx);
                gctx->eglctx = 0L;
                eglDestroySurface(gctx->egldpy, gctx->surface);
                gctx->surface = 0L;
                return GF_IO_ERR;
        }
        return GF_OK;
}

#endif


void GAPI_ReleaseObjects(GAPIPriv *ctx)
{
        ctx->raw_ptr = NULL;

#ifdef GPAC_USE_GLES1X
        if (ctx->output_3d_type) GAPI_ReleaseOGL_ES(ctx, GF_FALSE);
        else
#endif
                if (ctx->bitmap) DeleteObject(ctx->bitmap);
                else if (ctx->backbuffer) gf_free(ctx->backbuffer);
        ctx->backbuffer = NULL;
        ctx->bitmap = NULL;

        if (ctx->hdcBitmap) {
                if (ctx->old_bitmap) SelectObject(ctx->hdcBitmap, ctx->old_bitmap);
                ctx->old_bitmap = NULL;
                DeleteDC(ctx->hdcBitmap);
                ctx->hdcBitmap = NULL;
        }
        if (ctx->hdc) ReleaseDC(NULL/*ctx->hWnd*/, ctx->hdc);
        ctx->hdc = NULL;
}

GF_Err GAPI_Setup(GF_VideoOutput *dr, void *os_handle, void *os_display, u32 noover)
{
        struct GXDisplayProperties gx = GXGetDisplayProperties();
        RECT rc;
        GAPICTX(dr);
        gctx->hWnd = (HWND) os_handle;

        /*get keys in both 2D and 3D modes*/
        gctx->keys = GXGetDefaultKeys(GX_NORMALKEYS);

#if 0
        /*FIXME - not supported in rasterizer*/
        if (gx.ffFormat & kfDirect444) {
                gctx->pixel_format = GF_PIXEL_RGB_444;
                gctx->BPP = 2;
                gctx->bitsPP = 12;
        }
        else
#endif
                if (gx.ffFormat & kfDirect555) {
                        gctx->pixel_format = GF_PIXEL_RGB_555;
                        gctx->BPP = 2;
                        gctx->bits_per_pixel = 15;
                }
                else if (gx.ffFormat & kfDirect565) {
                        gctx->pixel_format = GF_PIXEL_RGB_565;
                        gctx->BPP = 2;
                        gctx->bits_per_pixel = 16;
                }
                else if (gx.ffFormat & kfDirect888) {
                        gctx->pixel_format = GF_PIXEL_RGB_24;
                        gctx->BPP = 3;
                        gctx->bits_per_pixel = 24;
                } else {
                        return GF_NOT_SUPPORTED;
                }

        GAPI_SetupWindow(dr);
        if (!gctx->hWnd) return GF_IO_ERR;

        /*setup GX*/
        if (!GXOpenDisplay(gctx->hWnd, 0L)) {
                MessageBox(NULL, _T("Cannot open display"), _T("GAPI Error"), MB_OK);
                return GF_IO_ERR;
        }
        GetClientRect(gctx->hWnd, &rc);
        gctx->backup_w = rc.right - rc.left;
        gctx->backup_h = rc.bottom - rc.top;

        return GAPI_InitBackBuffer(dr, gctx->backup_w, gctx->backup_h);
}

static void GAPI_Shutdown(GF_VideoOutput *dr)
{
        GAPICTX(dr);

        gf_mx_p(gctx->mx);
        GAPI_ReleaseObjects(gctx);

        GXCloseDisplay();
        GAPI_ShutdownWindow(dr);
        gf_mx_v(gctx->mx);
}

static GF_Err GAPI_SetFullScreen(GF_VideoOutput *dr, Bool bOn, u32 *outWidth, u32 *outHeight)
{
        Bool is_wide_scene = (bOn==2) ? GF_TRUE : GF_FALSE;
        GF_Err e;
        GAPICTX(dr);

        if (!gctx) return GF_BAD_PARAM;
        if (is_wide_scene) bOn = GF_TRUE;
        if (bOn == gctx->fullscreen) return GF_OK;

#ifdef GPAC_USE_GLES1X
        if (gctx->output_3d_type==1) {
                gctx->fullscreen = bOn;
                return GAPI_SetupOGL_ES(dr);
        }
#endif

        gf_mx_p(gctx->mx);
        GAPI_ReleaseObjects(gctx);
        GXCloseDisplay();
        e = GF_OK;
        if (bOn) {
                if (!GXOpenDisplay(GetParent(gctx->hWnd), 0L/*GX_FULLSCREEN*/)) {
                        GXOpenDisplay(gctx->hWnd, 0L);
                        gctx->fullscreen = GF_FALSE;
                        e = GF_IO_ERR;
                } else {
                        gctx->fullscreen = GF_TRUE;
                }
        } else {
                GXOpenDisplay(gctx->hWnd, 0L);
                gctx->fullscreen = GF_FALSE;
        }

        landscape = GF_FALSE;
        if (!e) {
                if (gctx->fullscreen) {
                        gctx->backup_w = *outWidth;
                        gctx->backup_h = *outHeight;

                        if (is_wide_scene && (gctx->screen_w < gctx->screen_h)) landscape = GF_TRUE;
                        else if (!is_wide_scene && (gctx->screen_w > gctx->screen_h)) landscape = GF_TRUE;
                        else landscape = GF_FALSE;

                        if (landscape) {
                                gctx->fs_w = gctx->screen_h;
                                gctx->fs_h = gctx->screen_w;
                        } else {
                                gctx->fs_w = gctx->screen_w;
                                gctx->fs_h = gctx->screen_h;
                        }

                        *outWidth = gctx->fs_w;
                        *outHeight = gctx->fs_h;
                } else {
                        *outWidth = gctx->backup_w;
                        *outHeight = gctx->backup_h;
                }
        }
        gf_mx_v(gctx->mx);

        return e;
}

GF_Err GAPI_ClearFS(GAPIPriv *gctx, unsigned char *ptr)
{
        gf_mx_p(gctx->mx);
        memset(ptr, 0, sizeof(char) * gctx->screen_w*gctx->screen_h*gctx->BPP);
        gf_mx_v(gctx->mx);
        return GF_OK;
}


static GF_Err GAPI_FlipBackBuffer(GF_VideoOutput *dr)
{
        GF_VideoSurface src, dst;
        unsigned char *ptr;
        GAPICTX(dr);
        if (!gctx || !gctx->gx_mode) return GF_BAD_PARAM;

        /*get a pointer to video memory*/
        if (gctx->gx_mode==1) {
                ptr = (unsigned char *) GXBeginDraw();
        } else {
                ptr = (unsigned char *) gctx->raw_ptr;
        }
        if (!ptr) return GF_IO_ERR;

        src.video_buffer = gctx->backbuffer;
        src.width = gctx->bb_width;
        src.height = gctx->bb_height;
        src.pitch_x = gctx->BPP;
        src.pitch_y = gctx->y_pitch;
        src.pixel_format = gctx->pixel_format;
        src.is_hardware_memory = GF_FALSE;


        dst.width = gctx->dst_blt.w;
        dst.height = gctx->dst_blt.h;
        dst.pixel_format = gctx->pixel_format;
        dst.is_hardware_memory = GF_TRUE;
        dst.video_buffer = (char*)ptr;
        dst.pitch_x = gctx->x_pitch;
        dst.pitch_y = gctx->y_pitch;

        if (gctx->fullscreen) {
                if (gctx->erase_dest) {
                        gctx->erase_dest = GF_FALSE;
                        GAPI_ClearFS(gctx, ptr);
                }
        } else {
                gctx->dst_blt.x += gctx->off_x;
                gctx->dst_blt.y += gctx->off_y;
        }

        /*apply x/y offset*/
        if (!gctx->fullscreen)
                dst.video_buffer += gctx->dst_blt.x * gctx->x_pitch + gctx->y_pitch * gctx->dst_blt.y;

        if (gctx->contiguous_mem) {
                memcpy(dst.video_buffer, src.video_buffer, src.width*gctx->BPP*src.height );
        } else if (landscape) {
                u32 y, lsize = dst.height*gctx->x_pitch;
                for (y=0; y<dst.width; y++) {
                        char *s = src.video_buffer + y*gctx->y_pitch;
                        char *d = dst.video_buffer + y*gctx->y_pitch;
                        memcpy(d, s, lsize);
                }
        } else {
                u32 y, lsize = dst.width*gctx->x_pitch;
                for (y=0; y<dst.height; y++) {
                        char *s = src.video_buffer + y*gctx->y_pitch;
                        char *d = dst.video_buffer + y*gctx->y_pitch;
                        memcpy(d, s, lsize);
                }
        }

        if (gctx->gx_mode==1) GXEndDraw();
        return GF_OK;
}



static GF_Err GAPI_Flush(GF_VideoOutput *dr, GF_Window *dest)
{
        GF_Err e;
        GAPICTX(dr);

        if (!gctx) return GF_BAD_PARAM;

        gf_mx_p(gctx->mx);

#ifdef GPAC_USE_GLES1X
        if (gctx->output_3d_type==1) {
#ifndef GLES_NO_PIXMAP
                if (gctx->fullscreen && gctx->surface && gctx->egldpy) {
#endif
                        if (gctx->erase_dest) {
                                InvalidateRect(gctx->hWnd, NULL, TRUE);
                                gctx->erase_dest = GF_FALSE;
                        }
                        eglSwapBuffers(gctx->egldpy, gctx->surface);
#ifndef GLES_NO_PIXMAP
                } else {
                        InvalidateRect(gctx->hWnd, NULL, gctx->erase_dest);
                        gctx->erase_dest = GF_FALSE;
                }
#endif
                gf_mx_v(gctx->mx);
                return GF_OK;
        }
#endif
        e = GF_OK;
        if (gctx->backbuffer) {
                if (dest) {
                        gctx->dst_blt = *dest;
                } else {
                        assert(0);
                        gctx->dst_blt.x = gctx->dst_blt.y = 0;
                        gctx->dst_blt.w = gctx->bb_width;
                        gctx->dst_blt.h = gctx->bb_height;
                }
                if (gctx->gx_mode) {
                        if (!gctx->fullscreen && gctx->erase_dest) {
                                InvalidateRect(gctx->hWnd, NULL, TRUE);
                                gctx->erase_dest = GF_FALSE;
                        }
                        e = GAPI_FlipBackBuffer(dr);
                } else {
#ifndef DIRECT_BITBLT
                        InvalidateRect(gctx->hWnd, NULL, gctx->erase_dest);
#else
//                      BitBlt(gctx->hdc, gctx->dst_blt.x, gctx->dst_blt.y, gctx->bb_width, gctx->bb_height, gctx->hdcBitmap, 0, 0, SRCCOPY);
                        HDC dc = GetDC(NULL);
                        BitBlt(dc, gctx->dst_blt.x, gctx->dst_blt.y, gctx->bb_width, gctx->bb_height, gctx->hdcBitmap, 0, 0, SRCCOPY);
                        ReleaseDC(NULL, dc);
#endif
                        gctx->erase_dest = GF_FALSE;
                }
        }
        gf_mx_v(gctx->mx);
        return e;
}

u32 get_sys_col(int idx)
{
        u32 res;
        DWORD val = GetSysColor(idx);
        res = (val)&0xFF;
        res<<=8;
        res |= (val>>8)&0xFF;
        res<<=8;
        res |= (val>>16)&0xFF;
        return res;
}

static GF_Err GAPI_ProcessEvent(GF_VideoOutput *dr, GF_Event *evt)
{
        GAPICTX(dr);
        if (!evt) return GF_OK;
        switch (evt->type) {
        case GF_EVENT_SHOWHIDE:
                if (gctx->hWnd) ShowWindow(gctx->hWnd, evt->show.show_type ? SW_SHOW : SW_HIDE);
                break;
        case GF_EVENT_SIZE:
                /*nothing to do since we don't own the window*/
                break;
        case GF_EVENT_VIDEO_SETUP:
                switch (evt->setup.opengl_mode) {
                case 0:
#ifdef GPAC_USE_GLES1X
                        gctx->output_3d_type = 0;
#endif
                        return GAPI_InitBackBuffer(dr, evt->setup.width, evt->setup.height);
#ifdef GPAC_USE_GLES1X
                case 1:
                        gctx->output_3d_type = 1;
                        return GAPI_SetupOGL_ES(the_video_driver);
                case 2:
                        gctx->output_3d_type = 2;
                        return GAPI_SetupOGL_ES_Offscreen(the_video_driver, evt->setup.width, evt->setup.height);
#else
                default:
                        return GF_NOT_SUPPORTED;
#endif
                }
        case GF_EVENT_SYS_COLORS:
                evt->sys_cols.sys_colors[0] = get_sys_col(COLOR_ACTIVEBORDER);
                evt->sys_cols.sys_colors[1] = get_sys_col(COLOR_ACTIVECAPTION);
                evt->sys_cols.sys_colors[2] = get_sys_col(COLOR_APPWORKSPACE);
                evt->sys_cols.sys_colors[3] = get_sys_col(COLOR_BACKGROUND);
                evt->sys_cols.sys_colors[4] = get_sys_col(COLOR_BTNFACE);
                evt->sys_cols.sys_colors[5] = get_sys_col(COLOR_BTNHIGHLIGHT);
                evt->sys_cols.sys_colors[6] = get_sys_col(COLOR_BTNSHADOW);
                evt->sys_cols.sys_colors[7] = get_sys_col(COLOR_BTNTEXT);
                evt->sys_cols.sys_colors[8] = get_sys_col(COLOR_CAPTIONTEXT);
                evt->sys_cols.sys_colors[9] = get_sys_col(COLOR_GRAYTEXT);
                evt->sys_cols.sys_colors[10] = get_sys_col(COLOR_HIGHLIGHT);
                evt->sys_cols.sys_colors[11] = get_sys_col(COLOR_HIGHLIGHTTEXT);
                evt->sys_cols.sys_colors[12] = get_sys_col(COLOR_INACTIVEBORDER);
                evt->sys_cols.sys_colors[13] = get_sys_col(COLOR_INACTIVECAPTION);
                evt->sys_cols.sys_colors[14] = get_sys_col(COLOR_INACTIVECAPTIONTEXT);
                evt->sys_cols.sys_colors[15] = get_sys_col(COLOR_INFOBK);
                evt->sys_cols.sys_colors[16] = get_sys_col(COLOR_INFOTEXT);
                evt->sys_cols.sys_colors[17] = get_sys_col(COLOR_MENU);
                evt->sys_cols.sys_colors[18] = get_sys_col(COLOR_MENUTEXT);
                evt->sys_cols.sys_colors[19] = get_sys_col(COLOR_SCROLLBAR);
                evt->sys_cols.sys_colors[20] = get_sys_col(COLOR_3DDKSHADOW);
                evt->sys_cols.sys_colors[21] = get_sys_col(COLOR_3DFACE);
                evt->sys_cols.sys_colors[22] = get_sys_col(COLOR_3DHIGHLIGHT);
                evt->sys_cols.sys_colors[23] = get_sys_col(COLOR_3DLIGHT);
                evt->sys_cols.sys_colors[24] = get_sys_col(COLOR_3DSHADOW);
                evt->sys_cols.sys_colors[25] = get_sys_col(COLOR_WINDOW);
                evt->sys_cols.sys_colors[26] = get_sys_col(COLOR_WINDOWFRAME);
                evt->sys_cols.sys_colors[27] = get_sys_col(COLOR_WINDOWTEXT);
                return GF_OK;
        }
        return GF_OK;
}

#if 1



#define ESC_QUERYESCSUPPORT 8
#define GETGXINFO                       0x00020000

typedef struct GXDeviceInfo
{
        long Version;           //00
        void * pvFrameBuffer;   //04
        unsigned long cbStride; //08
        unsigned long cxWidth;  //0c
        unsigned long cyHeight; //10
        unsigned long cBPP;     //14
        unsigned long ffFormat; //18
        char Unused[0x84-7*4];
} GXDeviceInfo;


static Bool check_resolution_switch(GF_VideoOutput *dr, u32 width, u32 height)
{
        GAPICTX(dr);

        gctx->sys_w = GetSystemMetrics(SM_CXSCREEN);
        gctx->sys_h = GetSystemMetrics(SM_CYSCREEN);
        gctx->scale_coords = GF_FALSE;
        if (gctx->sys_w != width) gctx->scale_coords = GF_TRUE;
        else if (gctx->sys_h != height) gctx->scale_coords = GF_TRUE;

        if (gctx->scale_coords) {
                gctx->off_x = gctx->off_x * width / gctx->sys_w;
                gctx->off_y = gctx->off_y * height / gctx->sys_h;
        }

        HDC hdc = GetDC(gctx->hWnd);
        dr->dpi_x = (u32) (width * 25.4 / GetDeviceCaps(hdc, HORZSIZE) );
        dr->dpi_y = (u32) (height * 25.4 / GetDeviceCaps(hdc, VERTSIZE) );
        ReleaseDC(gctx->hWnd, hdc);

        if ((gctx->screen_w==width) && (gctx->screen_h==height)) return GF_FALSE;

        GF_Event evt;
        dr->max_screen_width = gctx->screen_w = width;
        dr->max_screen_height = gctx->screen_h = height;
        dr->max_screen_bpp = 8;//we don't filter for bpp less than 8

        evt.type = GF_EVENT_RESOLUTION;
        evt.size.width = dr->max_screen_width;
        evt.size.height = dr->max_screen_height;
        dr->on_event(the_video_driver->evt_cbk_hdl, &evt);

        return GF_TRUE;
}

#ifndef GETRAWFRAMEBUFFER
#define GETRAWFRAMEBUFFER   0x00020001
typedef struct _RawFrameBufferInfo
{
        WORD wFormat;
        WORD wBPP;
        VOID *pFramePointer;
        int     cxStride;
        int     cyStride;
        int cxPixels;
        int cyPixels;
} RawFrameBufferInfo;

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

static GF_Err gapi_get_raw_fb(GF_VideoOutput *dr)
{
        long tmp;
        RawFrameBufferInfo Info;
        GAPICTX(dr);
        HDC DC = GetDC(NULL);
        memset(&Info,0,sizeof(RawFrameBufferInfo));

        ExtEscape(DC, GETRAWFRAMEBUFFER, 0, NULL, sizeof(RawFrameBufferInfo), (char*)&Info);
        if (!Info.pFramePointer /* && QueryPlatform(PLATFORM_VER) >= 421*/ )
        {
                //try gxinfo
                DWORD Code = GETGXINFO;
                if (ExtEscape(DC, ESC_QUERYESCSUPPORT, sizeof(DWORD), (char*)&Code, 0, NULL) > 0)
                {
                        DWORD DCWidth = GetDeviceCaps(DC,HORZRES);
                        DWORD DCHeight = GetDeviceCaps(DC,VERTRES);
                        GXDeviceInfo GXInfo;
                        memset(&GXInfo,0,sizeof(GXInfo));
                        GXInfo.Version = 100;
                        ExtEscape(DC, GETGXINFO, 0, NULL, sizeof(GXInfo), (char*)&GXInfo);

                        // avoid VGA devices (or QVGA smartphones emulating 176x220)
                        if (GXInfo.cbStride>0 && !(GXInfo.ffFormat & kfLandscape) &&
                                ((DCWidth == GXInfo.cxWidth && DCHeight == GXInfo.cyHeight) ||
                                 (DCWidth == GXInfo.cyHeight && DCHeight == GXInfo.cxWidth)))
                        {
                                Bool Detect = GF_FALSE;
                                int* p = (int*)GXInfo.pvFrameBuffer;
                                COLORREF Old = GetPixel(DC,0,0);
                                *p ^= -1;
                                Detect = (Bool) ( GetPixel(DC,0,0) != Old );
                                *p ^= -1;

                                if (Detect)
                                {
                                        Info.pFramePointer = GXInfo.pvFrameBuffer;
                                        Info.cxPixels = GXInfo.cxWidth;
                                        Info.cyPixels = GXInfo.cyHeight;
                                        Info.cxStride = GXInfo.cBPP/8;
                                        Info.cyStride = GXInfo.cbStride;
                                        Info.wBPP = (WORD)GXInfo.cBPP;
                                        Info.wFormat = (WORD)GXInfo.ffFormat;
                                }
                        }
                }
        }

        ReleaseDC(NULL,DC);

        if (!Info.pFramePointer) return GF_NOT_SUPPORTED;

        gctx->x_pitch = Info.cxStride;
        gctx->y_pitch = Info.cyStride;

        if (abs(Info.cyStride) < abs(Info.cxStride))
        {
                if (abs(Info.cxStride)*8 < Info.cyPixels*Info.wBPP &&
                        abs(Info.cxStride)*8 >= Info.cxPixels*Info.wBPP) { //swapped resolution
                        tmp = Info.cxPixels;
                        Info.cxPixels = Info.cyPixels;
                        Info.cyPixels = tmp;
                }
        }
        else
        {
                if (abs(Info.cyStride)*8 < Info.cxPixels*Info.wBPP &&
                        abs(Info.cyStride)*8 >= Info.cyPixels*Info.wBPP) {//swapped resolution
                        tmp = Info.cxPixels;
                        Info.cxPixels = Info.cyPixels;
                        Info.cyPixels = tmp;
                }
        }

        gctx->raw_ptr = (unsigned char *)Info.pFramePointer;

        if (Info.cxStride<0)
                gctx->raw_ptr += (Info.cxStride * (Info.cxPixels-1));
        if (Info.cyStride<0)
                gctx->raw_ptr += (Info.cyStride * (Info.cyPixels-1));

        if (check_resolution_switch(dr, Info.cxPixels, Info.cyPixels))
                return GF_EOS;

        return GF_OK;
}

#endif


static GF_Err GAPI_InitBackBuffer(GF_VideoOutput *dr, u32 VideoWidth, u32 VideoHeight)
{
        u32 gx_mode;
        GAPICTX(dr);

        if (!gctx || !VideoWidth || !VideoHeight) return GF_BAD_PARAM;

        gf_mx_p(gctx->mx);

        GAPI_ReleaseObjects(gctx);

        /*flip W&H in landscape mode*/
        if (landscape) {
                u32 t = VideoWidth;
                VideoWidth = VideoHeight;
                VideoHeight = t;
        }

        RECT rc;
        GetWindowRect(gctx->hWnd, &rc);
        gctx->off_x = rc.left;
        gctx->off_y = rc.top;
        gctx->erase_dest = GF_TRUE;

        const char *opt = gf_modules_get_option((GF_BaseInterface *)dr, "GAPI", "FBAccess");
        if (!opt || !strcmp(opt, "raw")) gx_mode = 2;
        else if (opt && !strcmp(opt, "gx")) gx_mode = 1;
        else gx_mode = 0;

        if ((gx_mode != gctx->gx_mode) || !gctx->screen_w) {
                struct GXDisplayProperties gx = GXGetDisplayProperties();

                gctx->x_pitch = gx.cbxPitch;
                gctx->y_pitch = gx.cbyPitch;

                gctx->gx_mode = gx_mode;
                if (gctx->gx_mode==2) {
                        if (gapi_get_raw_fb(dr) == GF_EOS) {
                                gf_mx_v(gctx->mx);
                                return GF_OK;
                        }
                } else if (check_resolution_switch(dr, gx.cxWidth, gx.cyHeight)) {
                        gf_mx_v(gctx->mx);
                        return GF_OK;
                }
        }
        if (gctx->gx_mode==2) {
                GF_Err e = gapi_get_raw_fb(dr);
                if (e) {
                        gf_mx_v(gctx->mx);
                        if (e==GF_EOS) return GF_OK;
                        else return e;
                }
        }


        gctx->bb_size = VideoWidth * VideoHeight * gctx->BPP;
        gctx->bb_width = VideoWidth;
        gctx->bb_height = VideoHeight;
        gctx->bb_pitch = VideoWidth * gctx->BPP;


        if (gctx->gx_mode) {
                gctx->backbuffer = (char *) gf_malloc(sizeof(unsigned char) * gctx->bb_size);

                gctx->contiguous_mem = ((gctx->x_pitch==gctx->BPP) && (gctx->y_pitch==gctx->screen_w*gctx->BPP)) ? GF_TRUE : GF_FALSE;
        } else {
                createPixmap(gctx, 0);
        }

        gf_mx_v(gctx->mx);
        return GF_OK;
}


static void GAPI_AdjustLandscape(GAPIPriv *gctx, GF_VideoSurface *dst, s32 x_pitch, s32 y_pitch)
{
        if (y_pitch>0) {
#if 1
                dst->pitch_x = -y_pitch;
                /*start of frame-buffer is top-left corner*/
                if (x_pitch>0) {
                        dst->video_buffer += dst->height * y_pitch;
                        dst->pitch_y = x_pitch;
                }
                /*start of frame-buffer is top-right corner*/
                else {
                        dst->video_buffer += dst->height * y_pitch + dst->width * x_pitch;
                        dst->pitch_y = -x_pitch;
                }
#else
                dst->pitch_x = y_pitch;
                /*start of frame-buffer is top-left corner*/
                if (x_pitch>0) {
                        dst->video_buffer += y_pitch - x_pitch;
                        dst->pitch_y = -x_pitch;
                }
                /*start of frame-buffer is top-right corner*/
                else {
                        dst->video_buffer += dst->height * y_pitch + dst->width * x_pitch;
                        dst->pitch_y = -x_pitch;
                }
#endif
        } else {
                dst->pitch_x = y_pitch;
                /*start of frame-buffer is bottom-left corner*/
                if (x_pitch>0) {
                        dst->pitch_y = x_pitch;
                }
                /*start of frame-buffer is bottom-right corner*/
                else {
                        dst->video_buffer += dst->width * x_pitch;
                        dst->pitch_y = x_pitch;
                }
        }
        u32 t = dst->width;
        dst->width = dst->height;
        dst->height = t;
}

static GF_Err GAPI_LockBackBuffer(GF_VideoOutput *dr, GF_VideoSurface *vi, Bool do_lock)
{
        GAPICTX(dr);

        if (do_lock) {
                if (!vi) return GF_BAD_PARAM;
                memset(vi, 0, sizeof(GF_VideoSurface));
                vi->width = gctx->bb_width;
                vi->height = gctx->bb_height;
                vi->video_buffer = gctx->backbuffer;
                vi->pixel_format = gctx->pixel_format;
                vi->is_hardware_memory = GF_FALSE;
                vi->pitch_x = gctx->x_pitch;
                vi->pitch_y = gctx->y_pitch;

                if (landscape)
                        GAPI_AdjustLandscape(gctx, vi, gctx->x_pitch, gctx->y_pitch);
        }
        return GF_OK;
}


static void *NewGAPIVideoOutput()
{
        GAPIPriv *priv;
        GF_VideoOutput *driv = (GF_VideoOutput *) gf_malloc(sizeof(GF_VideoOutput));
        memset(driv, 0, sizeof(GF_VideoOutput));
        GF_REGISTER_MODULE_INTERFACE(driv, GF_VIDEO_OUTPUT_INTERFACE, "GAPI Video Output", "gpac distribution")

        priv = (GAPIPriv *) gf_malloc(sizeof(GAPIPriv));
        memset(priv, 0, sizeof(GAPIPriv));
        priv->mx = gf_mx_new("GAPI");
        driv->opaque = priv;

#ifdef GPAC_USE_GLES1X
        driv->hw_caps = GF_VIDEO_HW_OPENGL | GF_VIDEO_HW_OPENGL_OFFSCREEN | GF_VIDEO_HW_OPENGL_OFFSCREEN_ALPHA;
#endif
        /*rgb, yuv to do*/

        driv->Setup = GAPI_Setup;
        driv->Shutdown = GAPI_Shutdown;
        driv->Flush = GAPI_Flush;
        driv->ProcessEvent = GAPI_ProcessEvent;
        driv->Blit = NULL;
        driv->LockBackBuffer = GAPI_LockBackBuffer;
        driv->SetFullScreen = GAPI_SetFullScreen;
        return (void *)driv;
}

static void DeleteVideoOutput(void *ifce)
{
        GF_VideoOutput *driv = (GF_VideoOutput *) ifce;
        GAPICTX(driv);
        GAPI_Shutdown(driv);
        gf_mx_del(gctx->mx);
        gf_free(gctx);
        gf_free(driv);
}

#ifdef __cplusplus
extern "C" {
#endif


/*interface query*/
GPAC_MODULE_EXPORT
const u32 *QueryInterfaces()
{
        static u32 si [] = {
                GF_VIDEO_OUTPUT_INTERFACE,
                0
        };
        return si;
}

/*interface create*/
GPAC_MODULE_EXPORT
GF_BaseInterface *LoadInterface(u32 InterfaceType)
{
        if (InterfaceType == GF_VIDEO_OUTPUT_INTERFACE) return (GF_BaseInterface *) NewGAPIVideoOutput();
        return NULL;
}

/*interface destroy*/
GPAC_MODULE_EXPORT
void ShutdownInterface(GF_BaseInterface *ifce)
{
        GF_VideoOutput *dd = (GF_VideoOutput *)ifce;
        switch (dd->InterfaceType) {
        case GF_VIDEO_OUTPUT_INTERFACE:
                DeleteVideoOutput(dd);
                break;
        }
}

GPAC_MODULE_STATIC_DECLARATION( gapi )

#ifdef __cplusplus
}
#endif


/* [<][>][^][v][top][bottom][index][help] */