view src/video/os2fslib/SDL_os2fslib.c @ 2413:ebeb6ca03766 gsoc2008_iphone

just updating XCode project files to include keyboard example as target
author Holmes Futrell <hfutrell@umail.ucsb.edu>
date Sat, 02 Aug 2008 00:55:28 +0000
parents c121d94672cb
children e1da92da346c 204be4fc2726
line wrap: on
line source

/*
    SDL - Simple DirectMedia Layer
    Copyright (C) 1997-2004 Sam Lantinga

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library 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
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public
    License along with this library; if not, write to the Free
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

    Sam Lantinga
    slouken@libsdl.org
*/
#include "SDL_config.h"

#define _ULS_CALLCONV_
#define CALLCONV _System
#include <unidef.h>             // Unicode API
#include <uconv.h>              // Unicode API (codepage conversion)

#include <process.h>
#include <time.h>

#include "SDL_video.h"
#include "SDL_mouse.h"
#include "../SDL_sysvideo.h"
#include "../SDL_pixels_c.h"
#include "../../events/SDL_events_c.h"

#include "SDL_os2fslib.h"

static ULONG ulFCFToUse =
    FCF_TITLEBAR |
    FCF_SYSMENU |
    FCF_MINBUTTON |
    FCF_MAXBUTTON | FCF_NOBYTEALIGN | FCF_SIZEBORDER | FCF_TASKLIST;

static int bMouseCaptured = 0;
static int bMouseCapturable = 0;
static HPOINTER hptrGlobalPointer = NULL;
static HPOINTER hptrCurrentIcon = NULL;
static int iWindowSizeX = 320;
static int iWindowSizeY = 200;
static int bWindowResized = 0;

#pragma pack(1)
typedef struct BMPINFO
{
    BITMAPINFO;
    RGB clr;
} BMPINFO, *PBMPINFO;
#pragma pack()


// Backdoors:
DECLSPEC void SDLCALL
SDL_OS2FSLIB_SetFCFToUse(ULONG ulFCF)
{
    ulFCFToUse = ulFCF;
}

// Configuration defines:

// We have to report empty alpha mask, otherwise SDL will select
// alpha blitters, and this will have unwanted results, as we don't
// support alpha channel in FSLib yet.
#define REPORT_EMPTY_ALPHA_MASK

// Experimental: Move every FSLib_BitBlt() call into window message
// processing function.
// This may fix dirt left on desktop. Or not.
//#define BITBLT_IN_WINMESSAGEPROC

// Experimental-2: Use WinLockWindowUpdate() in around bitblts!
// This is not enabled, because it seems to cause more problems
// than good.
//#define USE_WINLOCKWINDOWUPDATE_AROUND_BITBLTS

// Use the following to show resized image instead of black stuff
// even if the surface is resizable.
//#define RESIZE_EVEN_IF_RESIZABLE

/* The translation table from a VK keysym to a SDL keysym */
static SDLKey HWScanKeyMap[256];
static SDL_keysym *TranslateKey(int vkey, int chcode, int scancode,
                                SDL_keysym * keysym, int iPressed);
static int iShiftIsPressed;

#ifdef BITBLT_IN_WINMESSAGEPROC
#define WM_UPDATERECTSREQUEST   WM_USER+50
#endif

#ifdef USE_WINLOCKWINDOWUPDATE_AROUND_BITBLTS
#define FSLIB_BITBLT(hwnd, buffer, top, left, width, height) \
    { \
      WinLockWindowUpdate(HWND_DESKTOP, HWND_DESKTOP); \
      FSLib_BitBlt(hwnd, buffer, top, left, width, height); \
      WinLockWindowUpdate(HWND_DESKTOP, NULL); \
    }
#else
#define FSLIB_BITBLT(hwnd, buffer, top, left, width, height) \
    FSLib_BitBlt(hwnd, buffer, top, left, width, height);
#endif

/////////////////////////////////////////////////////////////////////
//
// SetAccessableWindowPos
//
// Same as WinSetWindowPos(), but takes care for the window to be
// always on the screen, the titlebar will be accessable everytime.
//
/////////////////////////////////////////////////////////////////////
static BOOL
SetAccessableWindowPos(HWND hwnd, HWND hwndInsertBehind,
                       LONG x, LONG y, LONG cx, LONG cy, ULONG fl)
{
    SWP swpDesktop, swp;
    // Get desktop area
    WinQueryWindowPos(HWND_DESKTOP, &swpDesktop);

    if ((fl & SWP_MOVE) && (fl & SWP_SIZE)) {
        // If both moving and sizing, then change size and pos now!!
        if (x + cx > swpDesktop.cx)
            x = swpDesktop.cx - cx;
        if (x < 0)
            x = 0;
        if (y < 0)
            y = 0;
        if (y + cy > swpDesktop.cy)
            y = swpDesktop.cy - cy;
        return WinSetWindowPos(hwnd, hwndInsertBehind, x, y, cx, cy, fl);
    } else if (fl & SWP_MOVE) {
        // Just moving
        WinQueryWindowPos(hwnd, &swp);
        if (x + swp.cx > swpDesktop.cx)
            x = swpDesktop.cx - swp.cx;
        if (x < 0)
            x = 0;
        if (y < 0)
            y = 0;
        if (y + swp.cy > swpDesktop.cy)
            y = swpDesktop.cy - swp.cy;
        return WinSetWindowPos(hwnd, hwndInsertBehind, x, y, cx, cy, fl);
    } else if (fl & SWP_SIZE) {
        // Just sizing
        WinQueryWindowPos(hwnd, &swp);
        x = swp.x;
        y = swp.y;
        if (x + cx > swpDesktop.cx)
            x = swpDesktop.cx - cx;
        if (x < 0)
            x = 0;
        if (y < 0)
            y = 0;
        if (y + cy > swpDesktop.cy)
            y = swpDesktop.cy - cy;
        return WinSetWindowPos(hwnd, hwndInsertBehind, x, y, cx, cy,
                               fl | SWP_MOVE);
    } else
        return WinSetWindowPos(hwnd, hwndInsertBehind, x, y, cx, cy, fl);
}

static UniChar
NativeCharToUniChar(int chcode)
{
    UniChar ucResult = (UniChar) chcode;
    int rc;
    UconvObject ucoTemp;
    char achFrom[2];
    char *pchFrom;
    size_t iFromCount;
    UniChar aucTo[10];
    UniChar *pucTo;
    size_t iToCount;
    size_t iNonIdentical;

    // Create unicode convert object
    rc = UniCreateUconvObject(L"", &ucoTemp);
    if (rc != ULS_SUCCESS) {
        // Could not create convert object!
        return ucResult;
    }
    // Convert language code string to unicode string
    achFrom[0] = (char) chcode;
    achFrom[1] = 0;
    iFromCount = sizeof(char) * 2;
    iToCount = sizeof(UniChar) * 2;
    pucTo = &(aucTo[0]);
    pchFrom = &(achFrom[0]);

    rc = UniUconvToUcs(ucoTemp,
                       &pchFrom,
                       &iFromCount, &pucTo, &iToCount, &iNonIdentical);

    if (rc != ULS_SUCCESS) {
        // Could not convert language code to UCS string!
        UniFreeUconvObject(ucoTemp);
        return ucResult;
    }

    UniFreeUconvObject(ucoTemp);

#ifdef DEBUG_BUILD
    printf("%02x converted to %02x\n", (int) chcode, (int) (aucTo[0]));
#endif

    return aucTo[0];
}

/////////////////////////////////////////////////////////////////////
//
// TranslateKey
//
// This creates SDL Keycodes from VK_ and hardware scan codes
//
/////////////////////////////////////////////////////////////////////
static SDL_keysym *
TranslateKey(int vkey, int chcode, int scancode, SDL_keysym * keysym,
             int iPressed)
{
    keysym->scancode = (unsigned char) scancode;
    keysym->mod = KMOD_NONE;
    keysym->unicode = 0;

    if (iPressed && SDL_TranslateUNICODE) {
        if (chcode)
            keysym->unicode = NativeCharToUniChar(chcode);
        else
            keysym->unicode = vkey;
    }

    keysym->sym = HWScanKeyMap[scancode];

    // Now stuffs based on state of shift key(s)!
    if (vkey == VK_SHIFT) {
        iShiftIsPressed = iPressed;
    }

    if ((iShiftIsPressed) && (SDL_TranslateUNICODE)) {
        // Change syms, if Unicode stuff is required
        // I think it's silly, but it's SDL...
        switch (keysym->sym) {
        case SDLK_BACKQUOTE:
            keysym->sym = '~';
            break;
        case SDLK_1:
            keysym->sym = SDLK_EXCLAIM;
            break;
        case SDLK_2:
            keysym->sym = SDLK_AT;
            break;
        case SDLK_3:
            keysym->sym = SDLK_HASH;
            break;
        case SDLK_4:
            keysym->sym = SDLK_DOLLAR;
            break;
        case SDLK_5:
            keysym->sym = '%';
            break;
        case SDLK_6:
            keysym->sym = SDLK_CARET;
            break;
        case SDLK_7:
            keysym->sym = SDLK_AMPERSAND;
            break;
        case SDLK_8:
            keysym->sym = SDLK_ASTERISK;
            break;
        case SDLK_9:
            keysym->sym = SDLK_LEFTPAREN;
            break;
        case SDLK_0:
            keysym->sym = SDLK_RIGHTPAREN;
            break;
        case SDLK_MINUS:
            keysym->sym = SDLK_UNDERSCORE;
            break;
        case SDLK_PLUS:
            keysym->sym = SDLK_EQUALS;
            break;

        case SDLK_LEFTBRACKET:
            keysym->sym = '{';
            break;
        case SDLK_RIGHTBRACKET:
            keysym->sym = '}';
            break;

        case SDLK_SEMICOLON:
            keysym->sym = SDLK_COLON;
            break;
        case SDLK_QUOTE:
            keysym->sym = SDLK_QUOTEDBL;
            break;
        case SDLK_BACKSLASH:
            keysym->sym = '|';
            break;

        case SDLK_COMMA:
            keysym->sym = SDLK_LESS;
            break;
        case SDLK_PERIOD:
            keysym->sym = SDLK_GREATER;
            break;
        case SDLK_SLASH:
            keysym->sym = SDLK_QUESTION;
            break;

        default:
            break;
        }
    }
    return keysym;
}

#define CONVERTMOUSEPOSITION()  \
        /* We have to inverse the mouse position, because every non-os/2 system */                                                \
        /* has a coordinate system where the (0;0) is the top-left corner,      */                                                \
        /* while on os/2 it's the bottom left corner!                           */                                                \
        if (FSLib_QueryFSMode(hwnd))                                                                                              \
        {                                                                                                                         \
          /* We're in FS mode!                                                        */                                          \
          /* In FS mode our window is as big as fullscreen mode, but not necessary as */                                          \
          /* big as the source buffer (can be bigger)                                 */                                          \
          /* So, limit mouse pos to source buffer size!                               */                                          \
          if (ppts->x<0) ppts->x = 0;                                                                                             \
          if (ppts->y<0) ppts->y = 0;                                                                                             \
          if (ppts->x>=pVideo->hidden->SrcBufferDesc.uiXResolution) ppts->x = pVideo->hidden->SrcBufferDesc.uiXResolution-1;      \
          if (ppts->y>=pVideo->hidden->SrcBufferDesc.uiYResolution) ppts->y = pVideo->hidden->SrcBufferDesc.uiYResolution-1;      \
          pVideo->hidden->iSkipWMMOUSEMOVE++; /* Don't take next WM_MOUSEMOVE into account!  */                                   \
          ptl.x = ppts->x; ptl.y = ppts->y;                                                                                       \
          WinMapWindowPoints(pVideo->hidden->hwndClient, HWND_DESKTOP, &ptl, 1);                                                  \
          WinSetPointerPos(HWND_DESKTOP, ptl.x, ptl.y);                                                                           \
          /* Then convert OS/2 position to SDL position */                                                                        \
          ppts->y = pVideo->hidden->SrcBufferDesc.uiYResolution - ppts->y - 1;                                                    \
        } else                                                                                                                    \
        {                                                                                                                         \
          SWP swpClient;                                                                                                          \
          /* We're in windowed mode! */                                                                                           \
          WinQueryWindowPos(pVideo->hidden->hwndClient, &swpClient);                                                              \
          /* Convert OS/2 mouse position to SDL position, and also scale it! */                                                   \
          (ppts->x) = (ppts->x) * pVideo->hidden->SrcBufferDesc.uiXResolution / swpClient.cx;                                       \
          (ppts->y) = (ppts->y) * pVideo->hidden->SrcBufferDesc.uiYResolution / swpClient.cy;                                       \
          (ppts->y) = pVideo->hidden->SrcBufferDesc.uiYResolution - (ppts->y)  - 1;                                                 \
        }



