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)
 - [WinRT](README-winrt.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
 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"
 #elif defined(__EMSCRIPTEN__)
 #include "SDL_config_emscripten.h"
+#elif defined(__NGAGE__)
+#include "SDL_config_ngage.h"
 #else
 /* This is a minimal configuration just to get SDL running on new platforms. */
 #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 <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_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? */
 #define __ANDROID__ 1
 #endif
+#if defined(__NGAGE__)
+#undef __NGAGE__
+#define __NGAGE__ 1
+#endif
 
 #if defined(__APPLE__)
 /* 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";
 #elif __VITA__
     return "PlayStation Vita";
+#elif __NGAGE__
+    return "Nokia N-Gage";
 #else
     return "Unknown (see SDL_platform.h)";
 #endif

+ 4 - 1
src/SDL_internal.h

@@ -27,7 +27,10 @@
 #endif
 
 /* 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 */
 #undef va_copy
 #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. */
 #elif defined(__VITA__)
 #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)
 #define SDL_DYNAMIC_API 0  /* we need dlopen(), but don't have it.... */
 #endif

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

@@ -0,0 +1,82 @@
+/*
+    EPOC version (originally for SDL 1.2) by Hannu Viitala
+    (hannu.j.viitala@mbnet.fi).
+*/
+#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"
 #elif SDL_THREAD_OS2
 #include "os2/SDL_systhread_c.h"
+#elif SDL_THREAD_NGAGE
+#include "ngage/SDL_systhread_c.h"
 #else
 #error Need thread implementation for this platform
 #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 <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"
+
+/* 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 <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"
+
+/* 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 <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_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 <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: */

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

@@ -0,0 +1,100 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2022 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(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 QNX_bootstrap;
 extern VideoBootStrap OFFSCREEN_bootstrap;
+extern VideoBootStrap NGAGE_bootstrap;
 extern VideoBootStrap OS2DIVE_bootstrap;
 extern VideoBootStrap OS2VMAN_bootstrap;
 

+ 3 - 0
src/video/SDL_video.c

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

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

@@ -0,0 +1,200 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2022 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_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 <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_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 <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_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 <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"
+
+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 <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 <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 <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_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 <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_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 <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_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: */
+