Browse Source

Add support for the Nokia N-Gage (#5597)

* Add initial support for the Nokia N-Gage

* N-Gage: disable clipping for the time being, issue needs to be resolved later

* Move va_copy definition to SDL_internal.h

* Move stdlib.h include to SDL_config_ngage.h, much cleaner this way

* Remove redundant include, add HAVE_STDLIB_H

* Revert "N-Gage: disable clipping for the time being, issue needs to be resolved later"

This reverts commit 4f5f0fc36cc7f34fad05e45671dfa7b8dc32fd51.

* N-Gage: fix clipping issue by providing proper math functions
Michael Fitzmayer 3 years ago
parent
commit
fbd230bb6c

+ 44 - 0
docs/README-ngage.md

@@ -0,0 +1,44 @@
+Nokia N-Gage
+============
+
+SDL2 port for Symbian S60v1/2 with a main focus on the Nokia N-Gage
+(Classic and QD) by [Michael Fitzmayer](https://github.com/mupfdev).
+
+Compiling
+---------
+
+SDL is part of the [N-Gage SDK.](https://github.com/ngagesdk) project.
+The library is included in the
+[toolchain](https://github.com/ngagesdk/ngage-toolchain) as a
+sub-module.
+
+A complete example project based on SDL2 can be found in the GitHub
+account of the SDK: [Example
+project](https://github.com/ngagesdk/wordle).
+
+Current level of implementation
+-------------------------------
+
+The video driver currently provides full screen video support with
+keyboard input.
+
+At the moment only the software renderer works.
+
+Audio is not yet implemented.
+
+Acknowledgements
+----------------
+
+Thanks to Hannu Viitala, Kimmo Kinnunen and Markus Mertama for the
+valuable insight into Symbian programming.  Without the SDL 1.2 port for
+CDoom, this adaptation would not have been possible.
+
+I would like to thank my friends
+[Razvan](https://twitter.com/bewarerazvan) and [Dan
+Whelan](https://danwhelan.ie/), for their continuous support.  Without
+you and the [N-Gage community](https://discord.gg/dbUzqJ26vs), I would
+have lost my patience long ago.
+
+Last but not least, I would like to say a special thank you to the
+[EKA2L1](https://12z1.com/) team.  Thank you for all your patience and
+support in troubleshooting.

+ 1 - 0
docs/README.md

@@ -51,6 +51,7 @@ More documentation and FAQs are available online at [the wiki](http://wiki.libsd
 - [Windows](README-windows.md)
 - [Windows](README-windows.md)
 - [WinRT](README-winrt.md)
 - [WinRT](README-winrt.md)
 - [PSVita](README-vita.md)
 - [PSVita](README-vita.md)
+- [Nokia N-Gage](README-ngage.md)
 
 
 If you need help with the library, or just want to discuss SDL related
 If you need help with the library, or just want to discuss SDL related
 issues, you can join the [SDL Discourse](https://discourse.libsdl.org/),
 issues, you can join the [SDL Discourse](https://discourse.libsdl.org/),

+ 2 - 0
include/SDL_config.h

@@ -43,6 +43,8 @@
 #include "SDL_config_os2.h"
 #include "SDL_config_os2.h"
 #elif defined(__EMSCRIPTEN__)
 #elif defined(__EMSCRIPTEN__)
 #include "SDL_config_emscripten.h"
 #include "SDL_config_emscripten.h"
+#elif defined(__NGAGE__)
+#include "SDL_config_ngage.h"
 #else
 #else
 /* This is a minimal configuration just to get SDL running on new platforms. */
 /* This is a minimal configuration just to get SDL running on new platforms. */
 #include "SDL_config_minimal.h"
 #include "SDL_config_minimal.h"

+ 89 - 0
include/SDL_config_ngage.h

@@ -0,0 +1,89 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2022 Sam Lantinga <[email protected]>
+
+  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_config_ngage_h_
+#define SDL_config_ngage_h_
+#define SDL_config_h_
+
+#include "SDL_platform.h"
+
+typedef signed char        int8_t;
+typedef unsigned char      uint8_t;
+typedef signed short       int16_t;
+typedef unsigned short     uint16_t;
+typedef signed int         int32_t;
+typedef unsigned int       uint32_t;
+typedef signed long long   int64_t;
+typedef unsigned long long uint64_t;
+typedef unsigned long      uintptr_t;
+
+#define HAVE_STDARG_H    1
+#define HAVE_STDDEF_H    1
+#define HAVE_STDIO_H     1
+#define HAVE_STDLIB_H    1
+#define HAVE_MATH_H      1
+#define HAVE_CEIL        1
+#define HAVE_COPYSIGN    1
+#define HAVE_COS         1
+#define HAVE_EXP         1
+#define HAVE_FABS        1
+#define HAVE_FLOOR       1
+#define HAVE_LOG         1
+#define HAVE_LOG10       1
+#define HAVE_SCALBN      1
+#define HAVE_SIN         1
+#define HAVE_SQRT        1
+#define HAVE_TAN         1
+#define HAVE_MALLOC      1
+#define SDL_MAIN_NEEDED  1
+#define LACKS_SYS_MMAN_H 1
+
+/* Enable the N-Gage thread support (src/thread/ngage/\*.c) */
+#define SDL_THREAD_NGAGE 1
+
+/* Enable the N-Gage timer support (src/timer/ngage/\*.c) */
+#define SDL_TIMER_NGAGE  1
+
+/* Enable the N=Hahe video driver (src/video/ngage/\*.c) */
+#define SDL_VIDEO_DRIVER_NGAGE 1
+
+/* Enable the dummy audio driver (src/audio/dummy/\*.c) */
+#define SDL_AUDIO_DRIVER_DUMMY  1
+
+/* Enable the stub joystick driver (src/joystick/dummy/\*.c) */
+#define SDL_JOYSTICK_DISABLED   1
+
+/* Enable the stub haptic driver (src/haptic/dummy/\*.c) */
+#define SDL_HAPTIC_DISABLED 1
+
+/* Enable the stub HIDAPI */
+#define SDL_HIDAPI_DISABLED 1
+
+/* Enable the stub sensor driver (src/sensor/dummy/\*.c) */
+#define SDL_SENSOR_DISABLED 1
+
+/* Enable the stub shared object loader (src/loadso/dummy/\*.c) */
+#define SDL_LOADSO_DISABLED 1
+
+/* Enable the dummy filesystem driver (src/filesystem/dummy/\*.c) */
+#define SDL_FILESYSTEM_DUMMY 1
+
+#endif /* SDL_config_ngage_h_ */

+ 4 - 0
include/SDL_platform.h

@@ -65,6 +65,10 @@
 #undef __LINUX__ /* do we need to do this? */
 #undef __LINUX__ /* do we need to do this? */
 #define __ANDROID__ 1
 #define __ANDROID__ 1
 #endif
 #endif
+#if defined(__NGAGE__)
+#undef __NGAGE__
+#define __NGAGE__ 1
+#endif
 
 
 #if defined(__APPLE__)
 #if defined(__APPLE__)
 /* lets us know what version of Mac OS X we're compiling on */
 /* lets us know what version of Mac OS X we're compiling on */

+ 2 - 0
src/SDL.c

@@ -567,6 +567,8 @@ SDL_GetPlatform(void)
     return "PlayStation Portable";
     return "PlayStation Portable";
 #elif __VITA__
 #elif __VITA__
     return "PlayStation Vita";
     return "PlayStation Vita";
+#elif __NGAGE__
+    return "Nokia N-Gage";
 #else
 #else
     return "Unknown (see SDL_platform.h)";
     return "Unknown (see SDL_platform.h)";
 #endif
 #endif

+ 4 - 1
src/SDL_internal.h

@@ -27,7 +27,10 @@
 #endif
 #endif
 
 
 /* Do our best to make sure va_copy is working */
 /* Do our best to make sure va_copy is working */
-#if defined(_MSC_VER) && _MSC_VER <= 1800
+#if defined(__NGAGE__)
+#undef va_copy
+#define va_copy(dst, src)   dst = src
+#elif defined(_MSC_VER) && _MSC_VER <= 1800
 /* Visual Studio 2013 tries to link with _vacopy in the C runtime. Newer versions do an inline assignment */
 /* Visual Studio 2013 tries to link with _vacopy in the C runtime. Newer versions do an inline assignment */
 #undef va_copy
 #undef va_copy
 #define va_copy(dst, src)   dst = src
 #define va_copy(dst, src)   dst = src

+ 2 - 0
src/dynapi/SDL_dynapi.h

@@ -59,6 +59,8 @@
 #define SDL_DYNAMIC_API 0  /* Turn off for static analysis, so reports are more clear. */
 #define SDL_DYNAMIC_API 0  /* Turn off for static analysis, so reports are more clear. */
 #elif defined(__VITA__)
 #elif defined(__VITA__)
 #define SDL_DYNAMIC_API 0  /* vitasdk doesn't support dynamic linking */
 #define SDL_DYNAMIC_API 0  /* vitasdk doesn't support dynamic linking */
+#elif defined(__NGAGE__)
+#define SDL_DYNAMIC_API 0  /* The N-Gage doesn't support dynamic linking either */
 #elif defined(DYNAPI_NEEDS_DLOPEN) && !defined(HAVE_DLOPEN)
 #elif defined(DYNAPI_NEEDS_DLOPEN) && !defined(HAVE_DLOPEN)
 #define SDL_DYNAMIC_API 0  /* we need dlopen(), but don't have it.... */
 #define SDL_DYNAMIC_API 0  /* we need dlopen(), but don't have it.... */
 #endif
 #endif

+ 82 - 0
src/main/ngage/SDL_ngage_main.cpp

@@ -0,0 +1,82 @@
+/*
+    EPOC version (originally for SDL 1.2) by Hannu Viitala
+    ([email protected]).
+*/
+#include "../../SDL_internal.h"
+
+/* Include the SDL main definition header */
+#include "SDL_main.h"
+
+#include <e32std.h>
+#include <e32def.h>
+#include <e32svr.h>
+#include <e32base.h>
+#include <estlib.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <w32std.h>
+#include <apgtask.h>
+
+#include "SDL_error.h"
+
+extern "C" int main(int argc, char *argv[]);
+
+TInt E32Main()
+{
+    /*  Get the clean-up stack */
+    CTrapCleanup* cleanup = CTrapCleanup::New();
+
+    /* Arrange for multi-threaded operation */
+    SpawnPosixServerThread();
+
+    /* Get args and environment */
+    int    argc = 0;
+    char** argv = 0;
+    char** envp = 0;
+
+    __crt0(argc,argv,envp);
+
+    /* Start the application! */
+
+    /* Create stdlib */
+    _REENT;
+
+    /* Set process and thread priority and name */
+
+    RThread  currentThread;
+    RProcess thisProcess;
+    TParse   exeName;
+    exeName.Set(thisProcess.FileName(), NULL, NULL);
+    currentThread.Rename(exeName.Name());
+    currentThread.SetProcessPriority(EPriorityLow);
+    currentThread.SetPriority(EPriorityMuchLess);
+
+    /* Increase heap size */
+    RHeap* newHeap  = NULL;
+    RHeap* oldHeap  = NULL;
+    TInt   heapSize = 7500000;
+    int    ret;
+
+    newHeap = User::ChunkHeap(NULL, heapSize, heapSize, KMinHeapGrowBy);
+
+    if (NULL == newHeap)
+    {
+        ret = 3;
+        goto cleanup;
+    }
+    else
+    {
+        oldHeap = User::SwitchHeap(newHeap);
+        /* Call stdlib main */
+        ret = main(argc, argv);
+    }
+
+cleanup:
+    _cleanup();
+
+    CloseSTDLIB();
+    delete cleanup;
+    return ret;
+}
+
+/* vi: set ts=4 sw=4 expandtab: */

+ 2 - 0
src/thread/SDL_thread_c.h

@@ -40,6 +40,8 @@
 #include "stdcpp/SDL_systhread_c.h"
 #include "stdcpp/SDL_systhread_c.h"
 #elif SDL_THREAD_OS2
 #elif SDL_THREAD_OS2
 #include "os2/SDL_systhread_c.h"
 #include "os2/SDL_systhread_c.h"
+#elif SDL_THREAD_NGAGE
+#include "ngage/SDL_systhread_c.h"
 #else
 #else
 #error Need thread implementation for this platform
 #error Need thread implementation for this platform
 #include "generic/SDL_systhread_c.h"
 #include "generic/SDL_systhread_c.h"

+ 107 - 0
src/thread/ngage/SDL_sysmutex.cpp

@@ -0,0 +1,107 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2022 Sam Lantinga <[email protected]>
+
+  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"
+
+/* An implementation of mutexes using semaphores */
+
+#include <e32std.h>
+
+#include "SDL_thread.h"
+#include "SDL_systhread_c.h"
+
+struct SDL_mutex
+{
+    TInt handle;
+};
+
+extern TInt CreateUnique(TInt (*aFunc)(const TDesC& aName, TAny*, TAny*), TAny*, TAny*);
+
+static TInt NewMutex(const TDesC& aName, TAny* aPtr1, TAny*)
+{
+    return ((RMutex*)aPtr1)->CreateGlobal(aName);
+}
+
+/* Create a mutex */
+SDL_mutex *
+SDL_CreateMutex(void)
+{
+    RMutex rmutex;
+
+    TInt status = CreateUnique(NewMutex, &rmutex, NULL);
+    if(status != KErrNone)
+    {
+        SDL_SetError("Couldn't create mutex.");
+    }
+    SDL_mutex* mutex = new /*(ELeave)*/ SDL_mutex;
+    mutex->handle = rmutex.Handle();
+    return(mutex);
+}
+
+/* Free the mutex */
+void
+SDL_DestroyMutex(SDL_mutex * mutex)
+{
+    if (mutex)
+    {
+        RMutex rmutex;
+        rmutex.SetHandle(mutex->handle);
+        rmutex.Signal();
+        rmutex.Close();
+        delete(mutex);
+        mutex = NULL;
+    }
+}
+
+/* Lock the mutex */
+int
+SDL_LockMutex(SDL_mutex * mutex)
+{
+    if (mutex == NULL)
+    {
+        SDL_SetError("Passed a NULL mutex.");
+        return -1;
+    }
+
+    RMutex rmutex;
+    rmutex.SetHandle(mutex->handle);
+    rmutex.Wait();
+
+    return(0);
+}
+
+/* Unlock the mutex */
+int
+SDL_mutexV(SDL_mutex * mutex)
+{
+    if ( mutex == NULL )
+    {
+        SDL_SetError("Passed a NULL mutex.");
+        return -1;
+    }
+
+    RMutex rmutex;
+    rmutex.SetHandle(mutex->handle);
+    rmutex.Signal();
+
+    return(0);
+}
+
+/* vi: set ts=4 sw=4 expandtab: */

+ 195 - 0
src/thread/ngage/SDL_syssem.cpp

@@ -0,0 +1,195 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2022 Sam Lantinga <[email protected]>
+
+  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"
+
+/* An implementation of semaphores using mutexes and condition variables */
+
+#include <e32std.h>
+
+#include "SDL_error.h"
+#include "SDL_thread.h"
+
+#define SDL_MUTEX_TIMEOUT -2
+
+struct SDL_semaphore
+{
+    TInt handle;
+    TInt count;
+};
+
+struct TInfo
+{
+    TInfo(TInt aTime, TInt aHandle) :
+        iTime(aTime), iHandle(aHandle), iVal(0) {}
+    TInt iTime;
+    TInt iHandle;
+    TInt iVal;
+};
+
+extern TInt CreateUnique(TInt (*aFunc)(const TDesC& aName, TAny*, TAny*), TAny*, TAny*);
+
+static TBool RunThread(TAny* aInfo)
+{
+    TInfo* info = STATIC_CAST(TInfo*, aInfo);
+    User::After(info->iTime);
+    RSemaphore sema;
+    sema.SetHandle(info->iHandle);
+    sema.Signal();
+    info->iVal = SDL_MUTEX_TIMEOUT;
+    return 0;
+}
+
+static TInt
+NewThread(const TDesC& aName, TAny* aPtr1, TAny* aPtr2)
+{
+    return ((RThread*)(aPtr1))->Create
+        (aName,
+         RunThread,
+         KDefaultStackSize,
+         NULL,
+         aPtr2);
+}
+
+static TInt NewSema(const TDesC& aName, TAny* aPtr1, TAny* aPtr2)
+{
+    TInt value = *((TInt*) aPtr2);
+    return ((RSemaphore*)aPtr1)->CreateGlobal(aName, value);
+}
+
+static void WaitAll(SDL_sem *sem)
+{
+    RSemaphore sema;
+    sema.SetHandle(sem->handle);
+    sema.Wait();
+    while(sem->count < 0)
+    {
+        sema.Wait();
+    }
+}
+
+SDL_sem *
+SDL_CreateSemaphore(Uint32 initial_value)
+{
+    RSemaphore s;
+    TInt status = CreateUnique(NewSema, &s, &initial_value);
+    if(status != KErrNone)
+    {
+        SDL_SetError("Couldn't create semaphore");
+    }
+    SDL_semaphore* sem = new /*(ELeave)*/ SDL_semaphore;
+    sem->handle = s.Handle();
+    sem->count = initial_value;
+    return(sem);
+}
+
+void
+SDL_DestroySemaphore(SDL_sem * sem)
+{
+    if (sem)
+    {
+        RSemaphore sema;
+        sema.SetHandle(sem->handle);
+        sema.Signal(sema.Count());
+        sema.Close();
+        delete sem;
+        sem = NULL;
+    }
+}
+
+int
+SDL_SemWaitTimeout(SDL_sem * sem, Uint32 timeout)
+{
+    if (! sem)
+    {
+        SDL_SetError("Passed a NULL sem");
+        return -1;
+    }
+
+    if (timeout == SDL_MUTEX_MAXWAIT)
+    {
+        WaitAll(sem);
+        return SDL_MUTEX_MAXWAIT;
+    }
+
+    RThread thread;
+    TInfo*  info   = new (ELeave)TInfo(timeout, sem->handle);
+    TInt    status = CreateUnique(NewThread, &thread, info);
+
+    if(status != KErrNone)
+    {
+        return status;
+    }
+
+    thread.Resume();
+    WaitAll(sem);
+
+    if(thread.ExitType() == EExitPending)
+    {
+        thread.Kill(SDL_MUTEX_TIMEOUT);
+    }
+
+    thread.Close();
+    return info->iVal;
+}
+
+int
+SDL_SemTryWait(SDL_sem *sem)
+{
+    if(sem->count > 0)
+    {
+        sem->count--;
+    }
+    return SDL_MUTEX_TIMEOUT;
+}
+
+int
+SDL_SemWait(SDL_sem * sem)
+{
+    return SDL_SemWaitTimeout(sem, SDL_MUTEX_MAXWAIT);
+}
+
+Uint32
+SDL_SemValue(SDL_sem * sem)
+{
+    if (! sem)
+    {
+        SDL_SetError("Passed a NULL sem.");
+        return 0;
+    }
+    return sem->count;
+}
+
+int
+SDL_SemPost(SDL_sem * sem)
+{
+    if (! sem)
+    {
+        SDL_SetError("Passed a NULL sem.");
+        return -1;
+    }
+    sem->count++;
+    RSemaphore sema;
+    sema.SetHandle(sem->handle);
+    sema.Signal();
+    return 0;
+}
+
+/* vi: set ts=4 sw=4 expandtab: */

+ 147 - 0
src/thread/ngage/SDL_systhread.cpp

@@ -0,0 +1,147 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2022 Sam Lantinga <[email protected]>
+
+  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_NGAGE
+
+/* N-Gage thread management routines for SDL */
+
+#include <e32std.h>
+
+extern "C" {
+#undef NULL
+#include "SDL_error.h"
+#include "SDL_thread.h"
+#include "../SDL_systhread.h"
+#include "../SDL_thread_c.h"
+};
+
+static int object_count;
+
+static int
+RunThread(TAny* data)
+{
+    SDL_RunThread((SDL_Thread*)data);
+    return(0);
+}
+
+static TInt
+NewThread(const TDesC& aName, TAny* aPtr1, TAny* aPtr2)
+{
+    return ((RThread*)(aPtr1))->Create
+        (aName,
+         RunThread,
+         KDefaultStackSize,
+         NULL,
+         aPtr2);
+}
+
+int
+CreateUnique(TInt (*aFunc)(const TDesC& aName, TAny*, TAny*), TAny* aPtr1, TAny* aPtr2)
+{
+    TBuf<16> name;
+    TInt     status = KErrNone;
+    do
+    {
+        object_count++;
+        name.Format(_L("SDL_%x"), object_count);
+        status = aFunc(name, aPtr1, aPtr2);
+    }
+    while(status == KErrAlreadyExists);
+    return status;
+}
+
+int
+SDL_SYS_CreateThread(SDL_Thread *thread)
+{
+    RThread rthread;
+
+    TInt status = CreateUnique(NewThread, &rthread, thread);
+    if (status != KErrNone)
+    {
+        delete(((RThread*)(thread->handle)));
+        thread->handle = NULL;
+        SDL_SetError("Not enough resources to create thread");
+        return(-1);
+    }
+
+    rthread.Resume();
+    thread->handle = rthread.Handle();
+    return(0);
+}
+
+void
+SDL_SYS_SetupThread(const char *name)
+{
+    return;
+}
+
+SDL_threadID
+SDL_ThreadID(void)
+{
+    RThread   current;
+    TThreadId id = current.Id();
+    return id;
+}
+
+int
+SDL_SYS_SetThreadPriority(SDL_ThreadPriority priority)
+{
+    return (0);
+}
+
+void
+SDL_SYS_WaitThread(SDL_Thread * thread)
+{
+    RThread t;
+    t.Open(thread->threadid);
+    if(t.ExitReason() == EExitPending)
+    {
+        TRequestStatus status;
+        t.Logon(status);
+        User::WaitForRequest(status);
+    }
+    t.Close();
+}
+
+void
+SDL_SYS_DetachThread(SDL_Thread * thread)
+{
+    return;
+}
+
+/* WARNING: This function is really a last resort.
+ * Threads should be signaled and then exit by themselves.
+ * TerminateThread() doesn't perform stack and DLL cleanup.
+ */
+void
+SDL_SYS_KillThread(SDL_Thread *thread)
+{
+    RThread rthread;
+    rthread.SetHandle(thread->handle);
+    rthread.Kill(0);
+    rthread.Close();
+}
+
+#endif /* SDL_THREAD_NGAGE */
+
+/* vim: ts=4 sw=4
+ */

+ 25 - 0
src/thread/ngage/SDL_systhread_c.h

@@ -0,0 +1,25 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2022 Sam Lantinga <[email protected]>
+
+  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: */

+ 100 - 0
src/timer/ngage/SDL_systimer.cpp

@@ -0,0 +1,100 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2022 Sam Lantinga <[email protected]>
+
+  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(SDL_TIMER_NGAGE)
+
+#include <e32std.h>
+#include <e32hal.h>
+
+#include "SDL_timer.h"
+
+static SDL_bool ticks_started = SDL_FALSE;
+static TUint    start         = 0;
+static TInt     tickPeriodMilliSeconds;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void
+SDL_TicksInit(void)
+{
+    if (ticks_started)
+    {
+        return;
+    }
+    ticks_started = SDL_TRUE;
+    start         = User::TickCount();
+
+    TTimeIntervalMicroSeconds32 period;
+    TInt                        tmp = UserHal::TickPeriod(period);
+
+    (void)tmp; /* Suppress redundant warning. */
+
+    tickPeriodMilliSeconds = period.Int() / 1000;
+}
+
+void
+SDL_TicksQuit(void)
+{
+    ticks_started = SDL_FALSE;
+}
+
+Uint64
+SDL_GetTicks64(void)
+{
+    if (! ticks_started)
+    {
+        SDL_TicksInit();
+    }
+
+    TUint deltaTics = User::TickCount() - start;
+
+    // Overlaps early, but should do the trick for now.
+    return (Uint64)(deltaTics * tickPeriodMilliSeconds);
+}
+
+Uint64
+SDL_GetPerformanceCounter(void)
+{
+    return (Uint64)User::TickCount();
+}
+
+Uint64
+SDL_GetPerformanceFrequency(void)
+{
+    return 1000000;
+}
+
+void
+SDL_Delay(Uint32 ms)
+{
+    User::After(TTimeIntervalMicroSeconds32(ms * 1000));
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SDL_TIMER_NGAGE */
+
+/* vi: set ts=4 sw=4 expandtab: */

+ 1 - 0
src/video/SDL_sysvideo.h

@@ -457,6 +457,7 @@ extern VideoBootStrap VIVANTE_bootstrap;
 extern VideoBootStrap Emscripten_bootstrap;
 extern VideoBootStrap Emscripten_bootstrap;
 extern VideoBootStrap QNX_bootstrap;
 extern VideoBootStrap QNX_bootstrap;
 extern VideoBootStrap OFFSCREEN_bootstrap;
 extern VideoBootStrap OFFSCREEN_bootstrap;
+extern VideoBootStrap NGAGE_bootstrap;
 extern VideoBootStrap OS2DIVE_bootstrap;
 extern VideoBootStrap OS2DIVE_bootstrap;
 extern VideoBootStrap OS2VMAN_bootstrap;
 extern VideoBootStrap OS2VMAN_bootstrap;
 
 

+ 3 - 0
src/video/SDL_video.c

@@ -118,6 +118,9 @@ static VideoBootStrap *bootstrap[] = {
 #if SDL_VIDEO_DRIVER_OFFSCREEN
 #if SDL_VIDEO_DRIVER_OFFSCREEN
     &OFFSCREEN_bootstrap,
     &OFFSCREEN_bootstrap,
 #endif
 #endif
+#if SDL_VIDEO_DRIVER_NGAGE
+    &NGAGE_bootstrap,
+#endif
 #if SDL_VIDEO_DRIVER_OS2
 #if SDL_VIDEO_DRIVER_OS2
     &OS2DIVE_bootstrap,
     &OS2DIVE_bootstrap,
     &OS2VMAN_bootstrap,
     &OS2VMAN_bootstrap,

+ 200 - 0
src/video/ngage/SDL_ngageevents.cpp

@@ -0,0 +1,200 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2022 Sam Lantinga <[email protected]>
+
+  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_NGAGE
+
+/* Being a ngage driver, there's no event stream. We just define stubs for
+   most of the API. */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "../../events/SDL_events_c.h"
+#include "../../events/SDL_keyboard_c.h"
+
+#ifdef __cplusplus
+}
+#endif
+
+#include "SDL_ngagevideo.h"
+#include "SDL_ngageevents_c.h"
+
+int HandleWsEvent(_THIS, const TWsEvent& aWsEvent);
+
+void
+NGAGE_PumpEvents(_THIS)
+{
+    SDL_VideoData *phdata = (SDL_VideoData*)_this->driverdata;
+
+    while (phdata->NGAGE_WsEventStatus != KRequestPending)
+    {
+        phdata->NGAGE_WsSession.GetEvent(phdata->NGAGE_WsEvent);
+
+        HandleWsEvent(_this, phdata->NGAGE_WsEvent);
+
+        phdata->NGAGE_WsEventStatus = KRequestPending;
+        phdata->NGAGE_WsSession.EventReady(&phdata->NGAGE_WsEventStatus);
+    }
+}
+
+/*****************************************************************************/
+/* Internal                                                                  */
+/*****************************************************************************/
+
+#include <bautils.h>
+#include <hal.h>
+
+extern void DisableKeyBlocking(_THIS);
+extern void RedrawWindowL(_THIS);
+
+TBool isCursorVisible = EFalse;
+
+static SDL_Scancode ConvertScancode(_THIS, int key)
+{
+    SDL_Keycode keycode;
+
+    switch(key)
+    {
+        case EStdKeyBackspace:    // Clear key
+            keycode = SDLK_BACKSPACE;
+            break;
+        case 0x31:                // 1
+            keycode = SDLK_1;
+            break;
+        case 0x32:                // 2
+            keycode = SDLK_2;
+            break;
+        case 0x33:                // 3
+            keycode = SDLK_3;
+            break;
+        case 0x34:                // 4
+            keycode = SDLK_4;
+            break;
+        case 0x35:                // 5
+            keycode = SDLK_5;
+            break;
+        case 0x36:                // 6
+            keycode = SDLK_6;
+            break;
+        case 0x37:                // 7
+            keycode = SDLK_7;
+            break;
+        case 0x38:                // 8
+            keycode = SDLK_8;
+            break;
+        case 0x39:                // 9
+            keycode = SDLK_9;
+            break;
+        case 0x30:                // 0
+            keycode = SDLK_0;
+            break;
+        case 0x2a:                // Asterisk
+            keycode = SDLK_ASTERISK;
+            break;
+        case EStdKeyHash:         // Hash
+            keycode = SDLK_SLASH;
+            break;
+        case EStdKeyDevice0:      // Left softkey
+            keycode = SDLK_F1;
+            break;
+        case EStdKeyDevice1:      // Right softkey
+            keycode = SDLK_F2;
+            break;
+        case EStdKeyApplication0: // Green softkey
+            keycode = SDLK_F3;
+            break;
+        case EStdKeyApplication1: // Red softkey
+            keycode = SDLK_F4;
+            break;
+        case EStdKeyDevice3:      // Middle softkey
+            keycode = SDLK_RETURN;
+            break;
+        case EStdKeyUpArrow:      // Up arrow
+            keycode = SDLK_UP;
+            break;
+        case EStdKeyDownArrow:    // Down arrow
+            keycode = SDLK_DOWN;
+            break;
+        case EStdKeyLeftArrow:    // Left arrow
+            keycode = SDLK_LEFT;
+            break;
+        case EStdKeyRightArrow:   // Right arrow
+            keycode = SDLK_RIGHT;
+            break;
+        default:
+            keycode = SDLK_UNKNOWN;
+            break;
+    }
+
+    return SDL_GetScancodeFromKey(keycode);
+}
+
+int HandleWsEvent(_THIS, const TWsEvent& aWsEvent)
+{
+    SDL_VideoData *phdata = (SDL_VideoData*)_this->driverdata;
+    int            posted = 0;
+
+    switch (aWsEvent.Type())
+    {
+        case EEventKeyDown: /* Key events */
+            SDL_SendKeyboardKey(SDL_PRESSED, ConvertScancode(_this, aWsEvent.Key()->iScanCode));
+            break;
+        case EEventKeyUp: /* Key events */
+            SDL_SendKeyboardKey(SDL_RELEASED, ConvertScancode(_this, aWsEvent.Key()->iScanCode));
+            break;
+        case EEventFocusGained: /* SDL window got focus */
+            phdata->NGAGE_IsWindowFocused = ETrue;
+            /* Draw window background and screen buffer */
+            DisableKeyBlocking(_this);  //Markus: guess why :-)
+            RedrawWindowL(_this);
+            break;
+        case EEventFocusLost: /* SDL window lost focus */
+        {
+            phdata->NGAGE_IsWindowFocused = EFalse;
+            RWsSession s;
+            s.Connect();
+            RWindowGroup g(s);
+            g.Construct(TUint32(&g), EFalse);
+            g.EnableReceiptOfFocus(EFalse);
+            RWindow w(s);
+            w.Construct(g, TUint32(&w));
+            w.SetExtent(TPoint(0, 0), phdata->NGAGE_WsWindow.Size());
+            w.SetOrdinalPosition(0);
+            w.Activate();
+            w.Close();
+            g.Close();
+            s.Close();
+            break;
+        }
+        case EEventModifiersChanged:
+            break;
+        default:
+            break;
+    }
+    return posted;
+}
+
+#endif /* SDL_VIDEO_DRIVER_NGAGE */
+
+/* vi: set ts=4 sw=4 expandtab: */

+ 28 - 0
src/video/ngage/SDL_ngageevents_c.h

@@ -0,0 +1,28 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2022 Sam Lantinga <[email protected]>
+
+  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_ngagevideo.h"
+
+extern void NGAGE_PumpEvents(_THIS);
+
+/* vi: set ts=4 sw=4 expandtab: */

+ 424 - 0
src/video/ngage/SDL_ngageframebuffer.cpp

@@ -0,0 +1,424 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2022 Sam Lantinga <[email protected]>
+
+  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_NGAGE
+
+#include <SDL.h>
+
+#include "../SDL_sysvideo.h"
+#include "SDL_ngagevideo.h"
+#include "SDL_ngageframebuffer_c.h"
+
+#define NGAGE_SURFACE "NGAGE_FrameBuffer"
+
+/* For 12 bit screen HW. Table for fast conversion from 8 bit to 12 bit
+ *
+ * TUint16 is enough, but using TUint32 so we can use better instruction
+ * selection on ARMI.
+ */
+static TUint32 NGAGE_HWPalette_256_to_Screen[256];
+
+int  GetBpp(TDisplayMode displaymode);
+void DirectUpdate(_THIS, int numrects, SDL_Rect *rects);
+void DrawBackground(_THIS);
+void DirectDraw(_THIS, int numrects, SDL_Rect *rects, TUint16* screenBuffer);
+void RedrawWindowL(_THIS);
+
+int SDL_NGAGE_CreateWindowFramebuffer(_THIS, SDL_Window * window, Uint32 * format, void ** pixels, int *pitch)
+{
+    SDL_VideoData *phdata = (SDL_VideoData*)_this->driverdata;
+    SDL_Surface   *surface;
+    const Uint32   surface_format = SDL_PIXELFORMAT_RGB444;
+    int w, h;
+
+    /* Free the old framebuffer surface */
+    SDL_NGAGE_DestroyWindowFramebuffer(_this, window);
+
+    /* Create a new one */
+    SDL_GetWindowSize(window, &w, &h);
+    surface = SDL_CreateRGBSurfaceWithFormat(0, w, h, 0, surface_format);
+    if (! surface) {
+        return -1;
+    }
+
+    /* Save the info and return! */
+    SDL_SetWindowData(window, NGAGE_SURFACE, surface);
+    *format = surface_format;
+    *pixels = surface->pixels;
+    *pitch  = surface->pitch;
+
+    /* Initialise Epoc frame buffer */
+
+    TDisplayMode          displayMode = phdata->NGAGE_WsScreen->DisplayMode();
+
+    TScreenInfoV01        screenInfo;
+    TPckg<TScreenInfoV01> sInfo(screenInfo);
+    UserSvr::ScreenInfo(sInfo);
+
+    phdata->NGAGE_ScreenSize       = screenInfo.iScreenSize;
+    phdata->NGAGE_DisplayMode      = displayMode;
+    phdata->NGAGE_HasFrameBuffer   = screenInfo.iScreenAddressValid;
+    phdata->NGAGE_FrameBuffer      = phdata->NGAGE_HasFrameBuffer ? (TUint8*) screenInfo.iScreenAddress : NULL;
+    phdata->NGAGE_BytesPerPixel    = ((GetBpp(displayMode)-1) / 8) + 1;
+
+    phdata->NGAGE_BytesPerScanLine = screenInfo.iScreenSize.iWidth * phdata->NGAGE_BytesPerPixel;
+    phdata->NGAGE_BytesPerScreen   = phdata->NGAGE_BytesPerScanLine * phdata->NGAGE_ScreenSize.iHeight;
+
+    SDL_Log("Screen width        %d", screenInfo.iScreenSize.iWidth);
+    SDL_Log("Screen height       %d", screenInfo.iScreenSize.iHeight);
+    SDL_Log("Screen dmode        %d", displayMode);
+    SDL_Log("Screen valid        %d", screenInfo.iScreenAddressValid);
+
+    SDL_Log("Bytes per pixel     %d", phdata->NGAGE_BytesPerPixel);
+    SDL_Log("Bytes per scan line %d", phdata->NGAGE_BytesPerScanLine);
+    SDL_Log("Bytes per screen    %d", phdata->NGAGE_BytesPerScreen);
+
+    /* It seems that in SA1100 machines for 8bpp displays there is a 512
+     * palette table at the beginning of the frame buffer.
+     *
+     * In 12 bpp machines the table has 16 entries.
+     */
+    if (phdata->NGAGE_HasFrameBuffer && GetBpp(displayMode) == 8)
+    {
+        phdata->NGAGE_FrameBuffer += 512;
+    }
+    else
+    {
+        phdata->NGAGE_FrameBuffer += 32;
+    }
+    /*if (phdata->NGAGE_HasFrameBuffer && GetBpp(displayMode) == 12)
+      phdata->NGAGE_FrameBuffer += 16 * 2;
+      if (phdata->NGAGE_HasFrameBuffer && GetBpp(displayMode) == 16)
+      phdata->NGAGE_FrameBuffer += 16 * 2;
+    */
+
+    // Get draw device for updating the screen
+    TScreenInfoV01 screenInfo2;
+
+    NGAGE_Runtime::GetScreenInfo(screenInfo2);
+
+    TRAPD(status, phdata->NGAGE_DrawDevice = CFbsDrawDevice::NewScreenDeviceL(screenInfo2, displayMode));
+    User::LeaveIfError(status);
+
+    /* Activate events for me */
+    phdata->NGAGE_WsEventStatus = KRequestPending;
+    phdata->NGAGE_WsSession.EventReady(&phdata->NGAGE_WsEventStatus);
+
+    SDL_Log("SDL:WsEventStatus");
+    User::WaitForRequest(phdata->NGAGE_WsEventStatus);
+
+    phdata->NGAGE_RedrawEventStatus = KRequestPending;
+    phdata->NGAGE_WsSession.RedrawReady(&phdata->NGAGE_RedrawEventStatus);
+
+    SDL_Log("SDL:RedrawEventStatus");
+    User::WaitForRequest(phdata->NGAGE_RedrawEventStatus);
+
+    phdata->NGAGE_WsWindow.PointerFilter(EPointerFilterDrag, 0);
+
+    phdata->NGAGE_ScreenOffset = TPoint(0, 0);
+
+    SDL_Log("SDL:DrawBackground");
+    DrawBackground(_this); // Clear screen
+
+    return 0;
+}
+
+int SDL_NGAGE_UpdateWindowFramebuffer(_THIS, SDL_Window * window, const SDL_Rect * rects, int numrects)
+{
+    static int   frame_number;
+    SDL_Surface *surface;
+
+    surface = (SDL_Surface *) SDL_GetWindowData(window, NGAGE_SURFACE);
+    if (! surface)
+    {
+        return SDL_SetError("Couldn't find ngage surface for window");
+    }
+
+    /* Send the data to the display */
+    if (SDL_getenv("SDL_VIDEO_NGAGE_SAVE_FRAMES"))
+    {
+        char file[128];
+        SDL_snprintf(file, sizeof(file), "SDL_window%d-%8.8d.bmp",
+                     (int)SDL_GetWindowID(window), ++frame_number);
+        SDL_SaveBMP(surface, file);
+    }
+
+    DirectUpdate(_this, numrects, (SDL_Rect*)rects);
+
+    return 0;
+}
+
+void SDL_NGAGE_DestroyWindowFramebuffer(_THIS, SDL_Window * window)
+{
+    SDL_Surface *surface;
+
+    surface = (SDL_Surface *) SDL_SetWindowData(window, NGAGE_SURFACE, NULL);
+    SDL_FreeSurface(surface);
+}
+
+/*****************************************************************************/
+/* Runtime                                                                   */
+/*****************************************************************************/
+
+#include <e32svr.h>
+#include <hal_data.h>
+#include <hal.h>
+
+EXPORT_C void NGAGE_Runtime::GetScreenInfo(TScreenInfoV01& screenInfo2)
+{
+    TPckg<TScreenInfoV01> sInfo2(screenInfo2);
+    UserSvr::ScreenInfo(sInfo2);
+}
+
+/*****************************************************************************/
+/* Internal                                                                  */
+/*****************************************************************************/
+
+int GetBpp(TDisplayMode displaymode)
+{
+    return TDisplayModeUtils::NumDisplayModeBitsPerPixel(displaymode);
+}
+
+void DrawBackground(_THIS)
+{
+    SDL_VideoData *phdata = (SDL_VideoData*)_this->driverdata;
+    /* Draw background */
+    TUint16* screenBuffer = (TUint16*)phdata->NGAGE_FrameBuffer;
+    /* Draw black background */
+    Mem::FillZ(screenBuffer, phdata->NGAGE_BytesPerScreen);
+}
+
+void DirectDraw(_THIS, int numrects, SDL_Rect *rects, TUint16* screenBuffer)
+{
+    SDL_VideoData *phdata = (SDL_VideoData*)_this->driverdata;
+    SDL_Surface   *screen = (SDL_Surface*)SDL_GetWindowData(_this->windows, NGAGE_SURFACE);
+
+    TInt i;
+
+    //const TInt   sourceNumBytesPerPixel = ((screen->format->BitsPerPixel-1) >> 3) + 1;
+    TDisplayMode displayMode            = phdata->NGAGE_DisplayMode;
+    const TInt   sourceNumBytesPerPixel = ((GetBpp(displayMode)-1) / 8) + 1;
+    //
+    const TPoint fixedOffset            = phdata->NGAGE_ScreenOffset;
+    const TInt   screenW                = screen->w;
+    const TInt   screenH                = screen->h;
+    const TInt   sourceScanlineLength   = screenW;
+    const TInt   targetScanlineLength   = phdata->NGAGE_ScreenSize.iWidth;
+
+    /* Render the rectangles in the list */
+
+    for (i = 0; i < numrects; ++i)
+    {
+        const SDL_Rect& currentRect = rects[i];
+        SDL_Rect        rect2;
+        rect2.x = currentRect.x;
+        rect2.y = currentRect.y;
+        rect2.w = currentRect.w;
+        rect2.h = currentRect.h;
+
+        if (rect2.w <= 0 || rect2.h <= 0) /* Sanity check */
+        {
+            continue;
+        }
+
+        /* All variables are measured in pixels */
+
+        /* Check rects validity, i.e. upper and lower bounds */
+        TInt maxX = Min(screenW - 1, rect2.x + rect2.w - 1);
+        TInt maxY = Min(screenH - 1, rect2.y + rect2.h - 1);
+        if (maxX < 0 || maxY < 0) /* sanity check */
+        {
+            continue;
+        }
+        /* Clip from bottom */
+
+        maxY = Min(maxY, phdata->NGAGE_ScreenSize.iHeight-1);
+        /* TODO: Clip from the right side */
+
+        const TInt  sourceRectWidth        = maxX - rect2.x + 1;
+        const TInt  sourceRectWidthInBytes = sourceRectWidth * sourceNumBytesPerPixel;
+        const TInt  sourceRectHeight       = maxY - rect2.y + 1;
+        const TInt  sourceStartOffset      = rect2.x + rect2.y * sourceScanlineLength;
+        const TUint skipValue              = 1; /* 1 = No skip */
+
+        TInt targetStartOffset = fixedOffset.iX + rect2.x + (fixedOffset.iY +rect2.y) * targetScanlineLength;
+
+        switch (screen->format->BitsPerPixel)
+        {
+            case 12:
+            {
+                TUint16* bitmapLine   = (TUint16*)screen->pixels + sourceStartOffset;
+                TUint16* screenMemory = screenBuffer + targetStartOffset;
+
+                if (skipValue == 1)
+                {
+                    for(TInt y = 0 ; y < sourceRectHeight ; y++)
+                    {
+                        Mem::Copy(screenMemory, bitmapLine, sourceRectWidthInBytes);
+                        bitmapLine   += sourceScanlineLength;
+                        screenMemory += targetScanlineLength;
+                    }
+                }
+                else
+                {
+                    for(TInt y = 0 ; y < sourceRectHeight ; y++)
+                    {
+                        TUint16* bitmapPos           = bitmapLine;   /* 2 bytes per pixel */
+                        TUint16* screenMemoryLinePos = screenMemory; /* 2 bytes per pixel */
+                        for(TInt x = 0 ; x < sourceRectWidth ; x++)
+                        {
+                            __ASSERT_DEBUG(screenMemory < (screenBuffer + phdata->NGAGE_ScreenSize.iWidth * phdata->NGAGE_ScreenSize.iHeight), User::Panic(_L("SDL"), KErrCorrupt));
+                            __ASSERT_DEBUG(screenMemory >= screenBuffer, User::Panic(_L("SDL"), KErrCorrupt));
+                            __ASSERT_DEBUG(bitmapLine < ((TUint16*)screen->pixels + (screen->w * screen->h)), User::Panic(_L("SDL"), KErrCorrupt));
+                            __ASSERT_DEBUG(bitmapLine >=  (TUint16*)screen->pixels, User::Panic(_L("SDL"), KErrCorrupt));
+
+                            *screenMemoryLinePos++ = *bitmapPos;
+                            bitmapPos += skipValue;
+                        }
+                        bitmapLine   += sourceScanlineLength;
+                        screenMemory += targetScanlineLength;
+                    }
+                }
+            }
+            break;
+            // 256 color paletted mode: 8 bpp --> 12 bpp
+            default:
+            {
+                if(phdata->NGAGE_BytesPerPixel <= 2)
+                {
+                    TUint8*  bitmapLine   = (TUint8*)screen->pixels + sourceStartOffset;
+                    TUint16* screenMemory = screenBuffer + targetStartOffset;
+
+                    for(TInt y = 0 ; y < sourceRectHeight ; y++)
+                    {
+                        TUint8*  bitmapPos           = bitmapLine;   /* 1 byte per pixel */
+                        TUint16* screenMemoryLinePos = screenMemory; /* 2 bytes per pixel */
+                        /* Convert each pixel from 256 palette to 4k color values */
+                        for(TInt x = 0 ; x < sourceRectWidth ; x++)
+                        {
+                            __ASSERT_DEBUG(screenMemoryLinePos < (screenBuffer + (phdata->NGAGE_ScreenSize.iWidth * phdata->NGAGE_ScreenSize.iHeight)), User::Panic(_L("SDL"), KErrCorrupt));
+                            __ASSERT_DEBUG(screenMemoryLinePos >= screenBuffer, User::Panic(_L("SDL"), KErrCorrupt));
+                            __ASSERT_DEBUG(bitmapPos < ((TUint8*)screen->pixels + (screen->w * screen->h)), User::Panic(_L("SDL"), KErrCorrupt));
+                            __ASSERT_DEBUG(bitmapPos >= (TUint8*)screen->pixels, User::Panic(_L("SDL"), KErrCorrupt));
+                            *screenMemoryLinePos++ = NGAGE_HWPalette_256_to_Screen[*bitmapPos++];
+                        }
+                        bitmapLine   += sourceScanlineLength;
+                        screenMemory += targetScanlineLength;
+                    }
+                }
+                else
+                {
+                    TUint8*  bitmapLine   = (TUint8*)screen->pixels + sourceStartOffset;
+                    TUint32* screenMemory = reinterpret_cast<TUint32*>(screenBuffer + targetStartOffset);
+                    for(TInt y = 0 ; y < sourceRectHeight ; y++)
+                    {
+                        TUint8*  bitmapPos           = bitmapLine;   /* 1 byte per pixel */
+                        TUint32* screenMemoryLinePos = screenMemory; /* 2 bytes per pixel */
+                        /* Convert each pixel from 256 palette to 4k color values */
+                        for(TInt x = 0 ; x < sourceRectWidth ; x++)
+                        {
+                            __ASSERT_DEBUG(screenMemoryLinePos < (reinterpret_cast<TUint32*>(screenBuffer) + (phdata->NGAGE_ScreenSize.iWidth * phdata->NGAGE_ScreenSize.iHeight)), User::Panic(_L("SDL"), KErrCorrupt));
+                            __ASSERT_DEBUG(screenMemoryLinePos >= reinterpret_cast<TUint32*>(screenBuffer), User::Panic(_L("SDL"), KErrCorrupt));
+                            __ASSERT_DEBUG(bitmapPos < ((TUint8*)screen->pixels + (screen->w * screen->h)), User::Panic(_L("SDL"), KErrCorrupt));
+                            __ASSERT_DEBUG(bitmapPos >= (TUint8*)screen->pixels, User::Panic(_L("SDL"), KErrCorrupt));
+                            *screenMemoryLinePos++ = NGAGE_HWPalette_256_to_Screen[*bitmapPos++];
+                        }
+                        bitmapLine   += sourceScanlineLength;
+                        screenMemory += targetScanlineLength;
+                    }
+                }
+            }
+        }
+    }
+}
+
+void DirectUpdate(_THIS, int numrects, SDL_Rect *rects)
+{
+    SDL_VideoData *phdata = (SDL_VideoData*)_this->driverdata;
+
+    if (! phdata->NGAGE_IsWindowFocused)
+    {
+        SDL_PauseAudio(1);
+        SDL_Delay(1000);
+        return;
+    }
+
+    SDL_PauseAudio(0);
+
+    TUint16* screenBuffer = (TUint16*)phdata->NGAGE_FrameBuffer;
+
+    /*if (phdata->NGAGE_ScreenOrientation == CFbsBitGc::EGraphicsOrientationRotated270)
+    {
+        // ...
+    }
+    else */
+    {
+        DirectDraw(_this, numrects, rects, screenBuffer);
+    }
+
+    //TRect rect2 = TRect(phdata->NGAGE_WsWindow.Size());
+    for (int i = 0; i < numrects; ++i)
+    {
+        TInt  aAx   = rects[i].x;
+        TInt  aAy   = rects[i].y;
+        TInt  aBx   = rects[i].w;
+        TInt  aBy   = rects[i].h;
+        TRect rect2 = TRect(aAx, aAy, aBx, aBy);
+
+        phdata->NGAGE_DrawDevice->UpdateRegion(rect2); /* Should we update rects parameter area only?? */
+        phdata->NGAGE_DrawDevice->Update();
+    }
+}
+
+void RedrawWindowL(_THIS)
+{
+    SDL_VideoData *phdata = (SDL_VideoData*)_this->driverdata;
+    SDL_Surface   *screen = (SDL_Surface*)SDL_GetWindowData(_this->windows, NGAGE_SURFACE);
+
+    int w = screen->w;
+    int h = screen->h;
+    if (phdata->NGAGE_ScreenOrientation == CFbsBitGc::EGraphicsOrientationRotated270) {
+        w = screen->h;
+        h = screen->w;
+    }
+    if ((w < phdata->NGAGE_ScreenSize.iWidth)
+        || (h < phdata->NGAGE_ScreenSize.iHeight)) {
+        DrawBackground(_this);
+    }
+
+    /* Tell the system that something has been drawn */
+    TRect  rect = TRect(phdata->NGAGE_WsWindow.Size());
+    phdata->NGAGE_WsWindow.Invalidate(rect);
+
+    /* Draw current buffer */
+    SDL_Rect fullScreen;
+    fullScreen.x = 0;
+    fullScreen.y = 0;
+    fullScreen.w = screen->w;
+    fullScreen.h = screen->h;
+    DirectUpdate(_this, 1, &fullScreen);
+}
+
+#endif /* SDL_VIDEO_DRIVER_NGAGE */
+
+/* vi: set ts=4 sw=4 expandtab: */

+ 38 - 0
src/video/ngage/SDL_ngageframebuffer_c.h

@@ -0,0 +1,38 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2022 Sam Lantinga <[email protected]>
+
+  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"
+
+extern int SDL_NGAGE_CreateWindowFramebuffer(_THIS, SDL_Window * window, Uint32 * format, void ** pixels, int *pitch);
+extern int SDL_NGAGE_UpdateWindowFramebuffer(_THIS, SDL_Window * window, const SDL_Rect * rects, int numrects);
+extern void SDL_NGAGE_DestroyWindowFramebuffer(_THIS, SDL_Window * window);
+
+/****************************************************************************/
+/* Runtime                                                                  */
+/****************************************************************************/
+
+class NGAGE_Runtime
+{
+public:
+    IMPORT_C static void GetScreenInfo(TScreenInfoV01& screenInfo2);
+};
+
+/* vi: set ts=4 sw=4 expandtab: */

+ 192 - 0
src/video/ngage/SDL_ngagevideo.cpp

@@ -0,0 +1,192 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2022 Sam Lantinga <[email protected]>
+
+  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 <stdlib.h>
+#ifdef NULL
+#undef NULL
+#endif
+#include "../../SDL_internal.h"
+
+#if SDL_VIDEO_DRIVER_NGAGE
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "SDL_video.h"
+#include "../SDL_sysvideo.h"
+#include "../SDL_pixels_c.h"
+#include "../../events/SDL_events_c.h"
+
+#ifdef __cplusplus
+}
+#endif
+
+#include "SDL_ngagevideo.h"
+#include "SDL_ngagewindow.h"
+#include "SDL_ngageevents_c.h"
+#include "SDL_ngageframebuffer_c.h"
+
+#define NGAGEVID_DRIVER_NAME "ngage"
+
+/* Initialization/Query functions */
+static int  NGAGE_VideoInit(_THIS);
+static int  NGAGE_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode);
+static void NGAGE_VideoQuit(_THIS);
+
+/* NGAGE driver bootstrap functions */
+
+static void
+NGAGE_DeleteDevice(SDL_VideoDevice * device)
+{
+    SDL_VideoData *phdata = (SDL_VideoData*)device->driverdata;
+
+    if (phdata)
+    {
+        /* Free Epoc resources */
+
+        /* Disable events for me */
+        if (phdata->NGAGE_WsEventStatus != KRequestPending)
+        {
+            phdata->NGAGE_WsSession.EventReadyCancel();
+        }
+        if (phdata->NGAGE_RedrawEventStatus != KRequestPending)
+        {
+            phdata->NGAGE_WsSession.RedrawReadyCancel();
+        }
+
+        free(phdata->NGAGE_DrawDevice);
+
+        if (phdata->NGAGE_WsWindow.WsHandle())
+        {
+            phdata->NGAGE_WsWindow.Close();
+        }
+
+        if (phdata->NGAGE_WsWindowGroup.WsHandle())
+        {
+            phdata->NGAGE_WsWindowGroup.Close();
+        }
+
+        delete phdata->NGAGE_WindowGc;
+        phdata->NGAGE_WindowGc = NULL;
+
+        delete phdata->NGAGE_WsScreen;
+        phdata->NGAGE_WsScreen = NULL;
+
+        if (phdata->NGAGE_WsSession.WsHandle())
+        {
+            phdata->NGAGE_WsSession.Close();
+        }
+
+        SDL_free(phdata);
+        phdata = NULL;
+    }
+
+    if (device)
+    {
+        SDL_free(device);
+        device = NULL;
+    }
+}
+
+static SDL_VideoDevice *
+NGAGE_CreateDevice(int devindex)
+{
+    SDL_VideoDevice *device;
+    SDL_VideoData   *phdata;
+
+    /* Initialize all variables that we clean on shutdown */
+    device = (SDL_VideoDevice *) SDL_calloc(1, sizeof(SDL_VideoDevice));
+    if (!device) {
+        SDL_OutOfMemory();
+        return (0);
+    }
+
+    /* Initialize internal N-Gage specific data */
+    phdata = (SDL_VideoData *) SDL_calloc(1, sizeof(SDL_VideoData));
+    if (! phdata)
+    {
+        SDL_OutOfMemory();
+        SDL_free(device);
+        return (0);
+    }
+
+    /* General video */
+    device->VideoInit                = NGAGE_VideoInit;
+    device->VideoQuit                = NGAGE_VideoQuit;
+    device->SetDisplayMode           = NGAGE_SetDisplayMode;
+    device->PumpEvents               = NGAGE_PumpEvents;
+    device->CreateWindowFramebuffer  = SDL_NGAGE_CreateWindowFramebuffer;
+    device->UpdateWindowFramebuffer  = SDL_NGAGE_UpdateWindowFramebuffer;
+    device->DestroyWindowFramebuffer = SDL_NGAGE_DestroyWindowFramebuffer;
+    device->free                     = NGAGE_DeleteDevice;
+
+    /* "Window" */
+    device->CreateSDLWindow = NGAGE_CreateWindow;
+    device->DestroyWindow   = NGAGE_DestroyWindow;
+
+    /* N-Gage specific data */
+    device->driverdata = phdata;
+
+    return device;
+}
+
+VideoBootStrap NGAGE_bootstrap = {
+    NGAGEVID_DRIVER_NAME, "SDL ngage video driver",
+    NGAGE_CreateDevice
+};
+
+int
+NGAGE_VideoInit(_THIS)
+{
+    SDL_DisplayMode mode;
+
+    /* Use 12-bpp desktop mode */
+    mode.format       = SDL_PIXELFORMAT_RGB444;
+    mode.w            = 176;
+    mode.h            = 208;
+    mode.refresh_rate = 0;
+    mode.driverdata   = NULL;
+    if (SDL_AddBasicVideoDisplay(&mode) < 0) {
+        return -1;
+    }
+
+    SDL_zero(mode);
+    SDL_AddDisplayMode(&_this->displays[0], &mode);
+
+    /* We're done! */
+    return 0;
+}
+
+static int
+NGAGE_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode)
+{
+    return 0;
+}
+
+void
+NGAGE_VideoQuit(_THIS)
+{
+}
+
+#endif /* SDL_VIDEO_DRIVER_NGAGE */
+
+/* vi: set ts=4 sw=4 expandtab: */

+ 75 - 0
src/video/ngage/SDL_ngagevideo.h

@@ -0,0 +1,75 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2022 Sam Lantinga <[email protected]>
+
+  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_ngagevideo_h
+#define _SDL_ngagevideo_h
+
+#include "../SDL_sysvideo.h"
+
+#include <e32std.h>
+#include <e32svr.h>
+#include <bitdev.h>
+#include <w32std.h>
+#include <bitdraw.h> // CFbsDrawDevice
+
+#define _THIS SDL_VideoDevice *_this
+
+typedef struct SDL_VideoData
+{
+    /* Epoc window server info */
+    RWsSession       NGAGE_WsSession;
+    RWindowGroup     NGAGE_WsWindowGroup;
+    TInt             NGAGE_WsWindowGroupID;
+    RWindow          NGAGE_WsWindow;
+    CWsScreenDevice* NGAGE_WsScreen;
+    CWindowGc*       NGAGE_WindowGc;
+    TRequestStatus   NGAGE_WsEventStatus;
+    TRequestStatus   NGAGE_RedrawEventStatus;
+    TWsEvent         NGAGE_WsEvent;
+    //TWsRedrawEvent   NGAGE_RedrawEvent;
+
+    CFbsDrawDevice*  NGAGE_DrawDevice;
+
+    TBool            NGAGE_IsWindowFocused; /* Not used yet */
+
+    /* Screen hardware frame buffer info */
+    TBool            NGAGE_HasFrameBuffer;
+    TInt             NGAGE_BytesPerPixel;
+    TInt             NGAGE_BytesPerScanLine;
+    TInt             NGAGE_BytesPerScreen;
+    TDisplayMode     NGAGE_DisplayMode;
+    TSize            NGAGE_ScreenSize;
+    TUint8*          NGAGE_FrameBuffer;
+    TPoint           NGAGE_ScreenOffset;
+
+    CFbsBitGc::TGraphicsOrientation NGAGE_ScreenOrientation;
+
+    /* Simulate double screen height */
+    //TInt             NGAGE_ScreenXScaleValue;
+    //TInt             NGAGE_ScreenYScaleValue;
+
+} SDL_VideoData;
+
+#endif /* _SDL_ngagevideo_h */
+
+/* vi: set ts=4 sw=4 expandtab: */

+ 129 - 0
src/video/ngage/SDL_ngagewindow.cpp

@@ -0,0 +1,129 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2022 Sam Lantinga <[email protected]>
+
+  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_NGAGE
+
+#include "../SDL_sysvideo.h"
+
+#include "SDL_ngagewindow.h"
+
+const TUint32 WindowClientHandle = 9210;
+
+void DisableKeyBlocking(_THIS);
+void ConstructWindowL(_THIS);
+
+int
+NGAGE_CreateWindow(_THIS, SDL_Window* window)
+{
+    NGAGE_Window* ngage_window = (NGAGE_Window*)SDL_calloc(1, sizeof(NGAGE_Window));
+
+    if (!ngage_window) {
+        return SDL_OutOfMemory();
+    }
+
+    window->driverdata = ngage_window;
+
+    if (window->x == SDL_WINDOWPOS_UNDEFINED) {
+        window->x = 0;
+    }
+
+    if (window->y == SDL_WINDOWPOS_UNDEFINED) {
+        window->y = 0;
+    }
+
+    ngage_window->sdl_window = window;
+
+    ConstructWindowL(_this);
+
+    return 0;
+}
+
+void
+NGAGE_DestroyWindow(_THIS, SDL_Window* window)
+{
+    NGAGE_Window* ngage_window = (NGAGE_Window*)window->driverdata;
+
+    if (ngage_window) {
+        SDL_free(ngage_window);
+    }
+
+    window->driverdata = NULL;
+}
+
+/*****************************************************************************/
+/* Internal                                                                  */
+/*****************************************************************************/
+
+void DisableKeyBlocking(_THIS)
+{
+    SDL_VideoData *phdata = (SDL_VideoData*)_this->driverdata;
+    TRawEvent      event;
+
+    event.Set((TRawEvent::TType) /*EDisableKeyBlock*/ 51);
+    phdata->NGAGE_WsSession.SimulateRawEvent(event);
+}
+
+void ConstructWindowL(_THIS)
+{
+    SDL_VideoData *phdata = (SDL_VideoData*)_this->driverdata;
+    TInt           error;
+
+    error = phdata->NGAGE_WsSession.Connect();
+    User::LeaveIfError(error);
+    phdata->NGAGE_WsScreen=new(ELeave) CWsScreenDevice(phdata->NGAGE_WsSession);
+    User::LeaveIfError(phdata->NGAGE_WsScreen->Construct());
+    User::LeaveIfError(phdata->NGAGE_WsScreen->CreateContext(phdata->NGAGE_WindowGc));
+
+    phdata->NGAGE_WsWindowGroup=RWindowGroup(phdata->NGAGE_WsSession);
+    User::LeaveIfError(phdata->NGAGE_WsWindowGroup.Construct(WindowClientHandle));
+    phdata->NGAGE_WsWindowGroup.SetOrdinalPosition(0);
+
+    RProcess thisProcess;
+    TParse   exeName;
+    exeName.Set(thisProcess.FileName(), NULL, NULL);
+    TBuf<32> winGroupName;
+    winGroupName.Append(0);
+    winGroupName.Append(0);
+    winGroupName.Append(0);              // UID
+    winGroupName.Append(0);
+    winGroupName.Append(exeName.Name()); // Caption
+    winGroupName.Append(0);
+    winGroupName.Append(0);              // DOC name
+    phdata->NGAGE_WsWindowGroup.SetName(winGroupName);
+
+    phdata->NGAGE_WsWindow=RWindow(phdata->NGAGE_WsSession);
+    User::LeaveIfError(phdata->NGAGE_WsWindow.Construct(phdata->NGAGE_WsWindowGroup,WindowClientHandle - 1));
+    phdata->NGAGE_WsWindow.SetBackgroundColor(KRgbWhite);
+    phdata->NGAGE_WsWindow.Activate();
+    phdata->NGAGE_WsWindow.SetSize(phdata->NGAGE_WsScreen->SizeInPixels());
+    phdata->NGAGE_WsWindow.SetVisible(ETrue);
+
+    phdata->NGAGE_WsWindowGroupID = phdata->NGAGE_WsWindowGroup.Identifier();
+    phdata->NGAGE_IsWindowFocused = EFalse;
+
+    DisableKeyBlocking(_this);
+}
+
+#endif /* SDL_VIDEO_DRIVER_NGAGE */
+
+/* vi: set ts=4 sw=4 expandtab: */

+ 45 - 0
src/video/ngage/SDL_ngagewindow.h

@@ -0,0 +1,45 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2022 Sam Lantinga <[email protected]>
+
+  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_ngagewindow_h
+#define _SDL_ngagewindow_h
+
+#include "../SDL_sysvideo.h"
+#include "SDL_syswm.h"
+
+#include "SDL_ngagevideo.h"
+
+typedef struct {
+    SDL_Window* sdl_window;
+
+} NGAGE_Window;
+
+
+extern int
+NGAGE_CreateWindow(_THIS, SDL_Window* window);
+
+extern void
+NGAGE_DestroyWindow(_THIS, SDL_Window* window);
+
+#endif /* _SDL_ngagewindow */
+
+/* vi: set ts=4 sw=4 expandtab: */
+