/////////////////////////////////////////////////////////////////////
//
// WndProc
//
// This is the message processing window procedure for the
// SDLWindowClass, which is the client window in our application.
// It handles switching back and away from the app (taking care of
// going out and back to and from fullscreen mode), sending keystrokes
// and mouse events to where it has to be sent, etc...
//
/////////////////////////////////////////////////////////////////////
static MRESULT EXPENTRY
WndProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
{
    HPS ps;
    RECTL rcl;
    SDL_VideoDevice *pVideo = NULL;

    switch (msg) {
    case WM_CHAR:              // Keypress notification
#ifdef DEBUG_BUILD
//      printf("WM_CHAR\n"); fflush(stdout);
#endif
        pVideo = WinQueryWindowPtr(hwnd, 0);
        if (pVideo) {
            /*
               // We skip repeated keys:
               if (CHARMSG(&msg)->cRepeat>1)
               {
               #ifdef DEBUG_BUILD
               //          printf("Repeated key (%d), skipping...\n", CHARMSG(&msg)->cRepeat); fflush(stdout);
               #endif
               return (MRESULT) TRUE;
               }
             */

            // If it's not repeated, then let's see if its pressed or released!
            if (SHORT1FROMMP(mp1) & KC_KEYUP) {
                // A key has been released
                SDL_keysym keysym;

#ifdef DEBUG_BUILD
//          printf("WM_CHAR, keyup, code is [0x%0x]\n", CHAR4FROMMP(mp1)); // HW scan code
#endif

                // One problem is with F1, which gets only the keyup message because
                // it is a system key.
                // So, when we get keyup message, we simulate keydown too!
                // UPDATE:
                //  This problem should be solved now, that the accelerator keys are
                //  disabled for this window!
                /*
                   if (SHORT2FROMMP(mp2)==VK_F1)
                   {
                   SDL_PrivateKeyboard(SDL_PRESSED, TranslateKey(SHORT2FROMMP(mp2), // VK_ code
                   SHORT1FROMMP(mp2), // Character code
                   CHAR4FROMMP(mp1),  // HW Scan code
                   &keysym,0));
                   } */

                SDL_PrivateKeyboard(SDL_RELEASED, TranslateKey(SHORT2FROMMP(mp2),       // VK_ code
                                                               SHORT1FROMMP(mp2),       // Character code
                                                               CHAR4FROMMP(mp1),        // HW Scan code
                                                               &keysym, 0));

            } else {
                // A key has been pressed
                SDL_keysym keysym;

#ifdef DEBUG_BUILD
//          printf("WM_CHAR, keydown, code is [0x%0x]\n", CHAR4FROMMP(mp1)); // HW scan code
#endif
                // Check for fastkeys: ALT+HOME to toggle FS mode
                //                     ALT+END to close app
                if ((SHORT1FROMMP(mp1) & KC_ALT) &&
                    (SHORT2FROMMP(mp2) == VK_HOME)) {
#ifdef DEBUG_BUILD
                    printf(" Pressed ALT+HOME!\n");
                    fflush(stdout);
#endif
                    // Only switch between fullscreen and back if it's not
                    // a resizable mode!
                    if ((!pVideo->hidden->pSDLSurface) ||
                        ((pVideo->hidden->pSDLSurface)
                         &&
                         ((pVideo->hidden->pSDLSurface->
                           flags & SDL_RESIZABLE) == 0)))
                        FSLib_ToggleFSMode(hwnd, !FSLib_QueryFSMode(hwnd));
#ifdef DEBUG_BUILD
                    else
                        printf(" Resizable mode, so discarding ALT+HOME!\n");
                    fflush(stdout);
#endif
                } else if ((SHORT1FROMMP(mp1) & KC_ALT) &&
                           (SHORT2FROMMP(mp2) == VK_END)) {
#ifdef DEBUG_BUILD
                    printf(" Pressed ALT+END!\n");
                    fflush(stdout);
#endif
                    // Close window, and get out of loop!
                    // Also send event to SDL application, but we won't
                    // wait for it to be processed!
                    SDL_PrivateQuit();
                    WinPostMsg(hwnd, WM_QUIT, 0, 0);
                } else {

                    SDL_PrivateKeyboard(SDL_PRESSED, TranslateKey(SHORT2FROMMP(mp2),    // VK_ code
                                                                  SHORT1FROMMP(mp2),    // Character code
                                                                  CHAR4FROMMP(mp1),     // HW Scan code
                                                                  &keysym,
                                                                  1));

                }
            }
        }
        return (MRESULT) TRUE;

    case WM_TRANSLATEACCEL:
        {
            PQMSG pqmsg;
            pqmsg = (PQMSG) mp1;
            if (mp1) {
                if (pqmsg->msg == WM_CHAR) {
                    // WM_CHAR message!
                    // Let's filter the ALT keypress and all other acceleration keys!
                    return (MRESULT) FALSE;
                }
            }
            break;              // Default processing (pass to parent until frame control)
        }

    case WM_PAINT:             // Window redraw!
#ifdef DEBUG_BUILD
        printf("WM_PAINT (0x%x)\n", hwnd);
        fflush(stdout);
#endif
        ps = WinBeginPaint(hwnd, 0, &rcl);
        pVideo = FSLib_GetUserParm(hwnd);
        if (pVideo) {
            if (!pVideo->hidden->pSDLSurface) {
                RECTL rclRect;
                // So, don't blit now!
#ifdef DEBUG_BUILD
                printf("WM_PAINT : Skipping blit while resizing (Pre!)!\n");
                fflush(stdout);
#endif
                WinQueryWindowRect(hwnd, &rclRect);
                // Fill with black
                WinFillRect(ps, &rclRect, CLR_BLACK);
            } else {
                if (DosRequestMutexSem
                    (pVideo->hidden->hmtxUseSrcBuffer, 1000) == NO_ERROR) {
                    int iTop, iLeft, iWidth, iHeight;
                    int iXScaleError, iYScaleError;
                    int iXScaleError2, iYScaleError2;
                    SWP swp;

                    // Re-blit the modified area!
                    // For this, we have to calculate the points, scaled!
                    WinQueryWindowPos(hwnd, &swp);
#ifdef DEBUG_BUILD
                    printf
                        ("WM_PAINT : WinSize: %d %d, BufSize: %d %d\n",
                         swp.cx, swp.cy,
                         pVideo->hidden->SrcBufferDesc.uiXResolution,
                         pVideo->hidden->SrcBufferDesc.uiYResolution);
                    fflush(stdout);
#endif

#ifndef RESIZE_EVEN_IF_RESIZABLE
                    // But only blit if the window is not resizable, or if
                    // the window is resizable and the source buffer size is the
                    // same as the destination buffer size!
                    if ((!pVideo->hidden->pSDLSurface) ||
                        ((pVideo->hidden->pSDLSurface) &&
                         (pVideo->hidden->pSDLSurface->flags & SDL_RESIZABLE)
                         &&
                         ((swp.cx !=
                           pVideo->hidden->SrcBufferDesc.uiXResolution)
                          || (swp.cy !=
                              pVideo->hidden->SrcBufferDesc.uiYResolution))
                         && (!FSLib_QueryFSMode(hwnd)))) {
                        RECTL rclRect;
                        // Resizable surface and in resizing!
                        // So, don't blit now!
#ifdef DEBUG_BUILD
                        printf("WM_PAINT : Skipping blit while resizing!\n");
                        fflush(stdout);
#endif
                        WinQueryWindowRect(hwnd, &rclRect);
                        // Fill with black
                        WinFillRect(ps, &rclRect, CLR_BLACK);
                    } else
#endif
                    {

                        iXScaleError =
                            (pVideo->hidden->SrcBufferDesc.
                             uiXResolution - 1) / swp.cx;
                        iYScaleError =
                            (pVideo->hidden->SrcBufferDesc.
                             uiYResolution - 1) / swp.cy;
                        if (iXScaleError < 0)
                            iXScaleError = 0;
                        if (iYScaleError < 0)
                            iYScaleError = 0;
                        iXScaleError2 =
                            (swp.cx -
                             1) /
                            (pVideo->hidden->SrcBufferDesc.uiXResolution);
                        iYScaleError2 =
                            (swp.cy -
                             1) /
                            (pVideo->hidden->SrcBufferDesc.uiYResolution);
                        if (iXScaleError2 < 0)
                            iXScaleError2 = 0;
                        if (iYScaleError2 < 0)
                            iYScaleError2 = 0;

                        iTop =
                            (swp.cy -
                             rcl.yTop) *
                            pVideo->hidden->SrcBufferDesc.
                            uiYResolution / swp.cy - iYScaleError;
                        iLeft =
                            rcl.xLeft *
                            pVideo->hidden->SrcBufferDesc.
                            uiXResolution / swp.cx - iXScaleError;
                        iWidth =
                            ((rcl.xRight -
                              rcl.xLeft) *
                             pVideo->hidden->SrcBufferDesc.
                             uiXResolution + swp.cx - 1) / swp.cx +
                            2 * iXScaleError;
                        iHeight =
                            ((rcl.yTop -
                              rcl.yBottom) *
                             pVideo->hidden->SrcBufferDesc.
                             uiYResolution + swp.cy - 1) / swp.cy +
                            2 * iYScaleError;

                        iWidth += iXScaleError2;
                        iHeight += iYScaleError2;

                        if (iTop < 0)
                            iTop = 0;
                        if (iLeft < 0)
                            iLeft = 0;
                        if (iTop + iHeight >
                            pVideo->hidden->SrcBufferDesc.uiYResolution)
                            iHeight =
                                pVideo->hidden->SrcBufferDesc.
                                uiYResolution - iTop;
                        if (iLeft + iWidth >
                            pVideo->hidden->SrcBufferDesc.uiXResolution)
                            iWidth =
                                pVideo->hidden->SrcBufferDesc.
                                uiXResolution - iLeft;

#ifdef DEBUG_BUILD
                        printf
                            ("WM_PAINT : BitBlt: %d %d -> %d %d (Buf %d x %d)\n",
                             iTop, iLeft, iWidth, iHeight,
                             pVideo->hidden->SrcBufferDesc.
                             uiXResolution,
                             pVideo->hidden->SrcBufferDesc.uiYResolution);
                        fflush(stdout);
#endif

                        FSLIB_BITBLT(hwnd,
                                     pVideo->hidden->pchSrcBuffer,
                                     iTop, iLeft, iWidth, iHeight);
                    }

                    DosReleaseMutexSem(pVideo->hidden->hmtxUseSrcBuffer);
                }
            }
        }
#ifdef DEBUG_BUILD
        else {
            printf("WM_PAINT : No pVideo!\n");
            fflush(stdout);
        }
#endif
        WinEndPaint(ps);
#ifdef DEBUG_BUILD
        printf("WM_PAINT : Done.\n");
        fflush(stdout);
#endif
        return 0;

    case WM_SIZE:
        {
#ifdef DEBUG_BUILD
            printf("WM_SIZE : (%d %d)\n",
                   SHORT1FROMMP(mp2), SHORT2FROMMP(mp2));
            fflush(stdout);
#endif
            iWindowSizeX = SHORT1FROMMP(mp2);
            iWindowSizeY = SHORT2FROMMP(mp2);
            bWindowResized = 1;

            // Make sure the window will be redrawn
            WinInvalidateRegion(hwnd, NULL, TRUE);
        }
        break;

    case WM_FSLIBNOTIFICATION:
#ifdef DEBUG_BUILD
        printf("WM_FSLIBNOTIFICATION\n");
        fflush(stdout);
#endif
        if ((int) mp1 == FSLN_TOGGLEFSMODE) {
            // FS mode changed, reblit image!
            pVideo = FSLib_GetUserParm(hwnd);
            if (pVideo) {
                if (!pVideo->hidden->pSDLSurface) {
                    // Resizable surface and in resizing!
                    // So, don't blit now!
#ifdef DEBUG_BUILD
                    printf
                        ("WM_FSLIBNOTIFICATION : Can not blit if there is no surface, doing nothing.\n");
                    fflush(stdout);
#endif
                } else {
                    if (DosRequestMutexSem
                        (pVideo->hidden->hmtxUseSrcBuffer,
                         1000) == NO_ERROR) {
                        if (pVideo->hidden->pSDLSurface) {
#ifndef RESIZE_EVEN_IF_RESIZABLE
                            SWP swp;

                            // But only blit if the window is not resizable, or if
                            // the window is resizable and the source buffer size is the
                            // same as the destination buffer size!
                            WinQueryWindowPos(hwnd, &swp);
                            if ((!pVideo->hidden->pSDLSurface) ||
                                ((pVideo->hidden->pSDLSurface) &&
                                 (pVideo->hidden->pSDLSurface->
                                  flags & SDL_RESIZABLE)
                                 &&
                                 ((swp.cx !=
                                   pVideo->hidden->SrcBufferDesc.
                                   uiXResolution)
                                  || (swp.cy !=
                                      pVideo->hidden->
                                      SrcBufferDesc.uiYResolution))
                                 && (!FSLib_QueryFSMode(hwnd)))) {
                                // Resizable surface and in resizing!
                                // So, don't blit now!
#ifdef DEBUG_BUILD
                                printf
                                    ("WM_FSLIBNOTIFICATION : Cannot blit while resizing, doing nothing.\n");
                                fflush(stdout);
#endif
                            } else
#endif
                            {
#ifdef DEBUG_BUILD
                                printf("WM_FSLIBNOTIFICATION : Blitting!\n");
                                fflush(stdout);
#endif
                                FSLIB_BITBLT(hwnd,
                                             pVideo->hidden->
                                             pchSrcBuffer, 0,
                                             0,
                                             pVideo->hidden->
                                             SrcBufferDesc.
                                             uiXResolution,
                                             pVideo->hidden->
                                             SrcBufferDesc.uiYResolution);
                            }
                        }
#ifdef DEBUG_BUILD
                        else
                            printf
                                ("WM_FSLIBNOTIFICATION : No public surface!\n");
                        fflush(stdout);
#endif

                        DosReleaseMutexSem(pVideo->hidden->hmtxUseSrcBuffer);
                    }
                }
            }
        }
        return (MPARAM) 1;

    case WM_ACTIVATE:
#ifdef DEBUG_BUILD
        printf("WM_ACTIVATE\n");
        fflush(stdout);
#endif

        pVideo = FSLib_GetUserParm(hwnd);
        if (pVideo) {
            pVideo->hidden->fInFocus = (int) mp1;
            if (pVideo->hidden->fInFocus) {
                // Went into focus
                if ((pVideo->hidden->iMouseVisible)
                    && (!bMouseCaptured))
                    WinSetPointer(HWND_DESKTOP,
                                  WinQuerySysPointer(HWND_DESKTOP,
                                                     SPTR_ARROW, FALSE));
                else
                    WinSetPointer(HWND_DESKTOP, NULL);

                if (bMouseCapturable) {
                    // Re-capture the mouse, if we captured it before!
                    WinSetCapture(HWND_DESKTOP, hwnd);
                    bMouseCaptured = 1;
                    {
                        SWP swpClient;
                        POINTL ptl;
                        // Center the mouse to the middle of the window!
                        WinQueryWindowPos(pVideo->hidden->hwndClient,
                                          &swpClient);
                        ptl.x = 0;
                        ptl.y = 0;
                        WinMapWindowPoints(pVideo->hidden->
                                           hwndClient, HWND_DESKTOP, &ptl, 1);
                        pVideo->hidden->iSkipWMMOUSEMOVE++;     /* Don't take next WM_MOUSEMOVE into account!  */
                        WinSetPointerPos(HWND_DESKTOP,
                                         ptl.x + swpClient.cx / 2,
                                         ptl.y + swpClient.cy / 2);
                    }
                }
            } else {
                // Went out of focus
                WinSetPointer(HWND_DESKTOP,
                              WinQuerySysPointer(HWND_DESKTOP,
                                                 SPTR_ARROW, FALSE));

                if (bMouseCaptured) {
                    // Release the mouse
                    WinSetCapture(HWND_DESKTOP, hwnd);
                    bMouseCaptured = 0;
                }
            }
        }
#ifdef DEBUG_BUILD
        printf("WM_ACTIVATE done\n");
        fflush(stdout);
#endif

        break;

    case WM_BUTTON1DOWN:
#ifdef DEBUG_BUILD
        printf("WM_BUTTON1DOWN\n");
        fflush(stdout);
#endif

        pVideo = FSLib_GetUserParm(hwnd);
        if (pVideo) {
            SDL_PrivateMouseButton(SDL_PRESSED, SDL_BUTTON_LEFT, 0, 0); // Don't report mouse movement!

            if (bMouseCapturable) {
                // We should capture the mouse!
                if (!bMouseCaptured) {
                    WinSetCapture(HWND_DESKTOP, hwnd);
                    WinSetPointer(HWND_DESKTOP, NULL);
                    bMouseCaptured = 1;
                    {
                        SWP swpClient;
                        POINTL ptl;
                        // Center the mouse to the middle of the window!
                        WinQueryWindowPos(pVideo->hidden->hwndClient,
                                          &swpClient);
                        ptl.x = 0;
                        ptl.y = 0;
                        WinMapWindowPoints(pVideo->hidden->
                                           hwndClient, HWND_DESKTOP, &ptl, 1);
                        pVideo->hidden->iSkipWMMOUSEMOVE++;     /* Don't take next WM_MOUSEMOVE into account!  */
                        WinSetPointerPos(HWND_DESKTOP,
                                         ptl.x + swpClient.cx / 2,
                                         ptl.y + swpClient.cy / 2);
                    }
                }
            }
        }
        break;
    case WM_BUTTON1UP:
#ifdef DEBUG_BUILD
        printf("WM_BUTTON1UP\n");
        fflush(stdout);
#endif
        SDL_PrivateMouseButton(SDL_RELEASED, SDL_BUTTON_LEFT, 0, 0);    // Don't report mouse movement!
        break;
    case WM_BUTTON2DOWN:
#ifdef DEBUG_BUILD
        printf("WM_BUTTON2DOWN\n");
        fflush(stdout);
#endif

        pVideo = FSLib_GetUserParm(hwnd);
        if (pVideo) {
            SDL_PrivateMouseButton(SDL_PRESSED, SDL_BUTTON_RIGHT, 0, 0);        // Don't report mouse movement!

            if (bMouseCapturable) {
                // We should capture the mouse!
                if (!bMouseCaptured) {
                    WinSetCapture(HWND_DESKTOP, hwnd);
                    WinSetPointer(HWND_DESKTOP, NULL);
                    bMouseCaptured = 1;
                    {
                        SWP swpClient;
                        POINTL ptl;
                        // Center the mouse to the middle of the window!
                        WinQueryWindowPos(pVideo->hidden->hwndClient,
                                          &swpClient);
                        ptl.x = 0;
                        ptl.y = 0;
                        WinMapWindowPoints(pVideo->hidden->
                                           hwndClient, HWND_DESKTOP, &ptl, 1);
                        pVideo->hidden->iSkipWMMOUSEMOVE++;     /* Don't take next WM_MOUSEMOVE into account!  */
                        WinSetPointerPos(HWND_DESKTOP,
                                         ptl.x + swpClient.cx / 2,
                                         ptl.y + swpClient.cy / 2);
                    }
                }
            }

        }
        break;
    case WM_BUTTON2UP:
#ifdef DEBUG_BUILD
        printf("WM_BUTTON2UP\n");
        fflush(stdout);
#endif
        SDL_PrivateMouseButton(SDL_RELEASED, SDL_BUTTON_RIGHT, 0, 0);   // Don't report mouse movement!
        break;
    case WM_BUTTON3DOWN:
#ifdef DEBUG_BUILD
        printf("WM_BUTTON3DOWN\n");
        fflush(stdout);
#endif

        pVideo = FSLib_GetUserParm(hwnd);
        if (pVideo) {
            SDL_PrivateMouseButton(SDL_PRESSED, SDL_BUTTON_MIDDLE, 0, 0);       // Don't report mouse movement!

            if (bMouseCapturable) {
                // We should capture the mouse!
                if (!bMouseCaptured) {
                    WinSetCapture(HWND_DESKTOP, hwnd);
                    WinSetPointer(HWND_DESKTOP, NULL);
                    bMouseCaptured = 1;
                    {
                        SWP swpClient;
                        POINTL ptl;
                        // Center the mouse to the middle of the window!
                        WinQueryWindowPos(pVideo->hidden->hwndClient,
                                          &swpClient);
                        ptl.x = 0;
                        ptl.y = 0;
                        WinMapWindowPoints(pVideo->hidden->
                                           hwndClient, HWND_DESKTOP, &ptl, 1);
                        pVideo->hidden->iSkipWMMOUSEMOVE++;     /* Don't take next WM_MOUSEMOVE into account!  */
                        WinSetPointerPos(HWND_DESKTOP,
                                         ptl.x + swpClient.cx / 2,
                                         ptl.y + swpClient.cy / 2);
                    }
                }
            }
        }
        break;
    case WM_BUTTON3UP:
#ifdef DEBUG_BUILD
        printf("WM_BUTTON3UP\n");
        fflush(stdout);
#endif
        SDL_PrivateMouseButton(SDL_RELEASED, SDL_BUTTON_MIDDLE, 0, 0);  // Don't report mouse movement!
        break;
    case WM_MOUSEMOVE:
#ifdef DEBUG_BUILD
//      printf("WM_MOUSEMOVE\n"); fflush(stdout);
#endif

        pVideo = FSLib_GetUserParm(hwnd);
        if (pVideo) {
            if (pVideo->hidden->iSkipWMMOUSEMOVE) {
                pVideo->hidden->iSkipWMMOUSEMOVE--;
            } else {
                POINTS *ppts = (POINTS *) (&mp1);
                POINTL ptl;

                if (bMouseCaptured) {
                    SWP swpClient;

                    WinQueryWindowPos(pVideo->hidden->hwndClient, &swpClient);

                    // Send relative mouse position, and re-center the mouse
                    // Reposition the mouse to the center of the screen/window
                    SDL_PrivateMouseMotion(0,   // Buttons not changed
                                           1,   // Relative position
                                           ppts->x -
                                           (swpClient.cx / 2),
                                           (swpClient.cy / 2) - ppts->y);

                    ptl.x = 0;
                    ptl.y = 0;
                    WinMapWindowPoints(pVideo->hidden->hwndClient,
                                       HWND_DESKTOP, &ptl, 1);
                    pVideo->hidden->iSkipWMMOUSEMOVE++; /* Don't take next WM_MOUSEMOVE into account!  */
                    // Center the mouse to the middle of the window!
                    WinSetPointerPos(HWND_DESKTOP,
                                     ptl.x + swpClient.cx / 2,
                                     ptl.y + swpClient.cy / 2);
                } else {
                    CONVERTMOUSEPOSITION();

                    // Send absolute mouse position
                    SDL_PrivateMouseMotion(0,   // Buttons not changed
                                           0,   // Absolute position
                                           ppts->x, ppts->y);
                }
            }
            if ((pVideo->hidden->iMouseVisible) && (!bMouseCaptured)) {
#ifdef DEBUG_BUILD
//          printf("WM_MOUSEMOVE : ptr = %p\n", hptrGlobalPointer); fflush(stdout);
#endif

                if (hptrGlobalPointer)
                    WinSetPointer(HWND_DESKTOP, hptrGlobalPointer);
                else
                    WinSetPointer(HWND_DESKTOP,
                                  WinQuerySysPointer(HWND_DESKTOP,
                                                     SPTR_ARROW, FALSE));
            } else {
                WinSetPointer(HWND_DESKTOP, NULL);
            }
        }
#ifdef DEBUG_BUILD
//      printf("WM_MOUSEMOVE done\n"); fflush(stdout);
#endif

        return (MRESULT) FALSE;
    case WM_CLOSE:             // Window close
#ifdef DEBUG_BUILD
        printf("WM_CLOSE\n");
        fflush(stdout);
#endif

        pVideo = FSLib_GetUserParm(hwnd);
        if (pVideo) {
            // Send Quit message to the SDL application!
            SDL_PrivateQuit();
            return 0;
        }
        break;

#ifdef BITBLT_IN_WINMESSAGEPROC
    case WM_UPDATERECTSREQUEST:
        pVideo = FSLib_GetUserParm(hwnd);
        if ((pVideo) && (pVideo->hidden->pSDLSurface)) {
            if (DosRequestMutexSem
                (pVideo->hidden->hmtxUseSrcBuffer,
                 SEM_INDEFINITE_WAIT) == NO_ERROR) {
                int numrects;
                SDL_Rect *rects;
                int i;
                SWP swp;

                numrects = (int) mp1;
                rects = (SDL_Rect *) mp2;

                WinQueryWindowPos(hwnd, &swp);
#ifndef RESIZE_EVEN_IF_RESIZABLE
                if ((!pVideo->hidden->pSDLSurface) ||
                    ((pVideo->hidden->pSDLSurface) &&
                     (pVideo->hidden->pSDLSurface->flags & SDL_RESIZABLE)
                     &&
                     ((swp.cx != pVideo->hidden->SrcBufferDesc.uiXResolution)
                      || (swp.cy !=
                          pVideo->hidden->SrcBufferDesc.uiYResolution))
                     && (!FSLib_QueryFSMode(hwnd)))) {
                    // Resizable surface and in resizing!
                    // So, don't blit now!
#ifdef DEBUG_BUILD
                    printf
                        ("[WM_UPDATERECTSREQUEST] : Skipping blit while resizing!\n");
                    fflush(stdout);
#endif
                } else
#endif
                {
#ifdef DEBUG_BUILD
                    printf("[WM_UPDATERECTSREQUEST] : Blitting!\n");
                    fflush(stdout);
#endif

                    // Blit the changed areas
                    for (i = 0; i < numrects; i++)
                        FSLIB_BITBLT(hwnd,
                                     pVideo->hidden->pchSrcBuffer,
                                     rects[i].y, rects[i].x,
                                     rects[i].w, rects[i].h);
                }
                DosReleaseMutexSem(pVideo->hidden->hmtxUseSrcBuffer);
            }
        }
        return 0;
#endif

    default:
#ifdef DEBUG_BUILD
        printf("Unhandled: %x\n", msg);
        fflush(stdout);
#endif

        break;
    }
    // Run the default window procedure for unhandled stuffs
    return WinDefWindowProc(hwnd, msg, mp1, mp2);
}

