os2: add port files for SDL2-2.0.4 from Andrey Vasilkin

only geniconv/iconv.h (was from LGPL libiconv) is replaced with a generic
minimal iconv.h based on public knowledge.
diff --git a/src/audio/os2/SDL_os2audio.c b/src/audio/os2/SDL_os2audio.c
new file mode 100644
index 0000000..786deb1
--- /dev/null
+++ b/src/audio/os2/SDL_os2audio.c
@@ -0,0 +1,472 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "../../SDL_internal.h"
+
+#if SDL_AUDIO_DRIVER_OS2
+
+/* Allow access to a raw mixing buffer */
+
+#include "../../core/os2/SDL_os2.h"
+
+#include "SDL_timer.h"
+#include "SDL_audio.h"
+#include "../SDL_audio_c.h"
+#include "SDL_os2audio.h"
+
+/*
+void lockIncr(volatile int *piVal);
+#pragma aux lockIncr = \
+" lock add [eax], 1 "\
+parm [eax]; 
+
+void lockDecr(volatile int *piVal);
+#pragma aux lockDecr = \
+" lock sub [eax], 1 "\
+parm [eax];
+*/
+
+
+static ULONG _getEnvULong(PSZ pszName, ULONG ulMax, ULONG ulDefault)
+{
+  ULONG      ulValue;
+  PCHAR      pcEnd;
+  PSZ        pszEnvVal = SDL_getenv( pszName );
+
+  if ( pszEnvVal == NULL )
+    return ulDefault;
+
+  ulValue = SDL_strtoul( (const char *)pszEnvVal, &pcEnd, 10 );
+  return ( pcEnd == pszEnvVal ) || ( ulValue > ulMax ) ? ulDefault : ulMax;
+}
+
+static int _MCIError(PSZ pszFunc, ULONG ulResult)
+{
+  CHAR			acBuf[128];
+
+  mciGetErrorString( ulResult, (PCHAR)&acBuf, sizeof(acBuf) );
+  return SDL_SetError( "[%s] %s", pszFunc, &acBuf );
+}
+
+static void _mixIOError(PSZ pszFunction, ULONG ulRC)
+{
+  debug( "%s() - failed, rc = 0x%X (%s)",
+         pszFunction, ulRC,
+         ulRC == MCIERR_INVALID_MODE ? "Mixer mode does not match request"
+         : ulRC == MCIERR_INVALID_BUFFER ? "Caller sent an invalid buffer"
+         : "unknown" );
+}
+
+LONG APIENTRY cbAudioWriteEvent(ULONG ulStatus, PMCI_MIX_BUFFER pBuffer,
+                                ULONG ulFlags)
+{
+  PSDL_PrivateAudioData pAData = (PSDL_PrivateAudioData)pBuffer->ulUserParm;
+  ULONG                 ulRC;
+
+  if ( ulFlags != MIX_WRITE_COMPLETE )
+  {
+    debug( "flags = 0x%X", ulFlags );
+    return 0;
+  }
+
+//  lockDecr( (int *)&pAData->ulQueuedBuf );
+  ulRC = DosPostEventSem( pAData->hevBuf );
+  if ( ( ulRC != NO_ERROR ) && ( ulRC != ERROR_ALREADY_POSTED ) )
+    debug( "DosPostEventSem(), rc = %u", ulRC );
+
+  return 1; // It seems, return value is not matter.
+}
+
+LONG APIENTRY cbAudioReadEvent(ULONG ulStatus, PMCI_MIX_BUFFER pBuffer,
+                               ULONG ulFlags)
+{
+  PSDL_PrivateAudioData pAData = (PSDL_PrivateAudioData)pBuffer->ulUserParm;
+  ULONG                 ulRC;
+
+  if ( ulFlags != MIX_READ_COMPLETE )
+  {
+    debug( "flags = 0x%X", ulFlags );
+    return 0;
+  }
+
+  pAData->stMCIMixSetup.pmixRead( pAData->stMCIMixSetup.ulMixHandle, pBuffer,
+                                  1 );
+
+  ulRC = DosPostEventSem( pAData->hevBuf );
+  if ( ( ulRC != NO_ERROR ) && ( ulRC != ERROR_ALREADY_POSTED ) )
+    debug( "DosPostEventSem(), rc = %u", ulRC );
+
+  return 1;
+}
+
+
+static void OS2_DetectDevices(void)
+{
+  MCI_SYSINFO_PARMS      stMCISysInfo;
+  CHAR                   acBuf[256];
+  ULONG                  ulDevicesNum;
+  MCI_SYSINFO_LOGDEVICE  stLogDevice;
+  MCI_SYSINFO_PARMS      stSysInfoParams;
+  ULONG                  ulRC;
+  ULONG                  ulHandle = 0;
+
+  acBuf[0] = '\0';
+  stMCISysInfo.pszReturn    = &acBuf;
+  stMCISysInfo.ulRetSize    = sizeof(acBuf);
+  stMCISysInfo.usDeviceType = MCI_DEVTYPE_AUDIO_AMPMIX;
+  ulRC = mciSendCommand( 0, MCI_SYSINFO, MCI_WAIT | MCI_SYSINFO_QUANTITY,
+                         &stMCISysInfo, 0 );
+  if ( ulRC != NO_ERROR )
+  {
+    debug( "MCI_SYSINFO, MCI_SYSINFO_QUANTITY - failed, rc = 0x%X", ulRC );
+    return;
+  }
+
+  ulDevicesNum = atol( stMCISysInfo.pszReturn );
+
+  for( stSysInfoParams.ulNumber = 0; stSysInfoParams.ulNumber < ulDevicesNum;
+       stSysInfoParams.ulNumber++ )
+  {
+    // Get device install name.
+    stSysInfoParams.pszReturn    = &acBuf;
+    stSysInfoParams.ulRetSize    = sizeof( acBuf );
+    stSysInfoParams.usDeviceType = MCI_DEVTYPE_AUDIO_AMPMIX;
+    ulRC = mciSendCommand( 0, MCI_SYSINFO, MCI_WAIT | MCI_SYSINFO_INSTALLNAME,
+                           &stSysInfoParams, 0 );
+    if ( ulRC != NO_ERROR )
+    {
+      debug( "MCI_SYSINFO, MCI_SYSINFO_INSTALLNAME - failed, rc = 0x%X", ulRC );
+      continue;
+    }
+
+    // Get textual product description.
+    stSysInfoParams.ulItem = MCI_SYSINFO_QUERY_DRIVER;
+    stSysInfoParams.pSysInfoParm = &stLogDevice;
+    strcpy( &stLogDevice.szInstallName, stSysInfoParams.pszReturn );
+    ulRC = mciSendCommand( 0, MCI_SYSINFO, MCI_WAIT | MCI_SYSINFO_ITEM,
+                           &stSysInfoParams, 0 );
+    if ( ulRC != NO_ERROR )
+    {
+      debug( "MCI_SYSINFO, MCI_SYSINFO_ITEM - failed, rc = 0x%X", ulRC );
+      continue;
+    }
+
+    ulHandle++;
+    SDL_AddAudioDevice( 0, &stLogDevice.szProductInfo, (void *)(ulHandle) );
+    ulHandle++;
+    SDL_AddAudioDevice( 1, &stLogDevice.szProductInfo, (void *)(ulHandle) );
+  }
+}
+
+static void OS2_WaitDevice(_THIS)
+{
+  PSDL_PrivateAudioData pAData = (PSDL_PrivateAudioData)this->hidden;
+  ULONG                 ulRC;
+
+  /* Wait for an audio chunk to finish */
+  ulRC = DosWaitEventSem( pAData->hevBuf, 5000 );
+  if ( ulRC != NO_ERROR )
+    debug( "DosWaitEventSem(), rc = %u", ulRC );
+}
+
+static Uint8 *OS2_GetDeviceBuf(_THIS)
+{
+  PSDL_PrivateAudioData pAData = (PSDL_PrivateAudioData)this->hidden;
+
+  return pAData->aMixBuffers[pAData->ulNextBuf].pBuffer;
+}
+
+static void OS2_PlayDevice(_THIS)
+{
+  PSDL_PrivateAudioData pAData = (PSDL_PrivateAudioData)this->hidden;
+  ULONG                 ulRC;
+  PMCI_MIX_BUFFER       pMixBuffer = &pAData->aMixBuffers[pAData->ulNextBuf];
+
+  /* Queue it up */
+//  lockIncr( (int *)&pAData->ulQueuedBuf );
+  ulRC = pAData->stMCIMixSetup.pmixWrite( pAData->stMCIMixSetup.ulMixHandle,
+                                          pMixBuffer, 1 );
+  if ( ulRC != MCIERR_SUCCESS )
+    _mixIOError( "pmixWrite", ulRC );
+  else
+    pAData->ulNextBuf = (pAData->ulNextBuf + 1) % pAData->cMixBuffers;
+}
+
+static void OS2_WaitDone(_THIS)
+{
+  PSDL_PrivateAudioData pAData = (PSDL_PrivateAudioData)this->hidden;
+
+  DosWaitEventSem( pAData->hevBuf, 3000 );
+}
+
+static void OS2_CloseDevice(_THIS)
+{
+  PSDL_PrivateAudioData pAData = (PSDL_PrivateAudioData)this->hidden;
+  MCI_GENERIC_PARMS     sMCIGenericParms;
+  ULONG                 ulRC;
+
+  if ( pAData == NULL )
+    return;
+
+  /* Close up audio */
+
+  if ( pAData->usDeviceId != (USHORT)~0 )
+  {
+    // Device is open.
+
+    if ( pAData->stMCIMixSetup.ulBitsPerSample != 0 )
+    {
+      // Mixer was initialized.
+      ulRC = mciSendCommand( pAData->usDeviceId, MCI_MIXSETUP,
+                             MCI_WAIT | MCI_MIXSETUP_DEINIT,
+                             &pAData->stMCIMixSetup, 0 );
+      if ( ulRC != MCIERR_SUCCESS )
+        debug( "MCI_MIXSETUP, MCI_MIXSETUP_DEINIT - failed" );
+    }
+
+    if ( pAData->cMixBuffers != 0 )
+    {
+      // Buffers was allocated.
+      MCI_BUFFER_PARMS             stMCIBuffer;
+
+      stMCIBuffer.ulBufferSize = pAData->aMixBuffers[0].ulBufferLength;
+      stMCIBuffer.ulNumBuffers = pAData->cMixBuffers;
+      stMCIBuffer.pBufList = &pAData->aMixBuffers;
+
+      ulRC = mciSendCommand( pAData->usDeviceId, MCI_BUFFER,
+                             MCI_WAIT | MCI_DEALLOCATE_MEMORY, &stMCIBuffer, 0 );
+      if ( ulRC != MCIERR_SUCCESS )
+        debug( "MCI_BUFFER, MCI_DEALLOCATE_MEMORY - failed" );
+    }
+
+    ulRC = mciSendCommand( pAData->usDeviceId, MCI_CLOSE, MCI_WAIT,
+                           &sMCIGenericParms, 0 );
+    if ( ulRC != MCIERR_SUCCESS )
+      debug( "MCI_CLOSE - failed" );
+  }
+
+  if ( pAData->hevBuf != NULLHANDLE )
+    DosCloseEventSem( pAData->hevBuf );
+
+  SDL_free( pAData );
+  this->hidden = NULL;
+}
+
+static int OS2_OpenDevice(_THIS, void *handle, const char *devname,
+                          int iscapture)
+{
+  PSDL_PrivateAudioData pAData;
+  SDL_AudioFormat       SDLAudioFmt;
+  MCI_AMP_OPEN_PARMS    stMCIAmpOpen = { 0 };
+  MCI_BUFFER_PARMS      stMCIBuffer = { 0 };
+  ULONG                 ulRC;
+  ULONG                 ulIdx;
+
+  for( SDLAudioFmt = SDL_FirstAudioFormat( this->spec.format );
+       SDLAudioFmt != 0; SDLAudioFmt = SDL_NextAudioFormat() )
+  {
+    if ( ( SDLAudioFmt == AUDIO_U8 ) || ( SDLAudioFmt == AUDIO_S16 ) )
+      break;
+  }
+
+  if ( SDLAudioFmt == 0 )
+  {
+    debug( "Unsupported audio format, AUDIO_S16 used" );
+    SDLAudioFmt = AUDIO_S16;
+    this->spec.freq = AUDIO_S16;
+  }
+
+  pAData = SDL_calloc( 1, sizeof(SDL_PrivateAudioData) );
+  if ( pAData == NULL )
+    return SDL_OutOfMemory();
+  this->hidden = pAData;
+
+  ulRC = DosCreateEventSem( NULL, &pAData->hevBuf, DCE_AUTORESET, TRUE );
+  if ( ulRC != NO_ERROR )
+  {
+    debug( "DosCreateEventSem() failed, rc = %u", ulRC );
+    return -1;
+  }
+
+  // Open audio device
+  stMCIAmpOpen.usDeviceID = handle != NULL ? ( ((ULONG)handle) - 1 ) : 0;
+  stMCIAmpOpen.pszDeviceType = (PSZ)MCI_DEVTYPE_AUDIO_AMPMIX;
+  ulRC = mciSendCommand( 0, MCI_OPEN,
+           _getEnvULong( "SDL_AUDIO_SHARE", 1, 0 ) != 0
+             ? MCI_WAIT | MCI_OPEN_TYPE_ID | MCI_OPEN_SHAREABLE
+             : MCI_WAIT | MCI_OPEN_TYPE_ID,
+           &stMCIAmpOpen,  0 );
+  if ( ulRC != MCIERR_SUCCESS )
+  {
+    stMCIAmpOpen.usDeviceID = (USHORT)~0;
+    OS2_CloseDevice( this );
+    return _MCIError( "MCI_OPEN", ulRC );
+  }
+  pAData->usDeviceId = stMCIAmpOpen.usDeviceID;
+
+  if ( iscapture != 0 )
+  {
+    MCI_CONNECTOR_PARMS  stMCIConnector = { 0 };
+    MCI_AMP_SET_PARMS    stMCIAmpSet = { 0 };
+    BOOL                 fLineIn = _getEnvULong( "SDL_AUDIO_LINEIN", 1, 0 );
+
+    // Set particular connector.
+    stMCIConnector.ulConnectorType = fLineIn ? MCI_LINE_IN_CONNECTOR
+                                             : MCI_MICROPHONE_CONNECTOR;
+    mciSendCommand( stMCIAmpOpen.usDeviceID, MCI_CONNECTOR,
+                    MCI_WAIT | MCI_ENABLE_CONNECTOR |
+                    MCI_CONNECTOR_TYPE, &stMCIConnector, 0 );
+
+    // Disable monitor.
+    stMCIAmpSet.ulItem = MCI_AMP_SET_MONITOR;
+    mciSendCommand( stMCIAmpOpen.usDeviceID, MCI_SET,
+                    MCI_WAIT | MCI_SET_OFF | MCI_SET_ITEM,
+                    &stMCIAmpSet, 0 );
+
+
+    // Set record volume.
+    stMCIAmpSet.ulLevel = _getEnvULong( "SDL_AUDIO_RECVOL", 100, 90 );
+    stMCIAmpSet.ulItem  = MCI_AMP_SET_AUDIO;
+    stMCIAmpSet.ulAudio = MCI_SET_AUDIO_ALL; // Both cnannels.
+    stMCIAmpSet.ulValue = fLineIn ? MCI_LINE_IN_CONNECTOR
+                                  : MCI_MICROPHONE_CONNECTOR ;
+
+    mciSendCommand( stMCIAmpOpen.usDeviceID, MCI_SET,
+                    MCI_WAIT | MCI_SET_AUDIO | MCI_AMP_SET_GAIN,
+                    &stMCIAmpSet, 0 );
+  }
+
+  this->spec.format = SDLAudioFmt;
+  this->spec.channels = this->spec.channels > 1 ? 2 : 1;
+  if ( this->spec.freq < 8000 )
+    this->spec.freq = 8000;
+  if ( this->spec.freq > 48000 )
+    this->spec.freq = 48000;
+
+  // Setup mixer.
+  pAData->stMCIMixSetup.ulFormatTag     = MCI_WAVE_FORMAT_PCM;
+  pAData->stMCIMixSetup.ulBitsPerSample = SDL_AUDIO_BITSIZE( SDLAudioFmt );
+  pAData->stMCIMixSetup.ulSamplesPerSec = this->spec.freq;
+  pAData->stMCIMixSetup.ulChannels      = this->spec.channels;
+  pAData->stMCIMixSetup.ulDeviceType    = MCI_DEVTYPE_WAVEFORM_AUDIO;
+  if ( iscapture == 0 )
+  {
+    pAData->stMCIMixSetup.ulFormatMode  = MCI_PLAY;
+    pAData->stMCIMixSetup.pmixEvent     = cbAudioWriteEvent;
+  }
+  else
+  {
+    pAData->stMCIMixSetup.ulFormatMode  = MCI_RECORD;
+    pAData->stMCIMixSetup.pmixEvent     = cbAudioReadEvent;
+  }
+
+  ulRC = mciSendCommand( pAData->usDeviceId, MCI_MIXSETUP,
+                     MCI_WAIT | MCI_MIXSETUP_INIT, &pAData->stMCIMixSetup, 0 );
+  if ( ( ulRC != MCIERR_SUCCESS ) && ( this->spec.freq > 44100 ) )
+  {
+    pAData->stMCIMixSetup.ulSamplesPerSec = 44100;
+    this->spec.freq = 44100;
+    ulRC = mciSendCommand( pAData->usDeviceId, MCI_MIXSETUP,
+                     MCI_WAIT | MCI_MIXSETUP_INIT, &pAData->stMCIMixSetup, 0 );
+  }
+  
+  debug( "Setup mixer [BPS: %u, Freq.: %u, Channels: %u]: %s",
+         pAData->stMCIMixSetup.ulBitsPerSample,
+         pAData->stMCIMixSetup.ulSamplesPerSec,
+         pAData->stMCIMixSetup.ulChannels,
+         ulRC == MCIERR_SUCCESS ? "SUCCESS" : "FAIL" );
+
+  if ( ulRC != MCIERR_SUCCESS )
+  {
+    pAData->stMCIMixSetup.ulBitsPerSample = 0;
+    OS2_CloseDevice( this );
+    return _MCIError( "MCI_MIXSETUP", ulRC );
+  }
+
+  this->spec.samples = pAData->stMCIMixSetup.ulSamplesPerSec;
+  /* Update the fragment size as size in bytes */
+  SDL_CalculateAudioSpec( &this->spec );
+
+  // Allocate memory buffers
+
+  stMCIBuffer.ulBufferSize = (this->spec.freq / 1000) * 100;// this->spec.size;
+  stMCIBuffer.ulNumBuffers = NUM_BUFFERS;
+  stMCIBuffer.pBufList     = &pAData->aMixBuffers;
+
+  ulRC = mciSendCommand( pAData->usDeviceId, MCI_BUFFER,
+                         MCI_WAIT | MCI_ALLOCATE_MEMORY, &stMCIBuffer, 0 );
+  if ( ulRC != MCIERR_SUCCESS )
+  {
+    OS2_CloseDevice( this );
+    return _MCIError( "MCI_BUFFER", ulRC );
+  }
+  pAData->cMixBuffers = stMCIBuffer.ulNumBuffers;
+  this->spec.size = stMCIBuffer.ulBufferSize;
+
+  // Fill all device buffers with data
+
+  for( ulIdx = 0; ulIdx < stMCIBuffer.ulNumBuffers; ulIdx++ )
+  {
+    pAData->aMixBuffers[ulIdx].ulFlags        = 0;
+    pAData->aMixBuffers[ulIdx].ulBufferLength = stMCIBuffer.ulBufferSize;
+    pAData->aMixBuffers[ulIdx].ulUserParm     = (ULONG)pAData;
+
+    memset( ((PMCI_MIX_BUFFER)stMCIBuffer.pBufList)[ulIdx].pBuffer,
+            this->spec.silence, stMCIBuffer.ulBufferSize );
+  }
+
+  // Write buffers to kick off the amp mixer
+//  pAData->ulQueuedBuf = 1;//stMCIBuffer.ulNumBuffers;
+  ulRC = pAData->stMCIMixSetup.pmixWrite( pAData->stMCIMixSetup.ulMixHandle,
+                                          &pAData->aMixBuffers,
+                                          1 );//stMCIBuffer.ulNumBuffers );
+  if ( ulRC != MCIERR_SUCCESS )
+  {
+    _mixIOError( "pmixWrite", ulRC );
+    return -1;
+  }
+
+  return 0;
+}
+
+
+static int OS2_Init(SDL_AudioDriverImpl * impl)
+{
+  /* Set the function pointers */
+  impl->DetectDevices = OS2_DetectDevices;
+  impl->OpenDevice    = OS2_OpenDevice;
+  impl->PlayDevice    = OS2_PlayDevice;
+  impl->WaitDevice    = OS2_WaitDevice;
+  impl->WaitDone      = OS2_WaitDone;
+  impl->GetDeviceBuf  = OS2_GetDeviceBuf;
+  impl->CloseDevice   = OS2_CloseDevice;
+
+// [Digi]: SDL 2.0 does not support recording yet (2016-02-24).
+//  impl->HasCaptureSupport = SDL_TRUE;
+
+  return 1;   /* this audio target is available. */
+}
+
+
+AudioBootStrap OS2AUDIO_bootstrap = { "MMOS2", "OS/2 DART", OS2_Init, 0 };
+
+#endif /* SDL_AUDIO_DRIVER_OS2 */
+
+/* vi: set ts=4 sw=4 expandtab: */
diff --git a/src/audio/os2/SDL_os2audio.h b/src/audio/os2/SDL_os2audio.h
new file mode 100644
index 0000000..014e35e
--- /dev/null
+++ b/src/audio/os2/SDL_os2audio.h
@@ -0,0 +1,53 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "../../SDL_internal.h"
+
+#ifndef _SDL_os2mm_h
+#define _SDL_os2mm_h
+
+#include "../SDL_sysaudio.h"
+
+#define  INCL_OS2MM
+#define  INCL_PM
+#define  INCL_DOS
+#define  INCL_DOSERRORS
+#include <os2.h>
+#include <os2me.h>
+
+/* Hidden "this" pointer for the audio functions */
+#define _THIS   SDL_AudioDevice *this
+
+#define NUM_BUFFERS    3
+
+typedef struct SDL_PrivateAudioData
+{
+  USHORT               usDeviceId;
+  MCI_MIXSETUP_PARMS   stMCIMixSetup;
+  HEV                  hevBuf;
+  ULONG                ulNextBuf;
+  ULONG                cMixBuffers;
+  MCI_MIX_BUFFER       aMixBuffers[NUM_BUFFERS];
+//  ULONG                ulQueuedBuf;
+} SDL_PrivateAudioData, *PSDL_PrivateAudioData;
+
+#endif /* _SDL_os2mm_h */
+
+/* vi: set ts=4 sw=4 expandtab: */
diff --git a/src/core/os2/SDL_os2.c b/src/core/os2/SDL_os2.c
new file mode 100644
index 0000000..d42e668
--- /dev/null
+++ b/src/core/os2/SDL_os2.c
@@ -0,0 +1,36 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "../../SDL_internal.h"
+
+#if defined(__OS2__)
+
+#include "core/os2/geniconv/geniconv.h"
+
+// SDL_OS2Quit() will be called from SDL_QuitSubSystem().
+
+void SDL_OS2Quit()
+{
+  // Unload DLL used for iconv. We can do it at any time and use iconv again -
+  // dynamic library will be loaded on first call iconv_open() (see geniconv).
+  libiconv_clean();
+}
+
+#endif
diff --git a/src/core/os2/SDL_os2.h b/src/core/os2/SDL_os2.h
new file mode 100644
index 0000000..a455c49
--- /dev/null
+++ b/src/core/os2/SDL_os2.h
@@ -0,0 +1,31 @@
+#ifndef _SDL_os2_h
+#define _SDL_os2_h
+
+#include "SDL_log.h"
+#include "SDL_stdinc.h"
+#include ".\src\core\os2\geniconv\geniconv.h"
+
+#if OS2DEBUG==SDLOUTPUT
+
+# define debug(s,...) SDL_LogDebug( SDL_LOG_CATEGORY_APPLICATION, \
+                                    __func__"(): "##s, ##__VA_ARGS__ )
+
+#elif defined(OS2DEBUG)
+
+# define debug(s,...) printf( __func__"(): "##s"\n", ##__VA_ARGS__ )
+
+#else
+
+# define debug(s,...)
+
+#endif // OS2DEBUG
+
+
+// StrUTF8New() - geniconv\sys2utf8.c.
+#define OS2_SysToUTF8(S) StrUTF8New( 1, S, SDL_strlen( S ) + 1 )
+#define OS2_UTF8ToSys(S) StrUTF8New( 0, (char *)(S), SDL_strlen( S ) + 1 )
+
+// SDL_OS2Quit() will be called from SDL_QuitSubSystem().
+void SDL_OS2Quit();
+
+#endif // _SDL_os2_h
diff --git a/src/core/os2/geniconv/geniconv.c b/src/core/os2/geniconv/geniconv.c
new file mode 100644
index 0000000..b82e903
--- /dev/null
+++ b/src/core/os2/geniconv/geniconv.c
@@ -0,0 +1,143 @@
+/*
+  Universal iconv implementation for OS/2.
+
+  Andrey Vasilkin, 2016.
+*/
+
+#define INCL_DOSMODULEMGR     /* Module Manager values */
+#define INCL_DOSERRORS        /* Error values */
+#include <os2.h>
+#include <iconv.h>
+
+//#define DEBUG
+
+#ifdef DEBUG
+# include <stdio.h>
+# define debug(s,...) printf(__func__"(): "##s"\n" ,##__VA_ARGS__)
+#else
+# define debug(s,...)
+#endif
+
+// Exports from os2iconv.c.
+extern iconv_t _System os2_iconv_open(const char* tocode, const char* fromcode);
+extern size_t _System os2_iconv(iconv_t cd, char* * inbuf,
+                                size_t *inbytesleft, char* * outbuf,
+                                size_t *outbytesleft);
+extern int _System os2_iconv_close(iconv_t cd);
+
+// Functions pointers types.
+typedef iconv_t _System (*FNICONV_OPEN)(const char* tocode, const char* fromcode);
+typedef size_t _System (*FNICONV)(iconv_t cd, char* * inbuf,
+                                  size_t *inbytesleft, char* * outbuf,
+                                  size_t *outbytesleft);
+typedef int _System (*FNICONV_CLOSE)(iconv_t cd);
+
+// Used DLL module handle.
+static HMODULE         hmIconv = NULLHANDLE;
+// Functions pointers.
+static FNICONV_OPEN    fn_iconv_open = NULL;
+static FNICONV         fn_iconv = NULL;
+static FNICONV_CLOSE   fn_iconv_close = NULL;
+
+
+static BOOL _loadDLL(PSZ pszName, PSZ pszIconvOpen, PSZ pszIconv,
+                     PSZ pszIconvClose)
+{
+  ULONG      ulRC;
+  CHAR       acError[256];
+
+  ulRC = DosLoadModule( &acError, sizeof(acError), pszName, &hmIconv );
+  if ( ulRC != NO_ERROR )
+  {
+    debug( "DLL not loaded: %s", &acError );
+    return FALSE;
+  }
+
+  do
+  {
+    ulRC = DosQueryProcAddr( hmIconv, 0, pszIconvOpen, (PFN *)&fn_iconv_open );
+    if ( ulRC != NO_ERROR )
+    {
+      debug( "Error: cannot find entry %s in %s", pszIconvOpen, pszName );
+      break;
+    }
+
+    ulRC = DosQueryProcAddr( hmIconv, 0, pszIconv, (PFN *)&fn_iconv );
+    if ( ulRC != NO_ERROR )
+    {
+      debug( "Error: cannot find entry %s in %s", pszIconv, pszName );
+      break;
+    }
+
+    ulRC = DosQueryProcAddr( hmIconv, 0, pszIconvClose, (PFN *)&fn_iconv_close );
+    if ( ulRC != NO_ERROR )
+    {
+      debug( "Error: cannot find entry %s in %s", pszIconvClose, pszName );
+      break;
+    }
+
+    debug( "DLL %s used", pszName );
+    return TRUE;
+  }
+  while( FALSE );
+
+  DosFreeModule( hmIconv );
+  hmIconv = NULLHANDLE;
+  return FALSE;
+}
+
+static void _init()
+{
+  if ( fn_iconv_open != NULL )
+    // Already was initialized.
+    return;
+
+  // Try to load kiconv.dll, iconv2.dll or iconv.dll.
+  if ( !_loadDLL( "KICONV", "_libiconv_open", "_libiconv", "_libiconv_close" )
+       && !_loadDLL( "ICONV2", "_libiconv_open", "_libiconv", "_libiconv_close" )
+       && !_loadDLL( "ICONV", "_iconv_open", "_iconv", "_iconv_close" ) )
+  {
+    // No one DLL was loaded - use OS/2 conversion objects API.
+
+    debug( "Uni*() API used" );
+    fn_iconv_open  = os2_iconv_open;
+    fn_iconv       = os2_iconv;
+    fn_iconv_close = os2_iconv_close;
+  }
+}
+
+
+
+//           Public routines.
+//           ----------------
+
+// Non-standard function for iconv to unload the used dynamic library.
+void libiconv_clean()
+{
+  if ( hmIconv != NULLHANDLE )
+  {
+    DosFreeModule( hmIconv );
+    hmIconv = NULLHANDLE;
+
+    fn_iconv_open  = NULL;
+    fn_iconv       = NULL;
+    fn_iconv_close = NULL;
+  }
+}
+
+iconv_t libiconv_open(const char* tocode, const char* fromcode)
+{
+  _init();
+  return fn_iconv_open( tocode, fromcode );
+}
+
+size_t libiconv(iconv_t cd, char* * inbuf, size_t *inbytesleft,
+                char* * outbuf, size_t *outbytesleft)
+{
+  return fn_iconv( cd, inbuf, inbytesleft, outbuf, outbytesleft );
+}
+
+int libiconv_close(iconv_t cd)
+{
+  return fn_iconv_close( cd );
+}
diff --git a/src/core/os2/geniconv/geniconv.h b/src/core/os2/geniconv/geniconv.h
new file mode 100644
index 0000000..e22ce64
--- /dev/null
+++ b/src/core/os2/geniconv/geniconv.h
@@ -0,0 +1,59 @@
+/*
+  Universal iconv implementation for OS/2.
+
+  Andrey Vasilkin, 2016.
+*/
+
+#ifndef GENICONV_H
+#define GENICONV_H
+
+#include <iconv.h>
+
+#ifdef iconv_open
+#undef iconv_open
+#endif
+#define iconv_open libiconv_open
+
+#ifdef iconv
+#undef iconv
+#endif
+#define iconv libiconv
+
+#ifdef iconv_close
+#undef iconv_close
+#endif
+#define iconv_close libiconv_close
+
+#define iconv_clean libiconv_clean
+
+// Non-standard function for iconv to unload the used dynamic library.
+void libiconv_clean();
+
+iconv_t libiconv_open(const char* tocode, const char* fromcode);
+size_t libiconv(iconv_t cd, char* * inbuf, size_t *inbytesleft,
+                 char* * outbuf, size_t *outbytesleft);
+int libiconv_close(iconv_t cd);
+
+// System codepage <-> UTF-8.
+
+// StrUTF8()
+// Coverts string from system cp. to UTF-8 (fToUTF8 is not 0) or from UTF-8 to
+// the system cp. (fToUTF8 is 0). Converted ASCIIZ string will be placed at the
+// buffer pcDst, up to cbDst - 1 (for sys->utf8) or 2 (for utf8->sys) bytes.
+// Returns the number of bytes written into pcDst, not counting the terminating
+// 0 byte(s) or -1 on error.
+int StrUTF8(int fToUTF8, char *pcDst, int cbDst, char *pcSrc, int cbSrc);
+
+// StrUTF8New()
+// Coverts string from system cp. to UTF-8 (fToUTF8 is not 0) or from UTF-8 to
+// the system cp. (fToUTF8 is 0). Memory for the new string is obtained by
+// using libc malloc().
+// Returns converted string, terminating two bytes 0 is appended to the result.
+// Returns null on error.
+char *StrUTF8New(int fToUTF8, char *pcStr, int cbStr);
+
+// StrUTF8Free()
+// Deallocates the memory block located by StrUTF8New() (just libc free()).
+void StrUTF8Free(char *pszStr);
+
+#endif // GENICONV_H
diff --git a/src/core/os2/geniconv/iconv.h b/src/core/os2/geniconv/iconv.h
new file mode 100644
index 0000000..b336dab
--- /dev/null
+++ b/src/core/os2/geniconv/iconv.h
@@ -0,0 +1,21 @@
+#ifndef ICONV_H_ /* minimal iconv.h header based on public knowledge */
+#define ICONV_H_
+
+#include <stddef.h> /* size_t */
+#include <errno.h>
+
+typedef void *iconv_t;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern iconv_t iconv_open(const char *, const char *);
+extern size_t iconv(iconv_t, char **, size_t *, char **, size_t *);
+extern int iconv_close(iconv_t);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ICONV_H_ */
diff --git a/src/core/os2/geniconv/makefile b/src/core/os2/geniconv/makefile
new file mode 100644
index 0000000..7dffd30
--- /dev/null
+++ b/src/core/os2/geniconv/makefile
@@ -0,0 +1,37 @@
+#
+# Universal iconv implementation for OS/2.
+#
+# OpenWatcom makefile to build a library that uses kiconv.dll / iconv2.dll /
+# iconv.dll or OS/2 Uni*() API.
+#
+# Andrey Vasilkin, 2016.
+#
+
+LIBFILE = geniconv.lib
+
+all: $(LIBFILE) test.exe .symbolic
+
+CFLAGS = -I$(%WATCOM)/h/os2 -I$(%WATCOM)/h -I. -bt=os2 -q -d0 -w2
+
+SRCS = geniconv.c os2cp.c os2iconv.c
+SRCS+= sys2utf8.c
+
+OBJS = $(SRCS:.c=.obj)
+
+LIBS = libuls.lib libconv.lib $(LIBFILE)
+
+test.exe: $(LIBFILE) test.obj
+  wlink op quiet system os2v2 file test.obj lib {$(LIBS)} name $*
+
+$(LIBFILE): $(OBJS)
+  @if exist $@ rm $@
+  @for %f in ($(OBJS)) do wlib -q -b $* +%f
+
+.c.obj:
+    wcc386 $(CFLAGS) -fo=$^@ $<
+
+clean: .SYMBOLIC
+  @if exist *.obj rm *.obj
+  @if exist *.err rm *.err
+  @if exist $(LIBFILE) rm $(LIBFILE)
+  @if exist test.exe rm test.exe
diff --git a/src/core/os2/geniconv/os2cp.c b/src/core/os2/geniconv/os2cp.c
new file mode 100644
index 0000000..dd9e83a
--- /dev/null
+++ b/src/core/os2/geniconv/os2cp.c
@@ -0,0 +1,383 @@
+#include "os2cp.h"
+#define INCL_DOSNLS
+#define INCL_DOSERRORS
+#include <os2.h>
+#include <string.h>
+#include <ctype.h>
+
+typedef struct _CP2NAME {
+  ULONG		ulCode;
+  PSZ		pszName;
+} CP2NAME;
+
+typedef struct _NAME2CP {
+  PSZ		pszName;
+  ULONG		ulCode;
+} NAME2CP;
+
+static CP2NAME		aCP2Name[] =
+{
+  {367, "ANSI_X3.4-1968"},
+  {813, "ECMA-118"},
+  {819, "CP819"},
+  {850, "850"},
+  {862, "862"},
+  {866, "866"},
+  {874, "ISO-IR-166"},
+  {878, "KOI8-R"},
+  {896, "JISX0201-1976"},
+  {901, "ISO-8859-13"},
+  {912, "ISO-8859-2"},
+  {913, "ISO-8859-3"},
+  {914, "ISO-8859-4"},
+  {915, "CYRILLIC"},
+  {920, "ISO-8859-9"},
+  {923, "ISO-8859-15"},
+  {943, "MS_KANJI"},
+  {954, "EUC-JP"},
+  {964, "EUC-TW"},
+  {970, "EUC-KR"},
+  {1051, "HP-ROMAN8"},
+  {1089, "ARABIC"},
+  {1129, "VISCII"},
+  {1168, "KOI8-U"},
+  {1200, "ISO-10646-UCS-2"},
+  {1202, "UTF-16LE"},
+  {1204, "UCS-2BE"},
+  {1208, "UTF-8"},
+  {1232, "UTF-32BE"},
+  {1234, "UTF-32LE"},
+  {1236, "ISO-10646-UCS-4"},
+  {1250, "CP1250"},
+  {1251, "CP1251"},
+  {1252, "CP1252"},
+  {1253, "CP1253"},
+  {1254, "CP1254"},
+  {1255, "CP1255"},
+  {1256, "CP1256"},
+  {1257, "CP1257"},
+  {1275, "MAC"},
+  {1383, "CN-GB"},
+  {1386, "GBK"},
+  {1392, "GB18030"},
+  {62210, "HEBREW"}
+};
+
+static NAME2CP		aName2CP[] =
+{
+  {"850", 850},
+  {"862", 862},
+  {"866", 866},
+  {"ANSI_X3.4-1968", 367},
+  {"ANSI_X3.4-1986", 367},
+  {"ARABIC", 1089},
+  {"ASCII", 367},
+  {"ASMO-708", 1089},
+  {"CN-GB", 1383},
+  {"CP1250", 1250},
+  {"CP1251", 1251},
+  {"CP1252", 1252},
+  {"CP1253", 1253},
+  {"CP1254", 1254},
+  {"CP1255", 1255},
+  {"CP1256", 1256},
+  {"CP1257", 1257},
+  {"CP367", 367},
+  {"CP819", 819},
+  {"CP850", 850},
+  {"CP862", 862},
+  {"CP866", 866},
+  {"CP936", 1386},
+  {"CSASCII", 367},
+  {"CSEUCKR", 970},
+  {"CSEUCPKDFMTJAPANESE", 954},
+  {"CSEUCTW", 964},
+  {"CSGB2312", 1383},
+  {"CSHALFWIDTHKATAKANA", 896},
+  {"CSHPROMAN8", 1051},
+  {"CSIBM866", 866},
+  {"CSISOLATIN1", 819},
+  {"CSISOLATIN2", 912},
+  {"CSISOLATIN3", 913},
+  {"CSISOLATIN4", 914},
+  {"CSISOLATIN5", 920},
+  {"CSISOLATINARABIC", 1089},
+  {"CSISOLATINCYRILLIC", 915},
+  {"CSISOLATINGREEK", 813},
+  {"CSISOLATINHEBREW", 62210},
+  {"CSKOI8R", 878},
+  {"CSKSC56011987", 970},
+  {"CSMACINTOSH", 1275},
+  {"CSPC850MULTILINGUAL", 850},
+  {"CSPC862LATINHEBREW", 862},
+  {"CSSHIFTJIS", 943},
+  {"CSUCS4", 1236},
+  {"CSUNICODE", 1200},
+  {"CSUNICODE11", 1204},
+  {"CSVISCII", 1129},
+  {"CYRILLIC", 915},
+  {"ECMA-114", 1089},
+  {"ECMA-118", 813},
+  {"ELOT_928", 813},
+  {"EUC-CN", 1383},
+  {"EUC-JP", 954},
+  {"EUC-KR", 970},
+  {"EUC-TW", 964},
+  {"EUCCN", 1383},
+  {"EUCJP", 954},
+  {"EUCKR", 970},
+  {"EUCTW", 964},
+  {"EXTENDED_UNIX_CODE_PACKED_FORMAT_FOR_JAPANESE", 954},
+  {"GB18030", 1392},
+  {"GB2312", 1383},
+  {"GBK", 1386},
+  {"GREEK", 813},
+  {"GREEK8", 813},
+  {"HEBREW", 62210},
+  {"HP-ROMAN8", 1051},
+  {"IBM367", 367},
+  {"IBM819", 819},
+  {"IBM850", 850},
+  {"IBM862", 862},
+  {"IBM866", 866},
+  {"ISO-10646-UCS-2", 1200},
+  {"ISO-10646-UCS-4", 1236},
+  {"ISO-8859-1", 819},
+  {"ISO-8859-13", 901},
+  {"ISO-8859-15", 923},
+  {"ISO-8859-2", 912},
+  {"ISO-8859-3", 913},
+  {"ISO-8859-4", 914},
+  {"ISO-8859-5", 915},
+  {"ISO-8859-6", 1089},
+  {"ISO-8859-7", 813},
+  {"ISO-8859-8", 62210},
+  {"ISO-8859-9", 920},
+  {"ISO-IR-100", 819},
+  {"ISO-IR-101", 912},
+  {"ISO-IR-109", 913},
+  {"ISO-IR-110", 914},
+  {"ISO-IR-126", 813},
+  {"ISO-IR-127", 1089},
+  {"ISO-IR-138", 62210},
+  {"ISO-IR-144", 915},
+  {"ISO-IR-148", 920},
+  {"ISO-IR-149", 970},
+  {"ISO-IR-166", 874},
+  {"ISO-IR-179", 901},
+  {"ISO-IR-203", 923},
+  {"ISO-IR-6", 367},
+  {"ISO646-US", 367},
+  {"ISO8859-1", 819},
+  {"ISO8859-13", 901},
+  {"ISO8859-15", 923},
+  {"ISO8859-2", 912},
+  {"ISO8859-3", 913},
+  {"ISO8859-4", 914},
+  {"ISO8859-5", 915},
+  {"ISO8859-6", 1089},
+  {"ISO8859-7", 813},
+  {"ISO8859-8", 62210},
+  {"ISO8859-9", 920},
+  {"ISO_646.IRV:1991", 367},
+  {"ISO_8859-1", 819},
+  {"ISO_8859-13", 901},
+  {"ISO_8859-15", 923},
+  {"ISO_8859-15:1998", 923},
+  {"ISO_8859-1:1987", 819},
+  {"ISO_8859-2", 912},
+  {"ISO_8859-2:1987", 912},
+  {"ISO_8859-3", 913},
+  {"ISO_8859-3:1988", 913},
+  {"ISO_8859-4", 914},
+  {"ISO_8859-4:1988", 914},
+  {"ISO_8859-5", 915},
+  {"ISO_8859-5:1988", 915},
+  {"ISO_8859-6", 1089},
+  {"ISO_8859-6:1987", 1089},
+  {"ISO_8859-7", 813},
+  {"ISO_8859-7:1987", 813},
+  {"ISO_8859-7:2003", 813},
+  {"ISO_8859-8", 62210},
+  {"ISO_8859-8:1988", 62210},
+  {"ISO_8859-9", 920},
+  {"ISO_8859-9:1989", 920},
+  {"JISX0201-1976", 896},
+  {"JIS_X0201", 896},
+  {"KOI8-R", 878},
+  {"KOI8-U", 1168},
+  {"KOREAN", 970},
+  {"KSC_5601", 970},
+  {"KS_C_5601-1987", 970},
+  {"KS_C_5601-1989", 970},
+  {"L1", 819},
+  {"L2", 912},
+  {"L3", 913},
+  {"L4", 914},
+  {"L5", 920},
+  {"L7", 901},
+  {"LATIN-9", 923},
+  {"LATIN1", 819},
+  {"LATIN2", 912},
+  {"LATIN3", 913},
+  {"LATIN4", 914},
+  {"LATIN5", 920},
+  {"LATIN7", 901},
+  {"MAC", 1275},
+  {"MACINTOSH", 1275},
+  {"MACROMAN", 1275},
+  {"MS-ANSI", 1252},
+  {"MS-ARAB", 1256},
+  {"MS-CYRL", 1251},
+  {"MS-EE", 1250},
+  {"MS-GREEK", 1253},
+  {"MS-HEBR", 1255},
+  {"MS-TURK", 1254},
+  {"MS936", 1386},
+  {"MS_KANJI", 943},
+  {"R8", 1051},
+  {"ROMAN8", 1051},
+  {"SHIFT-JIS", 943},
+  {"SHIFT_JIS", 943},
+  {"SJIS", 943},
+  {"TIS-620", 874},
+  {"TIS620", 874},
+  {"TIS620-0", 874},
+  {"TIS620.2529-1", 874},
+  {"TIS620.2533-0", 874},
+  {"TIS620.2533-1", 874},
+  {"UCS-2", 1200},
+  {"UCS-2BE", 1204},
+  {"UCS-4", 1236},
+  {"UNICODE-1-1", 1204},
+  {"UNICODEBIG", 1204},
+  {"US", 367},
+  {"US-ASCII", 367},
+  {"UTF-16", 1204},
+  {"UTF-16BE", 1200},
+  {"UTF-16LE", 1202},
+  {"UTF-32", 1236},
+  {"UTF-32BE", 1232},
+  {"UTF-32LE", 1234},
+  {"UTF-8", 1208},
+  {"VISCII", 1129},
+  {"VISCII1.1-1", 1129},
+  {"WINBALTRIM", 1257},
+  {"WINDOWS-1250", 1250},
+  {"WINDOWS-1251", 1251},
+  {"WINDOWS-1252", 1252},
+  {"WINDOWS-1253", 1253},
+  {"WINDOWS-1254", 1254},
+  {"WINDOWS-1255", 1255},
+  {"WINDOWS-1256", 1256},
+  {"WINDOWS-1257", 1257},
+  {"WINDOWS-936", 1386},
+  {"X0201", 896}
+};
+
+char * os2cpToName(unsigned long cp)
+{
+  ULONG		ulLo = 0;
+  ULONG		ulHi = ( sizeof(aCP2Name) / sizeof(struct _CP2NAME) ) - 1;
+  ULONG		ulNext;
+  LONG		lFound = -1;
+
+  if ( cp == SYSTEM_CP )
+  {
+    ULONG	aulCP[3];
+    ULONG	cCP;
+
+    if ( DosQueryCp( sizeof(aulCP), &aulCP, &cCP ) != NO_ERROR )
+      return NULL;
+
+    cp = aulCP[0];
+  }
+
+  if ( ( aCP2Name[0].ulCode > cp ) || ( aCP2Name[ulHi].ulCode < cp ) )
+    return NULL;
+
+  if ( aCP2Name[0].ulCode == cp )
+    return aCP2Name[0].pszName;
+
+  if ( aCP2Name[ulHi].ulCode == cp )
+    return aCP2Name[ulHi].pszName;
+
+  while( ( ulHi - ulLo ) > 1 )
+  {
+    ulNext = ( ulLo + ulHi ) / 2;
+
+    if ( aCP2Name[ulNext].ulCode < cp )
+      ulLo = ulNext;
+    else if ( aCP2Name[ulNext].ulCode > cp )
+      ulHi = ulNext;
+    else
+    {
+      lFound = ulNext;
+      break;
+    }
+  }
+
+  return lFound == -1 ? NULL : aCP2Name[lFound].pszName;
+}
+
+unsigned long os2cpFromName(char *cp)
+{
+  ULONG		ulLo = 0;
+  ULONG		ulHi = ( sizeof(aName2CP) / sizeof(struct _NAME2CP) ) - 1;
+  ULONG		ulNext;
+  LONG		lFound = -1;
+  LONG		lCmp;
+  PCHAR		pcEnd;
+  CHAR		acBuf[64];
+
+  if ( cp == NULL )
+  {
+    ULONG	aulCP[3];
+    ULONG	cCP;
+
+    return DosQueryCp( sizeof(aulCP), &aulCP, &cCP ) != NO_ERROR ? 0 : aulCP[0];
+  }
+
+  while( isspace( *cp ) ) cp++;
+  pcEnd = strchr( cp, ' ' );
+  if ( pcEnd == NULL )
+    pcEnd = strchr( cp, '\0' );
+
+  ulNext = pcEnd - cp;
+  if ( ulNext >= sizeof(acBuf) )
+    return 0;
+  
+  memcpy( &acBuf, cp, ulNext );
+  acBuf[ulNext] = '\0';
+  strupr( &acBuf ); 
+
+  lCmp = strcmp( aName2CP[0].pszName, &acBuf );
+  if ( lCmp > 0 )
+    return 0;
+  else if ( lCmp == 0 )
+    return aName2CP[0].ulCode;
+
+  lCmp = strcmp( aName2CP[ulHi].pszName, &acBuf );
+  if ( lCmp < 0 )
+    return 0;
+  else if ( lCmp == 0 )
+    return aName2CP[ulHi].ulCode;
+
+  while( ( ulHi - ulLo ) > 1 )
+  {
+    ulNext = ( ulLo + ulHi ) / 2;
+
+    lCmp = strcmp( aName2CP[ulNext].pszName, &acBuf );
+    if ( lCmp < 0 )
+      ulLo = ulNext;
+    else if ( lCmp > 0 )
+      ulHi = ulNext;
+    else
+    {
+      lFound = ulNext;
+      break;
+    }
+  }
+
+  return lFound == -1 ? 0 : aName2CP[lFound].ulCode;
+}
diff --git a/src/core/os2/geniconv/os2cp.h b/src/core/os2/geniconv/os2cp.h
new file mode 100644
index 0000000..542b41e
--- /dev/null
+++ b/src/core/os2/geniconv/os2cp.h
@@ -0,0 +1,9 @@
+#ifndef OS2CP_H
+#define OS2CP_H 1
+
+#define SYSTEM_CP	0
+
+char * os2cpToName(unsigned long cp);
+unsigned long os2cpFromName(char *cp);
+
+#endif // OS2CP_H
diff --git a/src/core/os2/geniconv/os2iconv.c b/src/core/os2/geniconv/os2iconv.c
new file mode 100644
index 0000000..1e2ba24
--- /dev/null
+++ b/src/core/os2/geniconv/os2iconv.c
@@ -0,0 +1,264 @@
+/*
+   Implementation iconv via OS/2 conversion objects API.
+
+   Andrey Vasilkin.
+*/
+
+#define ICONV_THREAD_SAFE 1
+//#undef ICONV_THREAD_SAFE
+
+#include "iconv.h"
+#include <uconv.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef ICONV_THREAD_SAFE
+#define INCL_DOSSEMAPHORES
+#define INCL_DOSERRORS
+#include <os2.h>
+#endif
+#include "os2cp.h"
+
+#define		MAX_CP_NAME_LEN		64
+
+typedef struct iuconv_obj {
+  UconvObject	uo_tocode;
+  UconvObject	uo_fromcode;
+  int		buf_len;
+  UniChar	*buf;
+#ifdef ICONV_THREAD_SAFE
+  HMTX		hMtx;
+#endif
+} iuconv_obj;
+
+
+static int _createUconvObj(const char *code, UconvObject *uobj)
+{
+  UniChar	uc_code[MAX_CP_NAME_LEN];
+  int		i;
+  const char	*ch = code;
+
+  if ( code == NULL )
+    uc_code[0] = 0;
+  else
+  {
+    for( i = 0; i < MAX_CP_NAME_LEN; i++ )
+    {
+      uc_code[i] = (unsigned short)*ch;
+      if ( !(*ch) ) break;
+      ch++;
+    }
+  }
+
+  return UniCreateUconvObject( &uc_code, uobj );
+}
+
+static int uconv_open(const char *code, UconvObject *uobj)
+{
+  int		rc;
+
+  if ( !stricmp( code, "UTF-16" ) )
+  {
+    *uobj = NULL;
+    return ULS_SUCCESS;
+  }
+
+  rc = _createUconvObj( code, uobj );
+  if ( rc != ULS_SUCCESS )
+  {
+    unsigned long	cp = os2cpFromName( (char *)code );
+    char		cp_name[16];
+
+    if ( cp != 0 && _snprintf( &cp_name, sizeof(cp_name), "IBM-%u", cp ) > 0 )
+      rc = _createUconvObj( &cp_name, uobj );
+  }
+
+  return rc;
+}
+
+
+extern iconv_t _System os2_iconv_open(const char* tocode, const char* fromcode)
+{
+  UconvObject	uo_tocode;
+  UconvObject	uo_fromcode;
+  int		rc;
+  iuconv_obj	*iuobj;
+
+  if ( tocode == NULL )
+    tocode = "";
+
+  if ( fromcode == NULL )
+    fromcode = "";
+
+  if ( stricmp(tocode, fromcode) != 0 )
+  {
+    rc = uconv_open( fromcode, &uo_fromcode );
+    if ( rc != ULS_SUCCESS )
+    {
+      errno = EINVAL;
+      return (iconv_t)(-1);
+    }
+
+    rc = uconv_open( tocode, &uo_tocode );
+    if ( rc != ULS_SUCCESS )
+    {
+      UniFreeUconvObject( uo_fromcode );
+      errno = EINVAL;
+      return (iconv_t)(-1);
+    }
+  }
+  else {
+    uo_tocode = NULL;
+    uo_fromcode = NULL;
+  }
+
+  iuobj = malloc( sizeof(iuconv_obj) );
+  iuobj->uo_tocode = uo_tocode;
+  iuobj->uo_fromcode = uo_fromcode;
+  iuobj->buf_len = 0;
+  iuobj->buf = NULL;
+#ifdef ICONV_THREAD_SAFE
+  DosCreateMutexSem( NULL, &iuobj->hMtx, 0, FALSE );
+#endif
+
+  return iuobj;
+}
+
+extern size_t _System os2_iconv(iconv_t cd, char* * inbuf,
+                                size_t *inbytesleft,
+                                char* * outbuf, size_t *outbytesleft)
+{
+  UconvObject	uo_tocode = ((iuconv_obj *)(cd))->uo_tocode;
+  UconvObject	uo_fromcode = ((iuconv_obj *)(cd))->uo_fromcode;
+  size_t	nonIdenticalConv = 0;  
+  UniChar	*uc_buf;
+  size_t	uc_buf_len;
+  UniChar	**uc_str;
+  size_t	*uc_str_len;
+  int		rc;
+  size_t	ret = (size_t)(-1);
+
+  if ( uo_tocode == NULL && uo_fromcode == NULL )
+  {
+    uc_buf_len = min( *inbytesleft, *outbytesleft );
+    memcpy( *outbuf, *inbuf, uc_buf_len );
+    *inbytesleft -= uc_buf_len;
+    *outbytesleft -= uc_buf_len;
+    outbuf += uc_buf_len;
+    inbuf += uc_buf_len;
+    return uc_buf_len;
+  }
+
+#ifdef ICONV_THREAD_SAFE
+  DosRequestMutexSem( ((iuconv_obj *)(cd))->hMtx, SEM_INDEFINITE_WAIT );
+#endif
+
+  if ( uo_tocode && uo_fromcode && 
+       (( ((iuconv_obj *)(cd))->buf_len >> 1 ) < (*inbytesleft)) )
+  {
+    if ( ( ((iuconv_obj *)(cd))->buf ) != NULL )
+      free( ((iuconv_obj *)(cd))->buf );
+    ((iuconv_obj *)(cd))->buf_len = *inbytesleft << 1;
+    ((iuconv_obj *)(cd))->buf = (UniChar *)malloc( ((iuconv_obj *)(cd))->buf_len );
+  }
+
+  if ( uo_fromcode )
+  {
+    if ( uo_tocode )
+    {
+      uc_buf = ((iuconv_obj *)(cd))->buf;
+      uc_buf_len = ((iuconv_obj *)(cd))->buf_len;
+      uc_str = &uc_buf;
+    }
+    else {
+      uc_str = (UniChar **)outbuf;
+      uc_buf_len = *outbytesleft;
+    }
+    uc_buf_len = uc_buf_len >> 1;
+    uc_str_len = &uc_buf_len;
+    rc = UniUconvToUcs( uo_fromcode, (void **)inbuf, inbytesleft, 
+                        uc_str, uc_str_len, &nonIdenticalConv );
+    uc_buf_len = uc_buf_len << 1;
+    if ( !uo_tocode )
+      *outbytesleft = uc_buf_len;
+
+    if ( rc != ULS_SUCCESS )
+    {
+      errno = EILSEQ;
+      goto done;
+    }
+    else
+      if ( *inbytesleft && !*uc_str_len )
+      {
+        errno = E2BIG;
+        goto done;
+      }
+
+    if ( !uo_tocode )
+      return nonIdenticalConv;
+
+    uc_buf = ((iuconv_obj *)(cd))->buf;
+    uc_buf_len = ((iuconv_obj *)(cd))->buf_len - uc_buf_len;
+    uc_str = &uc_buf;
+    uc_str_len = &uc_buf_len;
+  }
+  else {
+    uc_str = (UniChar **)inbuf;
+    uc_str_len = inbytesleft;
+  }
+
+  *uc_str_len = *uc_str_len>>1;
+  rc = UniUconvFromUcs( uo_tocode, uc_str, uc_str_len, (void **)outbuf,
+                        outbytesleft, &nonIdenticalConv );
+  if ( rc != ULS_SUCCESS )
+  {
+    switch ( rc )
+    {
+      case ULS_BUFFERFULL:
+        errno = E2BIG;
+        break;
+      case ULS_ILLEGALSEQUENCE:
+        errno = EILSEQ;
+        break;
+      case ULS_INVALID:
+        errno = EINVAL;
+        break;
+    }
+    goto done;
+  }
+  else
+    if ( *uc_str_len && !*outbytesleft )
+    {
+      errno = E2BIG;
+      goto done;
+    }
+
+  ret = nonIdenticalConv;
+
+done:
+
+#ifdef ICONV_THREAD_SAFE
+  DosReleaseMutexSem( ((iuconv_obj *)(cd))->hMtx );
+#endif
+  return ret;
+}
+
+extern int _System os2_iconv_close(iconv_t cd)
+{
+  if ( !cd )
+    return 0;
+
+#ifdef ICONV_THREAD_SAFE
+  DosCloseMutexSem( ((iuconv_obj *)(cd))->hMtx );
+#endif
+  if ( ((iuconv_obj *)(cd))->uo_tocode != NULL )
+    UniFreeUconvObject( ((iuconv_obj *)(cd))->uo_tocode );
+  if ( ((iuconv_obj *)(cd))->uo_fromcode != NULL )
+    UniFreeUconvObject( ((iuconv_obj *)(cd))->uo_fromcode );
+
+  if ( ( ((iuconv_obj *)(cd))->buf ) != NULL )
+    free( ((iuconv_obj *)(cd))->buf );
+
+  free(cd);
+
+  return 0;
+}
diff --git a/src/core/os2/geniconv/sys2utf8.c b/src/core/os2/geniconv/sys2utf8.c
new file mode 100644
index 0000000..51ce4a1
--- /dev/null
+++ b/src/core/os2/geniconv/sys2utf8.c
@@ -0,0 +1,97 @@
+#include <iconv.h>
+#include <stdlib.h>
+
+int StrUTF8(int fToUTF8, char *pcDst, int cbDst, char *pcSrc, int cbSrc)
+{
+  size_t     rc;
+  char       *pcDstStart = pcDst;
+  iconv_t    cd;
+  char       *pszToCP, *pszFromCP;
+  int        fError = 0;
+
+  if ( cbDst < 4 )
+    return -1;
+
+  if ( fToUTF8 )
+  {
+    pszToCP   = "UTF-8";
+    pszFromCP = "";
+  }
+  else
+  {
+    pszToCP   = "";
+    pszFromCP = "UTF-8";
+  }
+
+  cd = iconv_open( pszToCP, pszFromCP );
+  if ( cd == (iconv_t)-1 )
+    return -1;
+
+  while( cbSrc > 0 )
+  {
+    rc = iconv( cd, &pcSrc, (size_t *)&cbSrc, &pcDst, (size_t *)&cbDst );
+    if ( rc == (size_t)-1 )
+    {
+      if ( errno == EILSEQ )
+      {
+        // Try to skip invalid character.
+        pcSrc++;
+        cbSrc--;
+        continue;
+      }
+
+      fError = 1;
+      break;
+    }
+  }
+
+  iconv_close( cd );
+
+  // Write trailing ZERO (1 byte for UTF-8, 2 bytes for the system cp.).
+
+  if ( fToUTF8 )
+  {
+    if ( cbDst < 1 )
+    {
+      pcDst--;
+      fError = 1;      // The destination buffer overflow.
+    }
+    *pcDst = '\0';
+  }
+  else
+  {
+    if ( cbDst < 2 )
+    {
+      pcDst -= ( cbDst == 0 ) ? 2 : 1;
+      fError = 1;      // The destination buffer overflow.
+    }
+    *((short *)pcDst) = '\0';
+  }
+
+  return fError ? -1 : ( pcDst - pcDstStart );
+}
+
+char *StrUTF8New(int fToUTF8, char *pcStr, int cbStr)
+{
+  int        cbNewStr = ( ( cbStr > 4 ? cbStr : 4 ) + 1 ) * 2;
+  char       *pszNewStr = malloc( cbNewStr );
+
+  if ( pszNewStr == NULL )
+    return NULL;
+
+  cbNewStr = StrUTF8( fToUTF8, pszNewStr, cbNewStr, pcStr, cbStr );
+  if ( cbNewStr != -1 )
+  {
+    pcStr = realloc( pszNewStr, cbNewStr + ( fToUTF8 ? 1 : sizeof(short) ) );
+    if ( pcStr != NULL )
+      return pcStr;
+  }
+
+  free( pszNewStr );
+  return NULL;
+}
+
+void StrUTF8Free(char *pszStr)
+{
+  free( pszStr );
+}
diff --git a/src/core/os2/geniconv/test.c b/src/core/os2/geniconv/test.c
new file mode 100644
index 0000000..efd0b95
--- /dev/null
+++ b/src/core/os2/geniconv/test.c
@@ -0,0 +1,45 @@
+#include <stdio.h>
+#include <string.h>
+#include <geniconv.h>
+
+void main()
+{
+  char       acBuf[128];
+  char       *inbuf = "ôÅÓÔ - ÐÒÏ×ÅÒËÁ"; // KOI8-R string.
+  size_t     inbytesleft = strlen( inbuf );
+  char       *outbuf = &acBuf;
+  size_t     outbytesleft = sizeof( acBuf );
+  iconv_t    ic;
+
+  // KOI8 -> system cp.
+
+  ic = iconv_open( "", "KOI8-R" );
+  if ( ic == (iconv_t)(-1) )
+  {
+    puts( "iconv_open() fail" );
+    return;
+  }
+
+  iconv( ic, &inbuf, &inbytesleft, &outbuf, &outbytesleft );
+  printf( "KOI8-R to system cp.: %s\n", &acBuf );
+
+  iconv_close( ic );
+
+  // System cp. -> UTF-8 -> system cp.
+
+  // System cp. -> UTF-8 by StrUTF8New().
+  inbuf = StrUTF8New( 1, &acBuf, strlen( &acBuf ) );
+
+  // UTF-8 -> system cp. by StrUTF8().
+  if ( StrUTF8( 0, &acBuf, sizeof(acBuf), inbuf, strlen( inbuf ) ) == -1 )
+    puts( "StrUTF8() failed" );
+  else
+    printf( "system cp. -> UTF-8 -> system cp.: %s\n", &acBuf );
+
+  free( inbuf );
+
+  // Unload used DLL.
+  iconv_clean();
+
+  puts( "Done." );
+}
diff --git a/src/filesystem/os2/SDL_sysfilesystem.c b/src/filesystem/os2/SDL_sysfilesystem.c
new file mode 100644
index 0000000..2420ae2
--- /dev/null
+++ b/src/filesystem/os2/SDL_sysfilesystem.c
@@ -0,0 +1,122 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "../../SDL_internal.h"
+
+#ifdef SDL_FILESYSTEM_OS2
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+/* System dependent filesystem routines                                */
+
+#include "SDL_error.h"
+#include "SDL_filesystem.h"
+#include "../../core/os2/SDL_os2.h"
+
+#include <sys/types.h>
+#include <direct.h>
+#define INCL_DOSPROCESS
+#define INCL_DOSERRORS
+#include <os2.h>
+
+
+char *
+SDL_GetBasePath(void)
+{
+  PTIB       tib;
+  PPIB       pib;
+  ULONG      ulRC = DosGetInfoBlocks( &tib, &pib );
+  PCHAR      pcEnd;
+  ULONG      cbResult;
+  CHAR       acBuf[_MAX_PATH];
+  
+  if ( ulRC != NO_ERROR )
+  {
+    debug( "DosGetInfoBlocks() failed, rc = %u", ulRC );
+    return NULL;
+  }
+
+  pcEnd = SDL_strrchr( pib->pib_pchcmd, '\\' );
+  if ( pcEnd != NULL )
+    pcEnd++;
+  else
+  {
+    if ( pib->pib_pchcmd[1] == ':' )
+      pcEnd = &pib->pib_pchcmd[2];
+    else
+      return NULL;
+  }
+
+  cbResult = pcEnd - pib->pib_pchcmd;
+  SDL_memcpy( &acBuf, pib->pib_pchcmd, cbResult );
+  acBuf[cbResult] = '\0';
+
+  return OS2_SysToUTF8( acBuf );
+}
+
+char *
+SDL_GetPrefPath(const char *org, const char *app)
+{
+  PSZ        pszPath = SDL_getenv( "HOME" );
+  CHAR       acBuf[_MAX_PATH];
+  LONG       lPosApp, lPosOrg;
+  PSZ        pszApp, pszOrg = OS2_UTF8ToSys( org );
+
+  if ( pszOrg == NULL )
+  {
+    SDL_OutOfMemory();
+    return NULL;
+  }
+
+  if ( pszPath == NULL )
+  {
+    pszPath = SDL_getenv( "ETC" );
+    if ( pszPath == NULL )
+      return NULL;
+  }
+
+  lPosApp = SDL_snprintf( &acBuf, sizeof(acBuf) - 1, "%s\\%s", pszPath, pszOrg );
+  SDL_free( pszOrg );
+  if ( lPosApp == -1 )
+    return NULL;
+
+  mkdir( &acBuf );
+
+  pszApp = OS2_UTF8ToSys( app );
+  if ( pszApp == NULL )
+  {
+    SDL_OutOfMemory();
+    return NULL;
+  }
+
+  lPosOrg = SDL_snprintf( &acBuf[lPosApp], sizeof(acBuf) - lPosApp - 1, "\\%s",
+                          pszApp );
+  SDL_free( pszApp );
+  if ( lPosOrg == -1 )
+    return NULL;
+  
+  mkdir( &acBuf );
+  *((PUSHORT)&acBuf[lPosApp + lPosOrg]) = (USHORT)'\0\\';
+
+  return OS2_SysToUTF8( &acBuf );
+}
+
+#endif /* SDL_FILESYSTEM_OS2 */
+
+/* vi: set ts=4 sw=4 expandtab: */
diff --git a/src/loadso/os2/SDL_sysloadso.c b/src/loadso/os2/SDL_sysloadso.c
new file mode 100644
index 0000000..7d08f94
--- /dev/null
+++ b/src/loadso/os2/SDL_sysloadso.c
@@ -0,0 +1,79 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "../../SDL_internal.h"
+
+#ifdef SDL_LOADSO_OS2
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+/* System dependent library loading routines                           */
+
+#include "SDL_loadso.h"
+#include ".\core\os2\SDL_os2.h"
+
+#define INCL_DOSMODULEMGR
+#define INCL_DOSERRORS
+#include <os2.h>
+
+void *
+SDL_LoadObject(const char *sofile)
+{
+  ULONG      ulRC;
+  HMODULE    hModule;
+  PSZ        pszModName = OS2_UTF8ToSys( sofile );
+  CHAR       acError[256];
+
+  ulRC = DosLoadModule( &acError, sizeof(acError), pszModName, &hModule );
+  SDL_free( pszModName );
+  if ( ulRC != NO_ERROR )
+  {
+    SDL_SetError( "Failed loading, module: %s", pszModName );
+    return NULL;
+  }
+
+  return (void *)hModule;
+}
+
+void *
+SDL_LoadFunction(void *handle, const char *name)
+{
+  ULONG      ulRC;
+  PFN        pFN;
+
+  ulRC = DosQueryProcAddr( (HMODULE)handle, 0, name, &pFN );
+  if ( ulRC )
+  {
+    SDL_SetError( "Failed loading, procedure: %s", name );
+    return NULL;
+  }
+
+  return (void *)pFN;
+}
+
+void
+SDL_UnloadObject(void *handle)
+{
+  if ( handle != NULL )
+    DosFreeModule( (HMODULE)handle );
+}
+
+#endif /* SDL_LOADSO_OS2 */
+
+/* vi: set ts=4 sw=4 expandtab: */
diff --git a/src/thread/os2/SDL_sysmutex.c b/src/thread/os2/SDL_sysmutex.c
new file mode 100644
index 0000000..40d094d
--- /dev/null
+++ b/src/thread/os2/SDL_sysmutex.c
@@ -0,0 +1,132 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "../../SDL_internal.h"
+
+#if SDL_THREAD_OS2
+
+/* An implementation of mutexes for OS/2 */
+
+#include "SDL_thread.h"
+#include "SDL_systhread_c.h"
+#include "../../core/os2/SDL_os2.h"
+
+#define INCL_DOSSEMAPHORES
+#define INCL_DOSERRORS
+#include <os2.h>
+
+struct SDL_mutex
+{
+  ULONG      ulHandle;
+};
+
+/* Create a mutex */
+SDL_mutex *
+SDL_CreateMutex(void)
+{
+  ULONG      ulRC;
+  HMTX       hMtx;
+
+  ulRC = DosCreateMutexSem( NULL, &hMtx, 0, FALSE );
+  if ( ulRC != NO_ERROR )
+  {
+    debug( "DosCreateMutexSem(), rc = %u", ulRC );
+    return NULL;
+  }
+
+  return (SDL_mutex *)hMtx;
+}
+
+/* Free the mutex */
+void
+SDL_DestroyMutex(SDL_mutex * mutex)
+{
+  ULONG      ulRC;
+  HMTX       hMtx = (HMTX)mutex;
+
+  ulRC = DosCloseMutexSem( hMtx );
+  if ( ulRC != NO_ERROR )
+    debug( "DosCloseMutexSem(), rc = %u", ulRC );
+}
+
+/* Lock the mutex */
+int
+SDL_LockMutex(SDL_mutex * mutex)
+{
+  ULONG      ulRC;
+  HMTX       hMtx = (HMTX)mutex;
+
+  if ( hMtx == NULLHANDLE )
+    return SDL_SetError( "Passed a NULL mutex" );
+
+  ulRC = DosRequestMutexSem( hMtx, SEM_INDEFINITE_WAIT );
+  if ( ulRC != NO_ERROR )
+  {
+    debug( "DosRequestMutexSem(), rc = %u", ulRC );
+    return -1;
+  }
+
+  return 0;
+}
+
+/* try Lock the mutex */
+int
+SDL_TryLockMutex(SDL_mutex * mutex)
+{
+  ULONG      ulRC;
+  HMTX       hMtx = (HMTX)mutex;
+
+  if ( hMtx == NULLHANDLE )
+    return SDL_SetError( "Passed a NULL mutex" );
+
+  ulRC = DosRequestMutexSem( hMtx, SEM_IMMEDIATE_RETURN );
+
+  if ( ulRC == ERROR_TIMEOUT )
+    return SDL_MUTEX_TIMEDOUT;
+
+  if ( ulRC != NO_ERROR )
+  {
+    debug( "DosRequestMutexSem(), rc = %u", ulRC );
+    return -1;
+  }
+
+  return 0;
+}
+
+/* Unlock the mutex */
+int
+SDL_UnlockMutex(SDL_mutex * mutex)
+{
+  ULONG      ulRC;
+  HMTX       hMtx = (HMTX)mutex;
+
+  if ( hMtx == NULLHANDLE )
+    return SDL_SetError( "Passed a NULL mutex" );
+
+  ulRC = DosReleaseMutexSem( hMtx );
+  if ( ulRC != NO_ERROR )
+    return SDL_SetError( "DosReleaseMutexSem(), rc = %u", ulRC );
+
+  return 0;
+}
+
+#endif /* SDL_THREAD_OS2 */
+
+/* vi: set ts=4 sw=4 expandtab: */
diff --git a/src/thread/os2/SDL_syssem.c b/src/thread/os2/SDL_syssem.c
new file mode 100644
index 0000000..688b6c6
--- /dev/null
+++ b/src/thread/os2/SDL_syssem.c
@@ -0,0 +1,196 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "../../SDL_internal.h"
+
+#if SDL_THREAD_OS2
+
+/* An implementation of semaphores for OS/2 */
+
+#include "SDL_thread.h"
+#include "../../core/os2/SDL_os2.h"
+
+#define INCL_DOSSEMAPHORES
+#define INCL_DOSERRORS
+#define INCL_DOSMISC
+#include <os2.h>
+
+struct SDL_semaphore {
+  HEV        hEv;
+  HMTX       hMtx;
+  ULONG      cPost;
+};
+
+
+SDL_sem *
+SDL_CreateSemaphore(Uint32 initial_value)
+{
+  ULONG      ulRC;
+  SDL_sem    *pSDLSem = SDL_malloc( sizeof(SDL_sem) );
+
+  if ( pSDLSem == NULL )
+  {
+    SDL_OutOfMemory();
+    return NULL;
+  }
+
+  ulRC = DosCreateEventSem( NULL, &pSDLSem->hEv, DCE_AUTORESET, FALSE );
+  if ( ulRC != NO_ERROR )
+  {
+    debug( "DosCreateEventSem(), rc = %u", ulRC );
+    SDL_free( pSDLSem );
+    return NULL;
+  }
+
+  ulRC = DosCreateMutexSem( NULL, &pSDLSem->hMtx, 0, FALSE );
+  if ( ulRC != NO_ERROR )
+  {
+    debug( "DosCreateMutexSem(), rc = %u", ulRC );
+    DosCloseEventSem( pSDLSem->hEv );
+    SDL_free( pSDLSem );
+    return NULL;
+  }
+
+  pSDLSem->cPost = initial_value;
+
+  return pSDLSem;
+}
+
+void
+SDL_DestroySemaphore(SDL_sem * sem)
+{
+  if ( sem == NULL )
+    return;
+
+  DosCloseMutexSem( sem->hMtx );
+  DosCloseEventSem( sem->hEv );
+  SDL_free( sem );
+}
+
+int
+SDL_SemWaitTimeout(SDL_sem * sem, Uint32 timeout)
+{
+  ULONG      ulRC;
+  ULONG      ulStartTime, ulCurTime;
+  ULONG      ulTimeout;
+  ULONG      cPost;
+
+  if ( sem == NULL )
+    return SDL_SetError( "Passed a NULL sem" );
+
+  if ( timeout != SEM_INDEFINITE_WAIT )
+    DosQuerySysInfo( QSV_MS_COUNT, QSV_MS_COUNT, &ulStartTime, sizeof(ULONG) );
+
+  while( TRUE )
+  {
+    ulRC = DosRequestMutexSem( sem->hMtx, SEM_INDEFINITE_WAIT );
+    if ( ulRC != NO_ERROR )
+      return SDL_SetError( "DosRequestMutexSem() failed, rc = %u", ulRC );
+  
+    cPost = sem->cPost;
+    if ( sem->cPost != 0 )
+      sem->cPost--;
+
+    DosReleaseMutexSem( sem->hMtx );
+
+    if ( cPost != 0 )
+      break;
+
+    if ( timeout == SEM_INDEFINITE_WAIT )
+      ulTimeout = SEM_INDEFINITE_WAIT;
+    else
+    {
+      DosQuerySysInfo( QSV_MS_COUNT, QSV_MS_COUNT, &ulCurTime, sizeof(ULONG) );
+      ulTimeout = ulCurTime - ulStartTime;
+      if ( timeout < ulTimeout )
+        return SDL_MUTEX_TIMEDOUT;
+      ulTimeout = timeout - ulTimeout;
+    }
+
+    ulRC = DosWaitEventSem( sem->hEv, ulTimeout );
+    if ( ulRC == ERROR_TIMEOUT )
+      return SDL_MUTEX_TIMEDOUT;
+
+    if ( ulRC != NO_ERROR )
+      return SDL_SetError( "DosWaitEventSem() failed, rc = %u", ulRC );
+  }
+
+  return 0;
+}
+
+int
+SDL_SemTryWait(SDL_sem * sem)
+{
+  return SDL_SemWaitTimeout( sem, 0 );
+}
+
+int
+SDL_SemWait(SDL_sem * sem)
+{
+  return SDL_SemWaitTimeout( sem, SDL_MUTEX_MAXWAIT );
+}
+
+Uint32
+SDL_SemValue(SDL_sem * sem)
+{
+  ULONG      ulRC;
+
+  if ( sem == NULL )
+  {
+    SDL_SetError( "Passed a NULL sem" );
+    return 0;
+  }
+
+  ulRC = DosRequestMutexSem( sem->hMtx, SEM_INDEFINITE_WAIT );
+  if ( ulRC != NO_ERROR )
+    return SDL_SetError( "DosRequestMutexSem() failed, rc = %u", ulRC );
+
+  ulRC = sem->cPost;
+  DosReleaseMutexSem( sem->hMtx );
+
+  return ulRC;
+}
+
+int
+SDL_SemPost(SDL_sem * sem)
+{
+  ULONG      ulRC;
+
+  if ( sem == NULL )
+    return SDL_SetError( "Passed a NULL sem" );
+
+  ulRC = DosRequestMutexSem( sem->hMtx, SEM_INDEFINITE_WAIT );
+  if ( ulRC != NO_ERROR )
+    return SDL_SetError( "DosRequestMutexSem() failed, rc = %u", ulRC );
+
+  sem->cPost++;
+
+  ulRC = DosPostEventSem( sem->hEv );
+  if ( ( ulRC != NO_ERROR ) && ( ulRC != ERROR_ALREADY_POSTED ) )
+    debug( "DosPostEventSem() failed, rc = %u", ulRC );
+
+  DosReleaseMutexSem( sem->hMtx );
+
+  return 0;
+}
+
+#endif /* SDL_THREAD_OS2 */
+
+/* vi: set ts=4 sw=4 expandtab: */
diff --git a/src/thread/os2/SDL_systhread.c b/src/thread/os2/SDL_systhread.c
new file mode 100644
index 0000000..15453f7
--- /dev/null
+++ b/src/thread/os2/SDL_systhread.c
@@ -0,0 +1,187 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "../../SDL_internal.h"
+
+#if SDL_THREAD_OS2
+
+/* Thread management routines for SDL */
+
+#include "SDL_thread.h"
+#include "../SDL_systhread.h"
+#include "../SDL_thread_c.h"
+#include "../SDL_systhread.h"
+#include "SDL_systls_c.h"
+#include "../../core/os2/SDL_os2.h"
+
+#define INCL_DOSPROCESS
+#define INCL_DOSERRORS
+#include <os2.h>
+
+#ifndef SDL_PASSED_BEGINTHREAD_ENDTHREAD
+/* We'll use the C library from this DLL */
+#include <process.h>
+
+/* Cygwin gcc-3 ... MingW64 (even with a i386 host) does this like MSVC. */
+#if (defined(__MINGW32__) && (__GNUC__ < 4))
+typedef unsigned long (__cdecl *pfnSDL_CurrentBeginThread) (void *, unsigned,
+        unsigned (__stdcall *func)(void *), void *arg,
+        unsigned, unsigned *threadID);
+typedef void (__cdecl *pfnSDL_CurrentEndThread)(unsigned code);
+
+#elif defined(__WATCOMC__)
+/* This is for Watcom targets except OS2 */
+#if __WATCOMC__ < 1240
+#define __watcall
+#endif
+typedef unsigned long (__watcall * pfnSDL_CurrentBeginThread) (void *,
+                                                               unsigned,
+                                                               unsigned
+                                                               (__stdcall *
+                                                                func) (void
+                                                                       *),
+                                                               void *arg,
+                                                               unsigned,
+                                                               unsigned
+                                                               *threadID);
+typedef void (__watcall * pfnSDL_CurrentEndThread) (unsigned code);
+
+#else
+typedef uintptr_t(__cdecl * pfnSDL_CurrentBeginThread) (void *, unsigned,
+                                                        unsigned (__stdcall *
+                                                                  func) (void
+                                                                         *),
+                                                        void *arg, unsigned,
+                                                        unsigned *threadID);
+typedef void (__cdecl * pfnSDL_CurrentEndThread) (unsigned code);
+#endif
+#endif /* !SDL_PASSED_BEGINTHREAD_ENDTHREAD */
+
+
+typedef struct ThreadStartParms {
+  void *args;
+  pfnSDL_CurrentEndThread pfnCurrentEndThread;
+} tThreadStartParms, *pThreadStartParms;
+
+static void RunThread(void *data)
+{
+  pThreadStartParms           pThreadParms = (pThreadStartParms) data;
+  pfnSDL_CurrentEndThread     pfnEndThread = pThreadParms->pfnCurrentEndThread;
+  void                        *args = pThreadParms->args;
+
+  SDL_free( pThreadParms );
+
+  if ( ppSDLTLSData != NULL )
+    *ppSDLTLSData = NULL;
+
+  SDL_RunThread( args );
+
+  if ( pfnEndThread != NULL )
+    pfnEndThread();
+}
+
+
+
+#ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD
+int
+SDL_SYS_CreateThread(SDL_Thread * thread, void *args,
+                     pfnSDL_CurrentBeginThread pfnBeginThread,
+                     pfnSDL_CurrentEndThread pfnEndThread)
+#else
+int
+SDL_SYS_CreateThread(SDL_Thread * thread, void *args)
+#endif /* SDL_PASSED_BEGINTHREAD_ENDTHREAD */
+{
+  pThreadStartParms    pThreadParms = SDL_malloc( sizeof(tThreadStartParms) );
+
+  if ( pThreadParms == NULL )
+    return SDL_OutOfMemory();
+
+  // Save the function which we will have to call to clear the RTL of calling app!
+  pThreadParms->pfnCurrentEndThread = pfnEndThread;
+  // Also save the real parameters we have to pass to thread function
+  pThreadParms->args = args;
+
+  // Start the thread using the runtime library of calling app!
+  thread->handle = (SYS_ThreadHandle)
+  #ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD
+    ( (size_t) pfnBeginThread( RunThread, NULL, 65535, pThreadParms ) );
+  #else
+    _beginthread( RunThread, NULL, 65535, pThreadParms );
+  #endif
+
+  if ( thread->handle == -1 )
+      return SDL_SetError( "Not enough resources to create thread" );
+
+  return 0;
+}
+
+void
+SDL_SYS_SetupThread(const char *name)
+{
+    return;
+}
+
+SDL_threadID
+SDL_ThreadID(void)
+{
+  PTIB       tib;
+  PPIB       pib;
+
+  DosGetInfoBlocks( &tib, &pib );
+  return tib->tib_ptib2->tib2_ultid;
+}
+
+int
+SDL_SYS_SetThreadPriority(SDL_ThreadPriority priority)
+{
+  ULONG      ulRC;
+
+  ulRC = DosSetPriority( PRTYS_THREAD,
+                         priority == SDL_THREAD_PRIORITY_LOW
+                           ? PRTYC_IDLETIME
+                           : priority == SDL_THREAD_PRIORITY_HIGH
+                               ? PRTYC_TIMECRITICAL 
+                               : PRTYC_REGULAR ,
+                         0, 0 );
+  if ( ulRC != NO_ERROR )
+    return SDL_SetError( "DosSetPriority() failed, rc = %u", ulRC );
+
+  return 0;
+}
+
+void
+SDL_SYS_WaitThread(SDL_Thread * thread)
+{
+  ULONG      ulRC = DosWaitThread( (PTID)&thread->handle, DCWW_WAIT );
+
+  if ( ulRC != NO_ERROR )
+    debug( "DosWaitThread() failed, rc = %u", ulRC );
+}
+
+void
+SDL_SYS_DetachThread(SDL_Thread * thread)
+{
+  return;
+}
+
+#endif /* SDL_THREAD_OS2 */
+
+/* vi: set ts=4 sw=4 expandtab: */
diff --git a/src/thread/os2/SDL_systhread_c.h b/src/thread/os2/SDL_systhread_c.h
new file mode 100644
index 0000000..ba16277
--- /dev/null
+++ b/src/thread/os2/SDL_systhread_c.h
@@ -0,0 +1,25 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "../../SDL_internal.h"
+
+typedef int SYS_ThreadHandle;
+
+/* vi: set ts=4 sw=4 expandtab: */
diff --git a/src/thread/os2/SDL_systls.c b/src/thread/os2/SDL_systls.c
new file mode 100644
index 0000000..023a241
--- /dev/null
+++ b/src/thread/os2/SDL_systls.c
@@ -0,0 +1,89 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+
+#include "../../SDL_internal.h"
+
+#if SDL_THREAD_OS2
+
+#include "../../core/os2/SDL_os2.h"
+
+#include "SDL_thread.h"
+#include "../SDL_thread_c.h"
+
+#define INCL_DOSPROCESS
+#define INCL_DOSERRORS
+#include <os2.h>
+
+SDL_TLSData     **ppSDLTLSData = NULL;
+
+static ULONG           cTLSAlloc = 0;
+
+// SDL_OS2TLSAlloc() called from SDL_InitSubSystem()
+void SDL_OS2TLSAlloc()
+{
+  ULONG      ulRC;
+
+  if ( ( cTLSAlloc == 0 ) || ( ppSDLTLSData == NULL ) )
+  {
+    // First call - allocate the thread local memory (1 DWORD).
+    ulRC = DosAllocThreadLocalMemory( 1, (PULONG *)&ppSDLTLSData );
+    if ( ulRC != NO_ERROR )
+      debug( "DosAllocThreadLocalMemory() failed, rc = %u", ulRC );
+  }
+  cTLSAlloc++;
+}
+
+// SDL_OS2TLSFree() called from SDL_QuitSubSystem()
+void SDL_OS2TLSFree()
+{
+  ULONG      ulRC;
+
+  if ( cTLSAlloc != 0 )
+    cTLSAlloc--;
+
+  if ( ( cTLSAlloc == 0 ) && ( ppSDLTLSData != NULL ) )
+  {
+    // Last call - free the thread local memory.
+    ulRC = DosFreeThreadLocalMemory( (PULONG)ppSDLTLSData );
+    if ( ulRC != NO_ERROR )
+      debug( "DosFreeThreadLocalMemory() failed, rc = %u", ulRC );
+    else
+      ppSDLTLSData = NULL;
+  }
+}
+
+SDL_TLSData *SDL_SYS_GetTLSData()
+{
+  return ppSDLTLSData == NULL ? NULL : *ppSDLTLSData;
+}
+
+int SDL_SYS_SetTLSData(SDL_TLSData *data)
+{
+  if ( ppSDLTLSData == NULL )
+    return -1;
+
+  *ppSDLTLSData = data;
+  return 0;
+}
+
+#endif /* SDL_THREAD_OS2 */
+
+/* vi: set ts=4 sw=4 expandtab: */
diff --git a/src/thread/os2/SDL_systls_c.h b/src/thread/os2/SDL_systls_c.h
new file mode 100644
index 0000000..7c63e90
--- /dev/null
+++ b/src/thread/os2/SDL_systls_c.h
@@ -0,0 +1,38 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+
+#include "../../SDL_internal.h"
+
+#if SDL_THREAD_OS2
+
+#include "../SDL_thread_c.h"
+
+extern SDL_TLSData     **ppSDLTLSData;
+
+// SDL_OS2TLSAlloc() called from SDL_InitSubSystem()
+void SDL_OS2TLSAlloc();
+
+// SDL_OS2TLSFree() called from SDL_QuitSubSystem()
+void SDL_OS2TLSFree();
+
+#endif /* SDL_THREAD_OS2 */
+
+/* vi: set ts=4 sw=4 expandtab: */
diff --git a/src/timer/os2/SDL_systimer.c b/src/timer/os2/SDL_systimer.c
new file mode 100644
index 0000000..aa4ac93
--- /dev/null
+++ b/src/timer/os2/SDL_systimer.c
@@ -0,0 +1,189 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "../../SDL_internal.h"
+
+#if SDL_TIMER_OS2
+
+#include "SDL_timer.h"
+#include "../../core/os2/SDL_os2.h"
+
+#define INCL_DOSERRORS
+#define INCL_DOSMISC
+#define INCL_DOSPROFILE
+#define INCL_DOSSEMAPHORES
+#define INCL_DOSDATETIME
+#define INCL_DOSPROCESS
+#define INCL_DOSEXCEPTIONS
+#include <os2.h>
+
+// No need to switch priorities in SDL_Delay() for OS/2 versions > Warp3 fp 42.
+//#define _SWITCH_PRIORITY
+
+typedef unsigned long long       ULLONG;
+
+static ULONG           ulTmrFreq = 0;
+static ULLONG          ullTmrStart;
+
+void
+SDL_TicksInit(void)
+{
+  ULONG      ulRC;
+
+  ulRC = DosTmrQueryFreq( &ulTmrFreq );
+  if ( ulRC != NO_ERROR )
+  {
+    debug( "DosTmrQueryFreq() failed, rc = %u", ulRC );
+  }
+  else
+  {
+    ulRC = DosTmrQueryTime( (PQWORD)&ullTmrStart );
+    if ( ulRC == NO_ERROR )
+      return;
+
+    debug( "DosTmrQueryTime() failed, rc = %u", ulRC );
+  }
+
+  ulTmrFreq = 0; // Error - use DosQuerySysInfo() for timer.
+
+  DosQuerySysInfo( QSV_MS_COUNT, QSV_MS_COUNT, (PULONG)&ullTmrStart,
+                   sizeof(ULONG) );
+}
+
+void
+SDL_TicksQuit(void)
+{
+}
+
+Uint32
+SDL_GetTicks(void)
+{
+  ULONG      ulResult;
+  ULLONG     ullTmrNow;
+
+  if ( ulTmrFreq == 0 )
+    // Was not initialized.
+    SDL_TicksInit();
+
+  if ( ulTmrFreq != 0 )
+  {
+    DosTmrQueryTime( (PQWORD)&ullTmrNow );
+    ulResult = ( ullTmrNow - ullTmrStart ) * 1000 / ulTmrFreq;
+  }
+  else
+  {
+    DosQuerySysInfo( QSV_MS_COUNT, QSV_MS_COUNT, (PULONG)&ullTmrNow,
+                     sizeof(ULONG) );
+    ulResult = (ULONG)ullTmrNow - (ULONG)ullTmrStart;
+  }
+
+  return ulResult;
+}
+
+Uint64
+SDL_GetPerformanceCounter(void)
+{
+  QWORD      qwTmrNow;
+
+  if ( ( ulTmrFreq == 0 ) || ( DosTmrQueryTime( &qwTmrNow ) != NO_ERROR ) )
+    return SDL_GetTicks();
+
+  return *((Uint64 *)&qwTmrNow);
+}
+
+Uint64
+SDL_GetPerformanceFrequency(void)
+{
+  return ulTmrFreq == 0 ? 1000 : (Uint64)ulTmrFreq;
+}
+
+void
+SDL_Delay(Uint32 ms)
+{
+  HTIMER     hTimer = NULLHANDLE;
+  ULONG      ulRC;
+#ifdef _SWITCH_PRIORITY
+  PPIB       pib;
+  PTIB       tib;
+  BOOL       fSetPriority = ms < 50;
+  ULONG      ulSavePriority;
+  ULONG      ulNesting;
+#endif
+  HEV        hevTimer;
+
+  if ( ms == 0 )
+  {
+    DosSleep( 0 );
+    return;
+  }
+
+  ulRC = DosCreateEventSem( NULL, &hevTimer, DC_SEM_SHARED, FALSE );
+  if ( ulRC != NO_ERROR )
+  {
+    debug( "DosAsyncTimer() failed, rc = %u", ulRC );
+    DosSleep( ms );
+    return;
+  }
+
+#ifdef _SWITCH_PRIORITY
+  if ( fSetPriority )
+  {
+    if ( DosGetInfoBlocks( &tib, &pib ) != NO_ERROR )
+      fSetPriority = FALSE;
+    else
+    {
+      ulSavePriority = tib->tib_ptib2->tib2_ulpri;
+      if ( ( (ulSavePriority & 0xFF00) == 0x0300 ) ||  // already have high pr.
+           ( DosEnterMustComplete( &ulNesting ) != NO_ERROR ) )
+        fSetPriority = FALSE;
+      else
+        DosSetPriority( PRTYS_THREAD, PRTYC_TIMECRITICAL, 0, 0 );
+    }
+  }
+#endif
+
+  DosResetEventSem( hevTimer, &ulRC );
+  ulRC = DosAsyncTimer( ms, (HSEM)hevTimer, &hTimer );
+
+#ifdef _SWITCH_PRIORITY
+  if ( fSetPriority )
+  {
+    if ( DosSetPriority( PRTYS_THREAD, (ulSavePriority >> 8) & 0xFF, 0, 0 ) ==
+           NO_ERROR )
+      DosSetPriority( PRTYS_THREAD, 0, ulSavePriority & 0xFF, 0 );
+
+    DosExitMustComplete( &ulNesting );
+  }
+#endif
+
+  if ( ulRC != NO_ERROR )
+    debug( "DosAsyncTimer() failed, rc = %u", ulRC );
+  else
+    DosWaitEventSem( hevTimer, SEM_INDEFINITE_WAIT );
+
+  if ( ulRC != NO_ERROR )
+    DosSleep( ms );
+
+  DosCloseEventSem( hevTimer );
+}
+
+#endif /* SDL_TIMER_OS2 */
+
+/* vi: set ts=4 sw=4 expandtab: */
diff --git a/src/video/os2/SDL_os2dive.c b/src/video/os2/SDL_os2dive.c
new file mode 100644
index 0000000..d4f4982
--- /dev/null
+++ b/src/video/os2/SDL_os2dive.c
@@ -0,0 +1,348 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "../../SDL_internal.h"
+#include "../SDL_sysvideo.h"
+#define INCL_WIN
+#define INCL_GPI
+#include <os2.h>
+#define  _MEERROR_H_
+#include <mmioos2.h>
+#include <os2me.h>
+#define INCL_MM_OS2
+#include <dive.h>
+#include <fourcc.h>
+#include "SDL_os2output.h"
+
+typedef struct _VODATA {
+  HDIVE      hDive;
+  PVOID      pBuffer;
+  ULONG      ulDIVEBufNum;
+  FOURCC     fccColorEncoding;
+  ULONG      ulWidth;
+  ULONG      ulHeight;
+  BOOL       fBlitterReady;
+} VODATA;
+
+static BOOL voQueryInfo(PVIDEOOUTPUTINFO pInfo);
+static PVODATA voOpen();
+static VOID voClose(PVODATA pVOData);
+static BOOL voSetVisibleRegion(PVODATA pVOData, HWND hwnd,
+                               SDL_DisplayMode *pSDLDisplayMode,
+                               HRGN hrgnShape, BOOL fVisible);
+static PVOID voVideoBufAlloc(PVODATA pVOData, ULONG ulWidth, ULONG ulHeight,
+                             ULONG ulBPP, ULONG fccColorEncoding,
+                             PULONG pulScanLineSize);
+static VOID voVideoBufFree(PVODATA pVOData);
+static BOOL voUpdate(PVODATA pVOData, HWND hwnd, SDL_Rect *pSDLRects,
+                     ULONG cSDLRects);
+
+OS2VIDEOOUTPUT voDive = {
+  voQueryInfo,
+  voOpen,
+  voClose,
+  voSetVisibleRegion,
+  voVideoBufAlloc,
+  voVideoBufFree,
+  voUpdate
+};
+
+
+static BOOL voQueryInfo(PVIDEOOUTPUTINFO pInfo)
+{
+  DIVE_CAPS            sDiveCaps = { 0 };
+  FOURCC               fccFormats[100] = { 0 };
+
+  // Query information about display hardware from DIVE.
+
+  sDiveCaps.pFormatData    = &fccFormats;
+  sDiveCaps.ulFormatLength = 100;
+  sDiveCaps.ulStructLen    = sizeof(DIVE_CAPS);
+
+  if ( DiveQueryCaps( &sDiveCaps, DIVE_BUFFER_SCREEN ) )
+  {
+    debug( "DiveQueryCaps() failed." );
+    return FALSE;
+  }
+
+  if ( sDiveCaps.ulDepth < 8 )
+  {
+    debug( "Not enough screen colors to run DIVE. "
+           "Must be at least 256 colors." );
+    return FALSE;
+  }
+
+  pInfo->ulBPP             = sDiveCaps.ulDepth;
+  pInfo->fccColorEncoding  = sDiveCaps.fccColorEncoding;
+  pInfo->ulScanLineSize    = sDiveCaps.ulScanLineBytes;
+  pInfo->ulHorizResolution = sDiveCaps.ulHorizontalResolution;
+  pInfo->ulVertResolution  = sDiveCaps.ulVerticalResolution;
+
+  return TRUE;
+}
+
+PVODATA voOpen()
+{
+  PVODATA    pVOData = SDL_calloc( 1, sizeof(VODATA) );
+
+  if ( pVOData == NULL )
+  {
+    SDL_OutOfMemory();
+    return NULL;
+  }
+
+  if ( DiveOpen( &pVOData->hDive, FALSE, NULL ) != DIVE_SUCCESS )
+  {
+    SDL_free( pVOData );
+    SDL_SetError( "DIVE: A display engine instance open failed" );
+    return NULL;
+  }
+
+  return pVOData;
+}
+
+static VOID voClose(PVODATA pVOData)
+{
+  voVideoBufFree( pVOData );
+  DiveClose( pVOData->hDive );
+  SDL_free( pVOData );
+}
+
+static BOOL voSetVisibleRegion(PVODATA pVOData, HWND hwnd,
+                               SDL_DisplayMode *pSDLDisplayMode,
+                               HRGN hrgnShape, BOOL fVisible)
+{
+  HPS        hps;
+  HRGN       hrgn;
+  RGNRECT    rgnCtl;
+  PRECTL     prectl = NULL;
+  ULONG      ulRC;
+
+  if ( !fVisible )
+  {
+    if ( pVOData->fBlitterReady )
+    {
+      pVOData->fBlitterReady = FALSE;
+      DiveSetupBlitter( pVOData->hDive, 0 );
+      debug( "DIVE blitter is tuned off" );
+    }
+    return TRUE;
+  }
+
+  // Query visible rectangles
+
+  hps = WinGetPS( hwnd );
+  hrgn = GpiCreateRegion( hps, 0, NULL );
+  if ( hrgn == NULLHANDLE )
+  {
+    WinReleasePS( hps );
+    SDL_SetError( "GpiCreateRegion() failed" );
+  }
+  else
+  {
+    WinQueryVisibleRegion( hwnd, hrgn );
+    if ( hrgnShape != NULLHANDLE )
+      GpiCombineRegion( hps, hrgn, hrgn, hrgnShape, CRGN_AND );
+
+    rgnCtl.ircStart     = 1;
+    rgnCtl.crc          = 0;
+    rgnCtl.ulDirection  = 1;
+    GpiQueryRegionRects( hps, hrgn, NULL, &rgnCtl, NULL );
+    if ( rgnCtl.crcReturned != 0 )
+    {
+      prectl = SDL_malloc( rgnCtl.crcReturned * sizeof(RECTL) );
+      if ( prectl != NULL )
+      {
+        rgnCtl.ircStart     = 1;
+        rgnCtl.crc          = rgnCtl.crcReturned;
+        rgnCtl.ulDirection  = 1;
+        GpiQueryRegionRects( hps, hrgn, NULL, &rgnCtl, prectl );
+      }
+      else
+        SDL_OutOfMemory();
+    }
+    GpiDestroyRegion( hps, hrgn );
+    WinReleasePS( hps );
+
+    if ( prectl != NULL )
+    {
+      // Setup DIVE blitter.
+      SETUP_BLITTER      sSetupBlitter;
+      SWP                swp;
+      POINTL             pointl = { 0 };
+
+      WinQueryWindowPos( hwnd, &swp );
+      WinMapWindowPoints( hwnd, HWND_DESKTOP, &pointl, 1 );
+
+      sSetupBlitter.ulStructLen       = sizeof(SETUP_BLITTER);
+      sSetupBlitter.fccSrcColorFormat = pVOData->fccColorEncoding;
+      sSetupBlitter.fInvert           = FALSE;
+      sSetupBlitter.ulSrcWidth        = pVOData->ulWidth;
+      sSetupBlitter.ulSrcHeight       = pVOData->ulHeight;
+      sSetupBlitter.ulSrcPosX         = 0;
+      sSetupBlitter.ulSrcPosY         = 0;
+      sSetupBlitter.ulDitherType      = 0;
+      sSetupBlitter.fccDstColorFormat = FOURCC_SCRN;
+      sSetupBlitter.ulDstWidth        = swp.cx;
+      sSetupBlitter.ulDstHeight       = swp.cy;
+      sSetupBlitter.lDstPosX          = 0;
+      sSetupBlitter.lDstPosY          = 0;
+      sSetupBlitter.lScreenPosX       = pointl.x;
+      sSetupBlitter.lScreenPosY       = pointl.y;
+
+      sSetupBlitter.ulNumDstRects     = rgnCtl.crcReturned;
+      sSetupBlitter.pVisDstRects      = prectl;
+
+      ulRC = DiveSetupBlitter( pVOData->hDive, &sSetupBlitter );
+      SDL_free( prectl );
+
+      if ( ulRC == DIVE_SUCCESS )
+      {
+        pVOData->fBlitterReady = TRUE;
+        WinInvalidateRect( hwnd, NULL, TRUE );
+        debug( "DIVE blitter is ready now." );
+        return TRUE;
+      }
+
+      SDL_SetError( "DiveSetupBlitter(), rc = 0x%X", ulRC );
+    } // if ( prectl != NULL )
+  } // if ( hrgn == NULLHANDLE ) else
+
+  pVOData->fBlitterReady = FALSE;
+  DiveSetupBlitter( pVOData->hDive, 0 );
+  return FALSE;
+}
+
+static PVOID voVideoBufAlloc(PVODATA pVOData, ULONG ulWidth, ULONG ulHeight,
+                             ULONG ulBPP, FOURCC fccColorEncoding,
+                             PULONG pulScanLineSize)
+{
+  ULONG      ulRC;
+  ULONG      ulScanLineSize = ulWidth * (ulBPP >> 3);
+
+  // Destroy previous buffer.
+  voVideoBufFree( pVOData );
+
+  if ( ( ulWidth == 0 ) || ( ulHeight == 0 ) || ( ulBPP == 0 ) )
+    return NULL;
+
+  // Bytes per line.
+  ulScanLineSize = ( ulScanLineSize + 3 ) & ~3;	/* 4-byte aligning */
+  *pulScanLineSize = ulScanLineSize;
+
+  ulRC = DosAllocMem( &pVOData->pBuffer,
+                      (ulHeight * ulScanLineSize) + sizeof(ULONG),
+                      PAG_COMMIT | PAG_EXECUTE | PAG_READ | PAG_WRITE );
+  if ( ulRC != NO_ERROR )
+  {
+    debug( "DosAllocMem(), rc = %u", ulRC );
+    return NULL;
+  }
+
+  ulRC = DiveAllocImageBuffer( pVOData->hDive, &pVOData->ulDIVEBufNum,
+                               fccColorEncoding, ulWidth, ulHeight,
+                               ulScanLineSize, pVOData->pBuffer );
+  if ( ulRC != DIVE_SUCCESS )
+  {
+    debug( "DiveAllocImageBuffer(), rc = 0x%X", ulRC );
+    DosFreeMem( pVOData->pBuffer );
+    pVOData->pBuffer = NULL;
+    pVOData->ulDIVEBufNum = 0;
+    return NULL;
+  }
+
+  pVOData->fccColorEncoding = fccColorEncoding;
+  pVOData->ulWidth = ulWidth;
+  pVOData->ulHeight = ulHeight;
+
+  debug( "buffer: 0x%P, DIVE buffer number: %u",
+         pVOData->pBuffer, pVOData->ulDIVEBufNum );
+
+  return pVOData->pBuffer;
+}
+
+static VOID voVideoBufFree(PVODATA pVOData)
+{
+  ULONG      ulRC;
+
+  if ( pVOData->ulDIVEBufNum != 0 )
+  {
+    ulRC = DiveFreeImageBuffer( pVOData->hDive, pVOData->ulDIVEBufNum );
+    if ( ulRC != DIVE_SUCCESS )
+      debug( "DiveFreeImageBuffer(,%u), rc = %u", pVOData->ulDIVEBufNum, ulRC );
+    else
+      debug( "DIVE buffer %u destroyed", pVOData->ulDIVEBufNum );
+
+    pVOData->ulDIVEBufNum = 0;
+  }
+
+  if ( pVOData->pBuffer != NULL )
+  {
+    ulRC = DosFreeMem( pVOData->pBuffer );
+    if ( ulRC != NO_ERROR )
+      debug( "DosFreeMem(), rc = %u", ulRC );
+
+    pVOData->pBuffer = NULL;
+  }
+}
+
+static BOOL voUpdate(PVODATA pVOData, HWND hwnd, SDL_Rect *pSDLRects,
+                     ULONG cSDLRects)
+{
+  ULONG                ulRC;
+
+  if ( !pVOData->fBlitterReady || ( pVOData->ulDIVEBufNum == 0 ) )
+  {
+    debug( "DIVE blitter is not ready" );
+    return FALSE;
+  }
+
+  if ( pSDLRects != 0 )
+  {
+    PBYTE              pbLineMask;
+
+    pbLineMask = SDL_stack_alloc( BYTE, pVOData->ulHeight );
+    if ( pbLineMask == NULL )
+    {
+      debug( "Not enough stack size" );
+      return FALSE;
+    }
+    memset( pbLineMask, 0, pVOData->ulHeight );
+
+    for( ; ((LONG)cSDLRects) > 0; cSDLRects--, pSDLRects++ )
+      memset( &pbLineMask[pSDLRects->y], 1, pSDLRects->h );
+
+    ulRC = DiveBlitImageLines( pVOData->hDive, pVOData->ulDIVEBufNum,
+                               DIVE_BUFFER_SCREEN, pbLineMask );
+    SDL_stack_free( pbLineMask );
+
+    if ( ulRC != DIVE_SUCCESS )
+      debug( "DiveBlitImageLines(), rc = 0x%X", ulRC );
+  }
+  else
+  {
+    ulRC = DiveBlitImage( pVOData->hDive, pVOData->ulDIVEBufNum,
+                          DIVE_BUFFER_SCREEN );
+    if ( ulRC != DIVE_SUCCESS )
+      debug( "DiveBlitImage(), rc = 0x%X", ulRC );
+  }
+
+  return ulRC == DIVE_SUCCESS;
+}
diff --git a/src/video/os2/SDL_os2messagebox.c b/src/video/os2/SDL_os2messagebox.c
new file mode 100644
index 0000000..9361199
--- /dev/null
+++ b/src/video/os2/SDL_os2messagebox.c
@@ -0,0 +1,593 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "../../SDL_internal.h"
+
+#if SDL_VIDEO_DRIVER_OS2
+
+/* Display a OS/2 message box */
+
+#include "SDL.h"
+#include "../../core/os2/SDL_os2.h"
+#include "SDL_os2video.h"
+#define INCL_WIN
+#include <os2.h>
+
+#define IDD_TEXT_MESSAGE         1001
+#define IDD_BITMAP               1002
+#define IDD_PB_FIRST             1003
+
+typedef struct _MSGBOXDLGDATA {
+  USHORT     cb;
+  HWND       hwndUnder;
+} MSGBOXDLGDATA, *PMSGBOXDLGDATA;
+
+static VOID _wmInitDlg(HWND hwnd, PMSGBOXDLGDATA pDlgData)
+{
+  HPS        hps = WinGetPS( hwnd );
+  POINTL     aptText[TXTBOX_COUNT];
+  HENUM      hEnum;
+  HWND       hWndNext;
+  CHAR       acBuf[256];
+  ULONG      cbBuf;
+  ULONG      cButtons = 0;
+  ULONG      ulButtonsCY = 0;
+  ULONG      ulButtonsCX = 0;
+  RECTL      rectl;
+  ULONG      ulX;
+  ULONG      ulIdx;
+  struct _BUTTON {
+    HWND     hwnd;     // Button window handle.
+    ULONG    ulCX;     // Button width in dialog coordinates.
+  }          aButtons[32];
+  RECTL      rectlItem;
+  HAB        hab = WinQueryAnchorBlock( hwnd );
+
+  /* --- Align the buttons to the right/bottom. --- */
+
+  // Collect window handles of all buttons in dialog.
+
+  hEnum = WinBeginEnumWindows( hwnd );
+
+  while( ( hWndNext = WinGetNextWindow( hEnum ) ) != NULLHANDLE )
+  {
+    if ( WinQueryClassName( hWndNext, sizeof(acBuf), &acBuf ) == 0 )
+      continue;
+
+    if ( strcmp( &acBuf, "#3" ) == 0 ) // Class name of button.
+    {
+      if ( cButtons < sizeof(aButtons) / sizeof(struct _BUTTON) )
+      {
+        aButtons[cButtons].hwnd = hWndNext;
+        cButtons++;
+      }
+    }
+  }
+  WinEndEnumWindows( hEnum );
+
+  // Query size of text for each button, get width of each button, total
+  // buttons width (ulButtonsCX) and max. height (ulButtonsCX) in _dialog
+  // coordinates_.
+
+  hps = WinGetPS( hwnd );
+
+  for( ulIdx = 0; ulIdx < cButtons; ulIdx++ )
+  {
+    // Query size of text in window coordinates.
+    cbBuf = WinQueryWindowText( aButtons[ulIdx].hwnd, sizeof(acBuf), &acBuf );
+    GpiQueryTextBox( hps, cbBuf, acBuf, TXTBOX_COUNT, &aptText );
+    aptText[TXTBOX_TOPRIGHT].x -= aptText[TXTBOX_BOTTOMLEFT].x;
+    aptText[TXTBOX_TOPRIGHT].y -= aptText[TXTBOX_BOTTOMLEFT].y;
+    // Convert text size to dialog coordinates.
+    WinMapDlgPoints( hwnd, &aptText[TXTBOX_TOPRIGHT], 1, FALSE );
+    // Add vertical and horizontal space for button's frame (dialog coord.).
+    if ( aptText[TXTBOX_TOPRIGHT].x < 30 ) // Minimal button width.
+      aptText[TXTBOX_TOPRIGHT].x = 30;
+    else
+      aptText[TXTBOX_TOPRIGHT].x += 4;
+    aptText[TXTBOX_TOPRIGHT].y += 3;
+
+    aButtons[ulIdx].ulCX = aptText[TXTBOX_TOPRIGHT].x; // Store button width.
+    ulButtonsCX += aptText[TXTBOX_TOPRIGHT].x + 2;     // Add total btn. width.
+    // Get max. height for buttons.
+    if ( ulButtonsCY < aptText[TXTBOX_TOPRIGHT].y )
+      ulButtonsCY = aptText[TXTBOX_TOPRIGHT].y + 1;
+  }
+
+  WinReleasePS( hps );
+
+  // Expand horizontal size of the window to fit all buttons and move window
+  // to the center of parent window.
+
+  // Convert total width of buttons to window coordinates.
+  aptText[0].x = ulButtonsCX + 4;
+  WinMapDlgPoints( hwnd, &aptText[0], 1, TRUE );
+  // Check width of the window and expand as needed.
+  WinQueryWindowRect( hwnd, &rectlItem );
+  if ( rectlItem.xRight <= aptText[0].x )
+    rectlItem.xRight = aptText[0].x;
+
+  // Move window rectangle to the center of owner window.
+  WinQueryWindowRect( pDlgData->hwndUnder, &rectl );
+    // Left-bottom point of centered dialog on owner window.
+  rectl.xLeft = ( rectl.xRight - rectlItem.xRight ) / 2;
+  rectl.yBottom = ( rectl.yTop - rectlItem.yTop ) / 2;
+    // Map left-bottom point to desktop.
+  WinMapWindowPoints( pDlgData->hwndUnder, HWND_DESKTOP, (PPOINTL)&rectl, 1 );
+  WinOffsetRect( hab, &rectlItem, rectl.xLeft, rectl.yBottom );
+
+  // Set new rectangle for the window.
+  WinSetWindowPos( hwnd, HWND_TOP, rectlItem.xLeft, rectlItem.yBottom,
+                   rectlItem.xRight - rectlItem.xLeft,
+                   rectlItem.yTop - rectlItem.yBottom,
+                   SWP_SIZE | SWP_MOVE );
+
+  // Set buttons positions.
+
+  // Get horizontal position for the first button.
+  WinMapDlgPoints( hwnd, (PPOINTL)&rectlItem, 2, FALSE ); // Win size to dlg coord.
+  ulX = rectlItem.xRight - rectlItem.xLeft - ulButtonsCX - 2; // First button position.
+
+  // Set positions and sizes for all buttons.
+  for( ulIdx = 0; ulIdx < cButtons; ulIdx++ )
+  {
+    // Get poisition and size for the button in dialog coordinates.
+    aptText[0].x = ulX;
+    aptText[0].y = 2;
+    aptText[1].x = aButtons[ulIdx].ulCX;
+    aptText[1].y = ulButtonsCY;
+    // Convert to window coordinates.
+    WinMapDlgPoints( hwnd, &aptText, 2, TRUE );
+
+    WinSetWindowPos( aButtons[ulIdx].hwnd, HWND_TOP,
+                     aptText[0].x, aptText[0].y, aptText[1].x, aptText[1].y,
+                     SWP_MOVE | SWP_SIZE );
+
+    // Offset horizontal position for the next button.
+    ulX += aButtons[ulIdx].ulCX + 2;
+  }  
+
+  // Set right bound of the text to right bound of the last button and
+  // bottom bound of the text just above the buttons.
+
+  aptText[2].x = 25;              // Left bound of text in dlg coordinates.
+  aptText[2].y = ulButtonsCY + 3; // Bottom bound of the text in dlg coords.
+  WinMapDlgPoints( hwnd, &aptText[2], 1, TRUE ); // Convert ^^^ to win. coords.
+  hWndNext = WinWindowFromID( hwnd, IDD_TEXT_MESSAGE );
+  WinQueryWindowRect( hWndNext, &rectlItem );
+  rectlItem.xLeft = aptText[2].x;
+  rectlItem.yBottom = aptText[2].y;
+  // Right bound of the text equals right bound of the last button.
+  rectlItem.xRight = aptText[0].x + aptText[1].x;
+  WinSetWindowPos( hWndNext, HWND_TOP, rectlItem.xLeft, rectlItem.yBottom,
+                   rectlItem.xRight - rectlItem.xLeft,
+                   rectlItem.yTop - rectlItem.yBottom,
+                   SWP_MOVE | SWP_SIZE );
+}
+
+MRESULT EXPENTRY DynDlgProc(HWND hwnd, USHORT message, MPARAM mp1, MPARAM mp2)
+{
+  switch( message )
+  {
+    case WM_INITDLG:
+      _wmInitDlg( hwnd, (PMSGBOXDLGDATA)mp2 );
+      break;
+
+    case WM_COMMAND:
+      switch( SHORT1FROMMP(mp1) )
+      {
+        case DID_OK:
+          WinDismissDlg( hwnd, FALSE );
+          break;
+
+        default:
+          break;
+      }
+
+    default:
+      return( WinDefDlgProc( hwnd, message, mp1, mp2 ) );
+  }
+
+  return FALSE;
+}
+
+static HWND _makeDlg(const SDL_MessageBoxData *messageboxdata)
+{
+  SDL_MessageBoxButtonData       *pSDLBtnData =
+                           (SDL_MessageBoxButtonData *)messageboxdata->buttons;
+  ULONG                cSDLBtnData = messageboxdata->numbuttons;
+
+  PSZ                  pszTitle = OS2_UTF8ToSys( (PSZ)messageboxdata->title );
+  ULONG                cbTitle = pszTitle == NULL ?
+                                             0 : strlen( pszTitle );
+  PSZ                  pszText = OS2_UTF8ToSys( (PSZ)messageboxdata->message );
+  ULONG                cbText = pszText == NULL ?
+                                             0 : strlen( pszText );
+
+  PDLGTEMPLATE         pTemplate;
+  ULONG                cbTemplate;
+  ULONG                ulIdx;
+  PCHAR                pcDlgData;
+  PDLGTITEM            pDlgItem;
+  PSZ                  pszBtnText;
+  ULONG                cbBtnText;
+  HWND                 hwnd;
+  SDL_MessageBoxColor  *pSDLColors = messageboxdata->colorScheme == NULL
+                 ? NULL
+                 : (SDL_MessageBoxColor *)&messageboxdata->colorScheme->colors;
+  SDL_MessageBoxColor  *pSDLColor;
+  MSGBOXDLGDATA        stDlgData;
+
+  /* Build a dialog tamplate in memory */
+
+  // Size of template (cbTemplate).
+
+  cbTemplate = sizeof(DLGTEMPLATE) + ( (2 + cSDLBtnData) * sizeof(DLGTITEM) ) +
+               sizeof(ULONG) +    // First item data - frame control data.
+               cbTitle + 1 +      // First item data - frame title + ZERO.
+               cbText + 1 +       // Second item data - ststic text + ZERO.
+               3;                 // Third item data - system icon Id.
+  // Button items datas - text for buttons.
+  for( ulIdx = 0; ulIdx < cSDLBtnData; ulIdx++ )
+  {
+    pszBtnText = (PSZ)pSDLBtnData[ulIdx].text;
+    cbTemplate += pszBtnText == NULL ? 1 : ( strlen( pszBtnText ) + 1 );
+  }
+  // Presentation parameter space.
+  if ( pSDLColors != NULL )
+    cbTemplate += 26 /* PP for frame. */ + 26 /* PP for static text. */ +
+                  ( 48 * cSDLBtnData ); /* PP for buttons. */
+
+  // Allocate memory for the dialog template.
+  pTemplate = SDL_malloc( cbTemplate );
+  // Pointer on data for dialog items in allocated memory.
+  pcDlgData = &((PCHAR)pTemplate)[ sizeof(DLGTEMPLATE) +
+                                   ( (2 + cSDLBtnData) * sizeof(DLGTITEM) ) ];
+
+  /* Header info */
+
+  pTemplate->cbTemplate = cbTemplate; /* size of dialog template to pass to WinCreateDlg() */
+  pTemplate->type = 0;                /* Currently always 0. */
+  pTemplate->codepage = 0;
+  pTemplate->offadlgti = 14;          /* Offset to array of DLGTITEMs. */
+  pTemplate->fsTemplateStatus = 0;    /* Reserved field?  */
+
+  /* Index in array of dlg items of item to get focus,          */
+  /* if 0 then focus goes to first control that can have focus. */
+  pTemplate->iItemFocus = 0;
+  pTemplate->coffPresParams = 0;
+
+  /* First item info - frame */
+
+  pDlgItem = &pTemplate->adlgti;
+  pDlgItem->fsItemStatus = 0;  /* Reserved? */
+  /* Number of dialog item child windows owned by this item. */
+  pDlgItem->cChildren = 2 + cSDLBtnData; // Ststic text + buttons.
+  /* Length of class name, if 0 then offClassname contains a WC_ value. */
+  pDlgItem->cchClassName = 0;
+  pDlgItem->offClassName = (USHORT)WC_FRAME;
+  /* Length of text. */
+  pDlgItem->cchText = cbTitle + 1; /* +1 - trailing ZERO. */
+  pDlgItem->offText = pcDlgData - (PCHAR)pTemplate; /* Offset to title text.  */
+  /* Copy text for the title into the dialog template. */
+  if ( pszTitle != NULL )
+    strcpy( pcDlgData, pszTitle );
+  else
+    *pcDlgData = '\0';
+  pcDlgData += pDlgItem->cchText;
+
+  pDlgItem->flStyle = WS_GROUP | WS_VISIBLE | WS_CLIPSIBLINGS | 
+                      FS_DLGBORDER | WS_SAVEBITS;
+  pDlgItem->x  = 100;
+  pDlgItem->y  = 100;
+  pDlgItem->cx = 175;
+  pDlgItem->cy = 65;
+  pDlgItem->id = DID_OK;     /* An ID value? */
+  if ( pSDLColors == NULL )
+    pDlgItem->offPresParams = 0;
+  else
+  {
+    // Presentation parameter for the frame - dialog colors.
+    pDlgItem->offPresParams = pcDlgData - (PCHAR)pTemplate;
+    ((PPRESPARAMS)pcDlgData)->cb = 22;
+    pcDlgData += 4;
+    ((PPARAM)pcDlgData)->id = PP_FOREGROUNDCOLOR;
+    ((PPARAM)pcDlgData)->cb = 3;
+    ((PPARAM)pcDlgData)->ab[0] = pSDLColors[SDL_MESSAGEBOX_COLOR_TEXT].b;
+    ((PPARAM)pcDlgData)->ab[1] = pSDLColors[SDL_MESSAGEBOX_COLOR_TEXT].g;
+    ((PPARAM)pcDlgData)->ab[2] = pSDLColors[SDL_MESSAGEBOX_COLOR_TEXT].r;
+    pcDlgData += 11;
+    ((PPARAM)pcDlgData)->id = PP_BACKGROUNDCOLOR;
+    ((PPARAM)pcDlgData)->cb = 3;
+    ((PPARAM)pcDlgData)->ab[0] = pSDLColors[SDL_MESSAGEBOX_COLOR_BACKGROUND].b;
+    ((PPARAM)pcDlgData)->ab[1] = pSDLColors[SDL_MESSAGEBOX_COLOR_BACKGROUND].g;
+    ((PPARAM)pcDlgData)->ab[2] = pSDLColors[SDL_MESSAGEBOX_COLOR_BACKGROUND].r;
+    pcDlgData += 11;
+  }
+
+  /* Offset to ctl data. */
+  pDlgItem->offCtlData = pcDlgData - (PCHAR)pTemplate;
+  /* Put CtlData for the dialog in here */
+  *((PULONG)pcDlgData) = FCF_TITLEBAR | FCF_SYSMENU;
+  pcDlgData += sizeof(ULONG);
+
+  /* Second item info - static text (message). */
+
+  pDlgItem++;
+  pDlgItem->fsItemStatus = 0;
+  /* No children since its a control, it could have child control */
+  /* (ex. a group box).                                           */
+  pDlgItem->cChildren = 0;
+  /* Length of class name, 0 - offClassname contains a WC_ constant. */
+  pDlgItem->cchClassName = 0;
+  pDlgItem->offClassName = (USHORT)WC_STATIC;
+
+  pDlgItem->cchText = cbText + 1;
+  pDlgItem->offText = pcDlgData - (PCHAR)pTemplate;   /* Offset to the text. */
+  /* Copy message text into the dialog template. */
+  if ( pszText != NULL )
+    strcpy( pcDlgData, pszText );
+  else
+    *pcDlgData = '\0';
+  pcDlgData += pDlgItem->cchText;
+
+  pDlgItem->flStyle = SS_TEXT | DT_TOP | DT_LEFT | DT_WORDBREAK | WS_VISIBLE;
+  // It will be really set in _wmInitDlg().
+  pDlgItem->x = 25;
+  pDlgItem->y = 13;
+  pDlgItem->cx = 147;
+  pDlgItem->cy = 62;  // It will be used.
+
+  pDlgItem->id = IDD_TEXT_MESSAGE;	  /* an ID value */
+  if ( pSDLColors == NULL )
+    pDlgItem->offPresParams = 0;
+  else
+  {
+    // Presentation parameter for the static text - dialog colors.
+    pDlgItem->offPresParams = pcDlgData - (PCHAR)pTemplate;
+    ((PPRESPARAMS)pcDlgData)->cb = 22;
+    pcDlgData += 4;
+    ((PPARAM)pcDlgData)->id = PP_FOREGROUNDCOLOR;
+    ((PPARAM)pcDlgData)->cb = 3;
+    ((PPARAM)pcDlgData)->ab[0] = pSDLColors[SDL_MESSAGEBOX_COLOR_TEXT].b;
+    ((PPARAM)pcDlgData)->ab[1] = pSDLColors[SDL_MESSAGEBOX_COLOR_TEXT].g;
+    ((PPARAM)pcDlgData)->ab[2] = pSDLColors[SDL_MESSAGEBOX_COLOR_TEXT].r;
+    pcDlgData += 11;
+    ((PPARAM)pcDlgData)->id = PP_BACKGROUNDCOLOR;
+    ((PPARAM)pcDlgData)->cb = 3;
+    ((PPARAM)pcDlgData)->ab[0] = pSDLColors[SDL_MESSAGEBOX_COLOR_BACKGROUND].b;
+    ((PPARAM)pcDlgData)->ab[1] = pSDLColors[SDL_MESSAGEBOX_COLOR_BACKGROUND].g;
+    ((PPARAM)pcDlgData)->ab[2] = pSDLColors[SDL_MESSAGEBOX_COLOR_BACKGROUND].r;
+    pcDlgData += 11;
+  }
+  pDlgItem->offCtlData = 0;
+
+  /* Third item info - static bitmap. */
+
+  pDlgItem++;
+  pDlgItem->fsItemStatus = 0;
+  pDlgItem->cChildren = 0;
+  pDlgItem->cchClassName = 0;
+  pDlgItem->offClassName = (USHORT)WC_STATIC;
+
+  pDlgItem->cchText = 3; // 0xFF, low byte of the icon Id, high byte of icon Id.
+  pDlgItem->offText = pcDlgData - (PCHAR)pTemplate;   /* Offset to the Id. */
+  /* Write susyem icon ID into dialog template. */
+  *pcDlgData = 0xFF; // First byte is 0xFF - next 2 bytes is system pointer Id.
+  pcDlgData++;
+  *((PUSHORT)pcDlgData) = 
+    (messageboxdata->flags & SDL_MESSAGEBOX_ERROR) != 0
+      ? SPTR_ICONERROR
+      : (messageboxdata->flags & SDL_MESSAGEBOX_WARNING) != 0
+          ? SPTR_ICONWARNING : SPTR_ICONINFORMATION;
+  pcDlgData += 2;
+
+  pDlgItem->flStyle = SS_SYSICON | WS_VISIBLE;
+
+  pDlgItem->x = 4;
+  pDlgItem->y = 45; // It will be really set in _wmInitDlg().
+  pDlgItem->cx = 0;
+  pDlgItem->cy = 0;
+
+  pDlgItem->id = IDD_BITMAP;
+  pDlgItem->offPresParams = 0;
+  pDlgItem->offCtlData = 0;
+
+  /* Next items - buttons. */
+
+  for( ulIdx = 0; ulIdx < cSDLBtnData; ulIdx++ )
+  {
+    pDlgItem++;
+
+    pDlgItem->fsItemStatus = 0;
+    pDlgItem->cChildren = 0;     /* No children. */
+    pDlgItem->cchClassName = 0;  /* 0 - offClassname is WC_ constant. */
+    pDlgItem->offClassName = (USHORT)WC_BUTTON;
+
+    pszBtnText = OS2_UTF8ToSys( (PSZ)pSDLBtnData[ulIdx].text );
+    cbBtnText = ( pszBtnText == NULL ?
+                            0 : strlen( pszBtnText ) );
+    pDlgItem->cchText = cbBtnText + 1;
+    pDlgItem->offText = pcDlgData - (PCHAR)pTemplate; /* Offset to the text. */
+    /* Copy text for the button into the dialog template. */
+    if ( pszBtnText != NULL )
+      strcpy( pcDlgData, pszBtnText );
+    else
+      *pcDlgData = '\0';
+    pcDlgData += pDlgItem->cchText;
+    SDL_free( pszBtnText );
+
+    pDlgItem->flStyle = BS_PUSHBUTTON | WS_TABSTOP | WS_VISIBLE;
+    if ( pSDLBtnData[ulIdx].flags == SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT )
+    {
+      pDlgItem->flStyle |= BS_DEFAULT;
+      pTemplate->iItemFocus = ulIdx + 3; // +3 - frame, static text and icon.
+      pSDLColor = &pSDLColors[SDL_MESSAGEBOX_COLOR_BUTTON_SELECTED];
+    }
+    else
+      pSDLColor = &pSDLColors[SDL_MESSAGEBOX_COLOR_TEXT];
+
+    // It will be really set in _wmInitDlg()
+    pDlgItem->x = 10;
+    pDlgItem->y = 10;
+    pDlgItem->cx = 70;
+    pDlgItem->cy = 15;
+
+    pDlgItem->id = IDD_PB_FIRST + ulIdx;	  /* an ID value */
+    if ( pSDLColors == NULL )
+      pDlgItem->offPresParams = 0;
+    else
+    {
+      // Presentation parameter for the button - dialog colors.
+      pDlgItem->offPresParams = pcDlgData - (PCHAR)pTemplate;
+      ((PPRESPARAMS)pcDlgData)->cb = 44;
+      pcDlgData += 4;
+      ((PPARAM)pcDlgData)->id = PP_FOREGROUNDCOLOR;
+      ((PPARAM)pcDlgData)->cb = 3;
+      ((PPARAM)pcDlgData)->ab[0] = pSDLColor->b;
+      ((PPARAM)pcDlgData)->ab[1] = pSDLColor->g;
+      ((PPARAM)pcDlgData)->ab[2] = pSDLColor->r;
+      pcDlgData += 11;
+      ((PPARAM)pcDlgData)->id = PP_BACKGROUNDCOLOR;
+      ((PPARAM)pcDlgData)->cb = 3;
+      ((PPARAM)pcDlgData)->ab[0] = pSDLColors[SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND].b;
+      ((PPARAM)pcDlgData)->ab[1] = pSDLColors[SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND].g;
+      ((PPARAM)pcDlgData)->ab[2] = pSDLColors[SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND].r;
+      pcDlgData += 11;
+      ((PPARAM)pcDlgData)->id = PP_BORDERLIGHTCOLOR;
+      ((PPARAM)pcDlgData)->cb = 3;
+      ((PPARAM)pcDlgData)->ab[0] = pSDLColors[SDL_MESSAGEBOX_COLOR_BUTTON_BORDER].b;
+      ((PPARAM)pcDlgData)->ab[1] = pSDLColors[SDL_MESSAGEBOX_COLOR_BUTTON_BORDER].g;
+      ((PPARAM)pcDlgData)->ab[2] = pSDLColors[SDL_MESSAGEBOX_COLOR_BUTTON_BORDER].r;
+      pcDlgData += 11;
+      ((PPARAM)pcDlgData)->id = PP_BORDERDARKCOLOR;
+      ((PPARAM)pcDlgData)->cb = 3;
+      ((PPARAM)pcDlgData)->ab[0] = pSDLColors[SDL_MESSAGEBOX_COLOR_BUTTON_BORDER].b;
+      ((PPARAM)pcDlgData)->ab[1] = pSDLColors[SDL_MESSAGEBOX_COLOR_BUTTON_BORDER].g;
+      ((PPARAM)pcDlgData)->ab[2] = pSDLColors[SDL_MESSAGEBOX_COLOR_BUTTON_BORDER].r;
+      pcDlgData += 11;
+    }
+    pDlgItem->offCtlData = 0;
+  }
+  // Check, end of templ. data: &((PCHAR)pTemplate)[cbTemplate] == pcDlgData
+
+  // Create the dialog from template.
+
+  stDlgData.cb = sizeof(MSGBOXDLGDATA);
+  stDlgData.hwndUnder = ( messageboxdata->window != NULL ) &&
+                        ( messageboxdata->window->driverdata != NULL )
+                          ? ((PWINDATA)messageboxdata->window->driverdata)->hwnd
+                          : HWND_DESKTOP;
+
+  hwnd = WinCreateDlg( HWND_DESKTOP, // Parent is desktop.
+                       stDlgData.hwndUnder,
+                       (PFNWP)DynDlgProc, pTemplate, &stDlgData );
+  SDL_free( pTemplate );
+  SDL_free( pszTitle );
+  SDL_free( pszText );
+
+  return hwnd;
+}
+
+
+int OS2_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid)
+{
+  HWND                 hwnd;
+  ULONG                ulRC;
+  SDL_MessageBoxButtonData
+                       *pSDLBtnData =
+                           (SDL_MessageBoxButtonData *)messageboxdata->buttons;
+  ULONG                cSDLBtnData = messageboxdata->numbuttons;
+  BOOL                 fVideoInitialized = SDL_WasInit( SDL_INIT_VIDEO );
+  HAB                  hab;
+  HMQ                  hmq;
+  BOOL                 fSuccess = FALSE;
+
+  if ( !fVideoInitialized )
+  {
+    PTIB               tib;
+    PPIB               pib;
+
+    DosGetInfoBlocks( &tib, &pib );
+    if ( pib->pib_ultype == 2 || pib->pib_ultype == 0 )
+    {
+      // VIO windowable or fullscreen protect-mode session.
+      pib->pib_ultype = 3; // Presentation Manager protect-mode session.
+    }
+
+    hab = WinInitialize( 0 );
+    if ( hab == NULLHANDLE )
+    {
+      debug( "WinInitialize() failed" );
+      return -1;
+    }
+    hmq = WinCreateMsgQueue( hab, 0 );
+    if ( hmq == NULLHANDLE )
+    {
+      debug( "WinCreateMsgQueue() failed" );
+      return -1;
+    }
+  }
+
+  // Create dynamic dialog.
+  hwnd = _makeDlg( messageboxdata );
+  // Show dialog and obtain button Id.
+  ulRC = WinProcessDlg( hwnd );
+  // Destroy dialog,
+  WinDestroyWindow( hwnd );
+
+  if ( ulRC == DID_CANCEL )
+  {
+    // Window closed by ESC, Alt+F4 or system menu.
+    ULONG                ulIdx;
+
+    for( ulIdx = 0; ulIdx < cSDLBtnData; ulIdx++, pSDLBtnData++ )
+    {
+      if ( pSDLBtnData->flags == SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT )
+      {
+        *buttonid = pSDLBtnData->buttonid;
+        fSuccess = TRUE;
+        break;
+      }
+    }
+  }
+  else
+  {
+    // Button pressed.
+    ulRC -= IDD_PB_FIRST;
+    if ( ulRC < cSDLBtnData )
+    {
+      *buttonid = pSDLBtnData[ulRC].buttonid;
+      fSuccess = TRUE;
+    }
+  }
+
+  if ( !fVideoInitialized )
+  {
+    WinDestroyMsgQueue( hmq );
+    WinTerminate( hab );
+  }
+
+  return fSuccess ? 0 : -1;
+}
+
+#endif /* SDL_VIDEO_DRIVER_OS2 */
+
+/* vi: set ts=4 sw=4 expandtab: */
diff --git a/src/video/os2/SDL_os2messagebox.h b/src/video/os2/SDL_os2messagebox.h
new file mode 100644
index 0000000..bd2fdf7
--- /dev/null
+++ b/src/video/os2/SDL_os2messagebox.h
@@ -0,0 +1,29 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "../../SDL_internal.h"
+
+#if SDL_VIDEO_DRIVER_OS2
+
+extern int OS2_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid);
+
+#endif /* SDL_VIDEO_DRIVER_OS2 */
+
+/* vi: set ts=4 sw=4 expandtab: */
diff --git a/src/video/os2/SDL_os2mouse.c b/src/video/os2/SDL_os2mouse.c
new file mode 100644
index 0000000..67d3f1c
--- /dev/null
+++ b/src/video/os2/SDL_os2mouse.c
@@ -0,0 +1,204 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "../../SDL_internal.h"
+
+#if SDL_VIDEO_DRIVER_OS2
+
+#include "SDL_os2video.h"
+#include "../../events/SDL_mouse_c.h"
+#include "SDL_os2util.h"
+
+HPOINTER              hptrCursor = NULLHANDLE;
+
+static SDL_Cursor* OS2_CreateCursor(SDL_Surface *surface, int hot_x, int hot_y)
+{
+  ULONG                ulMaxW = WinQuerySysValue( HWND_DESKTOP, SV_CXPOINTER );
+  ULONG                ulMaxH = WinQuerySysValue( HWND_DESKTOP, SV_CYPOINTER );
+  HPOINTER             hptr;
+  SDL_Cursor           *pSDLCursor;
+
+  if ( ( surface->w > ulMaxW ) || ( surface->h > ulMaxH ) )
+  {
+    debug( "Given image size is %u x %u, maximum allowed size is %u x %u",
+           surface->w, surface->h, ulMaxW, ulMaxH );
+    return NULL;
+  }
+
+  hptr = utilCreatePointer( surface, hot_x, ulMaxH - hot_y - 1 );
+  if ( hptr == NULLHANDLE )
+    return NULL;
+
+  pSDLCursor = SDL_calloc( 1, sizeof(SDL_Cursor) );
+  if ( pSDLCursor == NULL )
+  {
+    WinDestroyPointer( hptr );
+    SDL_OutOfMemory();
+    return NULL;
+  }
+
+  pSDLCursor->driverdata = (void *)hptr;
+  return pSDLCursor;
+}
+
+static SDL_Cursor* OS2_CreateSystemCursor(SDL_SystemCursor id)
+{
+  SDL_Cursor*          pSDLCursor;
+  LONG                 lSysId;
+  HPOINTER             hptr;
+
+  switch( id )
+  {
+    case SDL_SYSTEM_CURSOR_ARROW:     lSysId = SPTR_ARROW;    break;
+    case SDL_SYSTEM_CURSOR_IBEAM:     lSysId = SPTR_TEXT;     break;
+    case SDL_SYSTEM_CURSOR_WAIT:      lSysId = SPTR_WAIT;     break;
+    case SDL_SYSTEM_CURSOR_CROSSHAIR: lSysId = SPTR_MOVE;     break;
+    case SDL_SYSTEM_CURSOR_WAITARROW: lSysId = SPTR_WAIT;     break;
+    case SDL_SYSTEM_CURSOR_SIZENWSE:  lSysId = SPTR_SIZENWSE; break;
+    case SDL_SYSTEM_CURSOR_SIZENESW:  lSysId = SPTR_SIZENESW; break;
+    case SDL_SYSTEM_CURSOR_SIZEWE:    lSysId = SPTR_SIZEWE;   break;
+    case SDL_SYSTEM_CURSOR_SIZENS:    lSysId = SPTR_SIZENS;   break;
+    case SDL_SYSTEM_CURSOR_SIZEALL:   lSysId = SPTR_MOVE;     break;
+    case SDL_SYSTEM_CURSOR_NO:        lSysId = SPTR_ILLEGAL;  break;
+    case SDL_SYSTEM_CURSOR_HAND:      lSysId = SPTR_ARROW;    break;
+    default:
+      debug( "Unknown cursor id: %u", id );
+      return NULL;
+  }
+
+  // On eCS SPTR_WAIT for last paramether fCopy=TRUE/FALSE gives different
+  // "wait" icons. -=8( )
+  hptr = WinQuerySysPointer( HWND_DESKTOP, lSysId,
+                             id == SDL_SYSTEM_CURSOR_WAIT );
+  if ( hptr == NULLHANDLE )
+  {
+    debug( "Cannot load OS/2 system pointer %u for SDL cursor id %u",
+           lSysId, id );
+    return NULL;
+  }
+
+  pSDLCursor = SDL_calloc( 1, sizeof(SDL_Cursor) );
+  if ( pSDLCursor == NULL )
+  {
+    WinDestroyPointer( hptr );
+    SDL_OutOfMemory();
+    return NULL;
+  }
+
+  pSDLCursor->driverdata = (void *)hptr;
+  return pSDLCursor;
+}
+
+static void OS2_FreeCursor(SDL_Cursor *cursor)
+{
+  HPOINTER             hptr = (HPOINTER)cursor->driverdata;
+
+  WinDestroyPointer( hptr );
+  SDL_free( cursor );
+}
+
+static int OS2_ShowCursor(SDL_Cursor *cursor)
+{
+  hptrCursor = cursor != NULL ? (HPOINTER)cursor->driverdata : NULLHANDLE;
+
+  return ( ( SDL_GetMouseFocus() == NULL ) ||
+           WinSetPointer( HWND_DESKTOP, hptrCursor ) ) ? 0 : -1;
+}
+
+static void OS2_WarpMouse(SDL_Window * window, int x, int y)
+{
+  PWINDATA             pWinData = (PWINDATA)window->driverdata;
+  POINTL               pointl;
+
+  pointl.x = x;
+  pointl.y = window->h - y;
+  WinMapWindowPoints( pWinData->hwnd, HWND_DESKTOP, &pointl, 1 );
+//  pWinData->lSkipWMMouseMove++; ???
+  WinSetPointerPos( HWND_DESKTOP, pointl.x, pointl.y );
+}
+
+static int OS2_WarpMouseGlobal(int x, int y)
+{
+  WinSetPointerPos( HWND_DESKTOP, x,
+                    WinQuerySysValue( HWND_DESKTOP, SV_CYSCREEN ) - y );
+  return 0;
+}
+
+static int OS2_CaptureMouse(SDL_Window *window)
+{
+  return WinSetCapture( HWND_DESKTOP,
+                    window == NULL ? NULLHANDLE
+                                   : ((PWINDATA)window->driverdata)->hwnd )
+           ? 0 : -1;
+}
+
+static Uint32 OS2_GetGlobalMouseState(int *x, int *y)
+{
+  POINTL               pointl;
+  ULONG                ulRes;
+
+  WinQueryPointerPos( HWND_DESKTOP, &pointl );
+  *x = pointl.x;
+  *y = WinQuerySysValue( HWND_DESKTOP, SV_CYSCREEN ) - pointl.y - 1;
+
+  ulRes = WinGetKeyState( HWND_DESKTOP, VK_BUTTON1 ) & 0x8000
+            ? SDL_BUTTON_LMASK : 0;
+  if ( WinGetKeyState( HWND_DESKTOP, VK_BUTTON2 ) & 0x8000 )
+    ulRes |= SDL_BUTTON_RMASK;
+  if ( WinGetKeyState( HWND_DESKTOP, VK_BUTTON3 ) & 0x8000 )
+    ulRes |= SDL_BUTTON_MMASK;
+
+  return ulRes;
+}
+
+
+void OS2_InitMouse(_THIS, ULONG hab)
+{
+  SDL_Mouse            *pSDLMouse = SDL_GetMouse();
+
+  pSDLMouse->CreateCursor         = OS2_CreateCursor;
+  pSDLMouse->CreateSystemCursor   = OS2_CreateSystemCursor;
+  pSDLMouse->ShowCursor           = OS2_ShowCursor;
+  pSDLMouse->FreeCursor           = OS2_FreeCursor;
+  pSDLMouse->WarpMouse            = OS2_WarpMouse;
+  pSDLMouse->WarpMouseGlobal      = OS2_WarpMouseGlobal;
+  pSDLMouse->CaptureMouse         = OS2_CaptureMouse;
+  pSDLMouse->GetGlobalMouseState  = OS2_GetGlobalMouseState;
+
+  SDL_SetDefaultCursor( OS2_CreateSystemCursor( SDL_SYSTEM_CURSOR_ARROW ) );
+  if ( hptrCursor == NULLHANDLE )
+    hptrCursor = WinQuerySysPointer( HWND_DESKTOP, SPTR_ARROW, TRUE );
+
+  SDL_SetDoubleClickTime( WinQuerySysValue( HWND_DESKTOP, SV_DBLCLKTIME ) );
+}
+
+void OS2_QuitMouse(_THIS)
+{
+  SDL_Mouse            *pSDLMouse = SDL_GetMouse();
+
+  if ( pSDLMouse->def_cursor != NULL )
+  {
+    SDL_free( pSDLMouse->def_cursor );
+    pSDLMouse->def_cursor = NULL;
+    pSDLMouse->cur_cursor = NULL;
+  }
+}
+
+#endif /* SDL_VIDEO_DRIVER_OS2 */
diff --git a/src/video/os2/SDL_os2mouse.h b/src/video/os2/SDL_os2mouse.h
new file mode 100644
index 0000000..82189a8
--- /dev/null
+++ b/src/video/os2/SDL_os2mouse.h
@@ -0,0 +1,33 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "../../SDL_internal.h"
+
+#ifndef _SDL_os2mouse_h
+#define _SDL_os2mouse_h
+
+extern HPOINTER        hptrCursor;
+
+extern void OS2_InitMouse(_THIS, ULONG hab);
+extern void OS2_QuitMouse(_THIS);
+
+#endif /* _SDL_os2mouse_h */
+
+/* vi: set ts=4 sw=4 expandtab: */
diff --git a/src/video/os2/SDL_os2output.h b/src/video/os2/SDL_os2output.h
new file mode 100644
index 0000000..0e90c4f
--- /dev/null
+++ b/src/video/os2/SDL_os2output.h
@@ -0,0 +1,54 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#ifndef _SDL_os2output_
+#define _SDL_os2output_
+
+#include ".\core\os2\SDL_os2.h"
+
+typedef struct _VODATA *PVODATA;
+
+typedef struct _VIDEOOUTPUTINFO {
+  ULONG      ulBPP;
+  ULONG      fccColorEncoding;
+  ULONG      ulScanLineSize;
+  ULONG      ulHorizResolution;
+  ULONG      ulVertResolution;
+} VIDEOOUTPUTINFO, *PVIDEOOUTPUTINFO;
+
+typedef struct _OS2VIDEOOUTPUT {
+  BOOL (*QueryInfo)(PVIDEOOUTPUTINFO pInfo);
+  PVODATA (*Open)();
+  VOID (*Close)(PVODATA pVOData);
+  BOOL (*SetVisibleRegion)(PVODATA pVOData, HWND hwnd,
+                           SDL_DisplayMode *pSDLDisplayMode, HRGN hrgnShape,
+                           BOOL fVisible);
+  PVOID (*VideoBufAlloc)(PVODATA pVOData, ULONG ulWidth, ULONG ulHeight,
+                        ULONG ulBPP, ULONG fccColorEncoding,
+                        PULONG pulScanLineSize);
+  VOID (*VideoBufFree)(PVODATA pVOData);
+  BOOL (*Update)(PVODATA pVOData, HWND hwnd, SDL_Rect *pSDLRects,
+                 ULONG cSDLRects);
+} OS2VIDEOOUTPUT, *POS2VIDEOOUTPUT;
+
+extern OS2VIDEOOUTPUT voDive;
+extern OS2VIDEOOUTPUT voVMan;
+
+#endif // _SDL_os2output_
diff --git a/src/video/os2/SDL_os2util.c b/src/video/os2/SDL_os2util.c
new file mode 100644
index 0000000..d5429f5
--- /dev/null
+++ b/src/video/os2/SDL_os2util.c
@@ -0,0 +1,118 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "../../SDL_internal.h"
+
+#if SDL_VIDEO_DRIVER_OS2
+
+#include "SDL_os2util.h"
+
+
+HPOINTER utilCreatePointer(SDL_Surface *surface, ULONG ulHotX, ULONG ulHotY)
+{
+  HBITMAP              hbm;
+  BITMAPINFOHEADER2    bmih = { 0 };
+  BITMAPINFO           bmi = { 0 };
+  HPS                  hps;
+  PULONG               pulBitmap;
+  PULONG               pulDst, pulSrc, pulDstMask;
+  ULONG                ulY, ulX;
+  HPOINTER             hptr = NULLHANDLE;
+
+  if ( surface->format->format != SDL_PIXELFORMAT_ARGB8888 )
+  {
+    debug( "Image format should be SDL_PIXELFORMAT_ARGB8888" );
+    return NULLHANDLE;
+  }
+
+  pulBitmap = SDL_malloc( surface->h * surface->w * 4 * 2 );
+  if ( pulBitmap == NULL )
+  {
+    SDL_OutOfMemory();
+    return NULLHANDLE;
+  }
+
+  // pulDst - last line of surface (image) part of the result bitmap's
+  pulDst = &pulBitmap[ (surface->h - 1) * surface->w ];
+  // pulDstMask - last line of mask part of the result bitmap's
+  pulDstMask = &pulBitmap[ (2 * surface->h - 1) * surface->w ];
+  // pulSrc - first line of source image
+  pulSrc = (PULONG)surface->pixels;
+
+  for( ulY = 0; ulY < surface->h; ulY++ )
+  {
+    for( ulX = 0; ulX < surface->w; ulX++ )
+    {
+      if ( (pulSrc[ulX] & 0xFF000000) == 0 )
+      {
+        pulDst[ulX] = 0;
+        pulDstMask[ulX] = 0xFFFFFFFF;
+      }
+      else
+      {
+        pulDst[ulX] = pulSrc[ulX] & 0xFFFFFF;
+        pulDstMask[ulX] = 0;
+      }
+    }
+
+    // Set image and mask pointers on one line up
+    pulDst -= surface->w;
+    pulDstMask -= surface->w;
+    // Set source image pointer to the next line
+    pulSrc = (PULONG)( ((PCHAR)pulSrc) + surface->pitch );
+  }
+
+  // Create system bitmap object.
+
+  bmih.cbFix           = sizeof(BITMAPINFOHEADER2);
+  bmih.cx              = surface->w;
+  bmih.cy              = 2 * surface->h;
+  bmih.cPlanes         = 1;
+  bmih.cBitCount       = 32;
+  bmih.ulCompression   = BCA_UNCOMP;
+  bmih.cbImage         = bmih.cx * bmih.cy * 4;
+
+  bmi.cbFix            = sizeof(BITMAPINFOHEADER);
+  bmi.cx               = bmih.cx;
+  bmi.cy               = bmih.cy;
+  bmi.cPlanes          = 1;
+  bmi.cBitCount        = 32;
+
+  hps = WinGetPS( HWND_DESKTOP );
+  hbm = GpiCreateBitmap( hps, (PBITMAPINFOHEADER2)&bmih, CBM_INIT,
+                         (PBYTE)pulBitmap, (PBITMAPINFO2)&bmi );
+  if ( hbm == GPI_ERROR )
+    debug( "GpiCreateBitmap() failed" );
+  else
+  {
+    // Create a system pointer object.
+    hptr = WinCreatePointer( HWND_DESKTOP, hbm, TRUE, ulHotX, ulHotY );
+    if ( hptr == NULLHANDLE )
+      debug( "WinCreatePointer() failed" );
+  }
+  GpiDeleteBitmap( hbm );
+
+  WinReleasePS( hps );
+  SDL_free( pulBitmap );
+
+  return hptr;
+}
+
+#endif /* SDL_VIDEO_DRIVER_OS2 */
diff --git a/src/video/os2/SDL_os2util.h b/src/video/os2/SDL_os2util.h
new file mode 100644
index 0000000..6aac3fd
--- /dev/null
+++ b/src/video/os2/SDL_os2util.h
@@ -0,0 +1,38 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+
+#ifndef _SDL_os2util_h
+#define _SDL_os2util_h
+
+#include "SDL_log.h"
+#include "../SDL_sysvideo.h"
+#include "../../core/os2/SDL_os2.h"
+
+#define INCL_WIN
+#define INCL_GPI
+#include <os2.h>
+
+/*#define debug(s,...) SDL_LogDebug( SDL_LOG_CATEGORY_VIDEO, \
+                                   __func__"(): "##s, ##__VA_ARGS__ )*/
+
+HPOINTER utilCreatePointer(SDL_Surface *surface, ULONG ulHotX, ULONG ulHotY);
+
+#endif // _SDL_os2util_h
diff --git a/src/video/os2/SDL_os2video.c b/src/video/os2/SDL_os2video.c
new file mode 100644
index 0000000..c6f5b7a
--- /dev/null
+++ b/src/video/os2/SDL_os2video.c
@@ -0,0 +1,1786 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "../../SDL_internal.h"
+
+#if SDL_VIDEO_DRIVER_OS2
+
+#include "SDL_video.h"
+#include "SDL_mouse.h"
+#include "../SDL_pixels_c.h"
+#include "../SDL_shape_internals.h"
+#include "../../events/SDL_events_c.h"
+#include "SDL_os2video.h"
+#include "SDL_syswm.h"
+#include "SDL_os2util.h"
+#define  _MEERROR_H_
+#include <mmioos2.h>
+#include <fourcc.h>
+
+#define WIN_CLIENT_CLASS         "SDL2"
+#define OS2DRIVER_NAME_DIVE      "DIVE"
+#define OS2DRIVER_NAME_VMAN      "VMAN"
+
+
+static const SDL_Scancode aSDLScancode[] = 
+{ 
+       /*   0                       1                           2                           3                           4                        5							 6                           7 */
+       /*   8                       9                           A                           B                           C                        D							 E                           F */
+       SDL_SCANCODE_UNKNOWN,        SDL_SCANCODE_ESCAPE,        SDL_SCANCODE_1,             SDL_SCANCODE_2,             SDL_SCANCODE_3,          SDL_SCANCODE_4,             SDL_SCANCODE_5,             SDL_SCANCODE_6,           /* 0 */
+       SDL_SCANCODE_7,              SDL_SCANCODE_8,             SDL_SCANCODE_9,             SDL_SCANCODE_0,             SDL_SCANCODE_MINUS,      SDL_SCANCODE_EQUALS,        SDL_SCANCODE_BACKSPACE,     SDL_SCANCODE_TAB,         /* 0 */
+
+       SDL_SCANCODE_Q,              SDL_SCANCODE_W,             SDL_SCANCODE_E,             SDL_SCANCODE_R,             SDL_SCANCODE_T,          SDL_SCANCODE_Y,             SDL_SCANCODE_U,             SDL_SCANCODE_I,           /* 1 */
+       SDL_SCANCODE_O,              SDL_SCANCODE_P,             SDL_SCANCODE_LEFTBRACKET,   SDL_SCANCODE_RIGHTBRACKET,  SDL_SCANCODE_RETURN,     SDL_SCANCODE_LCTRL,         SDL_SCANCODE_A,             SDL_SCANCODE_S,           /* 1 */
+
+       SDL_SCANCODE_D,              SDL_SCANCODE_F,             SDL_SCANCODE_G,             SDL_SCANCODE_H,             SDL_SCANCODE_J,          SDL_SCANCODE_K,             SDL_SCANCODE_L,             SDL_SCANCODE_SEMICOLON,   /* 2 */
+       SDL_SCANCODE_APOSTROPHE,     SDL_SCANCODE_GRAVE,         SDL_SCANCODE_LSHIFT,        SDL_SCANCODE_BACKSLASH,     SDL_SCANCODE_Z,          SDL_SCANCODE_X,             SDL_SCANCODE_C,             SDL_SCANCODE_V,           /* 2 */
+
+       SDL_SCANCODE_B,              SDL_SCANCODE_N,             SDL_SCANCODE_M,             SDL_SCANCODE_COMMA,         SDL_SCANCODE_PERIOD,     SDL_SCANCODE_SLASH,         SDL_SCANCODE_RSHIFT,  /*55*/SDL_SCANCODE_KP_MULTIPLY, /* 3 */
+       SDL_SCANCODE_LALT,           SDL_SCANCODE_SPACE,         SDL_SCANCODE_CAPSLOCK,      SDL_SCANCODE_F1,            SDL_SCANCODE_F2,         SDL_SCANCODE_F3,            SDL_SCANCODE_F4,            SDL_SCANCODE_F5,          /* 3 */
+
+       SDL_SCANCODE_F6,             SDL_SCANCODE_F7,            SDL_SCANCODE_F8,            SDL_SCANCODE_F9,            SDL_SCANCODE_F10,        SDL_SCANCODE_NUMLOCKCLEAR,  SDL_SCANCODE_SCROLLLOCK,    SDL_SCANCODE_KP_7,        /* 4 */
+ /*72*/SDL_SCANCODE_KP_8,     /*73*/SDL_SCANCODE_KP_9,          SDL_SCANCODE_KP_MINUS,/*75*/SDL_SCANCODE_KP_4,    /*76*/SDL_SCANCODE_KP_5, /*77*/SDL_SCANCODE_KP_6,    /*78*/SDL_SCANCODE_KP_PLUS, /*79*/SDL_SCANCODE_KP_1,        /* 4 */
+
+ /*80*/SDL_SCANCODE_KP_2,     /*81*/SDL_SCANCODE_KP_3,          SDL_SCANCODE_KP_0,    /*83*/SDL_SCANCODE_KP_PERIOD,     SDL_SCANCODE_UNKNOWN,    SDL_SCANCODE_UNKNOWN,       SDL_SCANCODE_NONUSBACKSLASH,SDL_SCANCODE_F11,         /* 5 */
+ /*88*/SDL_SCANCODE_F12,      /*89*/SDL_SCANCODE_PAUSE,   /*90*/SDL_SCANCODE_KP_ENTER,/*91*/SDL_SCANCODE_RCTRL,   /*92*/SDL_SCANCODE_KP_DIVIDE,  SDL_SCANCODE_APPLICATION,   SDL_SCANCODE_RALT,    /*95*/SDL_SCANCODE_UNKNOWN,     /* 5 */
+
+ /*96*/SDL_SCANCODE_HOME,     /*97*/SDL_SCANCODE_UP,      /*98*/SDL_SCANCODE_PAGEUP,        SDL_SCANCODE_LEFT,   /*100*/SDL_SCANCODE_RIGHT,      SDL_SCANCODE_END,    /*102*/SDL_SCANCODE_DOWN,   /*103*/SDL_SCANCODE_PAGEDOWN,    /* 6 */
+/*104*/SDL_SCANCODE_F17,     /*105*/SDL_SCANCODE_DELETE,        SDL_SCANCODE_F19,           SDL_SCANCODE_UNKNOWN,       SDL_SCANCODE_UNKNOWN,    SDL_SCANCODE_UNKNOWN,/*110*/SDL_SCANCODE_UNKNOWN,/*111*/SDL_SCANCODE_UNKNOWN,     /* 6 */
+	
+/*112*/SDL_SCANCODE_INTERNATIONAL2, SDL_SCANCODE_UNKNOWN,       SDL_SCANCODE_UNKNOWN,       SDL_SCANCODE_INTERNATIONAL1,SDL_SCANCODE_UNKNOWN,    SDL_SCANCODE_UNKNOWN,       SDL_SCANCODE_UNKNOWN,       SDL_SCANCODE_UNKNOWN,     /* 7 */
+/*120*/SDL_SCANCODE_UNKNOWN,        SDL_SCANCODE_INTERNATIONAL4,SDL_SCANCODE_UNKNOWN,       SDL_SCANCODE_INTERNATIONAL5,SDL_SCANCODE_APPLICATION,SDL_SCANCODE_INTERNATIONAL3,SDL_SCANCODE_LGUI,          SDL_SCANCODE_RGUI         /* 7 */
+};
+
+//           Utilites.
+//           ---------
+
+static BOOL _getSDLPixelFormatData(SDL_PixelFormat *pSDLPixelFormat,
+                                   ULONG ulBPP, ULONG fccColorEncoding)
+{
+  ULONG		ulRshift, ulGshift, ulBshift;
+  ULONG		ulRmask, ulGmask, ulBmask;
+  ULONG		ulRloss, ulGloss, ulBloss;
+
+  pSDLPixelFormat->BitsPerPixel = ulBPP;
+  pSDLPixelFormat->BytesPerPixel = ( pSDLPixelFormat->BitsPerPixel + 7 ) / 8;
+
+  switch( fccColorEncoding )
+  {
+    case FOURCC_LUT8:
+      ulRshift = 0; ulGshift = 0; ulBshift = 0;
+      ulRmask = 0; ulGmask = 0; ulBmask = 0;
+      ulRloss = 8; ulGloss = 8; ulBloss = 8;
+      break;
+
+    case FOURCC_R555:
+      ulRshift = 10; ulGshift = 5; ulBshift = 0;
+      ulRmask = 0x7C00; ulGmask = 0x03E0; ulBmask = 0x001F;
+      ulRloss = 3; ulGloss = 3; ulBloss = 3;
+      break;
+
+    case FOURCC_R565:
+      ulRshift = 11; ulGshift = 5; ulBshift = 0;
+      ulRmask = 0xF800; ulGmask = 0x07E0; ulBmask = 0x001F;
+      ulRloss = 3; ulGloss = 2; ulBloss = 3;
+      break;
+
+    case FOURCC_R664:
+      ulRshift = 10; ulGshift = 4; ulBshift = 0;
+      ulRmask = 0xFC00; ulGmask = 0x03F0; ulBmask = 0x000F;
+      ulRloss = 2; ulGloss = 4; ulBloss = 3;
+      break;
+
+/*    case FOURCC_R666:
+      ulRshift = 12; ulGshift = 6; ulBshift = 0;
+      ulRmask = 0x03F000; ulGmask = 0x000FC0; ulBmask = 0x00003F;
+      ulRloss = 2; ulGloss = 2; ulBloss = 2;
+      break;*/
+
+    case FOURCC_RGB3:
+    case FOURCC_RGB4:
+      ulRshift = 0; ulGshift = 8; ulBshift = 16;
+      ulRmask = 0x0000FF; ulGmask = 0x00FF00; ulBmask = 0xFF0000;
+      ulRloss = 0x00; ulGloss = 0x00; ulBloss = 0x00;
+      break;
+
+    case FOURCC_BGR3:
+    case FOURCC_BGR4:
+      ulRshift = 16; ulGshift = 8; ulBshift = 0;
+      ulRmask = 0xFF0000; ulGmask = 0x00FF00; ulBmask = 0x0000FF;
+      ulRloss = 0; ulGloss = 0; ulBloss = 0;
+      break;
+
+    default:
+//      printf( "Unknown color encoding: %.4s\n", fccColorEncoding );
+      memset( pSDLPixelFormat, 0, sizeof(SDL_PixelFormat) );
+      return FALSE;
+  }
+
+  pSDLPixelFormat->Rshift = ulRshift;
+  pSDLPixelFormat->Gshift = ulGshift;
+  pSDLPixelFormat->Bshift = ulBshift;
+  pSDLPixelFormat->Rmask  = ulRmask;
+  pSDLPixelFormat->Gmask  = ulGmask;
+  pSDLPixelFormat->Bmask  = ulBmask;
+  pSDLPixelFormat->Rloss  = ulRloss;
+  pSDLPixelFormat->Gloss  = ulGloss;
+  pSDLPixelFormat->Bloss  = ulBloss;
+
+  pSDLPixelFormat->Ashift = 0x00;
+  pSDLPixelFormat->Amask  = 0x00;
+  pSDLPixelFormat->Aloss  = 0x00;
+  return TRUE;
+}
+
+static Uint32 _getSDLPixelFormat(ULONG ulBPP, FOURCC fccColorEncoding)
+{
+  SDL_PixelFormat      stSDLPixelFormat;
+  Uint32               uiResult = SDL_PIXELFORMAT_UNKNOWN;
+
+  if ( _getSDLPixelFormatData( &stSDLPixelFormat, ulBPP, fccColorEncoding ) )
+    uiResult = SDL_MasksToPixelFormatEnum( ulBPP, stSDLPixelFormat.Rmask,
+                                           stSDLPixelFormat.Gmask,
+                                           stSDLPixelFormat.Bmask, 0 );
+
+  return uiResult;
+}
+
+static SDL_DisplayMode *_getDisplayModeForSDLWindow(SDL_Window *window)
+{
+  SDL_VideoDisplay     *pSDLDisplay = SDL_GetDisplayForWindow( window );
+
+  if ( pSDLDisplay == NULL )
+  {
+    debug( "No display for the window" );
+    return FALSE;
+  }
+
+  return &pSDLDisplay->current_mode;
+}
+
+
+static VOID _mouseCheck(PWINDATA pWinData)
+{
+  SDL_Mouse            *pSDLMouse = SDL_GetMouse();
+
+  if ( ( pSDLMouse->relative_mode ||
+         (pWinData->window->flags & SDL_WINDOW_INPUT_GRABBED) != 0 )
+       && ( (pWinData->window->flags & SDL_WINDOW_INPUT_FOCUS) != 0 ) )
+  {
+    // We will make a real capture in _wmMouseButton().
+  }
+  else
+    WinSetCapture( HWND_DESKTOP, NULLHANDLE );
+}
+
+
+//           PM window procedure.
+//           --------------------
+
+static int OS2_ResizeWindowShape(SDL_Window *window);
+
+static VOID _setVisibleRegion(PWINDATA pWinData, BOOL fVisible)
+{
+  SDL_VideoDisplay  *pSDLDisplay;
+
+  if ( pWinData->pVOData == NULL )
+    return;
+
+  pSDLDisplay = fVisible ? SDL_GetDisplayForWindow( pWinData->window ) : NULL;
+  pWinData->pOutput->SetVisibleRegion( pWinData->pVOData, pWinData->hwnd,
+                                       pSDLDisplay == NULL ?
+                                         NULL : &pSDLDisplay->current_mode,
+                                       pWinData->hrgnShape, fVisible );
+}
+
+static VOID _wmPaint(PWINDATA pWinData, HWND hwnd)
+{
+  if ( ( pWinData->pVOData == NULL ) ||
+       !pWinData->pOutput->Update( pWinData->pVOData, hwnd, NULL, 0 ) )
+  {
+    RECTL    rectl;
+    HPS      hps;
+
+    hps = WinBeginPaint( hwnd, 0, &rectl );
+    WinFillRect( hps, &rectl, CLR_BLACK );
+    WinEndPaint( hps );
+  }
+}
+
+static VOID _wmMouseMove(PWINDATA pWinData, SHORT lX, SHORT lY)
+{
+  SDL_Mouse  *pSDLMouse = SDL_GetMouse();
+  POINTL     pointl;
+  BOOL       fWinActive = (pWinData->window->flags & SDL_WINDOW_INPUT_FOCUS)
+                          != 0; 
+  if ( !pSDLMouse->relative_mode || pSDLMouse->relative_mode_warp )
+  {
+    if ( !pSDLMouse->relative_mode && fWinActive &&
+         ( (pWinData->window->flags & SDL_WINDOW_INPUT_GRABBED) != 0 ) &&
+         ( WinQueryCapture( HWND_DESKTOP ) == pWinData->hwnd ) )
+    {
+      pointl.x = lX;
+      pointl.y = lY;
+
+      if ( lX < 0 )
+        lX = 0;
+      else if ( lX >= pWinData->window->w )
+        lX = pWinData->window->w - 1;
+
+      if ( lY < 0 )
+        lY = 0;
+      else if ( lY >= pWinData->window->h )
+        lY = pWinData->window->h - 1;
+
+      if ( ( lX != pointl.x ) || ( lY != pointl.x ) )
+      {
+        pointl.x = lX;
+        pointl.y = lY;
+        WinMapWindowPoints( pWinData->hwnd, HWND_DESKTOP, &pointl, 1 );
+        pWinData->lSkipWMMouseMove++;
+        WinSetPointerPos( HWND_DESKTOP, pointl.x, pointl.y );
+      }
+    }
+
+    SDL_SendMouseMotion( pWinData->window, 0, 0, lX,
+                         pWinData->window->h - lY - 1 );
+    return;
+  }
+
+  if ( fWinActive )
+  {
+    pointl.x = pWinData->window->w / 2;
+    pointl.y = pWinData->window->h / 2;
+    WinMapWindowPoints( pWinData->hwnd, HWND_DESKTOP, &pointl, 1 );
+
+    SDL_SendMouseMotion( pWinData->window, 0, 1,
+                         lX - pointl.x, pointl.y - lY );
+
+    pWinData->lSkipWMMouseMove++;
+    WinSetPointerPos( HWND_DESKTOP, pointl.x, pointl.y );
+  }
+}
+
+static VOID _wmMouseButton(PWINDATA pWinData, ULONG ulButton, BOOL fDown)
+{
+  static ULONG         aBtnGROP2SDL[3] = { SDL_BUTTON_LEFT, SDL_BUTTON_RIGHT,
+                                           SDL_BUTTON_MIDDLE };
+  SDL_Mouse            *pSDLMouse = SDL_GetMouse();
+
+  if ( ( pSDLMouse->relative_mode ||
+         ( (pWinData->window->flags & SDL_WINDOW_INPUT_GRABBED) != 0 ) )
+       && ( (pWinData->window->flags & SDL_WINDOW_INPUT_FOCUS) != 0 )
+       && ( WinQueryCapture( HWND_DESKTOP ) != pWinData->hwnd ) )
+  {
+    // Mouse should be captured.
+
+    if ( pSDLMouse->relative_mode && !pSDLMouse->relative_mode_warp )
+    {
+      POINTL           pointl;
+
+      pointl.x = pWinData->window->w / 2;
+      pointl.y = pWinData->window->h / 2;
+      WinMapWindowPoints( pWinData->hwnd, HWND_DESKTOP, &pointl, 1 );
+      pWinData->lSkipWMMouseMove++;
+      WinSetPointerPos( HWND_DESKTOP, pointl.x, pointl.y );
+    }
+
+    WinSetCapture( HWND_DESKTOP, pWinData->hwnd );
+  }
+
+  SDL_SendMouseButton( pWinData->window, 0,
+                       fDown ? SDL_PRESSED : SDL_RELEASED,
+                       aBtnGROP2SDL[ulButton] );
+}
+
+static VOID _wmChar(PWINDATA pWinData, MPARAM mp1, MPARAM mp2)
+{
+  ULONG      ulFlags = SHORT1FROMMP(mp1);      // WM_CHAR flags
+  ULONG      ulVirtualKey = SHORT2FROMMP(mp2); // Virtual key code VK_*
+  ULONG      ulCharCode = SHORT1FROMMP(mp2);   // Character code
+  ULONG      ulScanCode = CHAR4FROMMP(mp1);    // Scan code
+
+  if ( ( (ulFlags & (KC_VIRTUALKEY | KC_KEYUP | KC_ALT)) ==
+         (KC_VIRTUALKEY | KC_ALT) ) &&
+       ( ulVirtualKey == VK_F4 ) )
+    SDL_SendWindowEvent( pWinData->window, SDL_WINDOWEVENT_CLOSE, 0, 0 );
+
+  if ( (ulFlags & KC_SCANCODE) != 0 )
+    SDL_SendKeyboardKey(
+      (ulFlags & KC_KEYUP) == 0 ? SDL_PRESSED : SDL_RELEASED,
+      aSDLScancode[ulScanCode] );
+
+  if ( (ulFlags & KC_CHAR) != 0 )
+  {
+    CHAR     acUTF8[4];
+    LONG     lRC = StrUTF8( 1, &acUTF8, sizeof(acUTF8), (PSZ)&ulCharCode, 1 );
+
+    SDL_SendKeyboardText( lRC > 0 ? &acUTF8 : (PSZ)&ulCharCode );
+  }
+}
+
+static VOID _wmMove(PWINDATA pWinData)
+{
+  SDL_DisplayMode      *pSDLDisplayMode =
+                               _getDisplayModeForSDLWindow( pWinData->window );
+  POINTL               pointl = { 0 };
+  RECTL                rectl;
+
+  WinQueryWindowRect( pWinData->hwnd, &rectl );
+  WinMapWindowPoints( pWinData->hwnd, HWND_DESKTOP, (PPOINTL)&rectl, 2 );
+
+  WinMapWindowPoints( pWinData->hwnd, HWND_DESKTOP, &pointl, 1 );
+  SDL_SendWindowEvent( pWinData->window, SDL_WINDOWEVENT_MOVED, rectl.xLeft,
+                       pSDLDisplayMode->h - rectl.yTop );
+}
+
+static MRESULT _wmDragOver(PWINDATA pWinData, PDRAGINFO pDragInfo)
+{
+  ULONG      ulIdx;
+  PDRAGITEM  pDragItem;
+  USHORT     usDrag    = DOR_NEVERDROP;
+  USHORT     usDragOp  = DO_UNKNOWN;
+
+  if ( !DrgAccessDraginfo( pDragInfo ) )
+    return MRFROM2SHORT( DOR_NEVERDROP, DO_UNKNOWN );
+
+  for( ulIdx = 0; ulIdx < pDragInfo->cditem; ulIdx++ )
+  {
+    pDragItem = DrgQueryDragitemPtr( pDragInfo, ulIdx );
+
+    // We accept WPS files only.
+    if ( !DrgVerifyRMF( pDragItem, "DRM_OS2FILE", NULL ) )
+    {
+      usDrag   = DOR_NEVERDROP;
+      usDragOp = DO_UNKNOWN;
+      break;
+    }
+
+    if ( ( pDragInfo->usOperation == DO_DEFAULT ) &&
+         ( (pDragItem->fsSupportedOps & DO_COPYABLE) != 0 ) )
+    {
+      usDrag   = DOR_DROP;
+      usDragOp = DO_COPY;
+    }
+    else if ( ( pDragInfo->usOperation == DO_LINK ) &&
+              ( (pDragItem->fsSupportedOps & DO_LINKABLE) != 0 ) )
+    {
+      usDrag   = DOR_DROP;
+      usDragOp = DO_LINK;
+    }
+    else
+    {
+      usDrag   = DOR_NODROPOP;
+      usDragOp = DO_UNKNOWN;
+      break;
+    }
+  }
+
+  // Update window (The DIVE surface spoiled while dragging).
+  WinInvalidateRect( pWinData->hwnd, NULL, FALSE );
+  WinUpdateWindow( pWinData->hwnd );
+
+  DrgFreeDraginfo( pDragInfo );
+  return MPFROM2SHORT( usDrag, usDragOp );
+}
+
+static MRESULT _wmDrop(PWINDATA pWinData, PDRAGINFO pDragInfo)
+{
+  ULONG      ulIdx;
+  PDRAGITEM  pDragItem;
+  CHAR       acFName[_MAX_PATH];
+  PCHAR      pcFName;
+
+  if ( !DrgAccessDraginfo( pDragInfo ) )
+    return MRFROM2SHORT( DOR_NEVERDROP, 0 );
+
+  for( ulIdx = 0; ulIdx < pDragInfo->cditem; ulIdx++ )
+  {
+    pDragItem = DrgQueryDragitemPtr( pDragInfo, ulIdx );
+
+    if ( DrgVerifyRMF( pDragItem, "DRM_OS2FILE", NULL ) &&
+         ( pDragItem->hstrContainerName != NULLHANDLE ) &&
+         ( pDragItem->hstrSourceName != NULLHANDLE ) )
+    {
+      // Get file name from the item.
+      DrgQueryStrName( pDragItem->hstrContainerName,
+                       sizeof(acFName), &acFName );
+      pcFName = strchr( &acFName, '\0' );
+      DrgQueryStrName( pDragItem->hstrSourceName,
+                       sizeof(acFName) - (pcFName - &acFName), pcFName );
+
+      // Send to SDL full file name converted to UTF-8.
+      pcFName = OS2_SysToUTF8( &acFName );
+      SDL_SendDropFile( pcFName );
+      SDL_free( pcFName );
+
+      // Notify a source that a drag operation is complete. 
+      if ( pDragItem->hwndItem )
+        DrgSendTransferMsg( pDragItem->hwndItem, DM_ENDCONVERSATION,
+                            (MPARAM)pDragItem->ulItemID,
+                            (MPARAM)DMFL_TARGETSUCCESSFUL );
+    }
+  }
+
+  DrgDeleteDraginfoStrHandles( pDragInfo );
+  DrgFreeDraginfo( pDragInfo );
+
+  return (MRESULT)FALSE;
+}
+
+MRESULT EXPENTRY wndFrameProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
+{
+  HWND       hwndClient = WinQueryWindow( hwnd, QW_BOTTOM );
+  PWINDATA   pWinData = (PWINDATA)WinQueryWindowULong( hwndClient, 0 );
+
+  if ( pWinData == NULL )
+    return WinDefWindowProc( hwnd, msg, mp1, mp2 );
+
+  /* Send a SDL_SYSWMEVENT if the application wants them */
+  if ( SDL_GetEventState( SDL_SYSWMEVENT ) == SDL_ENABLE )
+  {
+    SDL_SysWMmsg wmmsg;
+
+    SDL_VERSION( &wmmsg.version );
+    wmmsg.subsystem = SDL_SYSWM_OS2;
+    wmmsg.msg.os2.fFrame = TRUE;
+    wmmsg.msg.os2.hwnd = hwnd;
+    wmmsg.msg.os2.msg = msg;
+    wmmsg.msg.os2.mp1 = mp1;
+    wmmsg.msg.os2.mp2 = mp2;
+    SDL_SendSysWMEvent( &wmmsg );
+  }
+
+  switch( msg )
+  {
+    case WM_MINMAXFRAME:
+      if ( (((PSWP)mp1)->fl & SWP_RESTORE) != 0 )
+      {
+        pWinData->lSkipWMMove += 2;
+        SDL_SendWindowEvent( pWinData->window,
+                             SDL_WINDOWEVENT_RESTORED, 0, 0 );
+      }
+
+      if ( (((PSWP)mp1)->fl & SWP_MINIMIZE) != 0 )
+      {
+        pWinData->lSkipWMSize++;
+        pWinData->lSkipWMMove += 2;
+        SDL_SendWindowEvent( pWinData->window,
+                             SDL_WINDOWEVENT_MINIMIZED, 0, 0 );
+      }
+
+      if ( (((PSWP)mp1)->fl & SWP_MAXIMIZE) != 0 )
+        SDL_SendWindowEvent( pWinData->window,
+                             SDL_WINDOWEVENT_MAXIMIZED, 0, 0 );
+      break;
+
+    case WM_ADJUSTFRAMEPOS:
+      if ( pWinData->lSkipWMAdjustFramePos > 0 )
+      {
+        pWinData->lSkipWMAdjustFramePos++;
+        break;
+      }
+
+      if ( (pWinData->window->flags & SDL_WINDOW_FULLSCREEN) != 0
+           && (((PSWP)mp1)->fl & SWP_RESTORE) != 0 )
+      {
+        // Keep fullscreen window size on restore.
+        RECTL rectl;
+
+        rectl.xLeft = 0;
+        rectl.yBottom = 0;
+        rectl.xRight = WinQuerySysValue( HWND_DESKTOP, SV_CXSCREEN );
+        rectl.yTop = WinQuerySysValue( HWND_DESKTOP, SV_CYSCREEN );
+        WinCalcFrameRect( hwnd, &rectl, FALSE );
+        ((PSWP)mp1)->x = rectl.xLeft;
+        ((PSWP)mp1)->y = rectl.yBottom;
+        ((PSWP)mp1)->cx = rectl.xRight - rectl.xLeft;
+        ((PSWP)mp1)->cy = rectl.yTop - rectl.yBottom;
+      }
+
+      if ( (((PSWP)mp1)->fl & (SWP_SIZE | SWP_MINIMIZE)) == SWP_SIZE )
+      {
+        if ( (pWinData->window->flags & SDL_WINDOW_FULLSCREEN) != 0 )
+        { // SDL_WINDOW_FULLSCREEN_DESKTOP have flag SDL_WINDOW_FULLSCREEN...
+          if ( SDL_IsShapedWindow( pWinData->window ) )
+            OS2_ResizeWindowShape( pWinData->window );
+          break;
+        }
+
+        if ( (SDL_GetWindowFlags( pWinData->window ) & SDL_WINDOW_RESIZABLE) !=
+             0 )
+        {
+          RECTL        rectl;
+          int          iMinW, iMinH, iMaxW, iMaxH;
+          int          iWinW;
+          int          iWinH;
+
+          rectl.xLeft = 0;
+          rectl.yBottom = 0;
+          SDL_GetWindowSize( pWinData->window,
+                             (int *)&rectl.xRight, (int *)&rectl.yTop );
+          iWinW = rectl.xRight;
+          iWinH = rectl.yTop;
+
+          SDL_GetWindowMinimumSize( pWinData->window, &iMinW, &iMinH );
+          SDL_GetWindowMaximumSize( pWinData->window, &iMaxW, &iMaxH );
+
+          if ( iWinW < iMinW )
+            rectl.xRight = iMinW;
+          else if ( ( iMaxW != 0 ) && ( iWinW > iMaxW ) )
+            rectl.xRight = iMaxW;
+
+          if ( iWinH < iMinH )
+            rectl.yTop = iMinW;
+          else if ( ( iMaxH != 0 ) && ( iWinH > iMaxH ) )
+            rectl.yTop = iMaxH;
+
+          if ( ( rectl.xRight == iWinW ) && ( rectl.yTop == iWinH ) )
+          {
+            if ( SDL_IsShapedWindow( pWinData->window ) )
+              OS2_ResizeWindowShape( pWinData->window );
+            break;
+          }
+
+          WinCalcFrameRect( hwnd, &rectl, FALSE );
+          ((PSWP)mp1)->cx = rectl.xRight - rectl.xLeft;
+          ((PSWP)mp1)->cy = rectl.yTop - rectl.yBottom;
+        }
+      }
+      break;
+  }
+
+  return pWinData->fnWndFrameProc( hwnd, msg, mp1, mp2 );
+}
+
+MRESULT EXPENTRY wndProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
+{
+  PWINDATA             pWinData = (PWINDATA)WinQueryWindowULong( hwnd, 0 );
+
+  if ( pWinData == NULL )
+    return WinDefWindowProc( hwnd, msg, mp1, mp2 );
+
+  /* Send a SDL_SYSWMEVENT if the application wants them */
+  if ( SDL_GetEventState( SDL_SYSWMEVENT ) == SDL_ENABLE )
+  {
+    SDL_SysWMmsg wmmsg;
+
+    SDL_VERSION( &wmmsg.version );
+    wmmsg.subsystem = SDL_SYSWM_OS2;
+    wmmsg.msg.os2.fFrame = FALSE;
+    wmmsg.msg.os2.hwnd = hwnd;
+    wmmsg.msg.os2.msg = msg;
+    wmmsg.msg.os2.mp1 = mp1;
+    wmmsg.msg.os2.mp2 = mp2;
+    SDL_SendSysWMEvent( &wmmsg );
+  }
+
+  switch( msg )
+  {
+    case WM_CLOSE:
+    case WM_QUIT:
+      SDL_SendWindowEvent( pWinData->window, SDL_WINDOWEVENT_CLOSE, 0, 0 );
+      if ( pWinData->fnUserWndProc == NULL )
+        return (MRESULT)FALSE;
+      break;
+
+    case WM_PAINT:
+      _wmPaint( pWinData, hwnd );
+      break;
+
+    case WM_SHOW:
+      SDL_SendWindowEvent( pWinData->window,
+                           SHORT1FROMMP(mp1) == 0
+                             ? SDL_WINDOWEVENT_HIDDEN
+                             : SDL_WINDOWEVENT_SHOWN,
+                           0, 0 );
+      break;
+
+    case WM_UPDATEFRAME:
+      // Return TRUE - no further action for the frame control window procedure.
+      return (MRESULT)TRUE;
+
+    case WM_ACTIVATE:
+      if ( (BOOL)mp1 )
+      {
+        POINTL         pointl;
+
+        if ( SDL_GetKeyboardFocus() != pWinData->window )
+          SDL_SetKeyboardFocus( pWinData->window );
+
+        WinQueryPointerPos( HWND_DESKTOP, &pointl );
+        WinMapWindowPoints( HWND_DESKTOP, pWinData->hwnd, &pointl, 1 );
+        SDL_SendMouseMotion( pWinData->window, 0, 0,
+                             pointl.x, pWinData->window->h - pointl.y - 1 );
+      }
+      else
+      {
+        if ( SDL_GetKeyboardFocus() == pWinData->window )
+          SDL_SetKeyboardFocus( NULL );
+
+        WinSetCapture( HWND_DESKTOP,  NULLHANDLE );
+      }
+      break;
+
+    case WM_MOUSEMOVE:
+      WinSetPointer( HWND_DESKTOP, hptrCursor );
+
+      if ( pWinData->lSkipWMMouseMove > 0 )
+        pWinData->lSkipWMMouseMove--;
+      else
+        _wmMouseMove( pWinData, SHORT1FROMMP(mp1), SHORT2FROMMP(mp1) );
+      return (MRESULT)FALSE;
+
+    case WM_BUTTON1DOWN:
+    case WM_BUTTON1DBLCLK:
+      _wmMouseButton( pWinData, 0, TRUE );
+      break;
+
+    case WM_BUTTON1UP:
+      _wmMouseButton( pWinData, 0, FALSE );
+      break;
+
+    case WM_BUTTON2DOWN:
+    case WM_BUTTON2DBLCLK:
+      _wmMouseButton( pWinData, 1, TRUE );
+      break;
+
+    case WM_BUTTON2UP:
+      _wmMouseButton( pWinData, 1, FALSE );
+      break;
+
+    case WM_BUTTON3DOWN:
+    case WM_BUTTON3DBLCLK:
+      _wmMouseButton( pWinData, 2, TRUE );
+      break;
+
+    case WM_BUTTON3UP:
+      _wmMouseButton( pWinData, 2, FALSE );
+      break;
+
+    case WM_TRANSLATEACCEL:
+      // ALT and acceleration keys not allowed (must be processed in WM_CHAR)
+      if ( mp1 == NULL || ((PQMSG)mp1)->msg != WM_CHAR )
+        break;
+      return (MRESULT)FALSE;
+
+    case WM_CHAR:
+      _wmChar( pWinData, mp1, mp2 );
+      break;
+
+    case WM_SIZE:
+      if ( pWinData->lSkipWMSize > 0 )
+        pWinData->lSkipWMSize--;
+      else
+      {
+        if ( (pWinData->window->flags & SDL_WINDOW_FULLSCREEN) == 0 )
+          SDL_SendWindowEvent( pWinData->window, SDL_WINDOWEVENT_RESIZED,
+                               SHORT1FROMMP(mp2), SHORT2FROMMP(mp2) );
+        else
+          pWinData->lSkipWMVRNEnabled++;
+      }
+      break;
+
+    case WM_MOVE:
+      if ( pWinData->lSkipWMMove > 0 )
+        pWinData->lSkipWMMove--;
+      else if ( (pWinData->window->flags & SDL_WINDOW_FULLSCREEN) == 0 )
+        _wmMove( pWinData );
+      break;
+
+    case WM_VRNENABLED:
+      if ( pWinData->lSkipWMVRNEnabled > 0 )
+        pWinData->lSkipWMVRNEnabled--;
+      else
+        _setVisibleRegion( pWinData, TRUE );
+      return (MRESULT)TRUE;
+
+    case WM_VRNDISABLED:
+      _setVisibleRegion( pWinData, FALSE );
+      return (MRESULT)TRUE;
+
+    case DM_DRAGOVER:
+      return _wmDragOver( pWinData, (PDRAGINFO)PVOIDFROMMP(mp1) );
+
+    case DM_DROP:
+      return _wmDrop( pWinData, (PDRAGINFO)PVOIDFROMMP(mp1) );
+  }
+
+  return pWinData->fnUserWndProc != NULL
+           ? pWinData->fnUserWndProc( hwnd, msg, mp1, mp2 )
+           : WinDefWindowProc( hwnd, msg, mp1, mp2 );
+}
+
+
+//           SDL routnes.
+//           ------------
+
+static void OS2_PumpEvents(_THIS)
+{
+  PSDL_VideoData       pVData = (PSDL_VideoData)_this->driverdata;
+  QMSG                 qmsg;
+
+  if( WinPeekMsg( pVData->hab, &qmsg, NULLHANDLE, 0, 0, PM_REMOVE ) )
+    WinDispatchMsg( pVData->hab, &qmsg );
+}
+
+static PWINDATA _setupWindow(_THIS, SDL_Window *window, HWND hwndFrame,
+                             HWND hwnd)
+{
+  PSDL_VideoData       pVData = (PSDL_VideoData)_this->driverdata;
+  PWINDATA             pWinData = SDL_calloc( 1, sizeof(WINDATA) );
+
+  if ( pWinData == NULL )
+  {
+    SDL_OutOfMemory();
+    return NULL;
+  }
+  pWinData->hwnd = hwnd;
+  pWinData->hwndFrame = hwndFrame;
+  pWinData->window = window;
+  window->driverdata = pWinData;
+
+  WinSetWindowULong( hwnd, 0, (ULONG)pWinData );
+  pWinData->fnWndFrameProc = WinSubclassWindow( hwndFrame, wndFrameProc );
+
+  pWinData->pOutput = pVData->pOutput;
+  pWinData->pVOData = pVData->pOutput->Open();
+
+  WinSetVisibleRegionNotify( hwnd, TRUE );
+
+  return pWinData;
+}
+
+static int OS2_CreateWindow(_THIS, SDL_Window *window)
+{
+  RECTL                rectl;
+  HWND                 hwndFrame, hwnd;
+  SDL_DisplayMode      *pSDLDisplayMode = _getDisplayModeForSDLWindow( window );
+  ULONG                ulFrameFlags = FCF_TASKLIST | FCF_TITLEBAR | FCF_SYSMENU
+                                      | FCF_MINBUTTON | FCF_SHELLPOSITION;
+  ULONG                ulSWPFlags = SWP_SIZE | SWP_SHOW | SWP_ZORDER |
+                                    SWP_ACTIVATE;
+  PWINDATA             pWinData;
+
+  if ( pSDLDisplayMode == NULL )
+    return -1;
+
+  // Create a PM window.
+
+  if ( (window->flags & SDL_WINDOW_RESIZABLE) != 0 )
+    ulFrameFlags |= FCF_SIZEBORDER | FCF_DLGBORDER | FCF_MAXBUTTON;
+  else if ( (window->flags & SDL_WINDOW_BORDERLESS) == 0 )
+    ulFrameFlags |= FCF_DLGBORDER;
+
+  if ( (window->flags & SDL_WINDOW_MAXIMIZED) != 0 )
+    ulSWPFlags |= SWP_MAXIMIZE;
+  else if ( (window->flags & SDL_WINDOW_MINIMIZED) != 0 )
+    ulSWPFlags |= SWP_MINIMIZE;
+
+  hwndFrame = WinCreateStdWindow( HWND_DESKTOP, 0, &ulFrameFlags,
+                                  WIN_CLIENT_CLASS, "SDL2", 0, 0, 0, &hwnd );
+  if ( hwndFrame == NULLHANDLE )
+    return SDL_SetError( "Couldn't create window" );
+
+  // Setup window data and frame window procedure.
+  pWinData = _setupWindow( _this, window, hwndFrame, hwnd );
+  if ( pWinData == NULL )
+  {
+    WinDestroyWindow( hwndFrame );
+    return -1;
+  }
+
+  // Show window.
+  rectl.xLeft   = 0;
+  rectl.yBottom = 0;
+  rectl.xRight  = window->w;
+  rectl.yTop    = window->h;
+  WinCalcFrameRect( hwndFrame, &rectl, FALSE );
+  pWinData->lSkipWMSize++;
+  pWinData->lSkipWMMove++;
+  WinSetWindowPos( hwndFrame, HWND_TOP, rectl.xLeft, rectl.yBottom,
+                   rectl.xRight - rectl.xLeft, rectl.yTop - rectl.yBottom,
+                   ulSWPFlags );
+
+  rectl.xLeft   = 0;
+  rectl.yBottom = 0;
+  WinMapWindowPoints( hwnd, HWND_DESKTOP, (PPOINTL)&rectl, 1 );
+  window->x = rectl.xLeft;
+  window->y = pSDLDisplayMode->h - ( rectl.yBottom + window->h );
+
+  window->flags |= SDL_WINDOW_SHOWN;
+
+  return 0;
+}
+
+static int OS2_CreateWindowFrom(_THIS, SDL_Window *window, const void *data)
+{
+  PSDL_VideoData       pVData = (PSDL_VideoData)_this->driverdata;
+  CHAR                 acBuf[256];
+  CLASSINFO            stCI;
+  HWND                 hwndUser = (HWND)data;
+  HWND                 hwndFrame, hwnd;
+  ULONG                cbText;
+  PSZ                  pszText;
+  PWINDATA             pWinData;
+  SDL_DisplayMode      *pSDLDisplayMode = _getDisplayModeForSDLWindow( window );
+  SWP                  swp;
+  POINTL               pointl;
+
+  debug( "Enter" );
+  if ( pSDLDisplayMode == NULL )
+    return -1;
+
+  // User can accept client OR frame window handle.
+  // Get client and frame window handles.
+
+  WinQueryClassName( hwndUser, sizeof(acBuf), acBuf );
+  if ( !WinQueryClassInfo( pVData->hab, &acBuf, &stCI ) )
+    return SDL_SetError( "Cannot get user window class information" );
+
+  if ( (stCI.flClassStyle & CS_FRAME) == 0 )
+  {
+    // Client window handle is specified.
+
+    hwndFrame = WinQueryWindow( hwndUser, QW_PARENT );
+    if ( hwndFrame == NULLHANDLE )
+      return SDL_SetError( "Cannot get parent window handle" );
+
+    if ( (ULONG)WinSendMsg( hwndFrame, WM_QUERYFRAMEINFO, 0, 0 ) == 0 )
+      return SDL_SetError( "Parent window is not a frame window" );
+
+    hwnd = hwndUser;
+  }
+  else
+  {
+    // Frame window handle is specified.
+
+    hwnd = WinWindowFromID( hwndUser, FID_CLIENT );
+    if ( hwnd == NULLHANDLE )
+      return SDL_SetError( "Cannot get client window handle" );
+
+    hwndFrame = hwndUser;
+
+    WinQueryClassName( hwnd, sizeof(acBuf), acBuf );
+    if ( !WinQueryClassInfo( pVData->hab, &acBuf, &stCI ) )
+      return SDL_SetError( "Cannot get client window class information" );
+  }
+
+  // Check window's reserved storage.
+  if ( stCI.cbWindowData < sizeof(ULONG) )
+    return SDL_SetError( "Reserved storage of window must be at least %u bytes",
+                         sizeof(ULONG) );
+
+  // Set SDL-window title.
+
+  cbText = WinQueryWindowTextLength( hwndFrame );
+  pszText = SDL_stack_alloc( CHAR, cbText + 1 );
+
+  if ( pszText != NULL )
+    cbText = pszText != NULL ?
+               WinQueryWindowText( hwndFrame, cbText, pszText ) : 0;
+
+  if ( cbText != 0 )
+    window->title = OS2_SysToUTF8( pszText );
+
+  if ( pszText != NULL )
+    SDL_stack_free( pszText );
+
+  // Set SDL-window flags.
+
+  window->flags &= ~(SDL_WINDOW_SHOWN | SDL_WINDOW_BORDERLESS |
+                     SDL_WINDOW_RESIZABLE | SDL_WINDOW_MAXIMIZED |
+                     SDL_WINDOW_MINIMIZED | SDL_WINDOW_INPUT_FOCUS);
+
+  if ( WinIsWindowVisible( hwnd ) )
+    window->flags |= SDL_WINDOW_SHOWN;
+
+  WinSendMsg( hwndFrame, WM_QUERYBORDERSIZE, MPFROMP(&pointl), 0 );
+  if ( pointl.y == WinQuerySysValue( HWND_DESKTOP, SV_CYSIZEBORDER ) )
+    window->flags |= SDL_WINDOW_RESIZABLE;
+  else if ( pointl.y <= WinQuerySysValue( HWND_DESKTOP, SV_CYBORDER ) )
+    window->flags |= SDL_WINDOW_BORDERLESS;
+
+  WinQueryWindowPos( hwndFrame, &swp );
+
+  if ( (swp.fl & SWP_MAXIMIZE) != 0 )
+    window->flags |= SDL_WINDOW_MAXIMIZED;
+
+  if ( (swp.fl & SWP_MINIMIZE) != 0 )
+    window->flags |= SDL_WINDOW_MINIMIZED;
+
+  pointl.x = 0;
+  pointl.y = 0;
+  WinMapWindowPoints( hwnd, HWND_DESKTOP, &pointl, 1 );
+  window->x = pointl.x;
+  window->y = pSDLDisplayMode->h - ( pointl.y + swp.cy );
+
+  WinQueryWindowPos( hwnd, &swp );
+  window->w = swp.cx;
+  window->h = swp.cy;
+
+  // Setup window data and frame window procedure.
+  pWinData = _setupWindow( _this, window, hwndFrame, hwnd );
+  if ( pWinData == NULL )
+  {
+    SDL_free( window->title );
+    window->title = NULL;
+    return -1;
+  }
+  pWinData->fnUserWndProc = WinSubclassWindow( hwnd, wndProc );
+
+  if ( WinQueryActiveWindow( HWND_DESKTOP ) == hwndFrame )
+    SDL_SetKeyboardFocus( window );
+
+  return 0;
+}
+
+static void OS2_DestroyWindow(_THIS, SDL_Window * window)
+{
+  PSDL_VideoData       pVData = (PSDL_VideoData)_this->driverdata;
+  PWINDATA             pWinData = (PWINDATA)window->driverdata;
+
+  debug( "Enter" );
+
+  if ( pWinData == NULL )
+    return;
+
+  if ( pWinData->fnUserWndProc == NULL )
+    // Window was created by SDL ( OS2_CreateWindow() ), not by
+    // user ( OS2_CreateWindowFrom() ).
+    WinDestroyWindow( pWinData->hwndFrame );
+  else
+    WinSetWindowULong( pWinData->hwnd, 0, 0 );
+
+  if ( ( pVData != NULL ) && ( pWinData->pVOData != NULL ) )
+  {
+    pVData->pOutput->Close( pWinData->pVOData );
+    pWinData->pVOData = NULL;
+  }
+
+  if ( pWinData->hptrIcon != NULLHANDLE )
+  {
+    WinDestroyPointer( pWinData->hptrIcon );
+    pWinData->hptrIcon = NULLHANDLE;
+  }
+
+  SDL_free( pWinData );
+  window->driverdata = NULL;
+}
+
+static void OS2_SetWindowTitle(_THIS, SDL_Window *window)
+{
+  PSZ        pszTitle = window->title == NULL ?
+                          NULL : OS2_UTF8ToSys( window->title );
+
+  WinSetWindowText( ((PWINDATA)window->driverdata)->hwndFrame, pszTitle );
+  SDL_free( pszTitle );
+}
+
+static void OS2_SetWindowIcon(_THIS, SDL_Window *window, SDL_Surface *icon)
+{
+  PWINDATA             pWinData = (PWINDATA)window->driverdata;
+  HPOINTER             hptr = utilCreatePointer( icon, 0, 0 );
+
+  if ( hptr == NULLHANDLE )
+    return;
+
+  // Destroy old icon.
+  if ( pWinData->hptrIcon != NULLHANDLE )
+    WinDestroyPointer( pWinData->hptrIcon );
+
+  // Set new window icon.
+  pWinData->hptrIcon = hptr;
+  if ( !WinSendMsg( pWinData->hwndFrame, WM_SETICON, MPFROMLONG( hptr ), 0 ) )
+    debug( "Cannot set icon for the window" );
+}
+
+static void OS2_SetWindowPosition(_THIS, SDL_Window *window)
+{
+  PWINDATA             pWinData = (PWINDATA)window->driverdata;
+  RECTL                rectl;
+  ULONG                ulFlags;
+  SDL_DisplayMode      *pSDLDisplayMode = _getDisplayModeForSDLWindow( window );
+
+  debug( "Enter" );
+  if ( pSDLDisplayMode == NULL )
+    return;
+
+  rectl.xLeft = 0;
+  rectl.yBottom = 0;
+  rectl.xRight = window->w;
+  rectl.yTop = window->h;
+  WinCalcFrameRect( pWinData->hwndFrame, &rectl, FALSE );
+
+  if ( SDL_ShouldAllowTopmost() &&
+       (window->flags & (SDL_WINDOW_FULLSCREEN|SDL_WINDOW_INPUT_FOCUS)) ==
+         (SDL_WINDOW_FULLSCREEN|SDL_WINDOW_INPUT_FOCUS) )
+    ulFlags = SWP_ZORDER | SWP_MOVE | SWP_SIZE;
+  else
+    ulFlags = SWP_MOVE | SWP_SIZE;
+
+  pWinData->lSkipWMSize++;
+  pWinData->lSkipWMMove++;
+  WinSetWindowPos( pWinData->hwndFrame, HWND_TOP,
+                   window->x + rectl.xLeft,
+                   (pSDLDisplayMode->h - window->y) - window->h + rectl.yBottom,
+                   rectl.xRight - rectl.xLeft, rectl.yTop - rectl.yBottom,
+                   ulFlags );
+}
+
+static void OS2_SetWindowSize(_THIS, SDL_Window *window)
+{
+  debug( "Enter" );
+  OS2_SetWindowPosition( _this, window );
+}
+
+static void OS2_ShowWindow(_THIS, SDL_Window *window)
+{
+  PWINDATA             pWinData = (PWINDATA)window->driverdata;
+
+  debug( "Enter" );
+  WinShowWindow( pWinData->hwndFrame, TRUE );
+}
+
+static void OS2_HideWindow(_THIS, SDL_Window *window)
+{
+  PWINDATA             pWinData = (PWINDATA)window->driverdata;
+
+  debug( "Enter" );
+  WinShowWindow( pWinData->hwndFrame, FALSE );
+}
+
+static void OS2_RaiseWindow(_THIS, SDL_Window *window)
+{
+  debug( "Enter" );
+  OS2_SetWindowPosition( _this, window );
+}
+
+static void OS2_MaximizeWindow(_THIS, SDL_Window *window)
+{
+  PWINDATA             pWinData = (PWINDATA)window->driverdata;
+
+  debug( "Enter" );
+  WinSetWindowPos( pWinData->hwndFrame, HWND_TOP, 0, 0, 0, 0, SWP_MAXIMIZE );
+}
+
+static void OS2_MinimizeWindow(_THIS, SDL_Window *window)
+{
+  PWINDATA             pWinData = (PWINDATA)window->driverdata;
+
+  debug( "Enter" );
+  WinSetWindowPos( pWinData->hwndFrame, HWND_TOP, 0, 0, 0, 0,
+                   SWP_MINIMIZE | SWP_DEACTIVATE  );
+}
+
+static void OS2_RestoreWindow(_THIS, SDL_Window *window)
+{
+  PWINDATA             pWinData = (PWINDATA)window->driverdata;
+
+  debug( "Enter" );
+  WinSetWindowPos( pWinData->hwndFrame, HWND_TOP, 0, 0, 0, 0,
+                   SWP_RESTORE );
+}
+
+static void OS2_SetWindowBordered(_THIS, SDL_Window * window,
+                                  SDL_bool bordered)
+{
+  PWINDATA   pWinData = (PWINDATA)window->driverdata;
+  ULONG      ulStyle = WinQueryWindowULong( pWinData->hwndFrame, QWL_STYLE );
+  RECTL      rectl;
+
+  debug( "Enter" );
+
+  // New frame sytle.
+  if ( bordered )
+    ulStyle |= (window->flags & SDL_WINDOW_RESIZABLE) != 0
+                 ? FS_SIZEBORDER : FS_DLGBORDER;
+  else
+    ulStyle &= ~(FS_SIZEBORDER | FS_BORDER | FS_DLGBORDER);
+
+  // Save client window position.
+  WinQueryWindowRect( pWinData->hwnd, &rectl );
+  WinMapWindowPoints( pWinData->hwnd, HWND_DESKTOP, (PPOINTL)&rectl, 2 );
+  
+  // Change the frame.
+  WinSetWindowULong( pWinData->hwndFrame, QWL_STYLE, ulStyle );
+  WinSendMsg( pWinData->hwndFrame, WM_UPDATEFRAME, MPFROMLONG(FCF_BORDER), 0 );
+
+  // Restore client window position.
+  WinCalcFrameRect( pWinData->hwndFrame, &rectl, FALSE );
+  pWinData->lSkipWMMove++;
+  WinSetWindowPos( pWinData->hwndFrame, HWND_TOP, rectl.xLeft, rectl.yBottom,
+                   rectl.xRight - rectl.xLeft,
+                   rectl.yTop - rectl.yBottom,
+                   SWP_SIZE | SWP_MOVE | SWP_NOADJUST );
+}
+
+static void OS2_SetWindowFullscreen(_THIS, SDL_Window *window,
+                                    SDL_VideoDisplay *display,
+                                    SDL_bool fullscreen)
+{
+  RECTL                rectl;
+  ULONG                ulFlags;
+  PWINDATA             pWinData = (PWINDATA)window->driverdata;
+  SDL_DisplayMode      *pSDLDisplayMode = &display->current_mode;
+
+  debug( "Enter, fullscreen: %u", fullscreen );
+
+  if ( pSDLDisplayMode == NULL )
+    return;
+
+  if ( SDL_ShouldAllowTopmost() &&
+       (window->flags & (SDL_WINDOW_FULLSCREEN|SDL_WINDOW_INPUT_FOCUS)) ==
+       (SDL_WINDOW_FULLSCREEN|SDL_WINDOW_INPUT_FOCUS) )
+    ulFlags = SWP_SIZE | SWP_MOVE | SWP_ZORDER | SWP_NOADJUST;
+  else
+    ulFlags = SWP_SIZE | SWP_MOVE | SWP_NOADJUST;
+
+  if ( fullscreen )
+  {
+    rectl.xLeft = 0;
+    rectl.yBottom = 0;
+    rectl.xRight = pSDLDisplayMode->w;
+    rectl.yTop = pSDLDisplayMode->h;
+    // We need send the restore command now to allow WinCalcFrameRect().
+    WinSetWindowPos( pWinData->hwndFrame, HWND_TOP, 0, 0, 0, 0, SWP_RESTORE );
+  }
+  else
+  {
+    pWinData->lSkipWMMove++;
+    rectl.xLeft = window->windowed.x;
+    rectl.yTop = pSDLDisplayMode->h - window->windowed.y;
+    rectl.xRight = rectl.xLeft + window->windowed.w;
+    rectl.yBottom = rectl.yTop - window->windowed.h;
+  }
+
+  if ( !WinCalcFrameRect( pWinData->hwndFrame, &rectl, FALSE ) )
+    debug( "WinCalcFrameRect() failed" );
+  else if ( !WinSetWindowPos( pWinData->hwndFrame, HWND_TOP,
+                        rectl.xLeft, rectl.yBottom,
+                        rectl.xRight - rectl.xLeft, rectl.yTop - rectl.yBottom,
+                        ulFlags ) )
+    debug( "WinSetWindowPos() failed" );
+}
+
+static SDL_bool OS2_GetWindowWMInfo(_THIS, SDL_Window * window,
+                                    struct SDL_SysWMinfo *info)
+{
+  PWINDATA             pWinData = (PWINDATA)window->driverdata;
+
+  if ( info->version.major <= SDL_MAJOR_VERSION )
+  {
+    info->subsystem = SDL_SYSWM_OS2;
+    info->info.os2.hwnd = pWinData->hwnd;
+    info->info.os2.hwndFrame = pWinData->hwndFrame;
+    return SDL_TRUE;
+  }
+
+  SDL_SetError( "Application not compiled with SDL %u.%u",
+                SDL_MAJOR_VERSION, SDL_MINOR_VERSION );
+  return SDL_FALSE;
+}
+
+static void OS2_OnWindowEnter(_THIS, SDL_Window * window)
+{
+}
+
+static int OS2_SetWindowHitTest(SDL_Window *window, SDL_bool enabled)
+{
+  debug( "Enter" );
+  return 0;
+}
+
+static void OS2_SetWindowGrab(_THIS, SDL_Window *window, SDL_bool grabbed)
+{
+  PWINDATA             pWinData = (PWINDATA)window->driverdata;
+
+  debug( "Enter, %u", grabbed );
+  _mouseCheck( pWinData );
+}
+
+
+// Shaper.
+
+typedef struct _SHAPERECTS {
+  PRECTL     pRects;
+  ULONG      cRects;
+  ULONG      ulWinHeight;
+} SHAPERECTS, *PSHAPERECTS;
+
+static void _combineRectRegions(SDL_ShapeTree *node, void *closure)
+{
+  PSHAPERECTS          pShapeRects = (PSHAPERECTS)closure;
+  PRECTL               pRect;
+
+  // Expand rectangles list.
+  if ( (pShapeRects->cRects & 0x0F) == 0 )
+  {
+    pRect = SDL_realloc( pShapeRects->pRects,
+                         ( pShapeRects->cRects + 0x10 ) * sizeof(RECTL) );
+    if ( pRect == NULL )
+      return;
+    pShapeRects->pRects = pRect;
+  }
+
+  // Add a new rectangle.
+  pRect = &pShapeRects->pRects[pShapeRects->cRects];
+  pShapeRects->cRects++;
+  // Fill rectangle data.
+  pRect->xLeft = node->data.shape.x;
+  pRect->yTop = pShapeRects->ulWinHeight - node->data.shape.y;
+  pRect->xRight += node->data.shape.w;
+  pRect->yBottom = pRect->yTop - node->data.shape.h;
+}
+
+static SDL_WindowShaper* OS2_CreateShaper(SDL_Window * window)
+{
+  SDL_WindowShaper*    pSDLShaper = SDL_malloc( sizeof(SDL_WindowShaper) );
+
+  debug( "Enter" );
+  pSDLShaper->window = window;
+  pSDLShaper->mode.mode = ShapeModeDefault;
+  pSDLShaper->mode.parameters.binarizationCutoff = 1;
+  pSDLShaper->userx = 0;
+  pSDLShaper->usery = 0;
+  pSDLShaper->driverdata = (SDL_ShapeTree *)NULL;
+  window->shaper = pSDLShaper;
+
+  if ( OS2_ResizeWindowShape( window ) != 0 )
+  {
+    window->shaper = NULL;
+    SDL_free( pSDLShaper );
+    return NULL;
+  }
+
+  return pSDLShaper;
+}
+
+static int OS2_SetWindowShape(SDL_WindowShaper *shaper, SDL_Surface *shape,
+                              SDL_WindowShapeMode *shape_mode)
+{
+  SDL_ShapeTree        *pShapeTree;
+  PWINDATA             pWinData;
+  SHAPERECTS           stShapeRects = { 0 };
+  HPS                  hps;
+
+  debug( "Enter" );
+  if ( ( shaper == NULL ) || ( shape == NULL ) ||
+       ( ( shape->format->Amask == 0 ) &&
+         ( shape_mode->mode != ShapeModeColorKey ) ) ||
+       ( shape->w != shaper->window->w ) || ( shape->h != shaper->window->h ) )
+    return SDL_INVALID_SHAPE_ARGUMENT;
+
+  if ( shaper->driverdata != NULL )
+    SDL_FreeShapeTree( (SDL_ShapeTree **)&shaper->driverdata );
+
+  pShapeTree = SDL_CalculateShapeTree( *shape_mode, shape );
+  shaper->driverdata = pShapeTree;
+
+  stShapeRects.ulWinHeight = shaper->window->h;
+  SDL_TraverseShapeTree( pShapeTree, &_combineRectRegions, &stShapeRects );
+
+  pWinData = (PWINDATA)shaper->window->driverdata;
+  hps = WinGetPS( pWinData->hwnd );
+
+  if ( pWinData->hrgnShape != NULLHANDLE )
+    GpiDestroyRegion( hps, pWinData->hrgnShape );
+
+  pWinData->hrgnShape = stShapeRects.pRects == NULL
+                          ? NULLHANDLE
+                          : GpiCreateRegion( hps, stShapeRects.cRects,
+                                             stShapeRects.pRects );
+
+  WinReleasePS( hps );
+  SDL_free( stShapeRects.pRects );
+  WinSendMsg( pWinData->hwnd, WM_VRNENABLED, 0, 0 );
+
+  return 0;
+}
+
+static int OS2_ResizeWindowShape(SDL_Window *window)
+{
+  debug( "Enter" );
+
+  if ( window == NULL )
+    return -1;
+
+  if ( window->x != -1000 )
+  {
+    if ( window->shaper->driverdata != NULL )
+      SDL_FreeShapeTree( (SDL_ShapeTree **)window->shaper->driverdata );
+
+    if ( window->shaper->hasshape == SDL_TRUE )
+    {
+      window->shaper->userx = window->x;
+      window->shaper->usery = window->y;
+      SDL_SetWindowPosition( window, -1000, -1000 );
+    }
+  }
+
+  return 0;
+}
+
+
+// Frame buffer.
+
+static void OS2_DestroyWindowFramebuffer(_THIS, SDL_Window *window)
+{
+  PWINDATA             pWinData = (PWINDATA)window->driverdata;
+
+  debug( "Enter" );
+  if ( ( pWinData != NULL ) && ( pWinData->pVOData != NULL ) )
+    pWinData->pOutput->VideoBufFree( pWinData->pVOData );
+}
+
+static int OS2_CreateWindowFramebuffer(_THIS, SDL_Window *window,
+                                       Uint32 *format, void **pixels,
+                                       int *pitch)
+{
+  PWINDATA             pWinData = (PWINDATA)window->driverdata;
+  SDL_VideoDisplay     *pSDLDisplay = SDL_GetDisplayForWindow( window );
+  SDL_DisplayMode      *pSDLDisplayMode;
+  PMODEDATA            pModeData;
+  ULONG                ulWidth, ulHeight;
+
+  debug( "Enter" );
+
+  if ( pSDLDisplay == NULL )
+  {
+    debug( "No display for the window" );
+    return -1;
+  }
+
+  pSDLDisplayMode = &pSDLDisplay->current_mode;
+  pModeData = (PMODEDATA)pSDLDisplayMode->driverdata;
+  if ( pModeData == NULL )
+    return SDL_SetError( "No mode data for the display" );
+
+  SDL_GetWindowSize( window, (int *)&ulWidth, (int *)&ulHeight );
+  debug( "Window size: %u x %u", ulWidth, ulHeight );
+
+  *pixels = pWinData->pOutput->VideoBufAlloc(
+                      pWinData->pVOData, ulWidth, ulHeight, pModeData->ulDepth,
+                      pModeData->fccColorEncoding, (PULONG)pitch );
+  if ( *pixels == NULL )
+    return -1;
+
+  *format = pSDLDisplayMode->format;
+  debug( "Pitch: %u, frame buffer: 0x%X.", *pitch, *pixels );
+  WinSendMsg( pWinData->hwnd, WM_VRNENABLED, 0, 0 );
+
+  return 0;
+}
+
+static int OS2_UpdateWindowFramebuffer(_THIS, SDL_Window * window,
+                                       const SDL_Rect *rects, int numrects)
+{
+  PWINDATA             pWinData = (PWINDATA)window->driverdata;
+
+  return pWinData->pOutput->Update( pWinData->pVOData, pWinData->hwnd,
+                                    (SDL_Rect *)rects, (ULONG)numrects )
+           ? 0 : -1;
+}
+
+
+// Clipboard.
+
+static int OS2_SetClipboardText(_THIS, const char *text)
+{
+  PSDL_VideoData       pVData = (PSDL_VideoData)_this->driverdata;
+  PSZ                  pszClipboard;
+  PSZ                  pszText = text == NULL ? NULL : OS2_UTF8ToSys( text );
+  ULONG                cbText;
+  ULONG                ulRC;
+  BOOL                 fSuccess;
+
+  debug( "Enter" );
+  if ( pszText == NULL )
+    return -1;
+  cbText = SDL_strlen( pszText );
+
+  ulRC = DosAllocSharedMem( (PPVOID)&pszClipboard, 0, cbText + 1,
+                            PAG_COMMIT | PAG_READ | PAG_WRITE |
+                            OBJ_GIVEABLE | OBJ_GETTABLE | OBJ_TILE );
+  if ( ulRC != NO_ERROR )
+  {
+    debug( "DosAllocSharedMem() failed, rc = %u", ulRC );
+    SDL_free( pszText );
+    return -1;
+  }
+
+  strcpy( pszClipboard, pszText );
+  SDL_free( pszText );
+
+  if ( !WinOpenClipbrd( pVData->hab ) )
+  {
+    debug( "WinOpenClipbrd() failed" );
+    fSuccess = FALSE;
+  }
+  else
+  {    
+    WinEmptyClipbrd( pVData->hab );
+
+    fSuccess = WinSetClipbrdData( pVData->hab, (ULONG)pszClipboard, CF_TEXT,
+                                  CFI_POINTER );
+    if ( !fSuccess )
+      debug( "WinOpenClipbrd() failed" );
+
+    WinCloseClipbrd( pVData->hab );
+  }
+
+  if ( !fSuccess )
+  {
+    DosFreeMem( pszClipboard );
+    return -1;
+  }
+
+  return 0;
+}
+
+static char *OS2_GetClipboardText(_THIS)
+{
+  PSDL_VideoData       pVData = (PSDL_VideoData)_this->driverdata;
+  PSZ                  pszClipboard = NULL;
+
+  if ( !WinOpenClipbrd( pVData->hab ) )
+    debug( "WinOpenClipbrd() failed" );
+  else
+  {
+    pszClipboard = (PSZ)WinQueryClipbrdData( pVData->hab, CF_TEXT );
+    if ( pszClipboard != NULL )
+      pszClipboard = OS2_SysToUTF8( pszClipboard );
+    WinCloseClipbrd( pVData->hab );
+  }
+
+  return pszClipboard == NULL ? SDL_strdup( "" ) : pszClipboard;
+}
+
+static SDL_bool OS2_HasClipboardText(_THIS)
+{
+  PSDL_VideoData       pVData = (PSDL_VideoData)_this->driverdata;
+  SDL_bool             fClipboard;
+
+  if ( !WinOpenClipbrd( pVData->hab ) )
+  {
+    debug( "WinOpenClipbrd() failed" );
+    return SDL_FALSE;
+  }
+
+  fClipboard = (PSZ)WinQueryClipbrdData( pVData->hab, CF_TEXT ) != NULL
+                 ? SDL_TRUE : SDL_FALSE;
+  WinCloseClipbrd( pVData->hab );
+
+  return fClipboard;
+}
+
+
+static int OS2_VideoInit(_THIS)
+{
+  PSDL_VideoData       pVData;
+  PTIB                 tib;
+  PPIB                 pib;
+
+  // Create SDL video driver private data.
+
+  pVData = SDL_calloc( 1, sizeof(SDL_VideoData) );
+  if ( pVData == NULL )
+    return SDL_OutOfMemory();
+
+  // Change process type code for use Win* API from VIO session.
+
+  DosGetInfoBlocks( &tib, &pib );
+  if ( pib->pib_ultype == 2 || pib->pib_ultype == 0 )
+  {
+    // VIO windowable or fullscreen protect-mode session.
+    pib->pib_ultype = 3; // Presentation Manager protect-mode session.
+  }
+
+  // PM initialization.
+
+  pVData->hab = WinInitialize( 0 );
+  pVData->hmq = WinCreateMsgQueue( pVData->hab, 0 );
+  if ( pVData->hmq == NULLHANDLE )
+  {
+    SDL_free( pVData );
+    return SDL_SetError( "Message queue cannot be created." );
+  }
+
+  if ( !WinRegisterClass( pVData->hab, WIN_CLIENT_CLASS, wndProc,
+                          CS_SIZEREDRAW | CS_MOVENOTIFY | CS_SYNCPAINT,
+                          sizeof(PSDL_VideoData) ) )
+  {
+    SDL_free( pVData );
+    return SDL_SetError( "Window class not successfully registered." );
+  }
+
+  if ( stricmp( _this->name, OS2DRIVER_NAME_VMAN ) == 0 )
+    pVData->pOutput = &voVMan;
+  else
+    pVData->pOutput = &voDive;
+
+  _this->driverdata = pVData;
+
+  // Add display.
+
+  {
+    SDL_VideoDisplay     stSDLDisplay;
+    SDL_DisplayMode      stSDLDisplayMode;
+    PDISPLAYDATA         pDisplayData;
+    PMODEDATA            pModeData;
+    VIDEOOUTPUTINFO      stVOInfo;
+
+    if ( !pVData->pOutput->QueryInfo( &stVOInfo ) )
+    {
+      SDL_free( pVData );
+      return SDL_SetError( "Video mode query failed." );
+    }
+
+    stSDLDisplayMode.format = _getSDLPixelFormat( stVOInfo.ulBPP,
+                                                  stVOInfo.fccColorEncoding );
+    stSDLDisplayMode.w = stVOInfo.ulHorizResolution;
+    stSDLDisplayMode.h = stVOInfo.ulVertResolution;
+    stSDLDisplayMode.refresh_rate = 0;
+    stSDLDisplayMode.driverdata = NULL;
+
+    pModeData = SDL_malloc( sizeof(MODEDATA) );
+    if ( pModeData != NULL )
+    {
+      pModeData->ulDepth = stVOInfo.ulBPP;
+      pModeData->fccColorEncoding = stVOInfo.fccColorEncoding;
+      pModeData->ulScanLineBytes = stVOInfo.ulScanLineSize;
+      stSDLDisplayMode.driverdata = pModeData;
+    }
+
+    stSDLDisplay.name = "Primary";
+    stSDLDisplay.desktop_mode = stSDLDisplayMode;
+    stSDLDisplay.current_mode = stSDLDisplayMode;
+    stSDLDisplay.driverdata = NULL;
+    stSDLDisplay.num_display_modes = 0;
+
+    pDisplayData = SDL_malloc( sizeof(DISPLAYDATA) );
+    if ( pDisplayData != NULL )
+    {
+      HPS    hps = WinGetPS( HWND_DESKTOP );
+      HDC    hdc = GpiQueryDevice( hps );
+
+      // May be we can use CAPS_HORIZONTAL_RESOLUTION and
+      // CAPS_VERTICAL_RESOLUTION - pels per meter?
+      DevQueryCaps( hdc, CAPS_HORIZONTAL_FONT_RES, 1,
+                    (PLONG)&pDisplayData->ulDPIHor );
+      DevQueryCaps( hdc, CAPS_VERTICAL_FONT_RES, 1,
+                    (PLONG)&pDisplayData->ulDPIVer );
+      WinReleasePS( hps );
+
+      pDisplayData->ulDPIDiag = SDL_ComputeDiagonalDPI(
+              stVOInfo.ulHorizResolution, stVOInfo.ulVertResolution,
+              (float)stVOInfo.ulHorizResolution / pDisplayData->ulDPIHor,
+              (float)stVOInfo.ulVertResolution / pDisplayData->ulDPIVer );
+
+      stSDLDisplayMode.driverdata = pDisplayData;
+    }
+
+    SDL_AddVideoDisplay( &stSDLDisplay );
+  }
+
+  OS2_InitMouse( _this, pVData->hab );
+
+  return 0;
+}
+
+static void OS2_VideoQuit(_THIS)
+{
+  PSDL_VideoData       pVData = (PSDL_VideoData)_this->driverdata;
+  ULONG                ulDisplayIdx, ulModeIdx;
+  SDL_VideoDisplay     *pSDLDisplay;
+
+  OS2_QuitMouse( _this );
+
+  WinDestroyMsgQueue( pVData->hmq );
+  WinTerminate( pVData->hab );
+
+  // We support only one display. Free all listed displays data for the future.
+  for( ulDisplayIdx = 0; ulDisplayIdx < _this->num_displays; ulDisplayIdx++ )
+  {
+    pSDLDisplay = &_this->displays[ulDisplayIdx];
+
+    // Free video mode data (PMODEDATA).
+    if ( pSDLDisplay->desktop_mode.driverdata != NULL )
+      SDL_free( pSDLDisplay->desktop_mode.driverdata );
+
+    // We support only one mode - desktop_mode. Free all modes for the future.
+    for( ulModeIdx = 0; ulModeIdx < pSDLDisplay->num_display_modes;
+         ulModeIdx++ )
+      if ( pSDLDisplay->display_modes[ulModeIdx].driverdata != NULL )
+        SDL_free( pSDLDisplay->display_modes[ulModeIdx].driverdata );
+
+    // Free display data (PDISPLAYDATA).
+    if ( pSDLDisplay->driverdata != NULL )
+    {
+      SDL_free( pSDLDisplay->driverdata );
+      pSDLDisplay->driverdata = NULL;
+    }
+  }
+}
+
+static int OS2_GetDisplayBounds(_THIS, SDL_VideoDisplay *display,
+                                SDL_Rect *rect)
+{
+  debug( "Enter" );
+
+  rect->x = 0;
+  rect->y = 0;
+  rect->w = display->desktop_mode.w;
+  rect->h = display->desktop_mode.h;
+
+  return 0;
+}
+
+static int OS2_GetDisplayDPI(_THIS, SDL_VideoDisplay *display, float *ddpi,
+                             float *hdpi, float *vdpi)
+{
+  PDISPLAYDATA         pDisplayData = (PDISPLAYDATA)display->driverdata;
+
+  debug( "Enter" );
+
+  if ( pDisplayData == NULL )
+    return -1;
+
+  if ( ddpi != NULL )
+    *hdpi = pDisplayData->ulDPIDiag;
+
+  if ( hdpi != NULL )
+    *hdpi = pDisplayData->ulDPIHor;
+
+  if ( vdpi != NULL )
+    *vdpi = pDisplayData->ulDPIVer;
+
+  return 0;
+}
+
+static void OS2_GetDisplayModes(_THIS, SDL_VideoDisplay *display)
+{
+  debug( "Enter" );
+}
+
+static int OS2_SetDisplayMode(_THIS, SDL_VideoDisplay *display,
+                              SDL_DisplayMode *mode)
+{
+  debug( "Enter" );
+  return -1;
+}
+
+
+static void OS2_DeleteDevice(SDL_VideoDevice *device)
+{
+  SDL_free( device );
+}
+
+static SDL_VideoDevice *OS2_CreateDevice(int devindex)
+{
+  SDL_VideoDevice *device;
+
+  /* Initialize all variables that we clean on shutdown */
+  device = (SDL_VideoDevice *)SDL_calloc(1, sizeof(SDL_VideoDevice));
+  if (!device)
+  {
+    SDL_OutOfMemory();
+    return (0);
+  }
+
+  /* Set the function pointers */
+  device->VideoInit = OS2_VideoInit;
+  device->VideoQuit = OS2_VideoQuit;
+  device->SetDisplayMode = OS2_SetDisplayMode;
+  device->GetDisplayBounds = OS2_GetDisplayBounds;
+  device->GetDisplayDPI = OS2_GetDisplayDPI;
+  device->GetDisplayModes = OS2_GetDisplayModes;
+  device->SetDisplayMode = OS2_SetDisplayMode;
+  device->PumpEvents = OS2_PumpEvents;
+  device->CreateWindow = OS2_CreateWindow;
+  device->CreateWindowFrom = OS2_CreateWindowFrom;
+  device->DestroyWindow = OS2_DestroyWindow;
+  device->SetWindowTitle = OS2_SetWindowTitle;
+  device->SetWindowIcon = OS2_SetWindowIcon;
+  device->SetWindowPosition = OS2_SetWindowPosition;
+  device->SetWindowSize = OS2_SetWindowSize;
+  device->ShowWindow = OS2_ShowWindow;
+  device->HideWindow = OS2_HideWindow;
+  device->RaiseWindow = OS2_RaiseWindow;
+  device->MaximizeWindow = OS2_MaximizeWindow;
+  device->MinimizeWindow = OS2_MinimizeWindow;
+  device->RestoreWindow = OS2_RestoreWindow;
+  device->SetWindowBordered = OS2_SetWindowBordered;
+  device->SetWindowFullscreen = OS2_SetWindowFullscreen;
+  device->GetWindowWMInfo = OS2_GetWindowWMInfo;
+  device->OnWindowEnter = OS2_OnWindowEnter;
+  device->SetWindowHitTest = OS2_SetWindowHitTest;
+  device->SetWindowGrab = OS2_SetWindowGrab;
+  device->CreateWindowFramebuffer = OS2_CreateWindowFramebuffer;
+  device->UpdateWindowFramebuffer = OS2_UpdateWindowFramebuffer;
+  device->DestroyWindowFramebuffer = OS2_DestroyWindowFramebuffer;
+
+  device->SetClipboardText = OS2_SetClipboardText;
+  device->GetClipboardText = OS2_GetClipboardText;
+  device->HasClipboardText = OS2_HasClipboardText;
+
+  device->shape_driver.CreateShaper = OS2_CreateShaper;
+  device->shape_driver.SetWindowShape = OS2_SetWindowShape;
+  device->shape_driver.ResizeWindowShape = OS2_ResizeWindowShape;
+
+  device->free = OS2_DeleteDevice;
+
+  return device;
+}
+
+
+// Output video system availability checking.
+
+static int OS2DIVE_Available(void)
+{
+  VIDEOOUTPUTINFO      stVOInfo;
+
+  return voDive.QueryInfo( &stVOInfo );
+}
+
+static int OS2VMAN_Available(void)
+{
+  VIDEOOUTPUTINFO      stVOInfo;
+
+  return voVMan.QueryInfo( &stVOInfo );
+}
+
+
+// Both bootstraps for DIVE and VMAN are uing same function OS2_CreateDevice().
+// Video output system will be selected in OS2_VideoInit() by driver name.
+
+VideoBootStrap OS2DIVE_bootstrap =
+{
+  OS2DRIVER_NAME_DIVE, "OS/2 video driver",
+  OS2DIVE_Available, OS2_CreateDevice
+};
+
+VideoBootStrap OS2VMAN_bootstrap =
+{
+  OS2DRIVER_NAME_VMAN, "OS/2 video driver",
+  OS2VMAN_Available, OS2_CreateDevice
+};
+
+#endif /* SDL_VIDEO_DRIVER_OS2 */
diff --git a/src/video/os2/SDL_os2video.h b/src/video/os2/SDL_os2video.h
new file mode 100644
index 0000000..633efa2
--- /dev/null
+++ b/src/video/os2/SDL_os2video.h
@@ -0,0 +1,83 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "../../SDL_internal.h"
+
+#ifndef _SDL_os2video_h
+#define _SDL_os2video_h
+
+#include "../SDL_sysvideo.h"
+#include ".\core\os2\SDL_os2.h"
+
+#define INCL_DOS
+#define INCL_DOSERRORS
+#define INCL_DOSPROCESS
+#define INCL_WIN
+#define INCL_GPI
+#define INCL_OS2MM
+#define INCL_DOSMEMMGR
+#include <os2.h>
+//#include <gradd.h>               // Defines FOURCC_xxxx
+
+
+#include "SDL_os2mouse.h"
+#include "SDL_os2output.h"
+
+typedef struct SDL_VideoData {
+  HAB                  hab;
+  HMQ                  hmq;
+  POS2VIDEOOUTPUT      pOutput;            // Video output routines.
+} SDL_VideoData, *PSDL_VideoData;
+
+typedef struct _WINDATA {
+  SDL_Window           *window;
+  POS2VIDEOOUTPUT      pOutput;            // Video output routines.
+  HWND                 hwndFrame;
+  HWND                 hwnd;
+  PFNWP                fnUserWndProc;
+  PFNWP                fnWndFrameProc;
+
+  PVODATA              pVOData;            // Video output data.
+
+  HRGN                 hrgnShape;
+  HPOINTER             hptrIcon;
+  RECTL                rectlBeforeFS;
+
+  LONG                 lSkipWMSize;
+  LONG                 lSkipWMMove;
+  LONG                 lSkipWMMouseMove;
+  LONG                 lSkipWMVRNEnabled;
+  LONG                 lSkipWMAdjustFramePos;
+} WINDATA, *PWINDATA;
+
+typedef struct _DISPLAYDATA {
+  ULONG                ulDPIHor;
+  ULONG                ulDPIVer;
+  ULONG                ulDPIDiag;
+} DISPLAYDATA, *PDISPLAYDATA;
+
+typedef struct _MODEDATA {
+  ULONG                ulDepth;
+  ULONG                fccColorEncoding;
+  ULONG                ulScanLineBytes;
+} MODEDATA, *PMODEDATA;
+
+
+#endif /* _SDL_os2video_h */
diff --git a/src/video/os2/SDL_os2vman.c b/src/video/os2/SDL_os2vman.c
new file mode 100644
index 0000000..394c695
--- /dev/null
+++ b/src/video/os2/SDL_os2vman.c
@@ -0,0 +1,512 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "../../SDL_internal.h"
+#include "../SDL_sysvideo.h"
+#define INCL_DOSERRORS
+#define INCL_DOSPROCESS
+#define INCL_DOSMODULEMGR
+#define INCL_WIN
+#define INCL_GPI
+#define INCL_GPIBITMAPS         // GPI bit map functions
+#include <os2.h>
+#define INCL_GRE_DEVICE
+#define INCL_GRE_DEVMISC
+#include <pmddi.h>
+#include "SDL_os2output.h"
+#include "gradd.h"
+#include "SDL_os2video.h"
+
+typedef struct _VODATA {
+  PVOID      pBuffer;
+  HRGN       hrgnVisible;
+  ULONG      ulBPP;
+  ULONG      ulScanLineSize;
+  ULONG      ulWidth;
+  ULONG      ulHeight;
+  ULONG      ulScreenHeight;
+  ULONG      ulScreenBytesPerLine;
+  RECTL      rectlWin;
+
+  PRECTL     pRectl;
+  ULONG      cRectl;
+  PBLTRECT   pBltRect;
+  ULONG      cBltRect;
+} VODATA;
+
+static BOOL voQueryInfo(PVIDEOOUTPUTINFO pInfo);
+static PVODATA voOpen();
+static VOID voClose(PVODATA pVOData);
+static BOOL voSetVisibleRegion(PVODATA pVOData, HWND hwnd,
+                               SDL_DisplayMode *pSDLDisplayMode,
+                               HRGN hrgnShape, BOOL fVisible);
+static PVOID voVideoBufAlloc(PVODATA pVOData, ULONG ulWidth, ULONG ulHeight,
+                             ULONG ulBPP, ULONG fccColorEncoding,
+                             PULONG pulScanLineSize);
+static VOID voVideoBufFree(PVODATA pVOData);
+static BOOL voUpdate(PVODATA pVOData, HWND hwnd, SDL_Rect *pSDLRects,
+                     ULONG cSDLRects);
+
+OS2VIDEOOUTPUT voVMan = {
+  voQueryInfo,
+  voOpen,
+  voClose,
+  voSetVisibleRegion,
+  voVideoBufAlloc,
+  voVideoBufFree,
+  voUpdate
+};
+
+
+static HMODULE         hmodVMan = NULLHANDLE;
+static FNVMIENTRY      *pfnVMIEntry = NULL;
+static ULONG           ulVRAMAddress = 0;
+
+VOID APIENTRY ExitVMan(VOID)
+{
+  if ( ( ulVRAMAddress != 0 ) && ( hmodVMan != NULLHANDLE ) )
+  {
+    pfnVMIEntry( 0, VMI_CMD_TERMPROC, NULL, NULL );
+    DosFreeModule( hmodVMan );
+  }
+
+  DosExitList( EXLST_EXIT, (PFNEXITLIST)NULL );
+}
+
+static BOOL _vmanInit()
+{
+  ULONG                ulRC;
+  CHAR                 acBuf[255];
+  INITPROCOUT          stInitProcOut;
+
+  if ( hmodVMan != NULLHANDLE )
+    // Already was initialized.
+    return TRUE;
+
+  // Load vman.dll
+  ulRC = DosLoadModule( &acBuf, sizeof(acBuf), "VMAN", &hmodVMan );
+  if ( ulRC != NO_ERROR )
+  {
+    debug( "Could not load VMAN.DLL, rc = %u : %s", ulRC, &acBuf );
+    hmodVMan = NULLHANDLE;
+    return FALSE;
+  }
+
+  // Get VMIEntry.
+  ulRC = DosQueryProcAddr( hmodVMan, 0L, "VMIEntry", (PFN *)&pfnVMIEntry );
+  if ( ulRC != NO_ERROR )
+  {
+    debug( "Could not query address of pfnVMIEntry func. of VMAN.DLL, "
+           "rc = %u", ulRC );
+    DosFreeModule( hmodVMan );
+    hmodVMan = NULLHANDLE;
+    return FALSE;
+  }
+
+  // VMAN initialization.
+  stInitProcOut.ulLength = sizeof(stInitProcOut);
+  ulRC = pfnVMIEntry( 0, VMI_CMD_INITPROC, NULL, &stInitProcOut );
+  if ( ulRC != RC_SUCCESS )
+  {
+    debug( "Could not initialize VMAN for this process" );
+    pfnVMIEntry = NULL;
+    DosFreeModule( hmodVMan );
+    hmodVMan = NULLHANDLE;
+    return FALSE;
+  }
+
+  // Store video memory virtual address.
+  ulVRAMAddress = stInitProcOut.ulVRAMVirt;
+  // We use exit list for VMI_CMD_TERMPROC.
+  if ( DosExitList( EXLST_ADD | 0x00001000, (PFNEXITLIST)ExitVMan )
+       != NO_ERROR )
+    debug( "DosExitList() failed" );
+
+  return TRUE;
+}
+
+static PRECTL _getRectlArray(PVODATA pVOData, ULONG cRects)
+{
+  PRECTL     pRectl;
+
+  if ( pVOData->cRectl >= cRects )
+    return pVOData->pRectl;
+
+  pRectl = SDL_realloc( pVOData->pRectl, cRects * sizeof(RECTL) );
+  if ( pRectl == NULL )
+    return NULL;
+
+  pVOData->pRectl = pRectl;
+  pVOData->cRectl = cRects;
+  return pRectl;
+}
+
+static PBLTRECT _getBltRectArray(PVODATA pVOData, ULONG cRects)
+{
+  PBLTRECT   pBltRect;
+
+  if ( pVOData->cBltRect >= cRects )
+    return pVOData->pBltRect;
+
+  pBltRect = SDL_realloc( pVOData->pBltRect, cRects * sizeof(BLTRECT) );
+  if ( pBltRect == NULL )
+    return NULL;
+
+  pVOData->pBltRect = pBltRect;
+  pVOData->cBltRect = cRects;
+  return pBltRect;
+}
+
+
+static BOOL voQueryInfo(PVIDEOOUTPUTINFO pInfo)
+{
+  ULONG                ulRC;
+  GDDMODEINFO          sCurModeInfo;
+
+  if ( !_vmanInit() )
+    return FALSE;
+
+  // Query current (desktop) mode.
+  ulRC = pfnVMIEntry( 0, VMI_CMD_QUERYCURRENTMODE, NULL, &sCurModeInfo );
+  if ( ulRC != RC_SUCCESS )
+  {
+    debug( "Could not query desktop video mode." );
+    return FALSE;
+  }
+
+  pInfo->ulBPP             = sCurModeInfo.ulBpp;
+  pInfo->ulHorizResolution = sCurModeInfo.ulHorizResolution;
+  pInfo->ulVertResolution  = sCurModeInfo.ulVertResolution;
+  pInfo->ulScanLineSize    = sCurModeInfo.ulScanLineSize;
+  pInfo->fccColorEncoding  = sCurModeInfo.fccColorEncoding;
+
+  return TRUE;
+}
+
+static PVODATA voOpen()
+{
+  PVODATA              pVOData;
+
+  if ( !_vmanInit() )
+    return NULL;
+
+  pVOData = SDL_calloc( 1, sizeof(VODATA) );
+  if ( pVOData == NULL )
+  {
+    SDL_OutOfMemory();
+    return NULL;
+  }
+
+  return pVOData;
+}
+
+static VOID voClose(PVODATA pVOData)
+{
+  if ( pVOData->pRectl != NULL )
+    SDL_free( pVOData->pRectl );
+
+  if ( pVOData->pBltRect != NULL )
+    SDL_free( pVOData->pBltRect );
+
+  voVideoBufFree( pVOData );
+}
+
+static BOOL voSetVisibleRegion(PVODATA pVOData, HWND hwnd,
+                               SDL_DisplayMode *pSDLDisplayMode,
+                               HRGN hrgnShape, BOOL fVisible)
+{
+  HPS        hps;
+  BOOL       fSuccess = FALSE;
+
+  hps = WinGetPS( hwnd );
+
+  if ( pVOData->hrgnVisible != NULLHANDLE )
+  {
+    GpiDestroyRegion( hps, pVOData->hrgnVisible );
+    pVOData->hrgnVisible = NULLHANDLE;
+  }
+
+  if ( fVisible )
+  {
+    // Query visible rectangles
+
+    pVOData->hrgnVisible = GpiCreateRegion( hps, 0, NULL );
+    if ( pVOData->hrgnVisible == NULLHANDLE )
+    {
+      SDL_SetError( "GpiCreateRegion() failed" );
+    }
+    else
+    {
+      if ( WinQueryVisibleRegion( hwnd, pVOData->hrgnVisible ) == RGN_ERROR )
+      {
+        GpiDestroyRegion( hps, pVOData->hrgnVisible );
+        pVOData->hrgnVisible = NULLHANDLE;
+      }
+      else
+      {
+        if ( hrgnShape != NULLHANDLE )
+          GpiCombineRegion( hps, pVOData->hrgnVisible, pVOData->hrgnVisible,
+                            hrgnShape, CRGN_AND );
+        fSuccess = TRUE;
+      }
+    }
+
+    WinQueryWindowRect( hwnd, &pVOData->rectlWin );
+    WinMapWindowPoints( hwnd, HWND_DESKTOP, (PPOINTL)&pVOData->rectlWin, 2 );
+
+    if ( pSDLDisplayMode != NULL )
+    {
+      pVOData->ulScreenHeight = pSDLDisplayMode->h;
+      pVOData->ulScreenBytesPerLine =
+                     ((PMODEDATA)pSDLDisplayMode->driverdata)->ulScanLineBytes;
+    }
+  }
+
+  WinReleasePS( hps );
+
+  return fSuccess;
+}
+
+static PVOID voVideoBufAlloc(PVODATA pVOData, ULONG ulWidth, ULONG ulHeight,
+                             ULONG ulBPP, ULONG fccColorEncoding,
+                             PULONG pulScanLineSize)
+{
+  ULONG      ulRC;
+  ULONG      ulScanLineSize = ulWidth * (ulBPP >> 3);
+
+  // Destroy previous buffer.
+  voVideoBufFree( pVOData );
+
+  if ( ( ulWidth == 0 ) || ( ulHeight == 0 ) || ( ulBPP == 0 ) )
+    return NULL;
+
+  // Bytes per line.
+  ulScanLineSize = ( ulScanLineSize + 3 ) & ~3;	/* 4-byte aligning */
+  *pulScanLineSize = ulScanLineSize;
+
+  ulRC = DosAllocMem( &pVOData->pBuffer,
+                      (ulHeight * ulScanLineSize) + sizeof(ULONG),
+                      PAG_COMMIT | PAG_EXECUTE | PAG_READ | PAG_WRITE );
+  if ( ulRC != NO_ERROR )
+  {
+    debug( "DosAllocMem(), rc = %u", ulRC );
+    return NULL;
+  }
+
+  pVOData->ulBPP          = ulBPP;
+  pVOData->ulScanLineSize = ulScanLineSize;
+  pVOData->ulWidth        = ulWidth;
+  pVOData->ulHeight       = ulHeight;
+
+  return pVOData->pBuffer;
+}
+
+static VOID voVideoBufFree(PVODATA pVOData)
+{
+  ULONG      ulRC;
+
+  if ( pVOData->pBuffer == NULL )
+    return;
+
+  ulRC = DosFreeMem( pVOData->pBuffer );
+  if ( ulRC != NO_ERROR )
+    debug( "DosFreeMem(), rc = %u", ulRC );
+  else
+    pVOData->pBuffer = NULL;
+}
+
+static BOOL voUpdate(PVODATA pVOData, HWND hwnd, SDL_Rect *pSDLRects,
+                     ULONG cSDLRects)
+{
+  PRECTL               prectlDst, prectlScan;
+  HPS                  hps;
+  HRGN                 hrgnUpdate;
+  RGNRECT              rgnCtl;
+  SDL_Rect             stSDLRectDef;
+  BMAPINFO             bmiSrc;
+  BMAPINFO             bmiDst;
+  PPOINTL              pptlSrcOrg;
+  PBLTRECT             pbrDst;
+  HWREQIN              sHWReqIn;
+  BITBLTINFO           sBitbltInfo = { 0 };
+  ULONG                ulIdx;
+//  RECTL                rectlScreenUpdate;
+
+  if ( pVOData->pBuffer == NULL )
+    return FALSE;
+
+  if ( pVOData->hrgnVisible == NULLHANDLE )
+    return TRUE;
+
+  bmiSrc.ulLength = sizeof(BMAPINFO);
+  bmiSrc.ulType = BMAP_MEMORY;
+  bmiSrc.ulWidth = pVOData->ulWidth;
+  bmiSrc.ulHeight = pVOData->ulHeight;
+  bmiSrc.ulBpp = pVOData->ulBPP;
+  bmiSrc.ulBytesPerLine = pVOData->ulScanLineSize;
+  bmiSrc.pBits = (PBYTE)pVOData->pBuffer;
+
+  bmiDst.ulLength = sizeof(BMAPINFO);
+  bmiDst.ulType = BMAP_VRAM;
+  bmiDst.pBits = (PBYTE)ulVRAMAddress;
+  bmiDst.ulWidth = bmiSrc.ulWidth;
+  bmiDst.ulHeight = bmiSrc.ulHeight;
+  bmiDst.ulBpp = bmiSrc.ulBpp;
+  bmiDst.ulBytesPerLine = pVOData->ulScreenBytesPerLine;
+
+  // List of update rectangles. This is the intersection of requested
+  // rectangles and visible rectangles.
+
+  if ( cSDLRects == 0 )
+  {
+    // Full update requested.
+    stSDLRectDef.x = 0;
+    stSDLRectDef.y = 0;
+    stSDLRectDef.w = bmiSrc.ulWidth;
+    stSDLRectDef.h = bmiSrc.ulHeight;
+    pSDLRects = &stSDLRectDef;
+    cSDLRects = 1;
+  }
+
+  // Make list of destionation rectangles (prectlDst) list from the source
+  // list (prectl).
+  prectlDst = _getRectlArray( pVOData, cSDLRects );
+  if ( prectlDst == NULL )
+  {
+    debug( "Not enough memory" );
+    return FALSE;
+  }
+  prectlScan = prectlDst;
+  for( ulIdx = 0; ulIdx < cSDLRects; ulIdx++, pSDLRects++, prectlScan++ )
+  {
+    prectlScan->xLeft   = pSDLRects->x;
+    prectlScan->yTop    = pVOData->ulHeight - pSDLRects->y;
+    prectlScan->xRight  = prectlScan->xLeft + pSDLRects->w;
+    prectlScan->yBottom = prectlScan->yTop - pSDLRects->h;
+  }
+
+  hps = WinGetPS( hwnd );
+  if ( hps == NULLHANDLE )
+    return FALSE;
+
+  // Make destination region to update.
+  hrgnUpdate = GpiCreateRegion( hps, cSDLRects, prectlDst );
+  // "AND" on visible and destination regions, result is region to update.
+  GpiCombineRegion( hps, hrgnUpdate, hrgnUpdate, pVOData->hrgnVisible,
+                    CRGN_AND );
+
+  // Get rectangles of the region to update.
+  rgnCtl.ircStart     = 1;
+  rgnCtl.crc          = 0;
+  rgnCtl.ulDirection  = 1;
+  rgnCtl.crcReturned  = 0;
+  GpiQueryRegionRects( hps, hrgnUpdate, NULL, &rgnCtl, NULL );
+  if ( rgnCtl.crcReturned == 0 )
+  {
+    GpiDestroyRegion( hps, hrgnUpdate );
+    WinReleasePS( hps );
+    return TRUE;
+  }
+  // We don't need prectlDst, use it again to store update regions.
+  prectlDst = _getRectlArray( pVOData, rgnCtl.crcReturned );
+  if ( prectlDst == NULL )
+  {
+    debug( "Not enough memory" );
+    GpiDestroyRegion( hps, hrgnUpdate );
+    WinReleasePS( hps );
+    return FALSE;
+  }
+  rgnCtl.ircStart     = 1;
+  rgnCtl.crc          = rgnCtl.crcReturned;
+  rgnCtl.ulDirection  = 1;
+  GpiQueryRegionRects( hps, hrgnUpdate, NULL, &rgnCtl, prectlDst );
+  GpiDestroyRegion( hps, hrgnUpdate );
+  WinReleasePS( hps );
+  cSDLRects = rgnCtl.crcReturned;
+  // Now cRect/prectlDst is a list of regions in window (update && visible).
+
+  // Make lists for blitting from update regions.
+
+  pbrDst = _getBltRectArray( pVOData, cSDLRects );
+  if ( pbrDst == NULL )
+  {
+    debug( "Not enough memory" );
+    return FALSE;
+  }
+
+  prectlScan = prectlDst;
+  pptlSrcOrg = (PPOINTL)prectlDst; // Yes, this memory block will be used again.
+  for( ulIdx = 0; ulIdx < cSDLRects; ulIdx++, prectlScan++, pptlSrcOrg++ )
+  {
+    pbrDst[ulIdx].ulXOrg = pVOData->rectlWin.xLeft + prectlScan->xLeft;
+    pbrDst[ulIdx].ulYOrg = pVOData->ulScreenHeight -
+                           ( pVOData->rectlWin.yBottom + prectlScan->yTop );
+    pbrDst[ulIdx].ulXExt = prectlScan->xRight - prectlScan->xLeft;
+    pbrDst[ulIdx].ulYExt = prectlScan->yTop - prectlScan->yBottom;
+    pptlSrcOrg->x = prectlScan->xLeft;
+    pptlSrcOrg->y = bmiSrc.ulHeight - prectlScan->yTop;
+  }
+  pptlSrcOrg = (PPOINTL)prectlDst;
+
+  // Request HW
+  sHWReqIn.ulLength = sizeof(HWREQIN);
+  sHWReqIn.ulFlags = REQUEST_HW;
+  sHWReqIn.cScrChangeRects = 1;
+  sHWReqIn.arectlScreen = &pVOData->rectlWin;
+  if ( pfnVMIEntry( 0, VMI_CMD_REQUESTHW, &sHWReqIn, NULL ) != RC_SUCCESS )
+  {
+    debug( "pfnVMIEntry(,VMI_CMD_REQUESTHW,,) failed" );
+    sHWReqIn.cScrChangeRects = 0; // for fail signal only.
+  }
+  else
+  {
+    RECTL rclSrcBounds;
+
+    rclSrcBounds.xLeft = 0;
+    rclSrcBounds.yBottom = 0;
+    rclSrcBounds.xRight = bmiSrc.ulWidth;
+    rclSrcBounds.yTop = bmiSrc.ulHeight;
+
+    sBitbltInfo.ulLength = sizeof(BITBLTINFO);
+    sBitbltInfo.ulBltFlags = BF_DEFAULT_STATE | BF_ROP_INCL_SRC | BF_PAT_HOLLOW;
+    sBitbltInfo.cBlits = cSDLRects;
+    sBitbltInfo.ulROP = ROP_SRCCOPY;
+    sBitbltInfo.pSrcBmapInfo = &bmiSrc;
+    sBitbltInfo.pDstBmapInfo = &bmiDst;
+    sBitbltInfo.prclSrcBounds = &rclSrcBounds;
+    sBitbltInfo.prclDstBounds = &pVOData->rectlWin;
+    sBitbltInfo.aptlSrcOrg = pptlSrcOrg;
+    sBitbltInfo.abrDst = pbrDst;
+
+    // Screen update.
+    if ( pfnVMIEntry( 0, VMI_CMD_BITBLT, &sBitbltInfo, NULL ) != RC_SUCCESS )
+    {
+      debug( "pfnVMIEntry(,VMI_CMD_BITBLT,,) failed" );
+      sHWReqIn.cScrChangeRects = 0; // for fail signal only.
+    }
+
+    // Release HW.
+    sHWReqIn.ulFlags = 0;
+    if ( pfnVMIEntry( 0, VMI_CMD_REQUESTHW, &sHWReqIn, NULL ) != RC_SUCCESS )
+      debug( "pfnVMIEntry(,VMI_CMD_REQUESTHW,,) failed" );
+  }
+
+  return sHWReqIn.cScrChangeRects != 0;
+}
diff --git a/test/testnativeos2.c b/test/testnativeos2.c
new file mode 100644
index 0000000..29b4916
--- /dev/null
+++ b/test/testnativeos2.c
@@ -0,0 +1,59 @@
+/*
+  Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely.
+*/
+
+#include "testnative.h"
+
+#ifdef TEST_NATIVE_OS2
+
+#define WIN_CLIENT_CLASS         "SDL Test"
+
+static void *CreateWindowNative(int w, int h);
+static void DestroyWindowNative(void *window);
+
+NativeWindowFactory OS2WindowFactory = {
+  "OS/2",
+  CreateWindowNative,
+  DestroyWindowNative
+};
+
+static void *CreateWindowNative(int w, int h)
+{
+  HWND     hwnd;
+  HWND     hwndFrame;
+  ULONG    ulFrameFlags = FCF_TASKLIST | FCF_DLGBORDER | FCF_TITLEBAR |
+                          FCF_SYSMENU | FCF_SHELLPOSITION |
+                          FCF_SIZEBORDER | FCF_MINBUTTON | FCF_MAXBUTTON;
+
+  WinRegisterClass( 0, WIN_CLIENT_CLASS, WinDefWindowProc,
+                    CS_SIZEREDRAW | CS_MOVENOTIFY,
+                    sizeof(ULONG) );        // We should have minimum 4 bytes.
+
+  hwndFrame = WinCreateStdWindow( HWND_DESKTOP, 0, &ulFrameFlags,
+                              WIN_CLIENT_CLASS, "SDL Test", 0, 0, 1, &hwnd );
+  if ( hwndFrame == NULLHANDLE )
+  {
+    return 0;
+  }
+
+  WinSetWindowPos( hwndFrame, HWND_TOP, 0, 0, w, h,
+                   SWP_ZORDER | SWP_ACTIVATE | SWP_SIZE | SWP_SHOW );
+
+  return (void *)hwndFrame; // We may returns client or frame window handle
+                            // for SDL_CreateWindowFrom().
+}
+
+static void DestroyWindowNative(void *window)
+{
+  WinDestroyWindow( (HWND)window );
+}
+
+#endif /* TEST_NATIVE_OS2 */