/////////////////////////////////////////////////////////////////////
//
// FrameWndProc
//
// This is the message processing window procedure for the
// frame window of SDLWindowClass.
//
/////////////////////////////////////////////////////////////////////
static MRESULT EXPENTRY
FrameWndProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
{
    PFNWP pOldFrameProc;
    MRESULT result;
    PTRACKINFO ti;
    int cx, cy, ncx, ncy;
    RECTL rclTemp;
    PSWP pswpTemp;

    SDL_VideoDevice *pVideo = NULL;

    pVideo = (SDL_VideoDevice *) WinQueryWindowULong(hwnd, QWL_USER);

    pOldFrameProc = pVideo->hidden->pfnOldFrameProc;

    if ((pVideo->hidden->bProportionalResize) &&
        (msg == WM_ADJUSTWINDOWPOS) &&
        (!FSLib_QueryFSMode(pVideo->hidden->hwndClient))) {
        pswpTemp = (PSWP) mp1;

        /* Resizing? */
        if (pswpTemp->fl & SWP_SIZE) {
            /* Calculate client size */
            rclTemp.xLeft = pswpTemp->x;
            rclTemp.xRight = pswpTemp->x + pswpTemp->cx;
            rclTemp.yBottom = pswpTemp->y;
            rclTemp.yTop = pswpTemp->y + pswpTemp->cy;
            WinCalcFrameRect(hwnd, &rclTemp, TRUE);

            ncx = cx = rclTemp.xRight - rclTemp.xLeft;
            ncy = cy = rclTemp.yTop - rclTemp.yBottom;

            /* Calculate new size to keep it proportional */

            if ((pVideo->hidden->ulResizingFlag & TF_LEFT)
                || (pVideo->hidden->ulResizingFlag & TF_RIGHT)) {
                /* The window is resized horizontally */
                ncy =
                    pVideo->hidden->SrcBufferDesc.uiYResolution * cx /
                    pVideo->hidden->SrcBufferDesc.uiXResolution;
            } else if ((pVideo->hidden->ulResizingFlag & TF_TOP)
                       || (pVideo->hidden->ulResizingFlag & TF_BOTTOM)) {
                /* The window is resized vertically */
                ncx =
                    pVideo->hidden->SrcBufferDesc.uiXResolution * cy /
                    pVideo->hidden->SrcBufferDesc.uiYResolution;
            }

            /* Calculate back frame coordinates */
            rclTemp.xLeft = pswpTemp->x;
            rclTemp.xRight = pswpTemp->x + ncx;
            rclTemp.yBottom = pswpTemp->y;
            rclTemp.yTop = pswpTemp->y + ncy;
            WinCalcFrameRect(hwnd, &rclTemp, FALSE);

            /* Store new size/position info */
            pswpTemp->cx = rclTemp.xRight - rclTemp.xLeft;

            if (!(pVideo->hidden->ulResizingFlag & TF_TOP)) {
                pswpTemp->y =
                    pswpTemp->y + pswpTemp->cy - (rclTemp.yTop -
                                                  rclTemp.yBottom);
                pswpTemp->cy = rclTemp.yTop - rclTemp.yBottom;
            } else {
                pswpTemp->cy = rclTemp.yTop - rclTemp.yBottom;
            }
        }
    }

    result = (*pOldFrameProc) (hwnd, msg, mp1, mp2);

    if ((pVideo->hidden->bProportionalResize) && (msg == WM_QUERYTRACKINFO)) {
        ti = (PTRACKINFO) mp2;

        /* Store the direction of resizing */
        if ((ti->fs & TF_LEFT) || (ti->fs & TF_RIGHT) ||
            (ti->fs & TF_TOP) || (ti->fs & TF_BOTTOM))
            pVideo->hidden->ulResizingFlag = ti->fs;
    }

    return result;
}

/////////////////////////////////////////////////////////////////////
//
// PMThreadFunc
//
// This function implements the PM-Thread, which initializes the
// application window itself, the DIVE, and start message processing.
//
/////////////////////////////////////////////////////////////////////
int iNumOfPMThreadInstances = 0;        // Global!
static void
PMThreadFunc(void *pParm)
{
    SDL_VideoDevice *pVideo = pParm;
    HAB hab;
    HMQ hmq;
    QMSG msg;
    ULONG fcf;

#ifdef DEBUG_BUILD
    printf("[PMThreadFunc] : Starting\n");
    fflush(stdout);
#endif

    iNumOfPMThreadInstances++;

    // Initialize PM, create a message queue.

    hab = WinInitialize(0);
    hmq = WinCreateMsgQueue(hab, 0);
    if (hmq == 0) {
#ifdef DEBUG_BUILD
        printf("[PMThreadFunc] : Could not create message queue!\n");
        printf
            ("                 It might be that the application using SDL is not a PM app!\n");
        fflush(stdout);
#endif
        pVideo->hidden->iPMThreadStatus = 2;
    } else {
        int rc;
        RECTL rectl;

        fcf = ulFCFToUse;       // Get from global setting

#ifdef DEBUG_BUILD
        printf("[PMThreadFunc] : FSLib_CreateWindow()!\n");
        fflush(stdout);
#endif

        rc = FSLib_CreateWindow(HWND_DESKTOP, 0, &fcf,
                                "SDL Application",
                                NULLHANDLE, 0,
                                &(pVideo->hidden->SrcBufferDesc),
                                WndProc,
                                &(pVideo->hidden->hwndClient),
                                &(pVideo->hidden->hwndFrame));

#ifdef DEBUG_BUILD
        printf("[PMThreadFunc] : FSLib_CreateWindow() rc = %d\n", rc);
        fflush(stdout);
#endif

        if (!rc) {
#ifdef DEBUG_BUILD
            printf("[PMThreadFunc] : Could not create FSLib window!\n");
            fflush(stdout);
#endif
            pVideo->hidden->iPMThreadStatus = 3;
        } else {
#ifdef DEBUG_BUILD
            printf("[PMThreadFunc] : FSLib_AddUserParm()!\n");
            fflush(stdout);
#endif

            // Store pVideo pointer in window data for client window, so
            // it will know the instance to which it belongs to.
            FSLib_AddUserParm(pVideo->hidden->hwndClient, pVideo);

            // Now set default image width height and fourcc!
#ifdef DEBUG_BUILD
            printf("[PMThreadFunc] : SetWindowPos()!\n");
            fflush(stdout);
#endif

            // Set the position and size of the main window,
            // and make it visible!
            // Calculate frame window size from client window size
            rectl.xLeft = 0;
            rectl.yBottom = 0;
            rectl.xRight = pVideo->hidden->SrcBufferDesc.uiXResolution; // Noninclusive
            rectl.yTop = pVideo->hidden->SrcBufferDesc.uiYResolution;   // Noninclusive
            WinCalcFrameRect(pVideo->hidden->hwndFrame, &rectl, FALSE);

            SetAccessableWindowPos(pVideo->hidden->hwndFrame,
                                   HWND_TOP,
                                   (WinQuerySysValue
                                    (HWND_DESKTOP,
                                     SV_CXSCREEN) - (rectl.xRight -
                                                     rectl.xLeft)) / 2,
                                   (WinQuerySysValue
                                    (HWND_DESKTOP,
                                     SV_CYSCREEN) - (rectl.yTop -
                                                     rectl.yBottom)) / 2,
                                   (rectl.xRight - rectl.xLeft),
                                   (rectl.yTop - rectl.yBottom),
                                   SWP_SIZE | SWP_ACTIVATE | SWP_SHOW |
                                   SWP_MOVE);

            // Subclass frame procedure and store old window proc address
            pVideo->hidden->pfnOldFrameProc =
                WinSubclassWindow(pVideo->hidden->hwndFrame, FrameWndProc);
            WinSetWindowULong(pVideo->hidden->hwndFrame, QWL_USER,
                              (ULONG) pVideo);

#ifdef DEBUG_BUILD
            printf("[PMThreadFunc] : Entering message loop\n");
            fflush(stdout);
#endif
            pVideo->hidden->iPMThreadStatus = 1;

            while (WinGetMsg(hab, (PQMSG) & msg, 0, 0, 0))
                WinDispatchMsg(hab, (PQMSG) & msg);

#ifdef DEBUG_BUILD
            printf("[PMThreadFunc] : Leaving message loop\n");
            fflush(stdout);
#endif
            // We should release the captured the mouse!
            if (bMouseCaptured) {
                WinSetCapture(HWND_DESKTOP, NULLHANDLE);
                bMouseCaptured = 0;
            }
            // Destroy our window
            WinDestroyWindow(pVideo->hidden->hwndFrame);
            pVideo->hidden->hwndFrame = NULL;
            // Show pointer to make sure it will not be left hidden.
            WinSetPointer(HWND_DESKTOP,
                          WinQuerySysPointer(HWND_DESKTOP, SPTR_ARROW,
                                             FALSE));
            WinShowPointer(HWND_DESKTOP, TRUE);
        }
        // Uninitialize PM
        WinDestroyMsgQueue(hmq);
        // All done!
        pVideo->hidden->iPMThreadStatus = 0;
    }
    WinTerminate(hab);
    /* Commented out, should not be needed anymore, because we send it
       from WM_CLOSE.
       // Notify SDL that it should really die now...
       SDL_PrivateQuit(); SDL_PrivateQuit(); SDL_PrivateQuit(); //... :))
     */
#ifdef DEBUG_BUILD
    printf("[PMThreadFunc] : End, status is %d!\n",
           pVideo->hidden->iPMThreadStatus);
    fflush(stdout);
#endif

    iNumOfPMThreadInstances--;

    // HACK to prevent zombie and hanging SDL applications, which does not take
    // care of closing the window for some reason:
    // There are some apps which do not process messages, so do a lot of things
    // without noticing that the application should close. To close these,
    // I've thought about the following:
    // If the window is closed (the execution came here), I wait a bit to
    // give time to the app to finish its execution. If it does not, I kill it
    // using DosExit(). Brute force, but should work.
    if (pVideo->hidden->iPMThreadStatus == 0) {
        DosSleep(5000);         // Wait 5 secs
        // If a new PM thread has been spawned (reinitializing video mode), then all right.
        // Otherwise, we have a problem, the app doesn't want to stop. Kill!
        if (iNumOfPMThreadInstances == 0) {
#ifdef DEBUG_BUILD
            printf
                ("[PMThreadFunc] : It seems that the application haven't terminated itself\n");
            fflush(stdout);
            printf
                ("[PMThreadFunc] : in the last 5 seconds, so we go berserk.\n");
            fflush(stdout);
            printf
                ("[PMThreadFunc] : Brute force mode. :) Killing process! Dieeeee...\n");
            fflush(stdout);
#endif
            DosExit(EXIT_PROCESS, -1);
        }
    }
    _endthread();
}

struct WMcursor
{
    HBITMAP hbm;
    HPOINTER hptr;
    char *pchData;
};

/* Free a window manager cursor */
void
os2fslib_FreeWMCursor(_THIS, WMcursor * cursor)
{
    if (cursor) {
        GpiDeleteBitmap(cursor->hbm);
        WinDestroyPointer(cursor->hptr);
        SDL_free(cursor->pchData);
        SDL_free(cursor);
    }
}

/* Local functions to convert the SDL cursor mask into OS/2 format */
static void
memnot(Uint8 * dst, Uint8 * src, int len)
{
    while (len-- > 0)
        *dst++ = ~*src++;
}
static void
memxor(Uint8 * dst, Uint8 * src1, Uint8 * src2, int len)
{
    while (len-- > 0)
        *dst++ = (*src1++) ^ (*src2++);
}

/* Create a black/white window manager cursor */
WMcursor *
os2fslib_CreateWMCursor_Win(_THIS, Uint8 * data, Uint8 * mask,
                            int w, int h, int hot_x, int hot_y)
{
    HPOINTER hptr;
    HBITMAP hbm;
    BITMAPINFOHEADER bmih;
    BMPINFO bmi;
    HPS hps;
    char *pchTemp;
    char *xptr, *aptr;
    int maxx, maxy;
    int i, run, pad;
    WMcursor *pResult;

    maxx = WinQuerySysValue(HWND_DESKTOP, SV_CXPOINTER);
    maxy = WinQuerySysValue(HWND_DESKTOP, SV_CYPOINTER);

    // Check for max size!
    if ((w > maxx) || (h > maxy))
        return (WMcursor *) NULL;

    pResult = (WMcursor *) SDL_malloc(sizeof(WMcursor));
    if (!pResult)
        return (WMcursor *) NULL;

    pchTemp = (char *) SDL_malloc((maxx + 7) / 8 * maxy * 2);
    if (!pchTemp) {
        SDL_free(pResult);
        return (WMcursor *) NULL;
    }

    SDL_memset(pchTemp, 0, (maxx + 7) / 8 * maxy * 2);

    hps = WinGetPS(_this->hidden->hwndClient);

    bmi.cbFix = sizeof(BITMAPINFOHEADER);
    bmi.cx = maxx;
    bmi.cy = 2 * maxy;
    bmi.cPlanes = 1;
    bmi.cBitCount = 1;
    bmi.argbColor[0].bBlue = 0x00;
    bmi.argbColor[0].bGreen = 0x00;
    bmi.argbColor[0].bRed = 0x00;
    bmi.argbColor[1].bBlue = 0x00;
    bmi.argbColor[1].bGreen = 0x00;
    bmi.argbColor[1].bRed = 0xff;

    SDL_memset(&bmih, 0, sizeof(BITMAPINFOHEADER));
    bmih.cbFix = sizeof(BITMAPINFOHEADER);
    bmih.cx = maxx;
    bmih.cy = 2 * maxy;
    bmih.cPlanes = 1;
    bmih.cBitCount = 1;

    run = (w + 7) / 8;
    pad = (maxx + 7) / 8 - run;

    for (i = 0; i < h; i++) {
        xptr = pchTemp + (maxx + 7) / 8 * (maxy - 1 - i);
        aptr = pchTemp + (maxx + 7) / 8 * (maxy + maxy - 1 - i);
        memxor(xptr, data, mask, run);
        xptr += run;
        data += run;
        memnot(aptr, mask, run);
        mask += run;
        aptr += run;
        SDL_memset(xptr, 0, pad);
        xptr += pad;
        SDL_memset(aptr, ~0, pad);
        aptr += pad;
    }
    pad += run;
    for (i = h; i < maxy; i++) {
        xptr = pchTemp + (maxx + 7) / 8 * (maxy - 1 - i);
        aptr = pchTemp + (maxx + 7) / 8 * (maxy + maxy - 1 - i);

        SDL_memset(xptr, 0, (maxx + 7) / 8);
        xptr += (maxx + 7) / 8;
        SDL_memset(aptr, ~0, (maxx + 7) / 8);
        aptr += (maxx + 7) / 8;
    }

    hbm =
        GpiCreateBitmap(hps, (PBITMAPINFOHEADER2) & bmih, CBM_INIT,
                        (PBYTE) pchTemp, (PBITMAPINFO2) & bmi);
    hptr = WinCreatePointer(HWND_DESKTOP, hbm, TRUE, hot_x, maxy - hot_y - 1);

#ifdef DEBUG_BUILD
    printf("HotSpot          : %d ; %d\n", hot_x, hot_y);
    printf("HPS returned     : %x\n", (ULONG) hps);
    printf("HBITMAP returned : %x\n", (ULONG) hbm);
    printf("HPOINTER returned: %x\n", (ULONG) hptr);
#endif

    WinReleasePS(hps);

#ifdef DEBUG_BUILD
    printf("[CreateWMCursor] : ptr = %p\n", hptr);
    fflush(stdout);
#endif

    pResult->hptr = hptr;
    pResult->hbm = hbm;
    pResult->pchData = pchTemp;

#ifdef DEBUG_BUILD
    printf("[CreateWMCursor] : ptr = %p return.\n", hptr);
    fflush(stdout);
#endif

    return (WMcursor *) pResult;
}

WMcursor *
os2fslib_CreateWMCursor_FS(_THIS, Uint8 * data, Uint8 * mask,
                           int w, int h, int hot_x, int hot_y)
{
#ifdef DEBUG_BUILD
    printf("[CreateWMCursor_FS] : returning pointer NULL\n");
    fflush(stdout);
#endif

    // In FS mode we'll use software cursor
    return (WMcursor *) NULL;
}

/* Show the specified cursor, or hide if cursor is NULL */
int
os2fslib_ShowWMCursor(_THIS, WMcursor * cursor)
{
#ifdef DEBUG_BUILD
    printf("[ShowWMCursor] : ptr = %p\n", cursor);
    fflush(stdout);
#endif

    if (cursor) {
        WinSetPointer(HWND_DESKTOP, cursor->hptr);
        hptrGlobalPointer = cursor->hptr;
        _this->hidden->iMouseVisible = 1;
    } else {
        WinSetPointer(HWND_DESKTOP, FALSE);
        hptrGlobalPointer = NULL;
        _this->hidden->iMouseVisible = 0;
    }

#ifdef DEBUG_BUILD
    printf("[ShowWMCursor] : ptr = %p, DONE\n", cursor);
    fflush(stdout);
#endif

    return 1;
}

/* Warp the window manager cursor to (x,y)
 If NULL, a mouse motion event is posted internally.
 */
void
os2fslib_WarpWMCursor(_THIS, Uint16 x, Uint16 y)
{
    LONG lx, ly;
    SWP swpClient;
    POINTL ptlPoints;
    WinQueryWindowPos(_this->hidden->hwndClient, &swpClient);
    ptlPoints.x = swpClient.x;
    ptlPoints.y = swpClient.y;
    WinMapWindowPoints(_this->hidden->hwndFrame, HWND_DESKTOP, &ptlPoints, 1);
    lx = ptlPoints.x +
        (x * swpClient.cx) / _this->hidden->SrcBufferDesc.uiXResolution;
    ly = ptlPoints.y + swpClient.cy -
        ((y * swpClient.cy) / _this->hidden->SrcBufferDesc.uiYResolution) - 1;

    SDL_PrivateMouseMotion(0,   // Buttons not changed
                           0,   // Absolute position
                           x, y);

    WinSetPointerPos(HWND_DESKTOP, lx, ly);

}

/* If not NULL, this is called when a mouse motion event occurs */
void
os2fslib_MoveWMCursor(_THIS, int x, int y)
{
    /*
       SDL_Rect rect;

       #ifdef DEBUG_BUILD
       printf("[MoveWMCursor] : at %d ; %d\n", x, y); fflush(stdout);
       #endif

       rect.x = x;
       rect.y = y;
       rect.w = 32;
       rect.h = 32;
       os2fslib_UpdateRects(_this, 1, &rect);
       // TODO!
     */
}

/* Determine whether the mouse should be in relative mode or not.
 This function is called when the input grab state or cursor
 visibility state changes.
 If the cursor is not visible, and the input is grabbed, the
 driver can place the mouse in relative mode, which may result
 in higher accuracy sampling of the pointer motion.
 */
void
os2fslib_CheckMouseMode(_THIS)
{
}

static void
os2fslib_PumpEvents(_THIS)
{
    // Notify SDL that if window has been resized!
    if ((_this->hidden->pSDLSurface) &&
        (_this->hidden->pSDLSurface->flags & SDL_RESIZABLE) &&
        ((_this->hidden->SrcBufferDesc.uiXResolution != iWindowSizeX) ||
         (_this->hidden->SrcBufferDesc.uiYResolution != iWindowSizeY)) &&
        (iWindowSizeX > 0) && (iWindowSizeY > 0)) {
        static time_t prev_time;
        time_t curr_time;

        curr_time = time(NULL);
        if ((difftime(curr_time, prev_time) >= 0.25) || (bWindowResized)) {
            // Make sure we won't flood the event queue with resize events,
            // only send them at 250 msecs!
            // (or when the window is resized)
#ifdef DEBUG_BUILD
            printf
                ("[os2fslib_PumpEvents] : Calling PrivateResize (%d %d).\n",
                 iWindowSizeX, iWindowSizeY);
            fflush(stdout);
#endif
            // Tell SDL the new size
            SDL_PrivateResize(iWindowSizeX, iWindowSizeY);
            prev_time = curr_time;
            bWindowResized = 0;
        }
    }
}

/* We don't actually allow hardware surfaces other than the main one */
static int
os2fslib_AllocHWSurface(_THIS, SDL_Surface * surface)
{
    return (-1);
}
static void
os2fslib_FreeHWSurface(_THIS, SDL_Surface * surface)
{
    return;
}

/* We need to wait for vertical retrace on page flipped displays */
static int
os2fslib_LockHWSurface(_THIS, SDL_Surface * surface)
{
    return (0);
}

static void
os2fslib_UnlockHWSurface(_THIS, SDL_Surface * surface)
{
    return;
}

static int
os2fslib_SetColors(_THIS, int firstcolor, int ncolors, SDL_Color * colors)
{
    printf("[os2fslib_SetColors] : TODO!\n");
    fflush(stdout);
    // TODO: Implement paletted modes
    return (1);
}

static void
os2fslib_DestroyIcon(HWND hwndFrame)
{
    if (hptrCurrentIcon) {
        WinDestroyPointer(hptrCurrentIcon);
        hptrCurrentIcon = NULL;

        WinSendMsg(hwndFrame, WM_SETICON, NULL, NULL);
    }

}

/* Set the window icon image */
void
os2fslib_SetIcon(_THIS, SDL_Surface * icon, Uint8 * mask)
{
    HWND hwndFrame;
    SDL_Surface *icon_rgb;
    HPOINTER hptrIcon;
    HBITMAP hbm;
    BITMAPINFOHEADER bmih;
    BMPINFO bmi;
    HPS hps;
    char *pchTemp;
    char *pptr, *mptr, *dptr, *dmptr;
    int maxx, maxy, w, h, x, y;
    SDL_Rect bounds;

#ifdef DEBUG_BUILD
    printf("[os2fslib_SetIcon] : Creating and setting new icon\n");
    fflush(stdout);
#endif

    hwndFrame = WinQueryWindow(_this->hidden->hwndClient, QW_PARENT);

    // Make sure the old icon resource will be free'd!
    os2fslib_DestroyIcon(hwndFrame);

    if ((!icon) || (!mask))
        return;

    w = icon->w;
    h = icon->h;

    maxx = WinQuerySysValue(HWND_DESKTOP, SV_CXICON);
    maxy = WinQuerySysValue(HWND_DESKTOP, SV_CYICON);

    // Check for max size!
    if ((w > maxx) || (h > maxy))
        return;

    pchTemp = (char *) SDL_malloc(w * h * 2 * 4);
    if (!pchTemp)
        return;

    SDL_memset(pchTemp, 0, w * h * 2 * 4);

    // Convert surface to RGB, if it's not RGB yet!
    icon_rgb = SDL_CreateRGBSurface(SDL_SWSURFACE, icon->w, icon->h,
                                    32, 0, 0, 0, 0);
    if (icon_rgb == NULL) {
        SDL_free(pchTemp);
        return;
    }
    bounds.x = 0;
    bounds.y = 0;
    bounds.w = icon->w;
    bounds.h = icon->h;
    if (SDL_LowerBlit(icon, &bounds, icon_rgb, &bounds) < 0) {
        SDL_FreeSurface(icon_rgb);
        SDL_free(pchTemp);
        return;
    }

    /* Copy pixels upside-down from RGB surface into BMP, masked with the icon mask */

    // Pixels
    pptr = (char *) (icon_rgb->pixels);
    // Mask
    mptr = mask;

    for (y = 0; y < h; y++) {
        unsigned char uchMaskByte;

        // Destination
        dptr = pchTemp + w * 4 * (h - y - 1);
        // Destination mask
        dmptr = pchTemp + w * h * 4 + w * 4 * (h - y - 1);

        for (x = 0; x < w; x++) {
            if (x % 8 == 0) {
                uchMaskByte = (unsigned char) (*mptr);
                mptr++;
            } else
                uchMaskByte <<= 1;

            if (uchMaskByte & 0x80) {
                // Copy RGB
                *dptr++ = *pptr++;
                *dptr++ = *pptr++;
                *dptr++ = *pptr++;
                *dptr++ = *pptr++;

                *dmptr++ = 0;
                *dmptr++ = 0;
                *dmptr++ = 0;
                *dmptr++ = 0;
            } else {
                // Set pixels to fully transparent
                *dptr++ = 0;
                pptr++;
                *dptr++ = 0;
                pptr++;
                *dptr++ = 0;
                pptr++;
                *dptr++ = 0;
                pptr++;

                *dmptr++ = 255;
                *dmptr++ = 255;
                *dmptr++ = 255;
                *dmptr++ = 255;
            }
        }
    }

    // There is no more need for the RGB surface
    SDL_FreeSurface(icon_rgb);

    hps = WinGetPS(_this->hidden->hwndClient);

    bmi.cbFix = sizeof(BITMAPINFOHEADER);
    bmi.cx = w;
    bmi.cy = 2 * h;
    bmi.cPlanes = 1;
    bmi.cBitCount = 32;

    SDL_memset(&bmih, 0, sizeof(BITMAPINFOHEADER));
    bmih.cbFix = sizeof(BITMAPINFOHEADER);
    bmih.cx = w;
    bmih.cy = 2 * h;
    bmih.cPlanes = 1;
    bmih.cBitCount = 32;

    hbm =
        GpiCreateBitmap(hps, (PBITMAPINFOHEADER2) & bmih, CBM_INIT,
                        (PBYTE) pchTemp, (PBITMAPINFO2) & bmi);
    hptrIcon = WinCreatePointer(HWND_DESKTOP, hbm, FALSE, 0, 0);

    WinReleasePS(hps);

    // Free pixel array
    SDL_free(pchTemp);

    // Change icon in frame window
    WinSendMsg(hwndFrame, WM_SETICON, (MPARAM) hptrIcon, NULL);

    /*
       // Change icon in switchlist
       // Seems like it's not needed, the WM_SETICON already does it.
       {
       PID pidFrame;
       HSWITCH hswitchFrame;
       SWCNTRL swctl;

       WinQueryWindowProcess(hwndFrame, &pidFrame, NULL);
       hswitchFrame = WinQuerySwitchHandle(hwndFrame, pidFrame);
       WinQuerySwitchEntry(hswitchFrame, &swctl);

       swctl.hwndIcon = hptrIcon;

       WinChangeSwitchEntry(hswitchFrame, &swctl);
       }
     */

    // Store icon handle in global variable
    hptrCurrentIcon = hptrIcon;
}

// ------------------------ REAL FUNCTIONS -----------------


static void
os2fslib_SetCursorManagementFunctions(_THIS, int iForWindowedMode)
{
    if (iForWindowedMode) {
        _this->FreeWMCursor = os2fslib_FreeWMCursor;
        _this->CreateWMCursor = os2fslib_CreateWMCursor_Win;
        _this->ShowWMCursor = os2fslib_ShowWMCursor;
        _this->WarpWMCursor = os2fslib_WarpWMCursor;
        _this->MoveWMCursor = os2fslib_MoveWMCursor;
        _this->CheckMouseMode = NULL;   //os2fslib_CheckMouseMode;
    } else {
        // We'll have software mouse cursor in FS mode!
        _this->FreeWMCursor = os2fslib_FreeWMCursor;
        _this->CreateWMCursor = os2fslib_CreateWMCursor_FS;
        _this->ShowWMCursor = os2fslib_ShowWMCursor;
        _this->WarpWMCursor = os2fslib_WarpWMCursor;
        _this->MoveWMCursor = os2fslib_MoveWMCursor;
        _this->CheckMouseMode = NULL;   //os2fslib_CheckMouseMode;
    }
}

static void
os2fslib_InitOSKeymap(_THIS)
{
    int i;

    iShiftIsPressed = 0;

    /* Map the VK and CH keysyms */
    for (i = 0; i <= 255; ++i)
        HWScanKeyMap[i] = SDLK_UNKNOWN;

    // First line of keyboard:
    HWScanKeyMap[0x1] = SDLK_ESCAPE;
    HWScanKeyMap[0x3b] = SDLK_F1;
    HWScanKeyMap[0x3c] = SDLK_F2;
    HWScanKeyMap[0x3d] = SDLK_F3;
    HWScanKeyMap[0x3e] = SDLK_F4;
    HWScanKeyMap[0x3f] = SDLK_F5;
    HWScanKeyMap[0x40] = SDLK_F6;
    HWScanKeyMap[0x41] = SDLK_F7;
    HWScanKeyMap[0x42] = SDLK_F8;
    HWScanKeyMap[0x43] = SDLK_F9;
    HWScanKeyMap[0x44] = SDLK_F10;
    HWScanKeyMap[0x57] = SDLK_F11;
    HWScanKeyMap[0x58] = SDLK_F12;
    HWScanKeyMap[0x5d] = SDLK_PRINT;
    HWScanKeyMap[0x46] = SDLK_SCROLLOCK;
    HWScanKeyMap[0x5f] = SDLK_PAUSE;

    // Second line of keyboard:
    HWScanKeyMap[0x29] = SDLK_BACKQUOTE;
    HWScanKeyMap[0x2] = SDLK_1;
    HWScanKeyMap[0x3] = SDLK_2;
    HWScanKeyMap[0x4] = SDLK_3;
    HWScanKeyMap[0x5] = SDLK_4;
    HWScanKeyMap[0x6] = SDLK_5;
    HWScanKeyMap[0x7] = SDLK_6;
    HWScanKeyMap[0x8] = SDLK_7;
    HWScanKeyMap[0x9] = SDLK_8;
    HWScanKeyMap[0xa] = SDLK_9;
    HWScanKeyMap[0xb] = SDLK_0;
    HWScanKeyMap[0xc] = SDLK_MINUS;
    HWScanKeyMap[0xd] = SDLK_EQUALS;
    HWScanKeyMap[0xe] = SDLK_BACKSPACE;
    HWScanKeyMap[0x68] = SDLK_INSERT;
    HWScanKeyMap[0x60] = SDLK_HOME;
    HWScanKeyMap[0x62] = SDLK_PAGEUP;
    HWScanKeyMap[0x45] = SDLK_NUMLOCK;
    HWScanKeyMap[0x5c] = SDLK_KP_DIVIDE;
    HWScanKeyMap[0x37] = SDLK_KP_MULTIPLY;
    HWScanKeyMap[0x4a] = SDLK_KP_MINUS;

    // Third line of keyboard:
    HWScanKeyMap[0xf] = SDLK_TAB;
    HWScanKeyMap[0x10] = SDLK_q;
    HWScanKeyMap[0x11] = SDLK_w;
    HWScanKeyMap[0x12] = SDLK_e;
    HWScanKeyMap[0x13] = SDLK_r;
    HWScanKeyMap[0x14] = SDLK_t;
    HWScanKeyMap[0x15] = SDLK_y;
    HWScanKeyMap[0x16] = SDLK_u;
    HWScanKeyMap[0x17] = SDLK_i;
    HWScanKeyMap[0x18] = SDLK_o;
    HWScanKeyMap[0x19] = SDLK_p;
    HWScanKeyMap[0x1a] = SDLK_LEFTBRACKET;
    HWScanKeyMap[0x1b] = SDLK_RIGHTBRACKET;
    HWScanKeyMap[0x1c] = SDLK_RETURN;
    HWScanKeyMap[0x69] = SDLK_DELETE;
    HWScanKeyMap[0x65] = SDLK_END;
    HWScanKeyMap[0x67] = SDLK_PAGEDOWN;
    HWScanKeyMap[0x47] = SDLK_KP7;
    HWScanKeyMap[0x48] = SDLK_KP8;
    HWScanKeyMap[0x49] = SDLK_KP9;
    HWScanKeyMap[0x4e] = SDLK_KP_PLUS;

    // Fourth line of keyboard:
    HWScanKeyMap[0x3a] = SDLK_CAPSLOCK;
    HWScanKeyMap[0x1e] = SDLK_a;
    HWScanKeyMap[0x1f] = SDLK_s;
    HWScanKeyMap[0x20] = SDLK_d;
    HWScanKeyMap[0x21] = SDLK_f;
    HWScanKeyMap[0x22] = SDLK_g;
    HWScanKeyMap[0x23] = SDLK_h;
    HWScanKeyMap[0x24] = SDLK_j;
    HWScanKeyMap[0x25] = SDLK_k;
    HWScanKeyMap[0x26] = SDLK_l;
    HWScanKeyMap[0x27] = SDLK_SEMICOLON;
    HWScanKeyMap[0x28] = SDLK_QUOTE;
    HWScanKeyMap[0x2b] = SDLK_BACKSLASH;
    HWScanKeyMap[0x4b] = SDLK_KP4;
    HWScanKeyMap[0x4c] = SDLK_KP5;
    HWScanKeyMap[0x4d] = SDLK_KP6;

    // Fifth line of keyboard:
    HWScanKeyMap[0x2a] = SDLK_LSHIFT;
    HWScanKeyMap[0x56] = SDLK_WORLD_1;  // Code 161, letter i' on hungarian keyboard
    HWScanKeyMap[0x2c] = SDLK_z;
    HWScanKeyMap[0x2d] = SDLK_x;
    HWScanKeyMap[0x2e] = SDLK_c;
    HWScanKeyMap[0x2f] = SDLK_v;
    HWScanKeyMap[0x30] = SDLK_b;
    HWScanKeyMap[0x31] = SDLK_n;
    HWScanKeyMap[0x32] = SDLK_m;
    HWScanKeyMap[0x33] = SDLK_COMMA;
    HWScanKeyMap[0x34] = SDLK_PERIOD;
    HWScanKeyMap[0x35] = SDLK_SLASH;
    HWScanKeyMap[0x36] = SDLK_RSHIFT;
    HWScanKeyMap[0x61] = SDLK_UP;
    HWScanKeyMap[0x4f] = SDLK_KP1;
    HWScanKeyMap[0x50] = SDLK_KP2;
    HWScanKeyMap[0x51] = SDLK_KP3;
    HWScanKeyMap[0x5a] = SDLK_KP_ENTER;

    // Sixth line of keyboard:
    HWScanKeyMap[0x1d] = SDLK_LCTRL;
    HWScanKeyMap[0x7e] = SDLK_LSUPER;   // Windows key
    HWScanKeyMap[0x38] = SDLK_LALT;
    HWScanKeyMap[0x39] = SDLK_SPACE;
    HWScanKeyMap[0x5e] = SDLK_RALT;     // Actually, altgr on my keyboard...
    HWScanKeyMap[0x7f] = SDLK_RSUPER;
    HWScanKeyMap[0x7c] = SDLK_MENU;
    HWScanKeyMap[0x5b] = SDLK_RCTRL;
    HWScanKeyMap[0x63] = SDLK_LEFT;
    HWScanKeyMap[0x66] = SDLK_DOWN;
    HWScanKeyMap[0x64] = SDLK_RIGHT;
    HWScanKeyMap[0x52] = SDLK_KP0;
    HWScanKeyMap[0x53] = SDLK_KP_PERIOD;
}


/* Iconify the window.
 This function returns 1 if there is a window manager and the
 window was actually iconified, it returns 0 otherwise.
 */
int
os2fslib_IconifyWindow(_THIS)
{
    HAB hab;
    HMQ hmq;
    ERRORID hmqerror;

    // If there is no more window, nothing we can do!
    if (_this->hidden->iPMThreadStatus != 1)
        return 0;

    // Cannot do anything in fullscreen mode!
    if (FSLib_QueryFSMode(_this->hidden->hwndClient))
        return 0;

    // Make sure this thread is prepared for using the Presentation Manager!
    hab = WinInitialize(0);
    hmq = WinCreateMsgQueue(hab, 0);
    // Remember if there was an error at WinCreateMsgQueue(), because we don't
    // want to destroy somebody else's queue later. :)
    hmqerror = WinGetLastError(hab);

    WinSetWindowPos(_this->hidden->hwndFrame, HWND_TOP,
                    0, 0, 0, 0, SWP_MINIMIZE);

    // Now destroy the message queue, if we've created it!
    if (ERRORIDERROR(hmqerror) == 0)
        WinDestroyMsgQueue(hmq);

    return 1;
}

static SDL_GrabMode
os2fslib_GrabInput(_THIS, SDL_GrabMode mode)
{
    HAB hab;
    HMQ hmq;
    ERRORID hmqerror;


    // If there is no more window, nothing we can do!
    if (_this->hidden->iPMThreadStatus != 1)
        return SDL_GRAB_OFF;

    // Make sure this thread is prepared for using the Presentation Manager!
    hab = WinInitialize(0);
    hmq = WinCreateMsgQueue(hab, 0);
    // Remember if there was an error at WinCreateMsgQueue(), because we don't
    // want to destroy somebody else's queue later. :)
    hmqerror = WinGetLastError(hab);


    if (mode == SDL_GRAB_OFF) {
#ifdef DEBUG_BUILD
        printf("[os2fslib_GrabInput] : Releasing mouse\n");
        fflush(stdout);
#endif

        // Release the mouse
        bMouseCapturable = 0;
        if (bMouseCaptured) {
            WinSetCapture(HWND_DESKTOP, NULLHANDLE);
            bMouseCaptured = 0;
        }
    } else {
#ifdef DEBUG_BUILD
        printf("[os2fslib_GrabInput] : Capturing mouse\n");
        fflush(stdout);
#endif

        // Capture the mouse
        bMouseCapturable = 1;
        if (WinQueryFocus(HWND_DESKTOP) == _this->hidden->hwndClient) {
            WinSetCapture(HWND_DESKTOP, _this->hidden->hwndClient);
            bMouseCaptured = 1;
            {
                SWP swpClient;
                POINTL ptl;
                // Center the mouse to the middle of the window!
                WinQueryWindowPos(_this->hidden->hwndClient, &swpClient);
                ptl.x = 0;
                ptl.y = 0;
                WinMapWindowPoints(_this->hidden->hwndClient,
                                   HWND_DESKTOP, &ptl, 1);
                _this->hidden->iSkipWMMOUSEMOVE++;      /* Don't take next WM_MOUSEMOVE into account!  */
                WinSetPointerPos(HWND_DESKTOP,
                                 ptl.x + swpClient.cx / 2,
                                 ptl.y + swpClient.cy / 2);
            }
        }
    }

    // Now destroy the message queue, if we've created it!
    if (ERRORIDERROR(hmqerror) == 0)
        WinDestroyMsgQueue(hmq);

    return mode;
}

/* Set the title and icon text */
static void
os2fslib_SetCaption(_THIS, const char *title, const char *icon)
{
    HAB hab;
    HMQ hmq;
    ERRORID hmqerror;

    // If there is no more window, nothing we can do!
    if (_this->hidden->iPMThreadStatus != 1)
        return;

    // Make sure this thread is prepared for using the Presentation Manager!
    hab = WinInitialize(0);
    hmq = WinCreateMsgQueue(hab, 0);
    // Remember if there was an error at WinCreateMsgQueue(), because we don't
    // want to destroy somebody else's queue later. :)
    hmqerror = WinGetLastError(hab);

    WinSetWindowText(_this->hidden->hwndFrame, (char *) title);

    // Now destroy the message queue, if we've created it!
    if (ERRORIDERROR(hmqerror) == 0)
        WinDestroyMsgQueue(hmq);
}

static int
os2fslib_ToggleFullScreen(_THIS, int on)
{
#ifdef DEBUG_BUILD
    printf("[os2fslib_ToggleFullScreen] : %d\n", on);
    fflush(stdout);
#endif
    // If there is no more window, nothing we can do!
    if (_this->hidden->iPMThreadStatus != 1)
        return 0;

    FSLib_ToggleFSMode(_this->hidden->hwndClient, on);
    /* Cursor manager functions to Windowed/FS mode */
    os2fslib_SetCursorManagementFunctions(_this, !on);
    return 1;
}

/* This is called after the video mode has been set, to get the
 initial mouse state.  It should queue events as necessary to
 properly represent the current mouse focus and position.
 */
static void
os2fslib_UpdateMouse(_THIS)
{
    POINTL ptl;
    HAB hab;
    HMQ hmq;
    ERRORID hmqerror;
    SWP swpClient;

    // If there is no more window, nothing we can do!
    if (_this->hidden->iPMThreadStatus != 1)
        return;


    // Make sure this thread is prepared for using the Presentation Manager!
    hab = WinInitialize(0);
    hmq = WinCreateMsgQueue(hab, 0);
    // Remember if there was an error at WinCreateMsgQueue(), because we don't
    // want to destroy somebody else's queue later. :)
    hmqerror = WinGetLastError(hab);



    if (_this->hidden->fInFocus) {
        // If our app is in focus
        SDL_PrivateAppActive(1, SDL_APPMOUSEFOCUS);
        SDL_PrivateAppActive(1, SDL_APPINPUTFOCUS);
        SDL_PrivateAppActive(1, SDL_APPACTIVE);
        WinQueryPointerPos(HWND_DESKTOP, &ptl);
        WinMapWindowPoints(HWND_DESKTOP, _this->hidden->hwndClient, &ptl, 1);
        WinQueryWindowPos(_this->hidden->hwndClient, &swpClient);
        // Convert OS/2 mouse position to SDL position, and also scale it!
        ptl.x =
            ptl.x * _this->hidden->SrcBufferDesc.uiXResolution / swpClient.cx;
        ptl.y =
            ptl.y * _this->hidden->SrcBufferDesc.uiYResolution / swpClient.cy;
        ptl.y = _this->hidden->SrcBufferDesc.uiYResolution - ptl.y - 1;
        SDL_PrivateMouseMotion(0, 0, (Sint16) (ptl.x), (Sint16) (ptl.y));
    } else {
        // If we're not in focus
        SDL_PrivateAppActive(0, SDL_APPMOUSEFOCUS);
        SDL_PrivateAppActive(0, SDL_APPINPUTFOCUS);
        SDL_PrivateAppActive(0, SDL_APPACTIVE);
        SDL_PrivateMouseMotion(0, 0, (Sint16) - 1, (Sint16) - 1);
    }

    // Now destroy the message queue, if we've created it!
    if (ERRORIDERROR(hmqerror) == 0)
        WinDestroyMsgQueue(hmq);

}

/* This pointer should exist in the native video subsystem and should
 point to an appropriate update function for the current video mode
 */
static void
os2fslib_UpdateRects(_THIS, int numrects, SDL_Rect * rects)
{
    // If there is no more window, nothing we can do!
    if (_this->hidden->iPMThreadStatus != 1)
        return;

#ifdef BITBLT_IN_WINMESSAGEPROC
    WinSendMsg(_this->hidden->hwndClient,
               WM_UPDATERECTSREQUEST, (MPARAM) numrects, (MPARAM) rects);
#else
    if (DosRequestMutexSem
        (_this->hidden->hmtxUseSrcBuffer, SEM_INDEFINITE_WAIT) == NO_ERROR) {
        int i;

        if (_this->hidden->pSDLSurface) {
#ifndef RESIZE_EVEN_IF_RESIZABLE
            SWP swp;
            // But only blit if the window is not resizable, or if
            // the window is resizable and the source buffer size is the
            // same as the destination buffer size!
            WinQueryWindowPos(_this->hidden->hwndClient, &swp);
            if ((_this->hidden->pSDLSurface) &&
                (_this->hidden->pSDLSurface->flags & SDL_RESIZABLE) &&
                ((swp.cx != _this->hidden->SrcBufferDesc.uiXResolution) ||
                 (swp.cy != _this->hidden->SrcBufferDesc.uiYResolution))
                && (!FSLib_QueryFSMode(_this->hidden->hwndClient))) {
                // Resizable surface and in resizing!
                // So, don't blit now!
#ifdef DEBUG_BUILD
                printf("[UpdateRects] : Skipping blit while resizing!\n");
                fflush(stdout);
#endif
            } else
#endif
            {
                /*
                   // Blit the whole window
                   FSLIB_BITBLT(_this->hidden->hwndClient, _this->hidden->pchSrcBuffer,
                   0, 0,
                   _this->hidden->SrcBufferDesc.uiXResolution,
                   _this->hidden->SrcBufferDesc.uiYResolution);
                 */
#ifdef DEBUG_BUILD
                printf("[os2fslib_UpdateRects] : Blitting!\n");
                fflush(stdout);
#endif

                // Blit the changed areas
                for (i = 0; i < numrects; i++)
                    FSLIB_BITBLT(_this->hidden->hwndClient,
                                 _this->hidden->pchSrcBuffer,
                                 rects[i].y, rects[i].x, rects[i].w,
                                 rects[i].h);
            }
        }
#ifdef DEBUG_BUILD
        else
            printf("[os2fslib_UpdateRects] : No public surface!\n");
        fflush(stdout);
#endif
        DosReleaseMutexSem(_this->hidden->hmtxUseSrcBuffer);
    }
#ifdef DEBUG_BUILD
    else
        printf("[os2fslib_UpdateRects] : Error in mutex!\n");
    fflush(stdout);
#endif
#endif
}


/* Reverse the effects VideoInit() -- called if VideoInit() fails
 or if the application is shutting down the video subsystem.
 */
static void
os2fslib_VideoQuit(_THIS)
{
#ifdef DEBUG_BUILD
    printf("[os2fslib_VideoQuit]\n");
    fflush(stdout);
#endif
    // Close PM stuff if running!
    if (_this->hidden->iPMThreadStatus == 1) {
        int iTimeout;
        WinPostMsg(_this->hidden->hwndFrame, WM_QUIT, (MPARAM) 0, (MPARAM) 0);
        // HACK: We had this line before:
        //DosWaitThread((TID *) &(_this->hidden->tidPMThread), DCWW_WAIT);
        // We don't use it, because the PMThread will never stop, or if it stops,
        // it will kill the whole process as a emergency fallback.
        // So, we only check for the iPMThreadStatus stuff!
#ifdef DEBUG_BUILD
        printf("[os2fslib_VideoQuit] : Waiting for PM thread to die\n");
        fflush(stdout);
#endif

        iTimeout = 0;
        while ((_this->hidden->iPMThreadStatus == 1) && (iTimeout < 100)) {
            iTimeout++;
            DosSleep(64);
        }

#ifdef DEBUG_BUILD
        printf("[os2fslib_VideoQuit] : End of wait.\n");
        fflush(stdout);
#endif

        if (_this->hidden->iPMThreadStatus == 1) {
#ifdef DEBUG_BUILD
            printf("[os2fslib_VideoQuit] : Killing PM thread!\n");
            fflush(stdout);
#endif

            _this->hidden->iPMThreadStatus = 0;
            DosKillThread(_this->hidden->tidPMThread);

            if (_this->hidden->hwndFrame) {
#ifdef DEBUG_BUILD
                printf("[os2fslib_VideoQuit] : Destroying PM window!\n");
                fflush(stdout);
#endif

                WinDestroyWindow(_this->hidden->hwndFrame);
                _this->hidden->hwndFrame = NULL;
            }
        }

    }
    // Free result of an old ListModes() call, because there is
    // no FreeListModes() call in SDL!
    if (_this->hidden->pListModesResult) {
        SDL_free(_this->hidden->pListModesResult);
        _this->hidden->pListModesResult = NULL;
    }
    // Free list of available fullscreen modes
    if (_this->hidden->pAvailableFSLibVideoModes) {
        FSLib_FreeVideoModeList(_this->hidden->pAvailableFSLibVideoModes);
        _this->hidden->pAvailableFSLibVideoModes = NULL;
    }
    // Free application icon if we had one
    if (hptrCurrentIcon) {
        WinDestroyPointer(hptrCurrentIcon);
        hptrCurrentIcon = NULL;
    }
}

/* Set the requested video mode, returning a surface which will be
 set to the SDL_VideoSurface.  The width and height will already
 be verified by ListModes(), and the video subsystem is free to
 set the mode to a supported bit depth different from the one
 specified -- the desired bpp will be emulated with a shadow
 surface if necessary.  If a new mode is returned, this function
 should take care of cleaning up the current mode.
 */
static SDL_Surface *
os2fslib_SetVideoMode(_THIS, SDL_Surface * current,
                      int width, int height, int bpp, Uint32 flags)
{
    static int bFirstCall = 1;
    FSLib_VideoMode_p pModeInfo, pModeInfoFound;
    FSLib_VideoMode TempModeInfo;
    HAB hab;
    HMQ hmq;
    ERRORID hmqerror;
    RECTL rectl;
    SDL_Surface *pResult;

    // If there is no more window, nothing we can do!
    if (_this->hidden->iPMThreadStatus != 1)
        return NULL;

#ifdef DEBUG_BUILD
    printf
        ("[os2fslib_SetVideoMode] : Request for %dx%d @ %dBPP, flags=0x%x\n",
         width, height, bpp, flags);
    fflush(stdout);
#endif

    // We don't support palette modes!
    if (bpp == 8)
        bpp = 32;

    // Also, we don't support resizable modes in fullscreen mode.
    if (flags & SDL_RESIZABLE)
        flags &= ~SDL_FULLSCREEN;

    // No double buffered mode
    if (flags & SDL_DOUBLEBUF)
        flags &= ~SDL_DOUBLEBUF;

    // And, we don't support HWSURFACE yet.
    if (flags & SDL_HWSURFACE) {
        flags &= ~SDL_HWSURFACE;
        flags |= SDL_SWSURFACE;
    }
#ifdef DEBUG_BUILD
    printf
        ("[os2fslib_SetVideoMode] : Changed request to %dx%d @ %dBPP, flags=0x%x\n",
         width, height, bpp, flags);
    fflush(stdout);
#endif

    // First check if there is such a video mode they want!
    pModeInfoFound = NULL;

    // For fullscreen mode we don't support every resolution!
    // So, go through the video modes, and check for such a resolution!
    pModeInfoFound = NULL;
    pModeInfo = _this->hidden->pAvailableFSLibVideoModes;

    while (pModeInfo) {
        // Check all available fullscreen modes for this resolution
        if ((pModeInfo->uiXResolution == width) && (pModeInfo->uiYResolution == height) && (pModeInfo->uiBPP != 8))     // palettized modes not yet supported
        {
            // If good resolution, try to find the exact BPP, or at least
            // something similar...
            if (!pModeInfoFound)
                pModeInfoFound = pModeInfo;
            else if ((pModeInfoFound->uiBPP != bpp) &&
                     (pModeInfoFound->uiBPP < pModeInfo->uiBPP))
                pModeInfoFound = pModeInfo;
        }
        pModeInfo = pModeInfo->pNext;
    }

    // If we did not find a good fullscreen mode, then try a similar
    if (!pModeInfoFound) {
#ifdef DEBUG_BUILD
        printf
            ("[os2fslib_SetVideoMode] : Requested video mode not found, looking for a similar one!\n");
        fflush(stdout);
#endif
        // Go through the video modes again, and find a similar resolution!
        pModeInfo = _this->hidden->pAvailableFSLibVideoModes;
        while (pModeInfo) {
            // Check all available fullscreen modes for this resolution
            if ((pModeInfo->uiXResolution >= width) &&
                (pModeInfo->uiYResolution >= height) &&
                (pModeInfo->uiBPP == bpp)) {
                if (!pModeInfoFound)
                    pModeInfoFound = pModeInfo;
                else if (((pModeInfoFound->uiXResolution -
                           width) * (pModeInfoFound->uiYResolution -
                                     height)) >
                         ((pModeInfo->uiXResolution -
                           width) * (pModeInfo->uiYResolution - height))) {
                    // Found a mode which is closer than the current one
                    pModeInfoFound = pModeInfo;
                }
            }
            pModeInfo = pModeInfo->pNext;
        }
    }
    // If we did not find a good fullscreen mode, then return NULL
    if (!pModeInfoFound) {
#ifdef DEBUG_BUILD
        printf("[os2fslib_SetVideoMode] : Requested video mode not found!\n");
        fflush(stdout);
#endif
        return NULL;
    }
#ifdef DEBUG_BUILD
    printf("[os2fslib_SetVideoMode] : Found mode!\n");
    fflush(stdout);
#endif

    // We'll possibly adjust the structure, so copy out the values
    // into TempModeInfo!
    SDL_memcpy(&TempModeInfo, pModeInfoFound, sizeof(TempModeInfo));
    pModeInfoFound = &TempModeInfo;

    if (flags & SDL_RESIZABLE) {
#ifdef DEBUG_BUILD
        printf
            ("[os2fslib_SetVideoMode] : Requested mode is resizable, changing width/height\n");
        fflush(stdout);
#endif
        // Change width and height to requested one!
        TempModeInfo.uiXResolution = width;
        TempModeInfo.uiYResolution = height;
        TempModeInfo.uiScanLineSize = width * ((TempModeInfo.uiBPP + 7) / 8);
    }
    // We can try create new surface!

    // Make sure this thread is prepared for using the Presentation Manager!
    hab = WinInitialize(0);
    hmq = WinCreateMsgQueue(hab, 0);
    // Remember if there was an error at WinCreateMsgQueue(), because we don't
    // want to destroy somebody else's queue later. :)
    hmqerror = WinGetLastError(hab);



    if (DosRequestMutexSem
        (_this->hidden->hmtxUseSrcBuffer, SEM_INDEFINITE_WAIT) == NO_ERROR) {
#ifdef DEBUG_BUILD
        printf("[os2fslib_SetVideoMode] : Creating new SW surface\n");
        fflush(stdout);
#endif

        // Create new software surface!
        pResult = SDL_CreateRGBSurface(SDL_SWSURFACE,
                                       pModeInfoFound->uiXResolution,
                                       pModeInfoFound->uiYResolution,
                                       pModeInfoFound->uiBPP,
                                       ((unsigned int) pModeInfoFound->
                                        PixelFormat.
                                        ucRedMask) << pModeInfoFound->
                                       PixelFormat.ucRedPosition,
                                       ((unsigned int) pModeInfoFound->
                                        PixelFormat.
                                        ucGreenMask) << pModeInfoFound->
                                       PixelFormat.ucGreenPosition,
                                       ((unsigned int) pModeInfoFound->
                                        PixelFormat.
                                        ucBlueMask) << pModeInfoFound->
                                       PixelFormat.ucBluePosition,
                                       ((unsigned int) pModeInfoFound->
                                        PixelFormat.
                                        ucAlphaMask) << pModeInfoFound->
                                       PixelFormat.ucAlphaPosition);

        if (pResult == NULL) {
            DosReleaseMutexSem(_this->hidden->hmtxUseSrcBuffer);
            SDL_OutOfMemory();
            return NULL;
        }
#ifdef DEBUG_BUILD
        printf("[os2fslib_SetVideoMode] : Adjusting pixel format\n");
        fflush(stdout);
#endif

        // Adjust pixel format mask!
        pResult->format->Rmask =
            ((unsigned int) pModeInfoFound->PixelFormat.
             ucRedMask) << pModeInfoFound->PixelFormat.ucRedPosition;
        pResult->format->Rshift = pModeInfoFound->PixelFormat.ucRedPosition;
        pResult->format->Rloss = pModeInfoFound->PixelFormat.ucRedAdjust;
        pResult->format->Gmask =
            ((unsigned int) pModeInfoFound->PixelFormat.
             ucGreenMask) << pModeInfoFound->PixelFormat.ucGreenPosition;
        pResult->format->Gshift = pModeInfoFound->PixelFormat.ucGreenPosition;
        pResult->format->Gloss = pModeInfoFound->PixelFormat.ucGreenAdjust;
        pResult->format->Bmask =
            ((unsigned int) pModeInfoFound->PixelFormat.
             ucBlueMask) << pModeInfoFound->PixelFormat.ucBluePosition;
        pResult->format->Bshift = pModeInfoFound->PixelFormat.ucBluePosition;
        pResult->format->Bloss = pModeInfoFound->PixelFormat.ucBlueAdjust;
        pResult->format->Amask =
            ((unsigned int) pModeInfoFound->PixelFormat.
             ucAlphaMask) << pModeInfoFound->PixelFormat.ucAlphaPosition;
        pResult->format->Ashift = pModeInfoFound->PixelFormat.ucAlphaPosition;
        pResult->format->Aloss = pModeInfoFound->PixelFormat.ucAlphaAdjust;

#ifdef REPORT_EMPTY_ALPHA_MASK
        pResult->format->Amask =
            pResult->format->Ashift = pResult->format->Aloss = 0;
#endif

        // Adjust surface flags
        pResult->flags |= (flags & SDL_FULLSCREEN);
        pResult->flags |= (flags & SDL_RESIZABLE);

        // It might be that the software surface pitch is not the same as
        // the pitch we have, so adjust that!
        pModeInfoFound->uiScanLineSize = pResult->pitch;

        // Store new source buffer parameters!
        SDL_memcpy(&(_this->hidden->SrcBufferDesc), pModeInfoFound,
                   sizeof(*pModeInfoFound));
        _this->hidden->pchSrcBuffer = pResult->pixels;

#ifdef DEBUG_BUILD
        printf("[os2fslib_SetVideoMode] : Telling FSLib the stuffs\n");
        fflush(stdout);
#endif

        // Tell the FSLib window the new source image format
        FSLib_SetSrcBufferDesc(_this->hidden->hwndClient,
                               &(_this->hidden->SrcBufferDesc));

        if (((flags & SDL_RESIZABLE) == 0) || (bFirstCall)) {
            bFirstCall = 0;
#ifdef DEBUG_BUILD
            printf("[os2fslib_SetVideoMode] : Modifying window size\n");
            fflush(stdout);
#endif

            // Calculate frame window size from client window size
            rectl.xLeft = 0;
            rectl.yBottom = 0;
            rectl.xRight = pModeInfoFound->uiXResolution;       // Noninclusive
            rectl.yTop = pModeInfoFound->uiYResolution; // Noninclusive
            WinCalcFrameRect(_this->hidden->hwndFrame, &rectl, FALSE);

            // Set the new size of the main window
            SetAccessableWindowPos(_this->hidden->hwndFrame,
                                   HWND_TOP,
                                   0, 0,
                                   (rectl.xRight - rectl.xLeft),
                                   (rectl.yTop - rectl.yBottom),
                                   SWP_SIZE | SWP_ACTIVATE | SWP_SHOW);
        }
        // Set fullscreen mode flag, and switch to fullscreen if needed!
        if (flags & SDL_FULLSCREEN) {
#ifdef DEBUG_BUILD
            printf
                ("[os2fslib_SetVideoMode] : Also trying to switch to fullscreen\n");
            fflush(stdout);
#endif
            FSLib_ToggleFSMode(_this->hidden->hwndClient, 1);
            /* Cursor manager functions to FS mode */
            os2fslib_SetCursorManagementFunctions(_this, 0);
        } else {
#ifdef DEBUG_BUILD
            printf
                ("[os2fslib_SetVideoMode] : Also trying to switch to desktop mode\n");
            fflush(stdout);
#endif
            FSLib_ToggleFSMode(_this->hidden->hwndClient, 0);
            /* Cursor manager functions to Windowed mode */
            os2fslib_SetCursorManagementFunctions(_this, 1);
        }

        _this->hidden->pSDLSurface = pResult;

        DosReleaseMutexSem(_this->hidden->hmtxUseSrcBuffer);
    } else {
#ifdef DEBUG_BUILD
        printf("[os2fslib_SetVideoMode] : Could not get hmtxUseSrcBuffer!\n");
        fflush(stdout);
#endif

        pResult = NULL;
    }

    // As we have the new surface, we don't need the current one anymore!
    if ((pResult) && (current)) {
#ifdef DEBUG_BUILD
        printf("[os2fslib_SetVideoMode] : Freeing old surface\n");
        fflush(stdout);
#endif
        SDL_FreeSurface(current);
    }
    // Redraw window
    WinInvalidateRegion(_this->hidden->hwndClient, NULL, TRUE);

    // Now destroy the message queue, if we've created it!
    if (ERRORIDERROR(hmqerror) == 0) {
#ifdef DEBUG_BUILD
        printf("[os2fslib_SetVideoMode] : Destroying message queue\n");
        fflush(stdout);
#endif
        WinDestroyMsgQueue(hmq);
    }
#ifdef DEBUG_BUILD
    printf("[os2fslib_SetVideoMode] : Done\n");
    fflush(stdout);
#endif

    /* We're done */

    // Return with the new surface!
    return pResult;
}

/* List the available video modes for the given pixel format, sorted
 from largest to smallest.
 */
static SDL_Rect **
os2fslib_ListModes(_THIS, SDL_PixelFormat * format, Uint32 flags)
{
#ifdef DEBUG_BUILD
    printf("[os2fslib_ListModes] : ListModes of %d Bpp\n",
           format->BitsPerPixel);
#endif
    // Destroy result of previous call, if there is any
    if (_this->hidden->pListModesResult) {
        SDL_free(_this->hidden->pListModesResult);
        _this->hidden->pListModesResult = NULL;
    }
    // For resizable and windowed mode we support every resolution!
    if ((flags & SDL_RESIZABLE) && ((flags & SDL_FULLSCREEN) == 0))
        return (SDL_Rect **) - 1;

    // Check if they need fullscreen or non-fullscreen video modes!
    if ((flags & SDL_FULLSCREEN) == 0) {
        // For windowed mode we support every resolution!
        return (SDL_Rect **) - 1;
    } else {
        FSLib_VideoMode_p pFSMode;
        // For fullscreen mode we don't support every resolution!
        // Now create a new list
        pFSMode = _this->hidden->pAvailableFSLibVideoModes;
        while (pFSMode) {
            if (pFSMode->uiBPP == format->BitsPerPixel) {
                SDL_Rect *pRect = (SDL_Rect *) SDL_malloc(sizeof(SDL_Rect));
                if (pRect) {
                    // Fill description
                    pRect->x = 0;
                    pRect->y = 0;
                    pRect->w = pFSMode->uiXResolution;
                    pRect->h = pFSMode->uiYResolution;
#ifdef DEBUG_BUILD
//          printf("!!! Seems to be good!\n");
//        printf("F: %dx%d\n", pRect->w, pRect->h);
#endif
                    // And insert into list of pRects
                    if (!(_this->hidden->pListModesResult)) {
#ifdef DEBUG_BUILD
//            printf("!!! Inserting to beginning\n");
#endif

                        // We're the first one to be inserted!
                        _this->hidden->pListModesResult =
                            (SDL_Rect **) SDL_malloc(2 * sizeof(SDL_Rect *));
                        if (_this->hidden->pListModesResult) {
                            _this->hidden->pListModesResult[0] = pRect;
                            _this->hidden->pListModesResult[1] = NULL;
                        } else {
                            SDL_free(pRect);
                        }
                    } else {
                        // We're not the first ones, so find the place where we
                        // have to insert ourselves
                        SDL_Rect **pNewList;
                        int iPlace, iNumOfSlots, i;

#ifdef DEBUG_BUILD
//            printf("!!! Searching where to insert\n");
#endif

                        iPlace = -1;
                        iNumOfSlots = 1;        // Count the last NULL too!
                        for (i = 0; _this->hidden->pListModesResult[i]; i++) {
                            iNumOfSlots++;
                            if (iPlace == -1) {
                                if ((_this->hidden->
                                     pListModesResult[i]->w *
                                     _this->hidden->
                                     pListModesResult[i]->h) <
                                    (pRect->w * pRect->h)) {
                                    iPlace = i;
                                }
                            }
                        }
                        if (iPlace == -1)
                            iPlace = iNumOfSlots - 1;

#ifdef DEBUG_BUILD
//            printf("!!! From %d slots, it will be at %d\n", iNumOfSlots, iPlace);
#endif

                        pNewList =
                            (SDL_Rect **) SDL_realloc(_this->
                                                      hidden->
                                                      pListModesResult,
                                                      (iNumOfSlots
                                                       +
                                                       1) *
                                                      sizeof(SDL_Rect *));
                        if (pNewList) {
                            for (i = iNumOfSlots; i > iPlace; i--)
                                pNewList[i] = pNewList[i - 1];
                            pNewList[iPlace] = pRect;
                            _this->hidden->pListModesResult = pNewList;
                        } else {
                            SDL_free(pRect);
                        }
                    }
                }
            }
            pFSMode = pFSMode->pNext;
        }
    }
#ifdef DEBUG_BUILD
//  printf("Returning list\n");
#endif
    return _this->hidden->pListModesResult;
}

/* Initialize the native video subsystem, filling 'vformat' with the
 "best" display pixel format, returning 0 or -1 if there's an error.
 */
static int
os2fslib_VideoInit(_THIS, SDL_PixelFormat * vformat)
{
    FSLib_VideoMode_p pDesktopMode;

#ifdef DEBUG_BUILD
    printf("[os2fslib_VideoInit] : Enter\n");
    fflush(stdout);
#endif

    // Report the best pixel format. For this,
    // we'll use the current desktop format.
    pDesktopMode = FSLib_GetDesktopVideoMode();
    if (!pDesktopMode) {
        SDL_SetError("Could not query desktop video mode!");
#ifdef DEBUG_BUILD
        printf
            ("[os2fslib_VideoInit] : Could not query desktop video mode!\n");
#endif
        return -1;
    }

    /* Determine the current screen size */
    _this->info.current_w = pDesktopMode->uiXResolution;
    _this->info.current_h = pDesktopMode->uiYResolution;

    /* Determine the screen depth */
    vformat->BitsPerPixel = pDesktopMode->uiBPP;
    vformat->BytesPerPixel = (vformat->BitsPerPixel + 7) / 8;

    vformat->Rmask =
        ((unsigned int) pDesktopMode->PixelFormat.ucRedMask) << pDesktopMode->
        PixelFormat.ucRedPosition;
    vformat->Rshift = pDesktopMode->PixelFormat.ucRedPosition;
    vformat->Rloss = pDesktopMode->PixelFormat.ucRedAdjust;
    vformat->Gmask =
        ((unsigned int) pDesktopMode->PixelFormat.
         ucGreenMask) << pDesktopMode->PixelFormat.ucGreenPosition;
    vformat->Gshift = pDesktopMode->PixelFormat.ucGreenPosition;
    vformat->Gloss = pDesktopMode->PixelFormat.ucGreenAdjust;
    vformat->Bmask =
        ((unsigned int) pDesktopMode->PixelFormat.
         ucBlueMask) << pDesktopMode->PixelFormat.ucBluePosition;
    vformat->Bshift = pDesktopMode->PixelFormat.ucBluePosition;
    vformat->Bloss = pDesktopMode->PixelFormat.ucBlueAdjust;
    vformat->Amask =
        ((unsigned int) pDesktopMode->PixelFormat.
         ucAlphaMask) << pDesktopMode->PixelFormat.ucAlphaPosition;
    vformat->Ashift = pDesktopMode->PixelFormat.ucAlphaPosition;
    vformat->Aloss = pDesktopMode->PixelFormat.ucAlphaAdjust;

#ifdef REPORT_EMPTY_ALPHA_MASK
    vformat->Amask = vformat->Ashift = vformat->Aloss = 0;
#endif

    // Fill in some window manager capabilities
    _this->info.wm_available = 1;

    // Initialize some internal variables
    _this->hidden->pListModesResult = NULL;
    _this->hidden->fInFocus = 0;
    _this->hidden->iSkipWMMOUSEMOVE = 0;
    _this->hidden->iMouseVisible = 1;

    if (getenv("SDL_USE_PROPORTIONAL_WINDOW"))
        _this->hidden->bProportionalResize = 1;
    else {
        PPIB pib;
        PTIB tib;
        char *pchFileName, *pchTemp;
        char achConfigFile[CCHMAXPATH];
        FILE *hFile;

        /* No environment variable to have proportional window.
         * Ok, let's check if this executable is in config file!
         */
        _this->hidden->bProportionalResize = 0;

        DosGetInfoBlocks(&tib, &pib);
        pchTemp = pchFileName = pib->pib_pchcmd;
        while (*pchTemp) {
            if (*pchTemp == '\\')
                pchFileName = pchTemp + 1;
            pchTemp++;
        }
        if (getenv("HOME")) {
            sprintf(achConfigFile, "%s\\.sdl.proportionals", getenv("HOME"));
            hFile = fopen(achConfigFile, "rt");
            if (!hFile) {
                /* Seems like the file cannot be opened or does not exist.
                 * Let's try to create it with defaults!
                 */
                hFile = fopen(achConfigFile, "wt");
                if (hFile) {
                    fprintf(hFile,
                            "; This file is a config file of SDL/2, containing\n");
                    fprintf(hFile,
                            "; the list of executables that must have proportional\n");
                    fprintf(hFile, "; windows.\n");
                    fprintf(hFile, ";\n");
                    fprintf(hFile,
                            "; You can add executable filenames into this file,\n");
                    fprintf(hFile,
                            "; one under the other. If SDL finds that a given\n");
                    fprintf(hFile,
                            "; program is in this list, then that application\n");
                    fprintf(hFile,
                            "; will have proportional windows, just like if\n");
                    fprintf(hFile,
                            "; the SET SDL_USE_PROPORTIONAL_WINDOW env. variable\n");
                    fprintf(hFile,
                            "; would have been set for that process.\n");
                    fprintf(hFile, ";\n");
                    fprintf(hFile, "\n");
                    fprintf(hFile, "dosbox.exe\n");
                    fclose(hFile);
                }

                hFile = fopen(achConfigFile, "rt");
            }

            if (hFile) {
                while (fgets(achConfigFile, sizeof(achConfigFile), hFile)) {
                    /* Cut \n from end of string */

                    while (achConfigFile[strlen(achConfigFile) - 1]
                           == '\n')
                        achConfigFile[strlen(achConfigFile) - 1] = 0;

                    /* Compare... */
                    if (stricmp(achConfigFile, pchFileName) == 0) {
                        /* Found it in config file! */
                        _this->hidden->bProportionalResize = 1;
                        break;
                    }
                }
                fclose(hFile);
            }
        }
    }

    DosCreateMutexSem(NULL, &(_this->hidden->hmtxUseSrcBuffer), 0, FALSE);

    // Now create our window with a default size

    // For this, we select the first available fullscreen mode as
    // current window size!
    SDL_memcpy(&(_this->hidden->SrcBufferDesc),
               _this->hidden->pAvailableFSLibVideoModes,
               sizeof(_this->hidden->SrcBufferDesc));
    // Allocate new video buffer!
    _this->hidden->pchSrcBuffer =
        (char *) SDL_malloc(_this->hidden->pAvailableFSLibVideoModes->
                            uiScanLineSize *
                            _this->hidden->pAvailableFSLibVideoModes->
                            uiYResolution);
    if (!_this->hidden->pchSrcBuffer) {
#ifdef DEBUG_BUILD
        printf
            ("[os2fslib_VideoInit] : Yikes, not enough memory for new video buffer!\n");
        fflush(stdout);
#endif
        SDL_SetError("Not enough memory for new video buffer!\n");
        return -1;
    }
    // For this, we need a message processing thread.
    // We'll create a new thread for this, which will do everything
    // what is related to PM
    _this->hidden->iPMThreadStatus = 0;
    _this->hidden->tidPMThread =
        _beginthread(PMThreadFunc, NULL, 65536, (void *) _this);
    if (_this->hidden->tidPMThread <= 0) {
#ifdef DEBUG_BUILD
        printf("[os2fslib_VideoInit] : Could not create PM thread!\n");
#endif
        SDL_SetError("Could not create PM thread");
        return -1;
    }
#ifdef USE_DOSSETPRIORITY
    // Burst the priority of PM Thread!
    DosSetPriority(PRTYS_THREAD, PRTYC_TIMECRITICAL, 0,
                   _this->hidden->tidPMThread);
#endif
    // Wait for the PM thread to initialize!
    while (_this->hidden->iPMThreadStatus == 0)
        DosSleep(32);
    // If the PM thread could not set up everything, then
    // report an error!
    if (_this->hidden->iPMThreadStatus != 1) {
#ifdef DEBUG_BUILD
        printf("[os2fslib_VideoInit] : PMThread reported an error : %d\n",
               _this->hidden->iPMThreadStatus);
#endif
        SDL_SetError("Error initializing PM thread");
        return -1;
    }

    return 0;
}


static void
os2fslib_DeleteDevice(_THIS)
{
#ifdef DEBUG_BUILD
    printf("[os2fslib_DeleteDevice]\n");
    fflush(stdout);
#endif
    // Free used memory
    FSLib_FreeVideoModeList(_this->hidden->pAvailableFSLibVideoModes);
    if (_this->hidden->pListModesResult)
        SDL_free(_this->hidden->pListModesResult);
    if (_this->hidden->pchSrcBuffer)
        SDL_free(_this->hidden->pchSrcBuffer);
    DosCloseMutexSem(_this->hidden->hmtxUseSrcBuffer);
    SDL_free(_this->hidden);
    SDL_free(_this);
    FSLib_Uninitialize();
}

static int
os2fslib_Available(void)
{

    // If we can run, it means that we could load FSLib,
    // so we assume that it's available then!
    return 1;
}

static void
os2fslib_MorphToPM()
{
    PPIB pib;
    PTIB tib;

    DosGetInfoBlocks(&tib, &pib);

    // Change flag from VIO to PM:
    if (pib->pib_ultype == 2)
        pib->pib_ultype = 3;
}

static SDL_VideoDevice *
os2fslib_CreateDevice(int devindex)
{
    SDL_VideoDevice *device;

#ifdef DEBUG_BUILD
    printf("[os2fslib_CreateDevice] : Enter\n");
    fflush(stdout);
#endif

    /* Initialize all variables that we clean on shutdown */
    device = (SDL_VideoDevice *) SDL_malloc(sizeof(SDL_VideoDevice));
    if (device) {
        SDL_memset(device, 0, (sizeof *device));
        // Also allocate memory for private data
        device->hidden = (struct SDL_PrivateVideoData *)
            SDL_malloc((sizeof(struct SDL_PrivateVideoData)));
    }
    if ((device == NULL) || (device->hidden == NULL)) {
        SDL_OutOfMemory();
        if (device)
            SDL_free(device);
        return NULL;
    }
    SDL_memset(device->hidden, 0, (sizeof *device->hidden));

    /* Set the function pointers */
#ifdef DEBUG_BUILD
    printf("[os2fslib_CreateDevice] : VideoInit is %p\n", os2fslib_VideoInit);
    fflush(stdout);
#endif

    /* Initialization/Query functions */
    device->VideoInit = os2fslib_VideoInit;
    device->ListModes = os2fslib_ListModes;
    device->SetVideoMode = os2fslib_SetVideoMode;
    device->ToggleFullScreen = os2fslib_ToggleFullScreen;
    device->UpdateMouse = os2fslib_UpdateMouse;
    device->CreateYUVOverlay = NULL;
    device->SetColors = os2fslib_SetColors;
    device->UpdateRects = os2fslib_UpdateRects;
    device->VideoQuit = os2fslib_VideoQuit;
    /* Hardware acceleration functions */
    device->AllocHWSurface = os2fslib_AllocHWSurface;
    device->CheckHWBlit = NULL;
    device->FillHWRect = NULL;
    device->SetHWColorKey = NULL;
    device->SetHWAlpha = NULL;
    device->LockHWSurface = os2fslib_LockHWSurface;
    device->UnlockHWSurface = os2fslib_UnlockHWSurface;
    device->FlipHWSurface = NULL;
    device->FreeHWSurface = os2fslib_FreeHWSurface;
    /* Window manager functions */
    device->SetCaption = os2fslib_SetCaption;
    device->SetIcon = os2fslib_SetIcon;
    device->IconifyWindow = os2fslib_IconifyWindow;
    device->GrabInput = os2fslib_GrabInput;
    device->GetWMInfo = NULL;
    /* Cursor manager functions to Windowed mode */
    os2fslib_SetCursorManagementFunctions(device, 1);
    /* Event manager functions */
    device->InitOSKeymap = os2fslib_InitOSKeymap;
    device->PumpEvents = os2fslib_PumpEvents;
    /* The function used to dispose of this structure */
    device->free = os2fslib_DeleteDevice;

    // Make sure we'll be able to use Win* API even if the application
    // was linked to be a VIO application!
    os2fslib_MorphToPM();

    // Now initialize FSLib, and query available video modes!
    if (!FSLib_Initialize()) {
        // Could not initialize FSLib!
#ifdef DEBUG_BUILD
        printf("[os2fslib_CreateDevice] : Could not initialize FSLib!\n");
#endif
        SDL_SetError("Could not initialize FSLib!");
        SDL_free(device->hidden);
        SDL_free(device);
        return NULL;
    }
    device->hidden->pAvailableFSLibVideoModes = FSLib_GetVideoModeList();

    return device;
}

VideoBootStrap OS2FSLib_bootstrap = {
    "os2fslib", "OS/2 Video Output using FSLib",
    os2fslib_Available, os2fslib_CreateDevice
};

/* vi: set ts=4 sw=4 expandtab: */