diff --git a/.github/actions/setup-ngage-sdk/action.yml b/.github/actions/setup-ngage-sdk/action.yml new file mode 100644 index 0000000000000..fa83418ba21c6 --- /dev/null +++ b/.github/actions/setup-ngage-sdk/action.yml @@ -0,0 +1,102 @@ +name: 'Setup Nonka N-Gage SDK' +description: 'Download and setup Nokia N-Gage SDK' +inputs: + path: + description: 'Installation path' + default: 'default' +runs: + using: 'composite' + steps: + - uses: actions/setup-python@v5 + with: + python-version: '3.x' + - name: 'Verify platform' + id: calc + shell: sh + run: | + case "${{ runner.os }}-${{ runner.arch }}" in + "Windows-X86" | "Windows-X64") + echo "ok!" + echo "cache-key=ngage-sdk-windows" >> ${GITHUB_OUTPUT} + default_install_path="C:/ngagesdk" + ;; + *) + echo "Unsupported ${{ runner.os }}-${{ runner.arch }}" + exit 1; + ;; + esac + install_path="${{ inputs.path }}" + if [ "x$install_path" = "xdefault" ]; then + install_path="$default_install_path" + fi + echo "install-path=$install_path" >> ${GITHUB_OUTPUT} + + toolchain_repo="https://github.com/ngagesdk/ngage-toolchain" + toolchain_branch="main" + echo "toolchain-repo=${toolchain_repo}" >> ${GITHUB_OUTPUT} + echo "toolchain-branch=${toolchain_branch}" >> ${GITHUB_OUTPUT} + + sdk_repo="https://github.com/ngagesdk/sdk" + sdk_branch="main" + echo "sdk-repo=${sdk_repo}" >> ${GITHUB_OUTPUT} + echo "sdk-branch=${sdk_branch}" >> ${GITHUB_OUTPUT} + + tools_repo="https://github.com/ngagesdk/tools" + tools_branch="main" + echo "tools-repo=${tools_repo}" >> ${GITHUB_OUTPUT} + echo "tools-branch=${tools_branch}" >> ${GITHUB_OUTPUT} + + extras_repo="https://github.com/ngagesdk/extras" + extras_branch="main" + echo "extras-repo=${extras_repo}" >> ${GITHUB_OUTPUT} + echo "extras-branch=${extras_branch}" >> ${GITHUB_OUTPUT} +# - name: 'Restore cached ${{ steps.calc.outputs.archive }}' +# id: cache-restore +# uses: actions/cache/restore@v4 +# with: +# path: '${{ runner.temp }}' +# key: ${{ steps.calc.outputs.cache-key }} + - name: 'Download N-Gage SDK' +# if: ${{ !steps.cache-restore.outputs.cache-hit || steps.cache-restore.outputs.cache-hit == 'false' }} + shell: pwsh + run: | + + Invoke-WebRequest "${{ steps.calc.outputs.toolchain-repo }}/archive/refs/heads/${{ steps.calc.outputs.toolchain-branch }}.zip" -OutFile "${{ runner.temp }}/ngage-toolchain.zip" + Invoke-WebRequest "${{ steps.calc.outputs.sdk-repo }}/archive/refs/heads/${{ steps.calc.outputs.sdk-branch }}.zip" -OutFile "${{ runner.temp }}/sdk.zip" + Invoke-WebRequest "${{ steps.calc.outputs.tools-repo }}/archive/refs/heads/${{ steps.calc.outputs.tools-branch }}.zip" -OutFile "${{ runner.temp }}/tools.zip" + Invoke-WebRequest "${{ steps.calc.outputs.extras-repo }}/archive/refs/heads/${{ steps.calc.outputs.extras-branch }}.zip" -OutFile "${{ runner.temp }}/extras.zip" + +# - name: 'Cache ${{ steps.calc.outputs.archive }}' +# if: ${{ !steps.cache-restore.outputs.cache-hit || steps.cache-restore.outputs.cache-hit == 'false' }} +# uses: actions/cache/save@v4 +# with: +# path: | +# ${{ runner.temp }}/apps.zip +# ${{ runner.temp }}/sdk.zip +# ${{ runner.temp }}/tools.zip +# key: ${{ steps.calc.outputs.cache-key }} + - name: 'Extract N-Gage SDK' + shell: pwsh + run: | + New-Item -ItemType Directory -Path "${{ steps.calc.outputs.install-path }}" -Force + + New-Item -ItemType Directory -Path "${{ runner.temp }}/ngage-toolchain-temp" -Force + 7z "-o${{ runner.temp }}/ngage-toolchain-temp" x "${{ runner.temp }}/ngage-toolchain.zip" + Move-Item -Path "${{ runner.temp }}/ngage-toolchain-temp/ngage-toolchain-${{ steps.calc.outputs.toolchain-branch }}/*" -Destination "${{ steps.calc.outputs.install-path }}" + + 7z "-o${{ steps.calc.outputs.install-path }}/sdk" x "${{ runner.temp }}/sdk.zip" + Move-Item -Path "${{ steps.calc.outputs.install-path }}/sdk/sdk-${{ steps.calc.outputs.sdk-branch }}" -Destination "${{ steps.calc.outputs.install-path }}/sdk/sdk" + + 7z "-o${{ steps.calc.outputs.install-path }}/sdk" x "${{ runner.temp }}/tools.zip" + Move-Item -Path "${{ steps.calc.outputs.install-path }}/sdk/tools-${{ steps.calc.outputs.tools-branch }}" -Destination "${{ steps.calc.outputs.install-path }}/sdk/tools" + + 7z "-o${{ steps.calc.outputs.install-path }}/sdk" x "${{ runner.temp }}/extras.zip" + Move-Item -Path "${{ steps.calc.outputs.install-path }}/sdk/extras-${{ steps.calc.outputs.extras-branch }}" -Destination "${{ steps.calc.outputs.install-path }}/sdk/extras" + - name: 'Set output variables' + id: final + shell: sh + run: | + echo "${{ steps.calc.outputs.install-path }}/sdk/sdk/6.1/Shared/EPOC32/gcc/bin" >> $GITHUB_PATH + echo "${{ steps.calc.outputs.install-path }}/sdk/sdk/6.1/Shared/EPOC32/ngagesdk/bin" >> $GITHUB_PATH + echo "NGAGESDK=${{ steps.calc.outputs.install-path }}" >> $GITHUB_ENV + echo "CMAKE_TOOLCHAIN_FILE=${{ steps.calc.outputs.install-path }}/cmake/ngage-toolchain.cmake" >> $GITHUB_ENV diff --git a/.github/workflows/create-test-plan.py b/.github/workflows/create-test-plan.py index a137889c81a7d..59e61e23eaa05 100755 --- a/.github/workflows/create-test-plan.py +++ b/.github/workflows/create-test-plan.py @@ -54,6 +54,7 @@ class SdlPlatform(Enum): Riscos = "riscos" FreeBSD = "freebsd" NetBSD = "netbsd" + NGage = "ngage" class Msys2Platform(Enum): @@ -138,6 +139,7 @@ class JobSpec: "riscos": JobSpec(name="RISC OS", os=JobOs.UbuntuLatest, platform=SdlPlatform.Riscos, artifact="SDL-riscos", container="riscosdotinfo/riscos-gccsdk-4.7:latest", ), "netbsd": JobSpec(name="NetBSD", os=JobOs.UbuntuLatest, platform=SdlPlatform.NetBSD, artifact="SDL-netbsd-x64", ), "freebsd": JobSpec(name="FreeBSD", os=JobOs.UbuntuLatest, platform=SdlPlatform.FreeBSD, artifact="SDL-freebsd-x64", ), + "ngage": JobSpec(name="N-Gage", os=JobOs.WindowsLatest, platform=SdlPlatform.NGage, artifact="SDL-ngage", ), } @@ -163,6 +165,7 @@ class JobDetails: artifact: str no_cmake: bool ccache: bool = False + continue_on_error: bool = False build_tests: bool = True container: str = "" cmake_build_type: str = "RelWithDebInfo" @@ -222,6 +225,7 @@ class JobDetails: check_sources: bool = False setup_python: bool = False pypi_packages: list[str] = dataclasses.field(default_factory=list) + setup_gage_sdk_path: str = "" def to_workflow(self, enable_artifacts: bool) -> dict[str, str|bool]: data = { @@ -231,6 +235,7 @@ def to_workflow(self, enable_artifacts: bool) -> dict[str, str|bool]: "ccache": self.ccache, "container": self.container if self.container else "", "platform": self.platform, + "continue-on-error": self.continue_on_error, "artifact": self.artifact, "enable-artifacts": enable_artifacts, "shell": self.shell, @@ -289,6 +294,7 @@ def to_workflow(self, enable_artifacts: bool) -> dict[str, str|bool]: "check-sources": self.check_sources, "setup-python": self.setup_python, "pypi-packages": my_shlex_join(self.pypi_packages), + "setup-ngage-sdk-path": self.setup_gage_sdk_path, } return {k: v for k, v in data.items() if v != ""} @@ -737,6 +743,20 @@ def spec_to_job(spec: JobSpec, key: str, trackmem_symbol_names: bool) -> JobDeta job.cpactions_arch = "x86-64" job.cpactions_setup_cmd = "export PATH=\"/usr/pkg/sbin:/usr/pkg/bin:/sbin:$PATH\"; export PKG_CONFIG_PATH=\"/usr/pkg/lib/pkgconfig\";export PKG_PATH=\"https://cdn.netBSD.org/pub/pkgsrc/packages/NetBSD/$(uname -p)/$(uname -r|cut -f \"1 2\" -d.)/All/\";echo \"PKG_PATH=$PKG_PATH\";echo \"uname -a -> \"$(uname -a)\"\";sudo -E sysctl -w security.pax.aslr.enabled=0;sudo -E sysctl -w security.pax.aslr.global=0;sudo -E pkgin clean;sudo -E pkgin update" job.cpactions_install_cmd = "sudo -E pkgin -y install cmake dbus pkgconf ninja-build pulseaudio libxkbcommon wayland wayland-protocols libinotify libusb1" + case SdlPlatform.NGage: + job.cmake_arguments.extend([ + "-DBUILD_FOR_NOKIA_NGAGE=ON", # FIXME: remove this + ]) + job.continue_on_error = True # FIXME: remove this + job.setup_ninja = True + job.static_lib = None # FIXME: should be StaticLibType.A + job.shared_lib = None + job.clang_tidy = False + job.werror = False # FIXME: enable SDL_WERROR + job.shared = False + job.run_tests = False + job.setup_gage_sdk_path = "C:/ngagesdk" + job.cmake_toolchain_file = "C:/ngagesdk/cmake/ngage-toolchain.cmake" case _: raise ValueError(f"Unsupported platform={spec.platform}") diff --git a/.github/workflows/generic.yml b/.github/workflows/generic.yml index 9776431e3da83..8b3e45bec49d1 100644 --- a/.github/workflows/generic.yml +++ b/.github/workflows/generic.yml @@ -14,6 +14,7 @@ jobs: name: ${{ matrix.platform.name }} runs-on: ${{ matrix.platform.os }} container: ${{ matrix.platform.container }} + continue-on-error: ${{ matrix.platform.continue-on-error }} defaults: run: shell: ${{ matrix.platform.shell }} @@ -93,6 +94,11 @@ jobs: with: arch: ${{ matrix.platform.msvc-vcvars-arch }} sdk: ${{ matrix.platform.msvc-vcvars-sdk }} + - name: 'Set up Nokia N-Gage SDK' + uses: ./.github/actions/setup-ngage-sdk + if: ${{ matrix.platform.setup-ngage-sdk-path != '' }} + with: + path: '${{ matrix.platform.setup-ngage-sdk-path }}' - name: 'Set up Windows GDK Desktop' uses: ./.github/actions/setup-gdk-desktop if: ${{ matrix.platform.setup-gdk-folder != '' }} diff --git a/CMakeLists.txt b/CMakeLists.txt index 67389175e2200..a8b2a2b8291b2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,9 +4,18 @@ if(NOT DEFINED CMAKE_BUILD_TYPE) set(cmake_build_type_undefined 1) endif() +# FIXME: remove this +option(BUILD_FOR_NOKIA_NGAGE "Build for Nokia N-Gage" OFF) + # See docs/release_checklist.md project(SDL3 LANGUAGES C VERSION "3.3.0") +# FIXME: remove this +if (BUILD_FOR_NOKIA_NGAGE) + include(cmake/nokia_ngage.cmake) + return() +endif() + if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR) set(SDL3_MAINPROJECT ON) else() diff --git a/cmake/nokia_ngage.cmake b/cmake/nokia_ngage.cmake new file mode 100644 index 0000000000000..8dfad60c36dc0 --- /dev/null +++ b/cmake/nokia_ngage.cmake @@ -0,0 +1,125 @@ +cmake_minimum_required(VERSION 3.16) + +enable_language(CXX) + +file(GLOB SDL3_sources + "${SDL3_SOURCE_DIR}/src/*.c" + "${SDL3_SOURCE_DIR}/src/atomic/*.c" + "${SDL3_SOURCE_DIR}/src/audio/*.c" + "${SDL3_SOURCE_DIR}/src/audio/ngage/*.c" + "${SDL3_SOURCE_DIR}/src/audio/ngage/*.cpp" + "${SDL3_SOURCE_DIR}/src/core/*.c" + "${SDL3_SOURCE_DIR}/src/core/ngage/*.c" + "${SDL3_SOURCE_DIR}/src/core/ngage/*.cpp" + "${SDL3_SOURCE_DIR}/src/cpuinfo/*.c" + "${SDL3_SOURCE_DIR}/src/dynapi/*.c" + "${SDL3_SOURCE_DIR}/src/events/*.c" + "${SDL3_SOURCE_DIR}/src/file/*.c" + "${SDL3_SOURCE_DIR}/src/file/generic/*.c" + "${SDL3_SOURCE_DIR}/src/filesystem/*.c" + "${SDL3_SOURCE_DIR}/src/filesystem/ngage/*.c" + "${SDL3_SOURCE_DIR}/src/filesystem/ngage/*.cpp" + "${SDL3_SOURCE_DIR}/src/filesystem/posix/*.c" + "${SDL3_SOURCE_DIR}/src/hidapi/*.c" + "${SDL3_SOURCE_DIR}/src/io/*.c" + "${SDL3_SOURCE_DIR}/src/io/generic/*.c" + "${SDL3_SOURCE_DIR}/src/joystick/*.c" + "${SDL3_SOURCE_DIR}/src/joystick/dummy/*.c" + "${SDL3_SOURCE_DIR}/src/libm/s_isinff.c" + "${SDL3_SOURCE_DIR}/src/libm/s_isnanf.c" + "${SDL3_SOURCE_DIR}/src/locale/*.c" + "${SDL3_SOURCE_DIR}/src/locale/ngage/*.cpp" + "${SDL3_SOURCE_DIR}/src/main/*.c" + "${SDL3_SOURCE_DIR}/src/main/ngage/*.c" + "${SDL3_SOURCE_DIR}/src/main/ngage/*.cpp" + "${SDL3_SOURCE_DIR}/src/misc/*.c" + "${SDL3_SOURCE_DIR}/src/misc/dummy/*.c" + "${SDL3_SOURCE_DIR}/src/power/*.c" + "${SDL3_SOURCE_DIR}/src/render/*.c" + "${SDL3_SOURCE_DIR}/src/render/ngage/*.c" + "${SDL3_SOURCE_DIR}/src/render/ngage/*.cpp" + "${SDL3_SOURCE_DIR}/src/sensor/*.c" + "${SDL3_SOURCE_DIR}/src/sensor/dummy/*.c" + "${SDL3_SOURCE_DIR}/src/stdlib/*.c" + "${SDL3_SOURCE_DIR}/src/storage/*.c" + "${SDL3_SOURCE_DIR}/src/storage/generic/*.c" + "${SDL3_SOURCE_DIR}/src/thread/*.c" + "${SDL3_SOURCE_DIR}/src/thread/generic/*.c" + "${SDL3_SOURCE_DIR}/src/time/*.c" + "${SDL3_SOURCE_DIR}/src/time/ngage/*.cpp" + "${SDL3_SOURCE_DIR}/src/time/unix/*.c" + "${SDL3_SOURCE_DIR}/src/timer/*.c" + "${SDL3_SOURCE_DIR}/src/timer/ngage/*.cpp" + "${SDL3_SOURCE_DIR}/src/tray/*.c" + "${SDL3_SOURCE_DIR}/src/tray/dummy/*.c" + "${SDL3_SOURCE_DIR}/src/video/*.c" + "${SDL3_SOURCE_DIR}/src/video/ngage/*.c" + "${SDL3_SOURCE_DIR}/src/video/yuv2rgb/*.c") + +add_library(${PROJECT_NAME} STATIC ${SDL3_sources}) + +target_include_directories( + ${PROJECT_NAME} + PUBLIC + "${SDL3_SOURCE_DIR}/include" + PRIVATE + "${SDL3_SOURCE_DIR}/src" + "${SDL3_SOURCE_DIR}/include/build_config") + +target_compile_definitions( + ${PROJECT_NAME} + PUBLIC + SDL_STATIC_LIB) + +set(test_static_libs + ${CMAKE_CURRENT_BINARY_DIR}/libSDL3.a) + +set(test_libs + ${CMAKE_CURRENT_BINARY_DIR}/libSDL3.a + ${EPOC_LIB}/NRenderer.lib + ${EPOC_LIB}/3dtypes.a + ${EPOC_LIB}/cone.lib + ${EPOC_PLATFORM}/gcc/lib/gcc-lib/arm-epoc-pe/2.9-psion-98r2/libgcc.a + ${EPOC_PLATFORM}/ngagesdk/lib/gcc/arm-epoc-pe/4.6.4/libgcc_ngage.a + ${EPOC_PLATFORM}/ngagesdk/lib/gcc/arm-epoc-pe/4.6.4/libc_ngage.a + ${EPOC_LIB}/mediaclientaudiostream.lib + ${EPOC_LIB}/charconv.lib + ${EPOC_LIB}/bitgdi.lib + ${EPOC_LIB}/euser.lib + ${EPOC_LIB}/estlib.lib + ${EPOC_LIB}/ws32.lib + ${EPOC_LIB}/hal.lib + ${EPOC_LIB}/fbscli.lib + ${EPOC_LIB}/efsrv.lib + ${EPOC_LIB}/scdv.lib + ${EPOC_LIB}/gdi.lib) + +set(UID1 0x1000007a) # KExecutableImageUidValue, e32uid.h +set(UID2 0x100039ce) # KAppUidValue16, apadef.h +set(UID3 0x10005799) # ngage_test.exe UID + +add_library(ngage_test STATIC "${CMAKE_CURRENT_SOURCE_DIR}/cmake/test/ngagetest.c") + +build_exe_static(ngage_test exe ${UID1} ${UID2} ${UID3} "${test_static_libs}" "${test_libs}") + +add_dependencies( + ngage_test.exe + ngage_test) + +add_dependencies( + ngage_test + ${PROJECT_NAME}) + +target_compile_definitions( + ngage_test + PUBLIC + __EXE__ + UID1=${UID1} + UID2=${UID2} + UID3=${UID3}) + +target_include_directories( + ngage_test + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include + ${CMAKE_CURRENT_SOURCE_DIR}/include/SDL3) diff --git a/cmake/test/ngagetest.c b/cmake/test/ngagetest.c new file mode 100644 index 0000000000000..0215af1a0d331 --- /dev/null +++ b/cmake/test/ngagetest.c @@ -0,0 +1,33 @@ +// The purpose of this test is to validate that there are no undefined references +// when building with the N-Gage SDK since SDL can only be linked statically. + +#define IMPORT_C +#include + +int SDL_main(int argc, char* argv[]) +{ + for (int i = 0; i < argc; i++) { // C99 check. + /* Nothing to do here. */ + } + + return 0; +} + +SDL_AppResult SDL_AppInit(void** appstate, int argc, char* argv[]) +{ + return SDL_APP_CONTINUE; +} + +SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event) +{ + return SDL_APP_CONTINUE; +} + +SDL_AppResult SDL_AppIterate(void* appstate) +{ + return SDL_APP_CONTINUE; +} + +void SDL_AppQuit(void* appstate, SDL_AppResult result) +{ +} diff --git a/docs/README-ngage.md b/docs/README-ngage.md index 84192b01abaee..eb14bf68d0de1 100644 --- a/docs/README-ngage.md +++ b/docs/README-ngage.md @@ -1,5 +1,44 @@ -Support for the Nokia N-Gage has been removed from SDL3 (but will make a -comeback when newer compilers are available for the platform). +# Nokia N-Gage -SDL2 still supports this platform. +SDL port for the Nokia N-Gage [Homebrew toolchain](https://github.com/ngagesdk/ngage-toolchain) contributed by: +- [Michael Fitzmayer](https://github.com/mupfdev) + +Many thanks to: + +- icculus and slouken, who faithfully kept us a place by the fireside! +- The awesome people who ported SDL to other homebrew platforms. +- The Nokia N-Gage [Discord community](https://discord.gg/dbUzqJ26vs) + who keeps the platform alive. +- To all the staff and supporters of the + [Suomen pelimuseo](https://www.vapriikki.fi/nayttelyt/fantastinen-floppi/) + and Heikki Jungmann. You guys are great! + +## History + +After SDL support was removed because there was no support for C99 at that +time, this version was developed from scratch after the compiler problems +were resolved. + +Unlike the previous SDL2 port, this version has a dedicated rendering +backend and a limited audio interface that works, even if it is limited. +Support for the software renderer has been removed. + +The result is a much leaner, and much more performant port of SDL that +will hopefully breathe some new life into this obscure platform - that +we love so much. + +## Future prospects + +The revised toolchain includes EGL 1.3 and OpenGL ES 1.1. A native +integration with SDL is being considered. + +## Existing Issues + +- If the application is put in the background while sound is playing, + some of the audio is looped until the app is back in focus. + +- It is recommended initialising SDLs audio sub-system even when it + is not required. The backend is started at a higher level. + Initialising SDLs audio sub-system ensures that the backend is + properly deinitialised. diff --git a/include/SDL3/SDL_assert.h b/include/SDL3/SDL_assert.h index 6c90acc0290c9..ef5f85d0584fe 100644 --- a/include/SDL3/SDL_assert.h +++ b/include/SDL3/SDL_assert.h @@ -132,7 +132,7 @@ extern "C" { #define SDL_TriggerBreakpoint() __debugbreak() #elif defined(_MSC_VER) && defined(_M_IX86) #define SDL_TriggerBreakpoint() { _asm { int 0x03 } } -#elif defined(ANDROID) +#elif defined(ANDROID) || defined(__SYMBIAN32__) #include #define SDL_TriggerBreakpoint() assert(0) #elif SDL_HAS_BUILTIN(__builtin_debugtrap) diff --git a/include/SDL3/SDL_begin_code.h b/include/SDL3/SDL_begin_code.h index a6b47cf4b916d..7adf1b9f59177 100644 --- a/include/SDL3/SDL_begin_code.h +++ b/include/SDL3/SDL_begin_code.h @@ -389,7 +389,7 @@ #endif /* SDL_FORCE_INLINE not defined */ #ifndef SDL_NORETURN -#ifdef __GNUC__ +#if defined(__GNUC__) #define SDL_NORETURN __attribute__((noreturn)) #elif defined(_MSC_VER) #define SDL_NORETURN __declspec(noreturn) diff --git a/include/SDL3/SDL_platform_defines.h b/include/SDL3/SDL_platform_defines.h index 6b240a8be4579..5bb82ba930738 100644 --- a/include/SDL3/SDL_platform_defines.h +++ b/include/SDL3/SDL_platform_defines.h @@ -317,7 +317,7 @@ #define SDL_PLATFORM_CYGWIN 1 #endif -#if defined(_WIN32) || defined(SDL_PLATFORM_CYGWIN) +#if (defined(_WIN32) || defined(SDL_PLATFORM_CYGWIN)) && !defined(__NGAGE__) /** * A preprocessor macro that is only defined if compiling for Windows. @@ -473,4 +473,14 @@ #define SDL_PLATFORM_3DS 1 #endif +#ifdef __NGAGE__ + +/** + * A preprocessor macro that is only defined if compiling for the Nokia N-Gage. + * + * \since This macro is available since SDL 3.1.3. + */ +#define SDL_PLATFORM_NGAGE 1 +#endif + #endif /* SDL_platform_defines_h_ */ diff --git a/include/build_config/SDL_build_config.h b/include/build_config/SDL_build_config.h index 83031b70cc0e7..711399e4dcb23 100644 --- a/include/build_config/SDL_build_config.h +++ b/include/build_config/SDL_build_config.h @@ -45,6 +45,8 @@ #include "SDL_build_config_ios.h" #elif defined(SDL_PLATFORM_ANDROID) #include "SDL_build_config_android.h" +#elif defined(SDL_PLATFORM_NGAGE) +#include "SDL_build_config_ngage.h" #else /* This is a minimal configuration just to get SDL running on new platforms. */ #include "SDL_build_config_minimal.h" diff --git a/include/build_config/SDL_build_config_ngage.h b/include/build_config/SDL_build_config_ngage.h new file mode 100644 index 0000000000000..e52843735166a --- /dev/null +++ b/include/build_config/SDL_build_config_ngage.h @@ -0,0 +1,72 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + 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_build_config_ngage_h_ +#define SDL_build_config_ngage_h_ +#define SDL_build_config_h_ + +#include +#include +#include +#include +#include +#include + +#define SDL_AUDIO_DRIVER_NGAGE 1 +#define SDL_CAMERA_DISABLED 1 +#define SDL_ASSERT_LEVEL 0 +#define SDL_FSOPS_POSIX 1 +#define SDL_GPU_DISABLED 1 +#define SDL_HAPTIC_DISABLED 1 +#define SDL_JOYSTICK_DISABLED 1 +#define SDL_LEAN_AND_MEAN 1 +#define SDL_MAIN_USE_CALLBACKS 1 +#define SDL_SENSOR_DISABLED 1 +#define SDL_THREADS_DISABLED 1 +#define SDL_VIDEO_DRIVER_NGAGE 1 +#define SDL_VIDEO_RENDER_NGAGE 1 + +#define HAVE_ATAN 1 +#define HAVE_ATAN2 1 +#define HAVE_COPYSIGN 1 +#define HAVE_COS 1 +#define HAVE_EXP 1 +#define HAVE_FABS 1 +#define HAVE_FLOOR 1 +#define HAVE_FMOD 1 +#define HAVE_ISINF 1 +#define HAVE_ISNAN 1 +#define HAVE_LOG 1 +#define HAVE_LOG10 1 +#define HAVE_MALLOC 1 +#define HAVE_MATH_H 1 +#define HAVE_MODF 1 +#define HAVE_POW 1 +#define HAVE_SCALBN 1 +#define HAVE_SIN 1 +#define HAVE_STDIO_H 1 +#define HAVE_SQRT 1 +#define HAVE_TAN 1 + +#define isnanf(x) SDL_uclibc_isnanf(x); +#define isinff(x) SDL_uclibc_isinff(x); + +#endif /* SDL_build_config_ngage_h_ */ diff --git a/src/SDL.c b/src/SDL.c index 502f6617a4f26..34e15251b14f7 100644 --- a/src/SDL.c +++ b/src/SDL.c @@ -728,6 +728,8 @@ const char *SDL_GetPlatform(void) return "macOS"; #elif defined(SDL_PLATFORM_NETBSD) return "NetBSD"; +#elif defined(SDL_PLATFORM_NGAGE) + return "Nokia N-Gage"; #elif defined(SDL_PLATFORM_OPENBSD) return "OpenBSD"; #elif defined(SDL_PLATFORM_OS2) diff --git a/src/SDL_error.c b/src/SDL_error.c index 3c62c8aff4870..3f4273b4f9376 100644 --- a/src/SDL_error.c +++ b/src/SDL_error.c @@ -20,6 +20,8 @@ */ #include "SDL_internal.h" +#include "stdlib/SDL_vacopy.h" + // Simple error handling in SDL #include "SDL_error_c.h" diff --git a/src/SDL_log.c b/src/SDL_log.c index 10a814ff1b272..da55dcf1c19ca 100644 --- a/src/SDL_log.c +++ b/src/SDL_log.c @@ -587,6 +587,25 @@ void SDL_LogMessageV(int category, SDL_LogPriority priority, SDL_PRINTF_FORMAT_S return; } +#if defined(SDL_PLATFORM_NGAGE) + extern void NGAGE_vnprintf(char *buf, size_t size, const char *fmt, va_list ap); + char buf[1024]; + NGAGE_vnprintf(buf, sizeof(buf), fmt, ap); + +#ifdef ENABLE_FILE_LOG + FILE* file; + file = fopen("E:/SDL_Log.txt", "a"); + if (file) + { + vfprintf(file, fmt, ap); + fprintf(file, "\n"); + (void)fclose(file); + } +#endif + + return; +#endif + // Render into stack buffer va_copy(aq, ap); len = SDL_vsnprintf(stack_buf, sizeof(stack_buf), fmt, aq); @@ -767,9 +786,14 @@ static void SDLCALL SDL_LogOutput(void *userdata, int category, SDL_LogPriority (void)fclose(pFile); } } +#elif defined(SDL_PLATFORM_NGAGE) + { + /* Nothing to do here. */ + } #endif #if defined(HAVE_STDIO_H) && \ !(defined(SDL_PLATFORM_APPLE) && (defined(SDL_VIDEO_DRIVER_COCOA) || defined(SDL_VIDEO_DRIVER_UIKIT))) && \ + !(defined(SDL_PLATFORM_NGAGE)) && \ !(defined(SDL_PLATFORM_WIN32)) (void)fprintf(stderr, "%s%s\n", GetLogPriorityPrefix(priority), message); #endif diff --git a/src/audio/SDL_audio.c b/src/audio/SDL_audio.c index 583a159110977..c2719cb469517 100644 --- a/src/audio/SDL_audio.c +++ b/src/audio/SDL_audio.c @@ -77,6 +77,9 @@ static const AudioBootStrap *const bootstrap[] = { #ifdef SDL_AUDIO_DRIVER_N3DS &N3DSAUDIO_bootstrap, #endif +#ifdef SDL_AUDIO_DRIVER_NGAGE + &NGAGEAUDIO_bootstrap, +#endif #ifdef SDL_AUDIO_DRIVER_EMSCRIPTEN &EMSCRIPTENAUDIO_bootstrap, #endif diff --git a/src/audio/SDL_sysaudio.h b/src/audio/SDL_sysaudio.h index 4a88bd2302410..6b8f1c9824f12 100644 --- a/src/audio/SDL_sysaudio.h +++ b/src/audio/SDL_sysaudio.h @@ -386,6 +386,7 @@ extern AudioBootStrap PS2AUDIO_bootstrap; extern AudioBootStrap PSPAUDIO_bootstrap; extern AudioBootStrap VITAAUD_bootstrap; extern AudioBootStrap N3DSAUDIO_bootstrap; +extern AudioBootStrap NGAGEAUDIO_bootstrap; extern AudioBootStrap EMSCRIPTENAUDIO_bootstrap; extern AudioBootStrap QSAAUDIO_bootstrap; diff --git a/src/audio/ngage/SDL_ngageaudio.c b/src/audio/ngage/SDL_ngageaudio.c new file mode 100644 index 0000000000000..8bb6165571908 --- /dev/null +++ b/src/audio/ngage/SDL_ngageaudio.c @@ -0,0 +1,107 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +#include "SDL_internal.h" + +#ifdef SDL_AUDIO_DRIVER_NGAGE + +#include "../SDL_sysaudio.h" +#include "SDL_ngageaudio.h" + +static SDL_AudioDevice *devptr = NULL; + +SDL_AudioDevice *NGAGE_GetAudioDeviceAddr() +{ + return devptr; +} + +static bool NGAGEAUDIO_OpenDevice(SDL_AudioDevice* device) +{ + SDL_PrivateAudioData *phdata = SDL_calloc(1, sizeof(SDL_PrivateAudioData)); + if (!phdata) + { + SDL_OutOfMemory(); + return false; + } + device->hidden = phdata; + + phdata->buffer = SDL_calloc(1, device->buffer_size); + if (!phdata->buffer) + { + SDL_OutOfMemory(); + SDL_free(phdata); + return false; + } + devptr = device; + + // Since the phone can change the sample rate during a phone call, + // we set the sample rate to 8KHz to be safe. Even though it + // might be possible to adjust the sample rate dynamically, it's + // not supported by the current implementation. + + device->spec.format = SDL_AUDIO_S16LE; + device->spec.channels = 1; + device->spec.freq = 8000; + + SDL_UpdatedAudioDeviceFormat(device); + + return true; +} + +static Uint8* NGAGEAUDIO_GetDeviceBuf(SDL_AudioDevice* device, int* buffer_size) +{ + SDL_PrivateAudioData *phdata = (SDL_PrivateAudioData *)device->hidden; + if (!phdata) + { + *buffer_size = 0; + return 0; + } + + *buffer_size = device->buffer_size; + return phdata->buffer; +} + +static void NGAGEAUDIO_CloseDevice(SDL_AudioDevice* device) +{ + if (device->hidden) + { + SDL_free(device->hidden->buffer); + SDL_free(device->hidden); + } + + return; +} + +static bool NGAGEAUDIO_Init(SDL_AudioDriverImpl *impl) +{ + impl->OpenDevice = NGAGEAUDIO_OpenDevice; + impl->GetDeviceBuf = NGAGEAUDIO_GetDeviceBuf; + impl->CloseDevice = NGAGEAUDIO_CloseDevice; + + impl->ProvidesOwnCallbackThread = true; + impl->OnlyHasDefaultPlaybackDevice = true; + + return true; +} + +AudioBootStrap NGAGEAUDIO_bootstrap = { "N-Gage", "N-Gage audio driver", NGAGEAUDIO_Init, false }; + +#endif // SDL_AUDIO_DRIVER_NGAGE diff --git a/src/audio/ngage/SDL_ngageaudio.cpp b/src/audio/ngage/SDL_ngageaudio.cpp new file mode 100644 index 0000000000000..f0a0385ff2b36 --- /dev/null +++ b/src/audio/ngage/SDL_ngageaudio.cpp @@ -0,0 +1,410 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + 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. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "SDL_internal.h" +#include "SDL_ngageaudio.h" +#include "../SDL_sysaudio.h" + +#ifdef __cplusplus +} +#endif + +#ifdef SDL_AUDIO_DRIVER_NGAGE + +#include "SDL_ngageaudio.hpp" + +CAudio::CAudio() : CActive(EPriorityStandard), iBufDes(NULL, 0) {} + +CAudio* CAudio::NewL(TInt aLatency) +{ + CAudio* self = new (ELeave) CAudio(); + CleanupStack::PushL(self); + self->ConstructL(aLatency); + CleanupStack::Pop(self); + return self; +} + +void CAudio::ConstructL(TInt aLatency) +{ + CActiveScheduler::Add(this); + User::LeaveIfError(iTimer.CreateLocal()); + iTimerCreated = ETrue; + + iStream = CMdaAudioOutputStream::NewL(*this); + if (!iStream) + { + SDL_Log("Error: Failed to create audio stream"); + User::Leave(KErrNoMemory); + } + + iLatency = aLatency; + iLatencySamples = aLatency * 8; // 8kHz. + + // Determine minimum and maximum number of samples to write with one + // WriteL request. + iMinWrite = iLatencySamples / 8; + iMaxWrite = iLatencySamples / 2; + + // Set defaults. + iState = EStateNone; + iTimerCreated = EFalse; + iTimerActive = EFalse; +} + +CAudio::~CAudio() +{ + if (iStream) + { + iStream->Stop(); + + while (iState != EStateDone) + { + User::After(100000); // 100ms. + } + + delete iStream; + } +} + +void CAudio::Start() +{ + if (iStream) + { + // Set to 8kHz mono audio. + iStreamSettings.iChannels = TMdaAudioDataSettings::EChannelsMono; + iStreamSettings.iSampleRate = TMdaAudioDataSettings::ESampleRate8000Hz; + iStream->Open(&iStreamSettings); + iState = EStateOpening; + } + else + { + SDL_Log("Error: Failed to open audio stream"); + } +} + +// Feeds more processed data to the audio stream. +void CAudio::Feed() +{ + // If a WriteL is already in progress, or we aren't even playing; + // do nothing! + if ((iState != EStateWriting) && (iState != EStatePlaying)) + { + return; + } + + // Figure out the number of samples that really have been played + // through the output. + TTimeIntervalMicroSeconds pos = iStream->Position(); + + TInt played = 8 * (pos.Int64() / TInt64(1000)).GetTInt(); // 8kHz. + + played += iBaseSamplesPlayed; + + // Determine the difference between the number of samples written to + // CMdaAudioOutputStream and the number of samples it has played. + // The difference is the amount of data in the buffers. + if ( played < 0 ) + { + played = 0; + } + + TInt buffered = iSamplesWritten - played; + if (buffered < 0) + { + buffered = 0; + } + + if (iState == EStateWriting) + { + return; + } + + // The trick for low latency: Do not let the buffers fill up beyond the + // latency desired! We write as many samples as the difference between + // the latency target (in samples) and the amount of data buffered. + TInt samplesToWrite = iLatencySamples - buffered; + + // Do not write very small blocks. This should improve efficiency, since + // writes to the streaming API are likely to be expensive. + if (samplesToWrite < iMinWrite) + { + // Not enough data to write, set up a timer to fire after a while. + // Try againwhen it expired. + if (iTimerActive) + { + return; + } + iTimerActive = ETrue; + SetActive(); + iTimer.After(iStatus, (1000 * iLatency) / 8); + return; + } + + // Do not write more than the set number of samples at once. + int numSamples = samplesToWrite; + if (numSamples > iMaxWrite) + { + numSamples = iMaxWrite; + } + + SDL_AudioDevice *device = NGAGE_GetAudioDeviceAddr(); + if (device) + { + SDL_PrivateAudioData *phdata = (SDL_PrivateAudioData *)device->hidden; + + iBufDes.Set(phdata->buffer, 2 * numSamples, 2 * numSamples); + iStream->WriteL(iBufDes); + iState = EStateWriting; + + // Keep track of the number of samples written (for latency calculations). + iSamplesWritten += numSamples; + } + else + { + // Output device not ready yet. Let's go for another round. + if (iTimerActive) + { + return; + } + iTimerActive = ETrue; + SetActive(); + iTimer.After(iStatus, (1000 * iLatency) / 8); + } +} + +void CAudio::RunL() +{ + iTimerActive = EFalse; + Feed(); +} + +void CAudio::DoCancel() +{ + iTimerActive = EFalse; + iTimer.Cancel(); +} + +void CAudio::StartThread() +{ + TInt heapMinSize = 8192; // 8 KB initial heap size. + TInt heapMaxSize = 1024 * 1024; // 1 MB maximum heap size. + + TInt err = iProcess.Create(_L("ProcessThread"), ProcessThreadCB, KDefaultStackSize * 2, heapMinSize, heapMaxSize, this); + if (err == KErrNone) + { + iProcess.SetPriority(EPriorityLess); + iProcess.Resume(); + } + else + { + SDL_Log("Error: Failed to create audio processing thread: %d", err); + } +} + +void CAudio::StopThread() +{ + if (iStreamStarted) + { + iProcess.Kill(KErrNone); + iProcess.Close(); + iStreamStarted = EFalse; + } +} + +TInt CAudio::ProcessThreadCB(TAny* aPtr) +{ + CAudio* self = static_cast(aPtr); + SDL_AudioDevice* device = NGAGE_GetAudioDeviceAddr(); + + while (self->iStreamStarted) + { + if (device) + { + SDL_PlaybackAudioThreadIterate(device); + } + else + { + device = NGAGE_GetAudioDeviceAddr(); + } + User::After(100000); // 100ms. + } + return KErrNone; +} + +void CAudio::MaoscOpenComplete(TInt aError) +{ + if (aError == KErrNone) + { + iStream->SetVolume(1); + iStreamStarted = ETrue; + StartThread(); + + } + else + { + SDL_Log("Error: Failed to open audio stream: %d", aError); + } +} + +void CAudio::MaoscBufferCopied(TInt aError, const TDesC8& /*aBuffer*/) +{ + if (aError == KErrNone) + { + iState = EStatePlaying; + Feed(); + } + else if (aError == KErrAbort) + { + // The stream has been stopped. + iState = EStateDone; + } + else + { + SDL_Log("Error: Failed to copy audio buffer: %d", aError); + } +} + +void CAudio::MaoscPlayComplete(TInt aError) +{ + // If we finish due to an underflow, we'll need to restart playback. + // Normally KErrUnderlow is raised at stream end, but in our case the API + // should never see the stream end -- we are continuously feeding it more + // data! Many underflow errors mean that the latency target is too low. + if (aError == KErrUnderflow) + { + // The number of samples played gets resetted to zero when we restart + // playback after underflow. + iBaseSamplesPlayed = iSamplesWritten; + + iStream->Stop(); + Cancel(); + + iStream->SetAudioPropertiesL(TMdaAudioDataSettings::ESampleRate8000Hz, TMdaAudioDataSettings::EChannelsMono); + + iState = EStatePlaying; + Feed(); + return; + + } + else if (aError != KErrNone) + { + // Handle error. + } + + // We shouldn't get here. + SDL_Log("%s: %d", __FUNCTION__, aError); +} + +static TBool gAudioRunning; + +TBool AudioIsReady() +{ + return gAudioRunning; +} + +TInt AudioThreadCB(TAny* aParams) +{ + CTrapCleanup* cleanup = CTrapCleanup::New(); + if (!cleanup) + { + return KErrNoMemory; + } + + CActiveScheduler* scheduler = new CActiveScheduler(); + if (!scheduler) + { + delete cleanup; + return KErrNoMemory; + } + + CActiveScheduler::Install(scheduler); + + TRAPD(err, + { + TInt latency = *(TInt*)aParams; + CAudio* audio = CAudio::NewL(latency); + CleanupStack::PushL(audio); + + gAudioRunning = ETrue; + audio->Start(); + TBool once = EFalse; + + while (gAudioRunning) + { + // Allow active scheduler to process any events. + TInt error; + CActiveScheduler::RunIfReady(error, CActive::EPriorityIdle); + + if (!once) + { + SDL_AudioDevice* device = NGAGE_GetAudioDeviceAddr(); + if (device) + { + // Stream ready; start feeding audio data. + // After feeding it once, the callbacks will take over. + audio->iState = CAudio::EStatePlaying; + audio->Feed(); + once = ETrue; + } + } + + User::After(100000); // 100ms. + } + + CleanupStack::PopAndDestroy(audio); + }); + + delete scheduler; + delete cleanup; + return err; +} + +RThread audioThread; + +void InitAudio(TInt* aLatency) +{ + _LIT(KAudioThreadName, "AudioThread"); + + TInt err = audioThread.Create(KAudioThreadName, AudioThreadCB, KDefaultStackSize, 0, aLatency); + if (err != KErrNone) + { + User::Leave(err); + } + + audioThread.Resume(); +} + +void DeinitAudio() +{ + gAudioRunning = EFalse; + + TRequestStatus status; + audioThread.Logon(status); + User::WaitForRequest(status); + + audioThread.Close(); +} + +#endif // SDL_AUDIO_DRIVER_NGAGE diff --git a/src/audio/ngage/SDL_ngageaudio.h b/src/audio/ngage/SDL_ngageaudio.h new file mode 100644 index 0000000000000..647609280bd1d --- /dev/null +++ b/src/audio/ngage/SDL_ngageaudio.h @@ -0,0 +1,44 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + 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_ngageaudio_h +#define SDL_ngageaudio_h + +typedef struct SDL_PrivateAudioData +{ + Uint8 *buffer; + +} SDL_PrivateAudioData; + +#ifdef __cplusplus +extern "C" { +#endif + +#include "../SDL_sysaudio.h" + + SDL_AudioDevice* NGAGE_GetAudioDeviceAddr(); + +#ifdef __cplusplus +} +#endif + +#endif // SDL_ngageaudio_h diff --git a/src/audio/ngage/SDL_ngageaudio.hpp b/src/audio/ngage/SDL_ngageaudio.hpp new file mode 100644 index 0000000000000..11ebea074f0f7 --- /dev/null +++ b/src/audio/ngage/SDL_ngageaudio.hpp @@ -0,0 +1,98 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + 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_ngageaudio_hpp +#define SDL_ngageaudio_hpp + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#include "SDL_ngageaudio.h" +#include "../SDL_sysaudio.h" + +#ifdef __cplusplus +} +#endif + +TBool AudioIsReady(); +void InitAudio(TInt* aLatency); +void DeinitAudio(); + +class CAudio : public CActive, public MMdaAudioOutputStreamCallback +{ +public: + static CAudio* NewL(TInt aLatency); + ~CAudio(); + + void ConstructL(TInt aLatency); + void Start(); + void Feed(); + + void RunL(); + void DoCancel(); + + static TInt ProcessThreadCB(TAny* /*aPtr*/); + + // From MMdaAudioOutputStreamCallback + void MaoscOpenComplete(TInt aError); + void MaoscBufferCopied(TInt aError, const TDesC8& aBuffer); + void MaoscPlayComplete(TInt aError); + + enum { + EStateNone = 0, + EStateOpening, + EStatePlaying, + EStateWriting, + EStateDone + } iState; + +private: + CAudio(); + void StartThread(); + void StopThread(); + + CMdaAudioOutputStream* iStream; + TMdaAudioDataSettings iStreamSettings; + TBool iStreamStarted; + + TPtr8 iBufDes; // Descriptor for the buffer. + TInt iLatency; // Latency target in ms + TInt iLatencySamples; // Latency target in samples. + TInt iMinWrite; // Min number of samples to write per turn. + TInt iMaxWrite; // Max number of samples to write per turn. + TInt iBaseSamplesPlayed; // amples played before last restart. + TInt iSamplesWritten; // Number of samples written so far. + + RTimer iTimer; + TBool iTimerCreated; + TBool iTimerActive; + + RThread iProcess; +}; + + +#endif // SDL_ngageaudio_hpp \ No newline at end of file diff --git a/src/core/ngage/SDL_ngage.c b/src/core/ngage/SDL_ngage.c new file mode 100644 index 0000000000000..f9d31befc570a --- /dev/null +++ b/src/core/ngage/SDL_ngage.c @@ -0,0 +1,34 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + 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" + +const void* nullptr = ((void*)0); + +void va_copy(char *dest, char *src) +{ + dest = src; +} + +//int vsnprintf(char *str, size_t size, const char *format, va_list ap) +//{ +// // Safely do nothing. +// return 0; +//} diff --git a/src/core/ngage/SDL_ngage.cpp b/src/core/ngage/SDL_ngage.cpp new file mode 100644 index 0000000000000..4e541b4b1ce0e --- /dev/null +++ b/src/core/ngage/SDL_ngage.cpp @@ -0,0 +1,77 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + 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 +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +bool NGAGE_IsClassicModel() +{ + int phone_id; + HAL::Get(HALData::EMachineUid, phone_id); + + return (0x101f8c19 == phone_id); +} + +void NGAGE_printf(const char *fmt, ...) +{ + char buffer[512] = { 0 }; + + va_list ap; + va_start(ap, fmt); + vsprintf(buffer, fmt, ap); + va_end(ap); + + TBuf<512> buf; + buf.Copy(TPtrC8((TText8*)buffer)); + + RDebug::Print(_L("%S"), &buf); +} + +void NGAGE_vnprintf(char *buf, size_t size, const char *fmt, va_list ap) +{ + char buffer[512] = { 0 }; + + vsprintf(buffer, fmt, ap); + + TBuf<512> tbuf; + tbuf.Copy(TPtrC8((TText8*)buffer)); + + RDebug::Print(_L("%S"), &tbuf); + + strncpy(buf, buffer, size - 1); + buf[size - 1] = '\0'; +} + +TInt NGAGE_GetFreeHeapMemory() +{ + TInt free = 0; + return User::Available(free); +} + +#ifdef __cplusplus +} +#endif diff --git a/src/core/ngage/SDL_ngage.h b/src/core/ngage/SDL_ngage.h new file mode 100644 index 0000000000000..56ad555616a0a --- /dev/null +++ b/src/core/ngage/SDL_ngage.h @@ -0,0 +1,36 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + 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_ngage_h +#define SDL_ngage_h + +#ifdef __cplusplus +extern "C" { +#endif + + bool NGAGE_IsClassicModel(); + +#ifdef __cplusplus +} +#endif + +#endif /* SDL_ngage_h */ diff --git a/src/cpuinfo/SDL_cpuinfo.c b/src/cpuinfo/SDL_cpuinfo.c index bf425a38c7629..217417b07e998 100644 --- a/src/cpuinfo/SDL_cpuinfo.c +++ b/src/cpuinfo/SDL_cpuinfo.c @@ -413,6 +413,12 @@ static int CPU_haveARMSIMD(void) return regs.r[0]; } +#elif defined(SDL_PLATFORM_NGAGE) +static int CPU_haveARMSIMD(void) +{ + // The RM920T is based on the ARMv4T architecture and doesn't have SIMD. + return 0; +} #else static int CPU_haveARMSIMD(void) { @@ -460,6 +466,8 @@ static int CPU_haveNEON(void) return 1; #elif defined(SDL_PLATFORM_3DS) return 0; +#elif defined(SDL_PLATFORM_NGAGE) + return 0; // The ARM920T is based on the ARMv4T architecture and doesn't have NEON. #elif defined(SDL_PLATFORM_APPLE) && defined(__ARM_ARCH) && (__ARM_ARCH >= 7) // (note that sysctlbyname("hw.optional.neon") doesn't work!) return 1; // all Apple ARMv7 chips and later have NEON. diff --git a/src/dynapi/SDL_dynapi.h b/src/dynapi/SDL_dynapi.h index 99ef9a9e93f0f..e975be08e1ad5 100644 --- a/src/dynapi/SDL_dynapi.h +++ b/src/dynapi/SDL_dynapi.h @@ -63,6 +63,8 @@ #define SDL_DYNAMIC_API 0 // vitasdk doesn't support dynamic linking #elif defined(SDL_PLATFORM_3DS) #define SDL_DYNAMIC_API 0 // devkitARM doesn't support dynamic linking +#elif defined(SDL_PLATFORM_NGAGE) +#define SDL_DYNAMIC_API 0 #elif defined(DYNAPI_NEEDS_DLOPEN) && !defined(HAVE_DLOPEN) #define SDL_DYNAMIC_API 0 // we need dlopen(), but don't have it.... #endif diff --git a/src/filesystem/ngage/SDL_sysfilesystem.c b/src/filesystem/ngage/SDL_sysfilesystem.c new file mode 100644 index 0000000000000..eb2429dd937a4 --- /dev/null +++ b/src/filesystem/ngage/SDL_sysfilesystem.c @@ -0,0 +1,67 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2024 Sam Lantinga + + 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 void NGAGE_GetAppPath(char* path); + +char *SDL_SYS_GetBasePath(void) +{ + char app_path[512]; + NGAGE_GetAppPath(app_path); + char *base_path = SDL_strdup(app_path); + return base_path; +} + +char *SDL_SYS_GetPrefPath(const char *org, const char *app) +{ + char *pref_path; + if (SDL_asprintf(&pref_path, "C:/System/Apps/%s/%s/", org, app) < 0) + return NULL; + else + return pref_path; +} + +char *SDL_SYS_GetUserFolder(SDL_Folder folder) +{ + const char *folder_path = NULL; + switch (folder) + { + case SDL_FOLDER_HOME: + folder_path = "C:/"; + break; + case SDL_FOLDER_PICTURES: + folder_path = "C:/Nokia/Pictures/"; + break; + case SDL_FOLDER_SAVEDGAMES: + folder_path = "C:/"; + break; + case SDL_FOLDER_SCREENSHOTS: + folder_path = "C:/Nokia/Pictures/"; + break; + case SDL_FOLDER_VIDEOS: + folder_path = "C:/Nokia/Videos/"; + break; + default: + folder_path = "C:/Nokia/Others/"; + break; + } + return SDL_strdup(folder_path); +} diff --git a/src/filesystem/ngage/SDL_sysfilesystem.cpp b/src/filesystem/ngage/SDL_sysfilesystem.cpp new file mode 100644 index 0000000000000..311c22f7a9bb6 --- /dev/null +++ b/src/filesystem/ngage/SDL_sysfilesystem.cpp @@ -0,0 +1,68 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2024 Sam Lantinga + + 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. +*/ +#ifdef __cplusplus +extern "C" { +#endif + +#include "SDL_internal.h" + +#ifdef __cplusplus +} +#endif + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +void NGAGE_GetAppPath(char* path) +{ + TBuf<512> aPath; + + TFileName fullExePath = RProcess().FileName(); + + TParsePtrC parser(fullExePath); + aPath.Copy(parser.DriveAndPath()); + + TBuf8<512> utf8Path; // Temporary buffer for UTF-8 data. + CnvUtfConverter::ConvertFromUnicodeToUtf8(utf8Path, aPath); + + // Copy UTF-8 data to the provided char* buffer. + strncpy(path, (const char*)utf8Path.Ptr(), utf8Path.Length()); + path[utf8Path.Length()] = '\0'; + + // Replace backslashes with forward slashes. + for (int i = 0; i < utf8Path.Length(); i++) + { + if (path[i] == '\\') + { + path[i] = '/'; + } + } +} + +#ifdef __cplusplus +} +#endif diff --git a/src/locale/ngage/SDL_syslocale.cpp b/src/locale/ngage/SDL_syslocale.cpp new file mode 100644 index 0000000000000..b1ace6fd0d485 --- /dev/null +++ b/src/locale/ngage/SDL_syslocale.cpp @@ -0,0 +1,299 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + 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_syslocale.h" + +#include +#include +#include +#include + +bool SDL_SYS_GetPreferredLocales(char *buf, size_t buflen) +{ + TLanguage language = User::Language(); + const char* locale; + + switch (language) { + case ELangFrench: + case ELangSwissFrench: + case ELangBelgianFrench: + case ELangInternationalFrench: + locale = "fr_FR"; + break; + case ELangGerman: + case ELangSwissGerman: + case ELangAustrian: + locale = "de_DE"; + break; + case ELangSpanish: + case ELangInternationalSpanish: + case ELangLatinAmericanSpanish: + locale = "es_ES"; + break; + case ELangItalian: + case ELangSwissItalian: + locale = "it_IT"; + break; + case ELangSwedish: + case ELangFinlandSwedish: + locale = "sv_SE"; + break; + case ELangDanish: + locale = "da_DK"; + break; + case ELangNorwegian: + case ELangNorwegianNynorsk: + locale = "no_NO"; + break; + case ELangFinnish: + locale = "fi_FI"; + break; + case ELangPortuguese: + case ELangBrazilianPortuguese: + locale = "pt_PT"; + break; + case ELangTurkish: + case ELangCyprusTurkish: + locale = "tr_TR"; + break; + case ELangIcelandic: + locale = "is_IS"; + break; + case ELangRussian: + locale = "ru_RU"; + break; + case ELangHungarian: + locale = "hu_HU"; + break; + case ELangDutch: + case ELangBelgianFlemish: + locale = "nl_NL"; + break; + case ELangAustralian: + case ELangNewZealand: + locale = "en_AU"; + break; + case ELangCzech: + locale = "cs_CZ"; + break; + case ELangSlovak: + locale = "sk_SK"; + break; + case ELangPolish: + locale = "pl_PL"; + break; + case ELangSlovenian: + locale = "sl_SI"; + break; + case ELangTaiwanChinese: + locale = "zh_TW"; + break; + case ELangHongKongChinese: + locale = "zh_HK"; + break; + case ELangPrcChinese: + locale = "zh_CN"; + break; + case ELangJapanese: + locale = "ja_JP"; + break; + case ELangThai: + locale = "th_TH"; + break; + case ELangAfrikaans: + locale = "af_ZA"; + break; + case ELangAlbanian: + locale = "sq_AL"; + break; + case ELangAmharic: + locale = "am_ET"; + break; + case ELangArabic: + locale = "ar_SA"; + break; + case ELangArmenian: + locale = "hy_AM"; + break; + case ELangAzerbaijani: + locale = "az_AZ"; + break; + case ELangBelarussian: + locale = "be_BY"; + break; + case ELangBengali: + locale = "bn_IN"; + break; + case ELangBulgarian: + locale = "bg_BG"; + break; + case ELangBurmese: + locale = "my_MM"; + break; + case ELangCatalan: + locale = "ca_ES"; + break; + case ELangCroatian: + locale = "hr_HR"; + break; + case ELangEstonian: + locale = "et_EE"; + break; + case ELangFarsi: + locale = "fa_IR"; + break; + case ELangCanadianFrench: + locale = "fr_CA"; + break; + case ELangScotsGaelic: + locale = "gd_GB"; + break; + case ELangGeorgian: + locale = "ka_GE"; + break; + case ELangGreek: + case ELangCyprusGreek: + locale = "el_GR"; + break; + case ELangGujarati: + locale = "gu_IN"; + break; + case ELangHebrew: + locale = "he_IL"; + break; + case ELangHindi: + locale = "hi_IN"; + break; + case ELangIndonesian: + locale = "id_ID"; + break; + case ELangIrish: + locale = "ga_IE"; + break; + case ELangKannada: + locale = "kn_IN"; + break; + case ELangKazakh: + locale = "kk_KZ"; + break; + case ELangKhmer: + locale = "km_KH"; + break; + case ELangKorean: + locale = "ko_KR"; + break; + case ELangLao: + locale = "lo_LA"; + break; + case ELangLatvian: + locale = "lv_LV"; + break; + case ELangLithuanian: + locale = "lt_LT"; + break; + case ELangMacedonian: + locale = "mk_MK"; + break; + case ELangMalay: + locale = "ms_MY"; + break; + case ELangMalayalam: + locale = "ml_IN"; + break; + case ELangMarathi: + locale = "mr_IN"; + break; + case ELangMoldavian: + locale = "ro_MD"; + break; + case ELangMongolian: + locale = "mn_MN"; + break; + case ELangPunjabi: + locale = "pa_IN"; + break; + case ELangRomanian: + locale = "ro_RO"; + break; + case ELangSerbian: + locale = "sr_RS"; + break; + case ELangSinhalese: + locale = "si_LK"; + break; + case ELangSomali: + locale = "so_SO"; + break; + case ELangSwahili: + locale = "sw_KE"; + break; + case ELangTajik: + locale = "tg_TJ"; + break; + case ELangTamil: + locale = "ta_IN"; + break; + case ELangTelugu: + locale = "te_IN"; + break; + case ELangTibetan: + locale = "bo_CN"; + break; + case ELangTigrinya: + locale = "ti_ET"; + break; + case ELangTurkmen: + locale = "tk_TM"; + break; + case ELangUkrainian: + locale = "uk_UA"; + break; + case ELangUrdu: + locale = "ur_PK"; + break; + case ELangUzbek: + locale = "uz_UZ"; + break; + case ELangVietnamese: + locale = "vi_VN"; + break; + case ELangWelsh: + locale = "cy_GB"; + break; + case ELangZulu: + locale = "zu_ZA"; + break; + case ELangEnglish: + locale = "en_GB"; + break; + case ELangAmerican: + case ELangCanadianEnglish: + case ELangInternationalEnglish: + case ELangSouthAfricanEnglish: + default: + locale = "en_US"; + break; + } + + SDL_strlcpy(buf, locale, buflen); + + return true; +} diff --git a/src/main/ngage/SDL_sysmain_callbacks.c b/src/main/ngage/SDL_sysmain_callbacks.c new file mode 100644 index 0000000000000..1e3fca5cdea09 --- /dev/null +++ b/src/main/ngage/SDL_sysmain_callbacks.c @@ -0,0 +1,31 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "SDL_internal.h" + +#ifdef SDL_PLATFORM_NGAGE + +int SDL_EnterAppMainCallbacks(int argc, char* argv[], SDL_AppInit_func appinit, SDL_AppIterate_func appiter, SDL_AppEvent_func appevent, SDL_AppQuit_func appquit) +{ + // Intentionally does nothing; Callbacks are called using the RunL() method. + return 0; +} + +#endif // SDL_PLATFORM_NGAGE diff --git a/src/main/ngage/SDL_sysmain_main.cpp b/src/main/ngage/SDL_sysmain_main.cpp new file mode 100644 index 0000000000000..817f2ccea0331 --- /dev/null +++ b/src/main/ngage/SDL_sysmain_main.cpp @@ -0,0 +1,194 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + 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. +*/ +#ifdef __cplusplus +extern "C" { +#endif + +#include "SDL_internal.h" + +#ifdef __cplusplus +} +#endif + +#include +#include +#include +#include + +#include "SDL_sysmain_main.hpp" +#include "../../audio/ngage/SDL_ngageaudio.hpp" +#include "../../render/ngage/SDL_render_ngage_c.hpp" + +CRenderer *gRenderer = 0; + +GLDEF_C TInt E32Main() +{ + // Get args and environment. + int argc = 1; + char* argv[] = { "game", NULL }; + char** envp = NULL; + + // Create lvalue variables for __crt0 arguments. + char** argv_lvalue = argv; + char** envp_lvalue = envp; + + CTrapCleanup* cleanup = CTrapCleanup::New(); + if (!cleanup) + { + return KErrNoMemory; + } + + TRAPD(err, + { + CActiveScheduler* scheduler = new (ELeave) CActiveScheduler(); + CleanupStack::PushL(scheduler); + CActiveScheduler::Install(scheduler); + + TInt posixErr = SpawnPosixServerThread(); + if (posixErr != KErrNone) + { + SDL_Log("Error: Failed to spawn POSIX server thread: %d", posixErr); + User::Leave(posixErr); + } + + __crt0(argc, argv_lvalue, envp_lvalue); + + // Increase heap size. + RHeap* newHeap = User::ChunkHeap(NULL, 7500000, 7500000, KMinHeapGrowBy); + if (!newHeap) + { + SDL_Log("Error: Failed to create new heap"); + User::Leave(KErrNoMemory); + } + CleanupStack::PushL(newHeap); + + RHeap* oldHeap = User::SwitchHeap(newHeap); + + TInt targetLatency = 225; + InitAudio(&targetLatency); + + // Wait until audio is ready. + while (!AudioIsReady()) + { + User::After(100000); // 100ms. + } + + // Create and start the rendering backend. + gRenderer = CRenderer::NewL(); + CleanupStack::PushL(gRenderer); + + // Create and start the SDL main runner. + CSDLmain* mainApp = CSDLmain::NewL(); + CleanupStack::PushL(mainApp); + mainApp->Start(); + + // Start the active scheduler to handle events. + CActiveScheduler::Start(); + + CleanupStack::PopAndDestroy(gRenderer); + CleanupStack::PopAndDestroy(mainApp); + + User::SwitchHeap(oldHeap); + + CleanupStack::PopAndDestroy(newHeap); + CleanupStack::PopAndDestroy(scheduler); + }); + + if (err != KErrNone) + { + SDL_Log("Error: %d", err); + } + + return err; +} + +CSDLmain* CSDLmain::NewL() +{ + CSDLmain* self = new (ELeave) CSDLmain(); + CleanupStack::PushL(self); + self->ConstructL(); + CleanupStack::Pop(self); + return self; +} + +CSDLmain::CSDLmain() : CActive(EPriorityLow) {} + +void CSDLmain::ConstructL() +{ + CActiveScheduler::Add(this); +} + +CSDLmain::~CSDLmain() +{ + Cancel(); +} + +void CSDLmain::Start() +{ + SetActive(); + TRequestStatus* status = &iStatus; + User::RequestComplete(status, KErrNone); +} + +void CSDLmain::DoCancel() {} + +static bool callbacks_initialized = false; + +void CSDLmain::RunL() +{ + if (callbacks_initialized) + { + SDL_Event event; + + iResult = SDL_AppIterate(NULL); + if (iResult != SDL_APP_CONTINUE) + { + DeinitAudio(); + SDL_AppQuit(NULL, iResult); + SDL_Quit(); + CActiveScheduler::Stop(); + return; + } + + SDL_PumpEvents(); + if (SDL_PollEvent(&event)) + { + iResult = SDL_AppEvent(NULL, &event); + if (iResult != SDL_APP_CONTINUE) + { + DeinitAudio(); + SDL_AppQuit(NULL, iResult); + SDL_Quit(); + CActiveScheduler::Stop(); + return; + } + } + + Start(); + } + else + { + SDL_SetMainReady(); + SDL_AppInit(NULL, 0, NULL); + callbacks_initialized = true; + Start(); + } +} diff --git a/src/main/ngage/SDL_sysmain_main.hpp b/src/main/ngage/SDL_sysmain_main.hpp new file mode 100644 index 0000000000000..e78df799665b6 --- /dev/null +++ b/src/main/ngage/SDL_sysmain_main.hpp @@ -0,0 +1,41 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + 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 + +class CSDLmain : public CActive +{ +public: + static CSDLmain* NewL(); + ~CSDLmain(); + + void Start(); + +protected: + void DoCancel() ; + void RunL(); + +private: + CSDLmain(); + void ConstructL(); + SDL_AppResult iResult; +}; diff --git a/src/render/SDL_render.c b/src/render/SDL_render.c index 869fbabe11271..2004be217d83e 100644 --- a/src/render/SDL_render.c +++ b/src/render/SDL_render.c @@ -120,6 +120,9 @@ static const SDL_RenderDriver *render_drivers[] = { #ifdef SDL_VIDEO_RENDER_METAL &METAL_RenderDriver, #endif +#ifdef SDL_VIDEO_RENDER_NGAGE + &NGAGE_RenderDriver, +#endif #ifdef SDL_VIDEO_RENDER_OGL &GL_RenderDriver, #endif @@ -3204,6 +3207,7 @@ bool SDL_SetRenderDrawColorFloat(SDL_Renderer *renderer, float r, float g, float renderer->color.g = g; renderer->color.b = b; renderer->color.a = a; + return true; } diff --git a/src/render/SDL_sysrender.h b/src/render/SDL_sysrender.h index 4d87ad6a06790..816a19044a824 100644 --- a/src/render/SDL_sysrender.h +++ b/src/render/SDL_sysrender.h @@ -330,6 +330,7 @@ extern SDL_RenderDriver D3D12_RenderDriver; extern SDL_RenderDriver GL_RenderDriver; extern SDL_RenderDriver GLES2_RenderDriver; extern SDL_RenderDriver METAL_RenderDriver; +extern SDL_RenderDriver NGAGE_RenderDriver; extern SDL_RenderDriver VULKAN_RenderDriver; extern SDL_RenderDriver PS2_RenderDriver; extern SDL_RenderDriver PSP_RenderDriver; diff --git a/src/render/ngage/SDL_render_ngage.c b/src/render/ngage/SDL_render_ngage.c new file mode 100644 index 0000000000000..6416605f33a7a --- /dev/null +++ b/src/render/ngage/SDL_render_ngage.c @@ -0,0 +1,581 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "SDL_internal.h" + +#ifdef SDL_VIDEO_RENDER_NGAGE + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +#ifndef Int2Fix +#define Int2Fix(i) ((i)<<16) +#endif + +#ifndef Fix2Int +#define Fix2Int(i) ((((unsigned int)(i)>0xFFFF0000)?0:((i)>>16))) +#endif + +#ifndef Fix2Real +#define Fix2Real(i) ((i)/65536.0) +#endif + +#ifndef Real2Fix +#define Real2Fix(i) ((int)((i)*65536.0)) +#endif + +#include "SDL_render_ngage_c.h" +#include "../SDL_sysrender.h" + +static void NGAGE_WindowEvent(SDL_Renderer* renderer, const SDL_WindowEvent* event); +static bool NGAGE_GetOutputSize(SDL_Renderer* renderer, int* w, int* h); +static bool NGAGE_SupportsBlendMode(SDL_Renderer* renderer, SDL_BlendMode blendMode); +static bool NGAGE_CreateTexture(SDL_Renderer* renderer, SDL_Texture* texture, SDL_PropertiesID create_props); +static bool NGAGE_QueueSetViewport(SDL_Renderer* renderer, SDL_RenderCommand* cmd); +static bool NGAGE_QueueSetDrawColor(SDL_Renderer* renderer, SDL_RenderCommand* cmd); +static bool NGAGE_QueueDrawVertices(SDL_Renderer* renderer, SDL_RenderCommand* cmd, const SDL_FPoint* points, int count); +static bool NGAGE_QueueFillRects(SDL_Renderer *renderer, SDL_RenderCommand *cmd, const SDL_FRect *rects, int count); +static bool NGAGE_QueueCopy(SDL_Renderer* renderer, SDL_RenderCommand* cmd, SDL_Texture* texture, const SDL_FRect* srcrect, const SDL_FRect* dstrect); +static bool NGAGE_QueueCopyEx(SDL_Renderer* renderer, SDL_RenderCommand* cmd, SDL_Texture* texture, const SDL_FRect* srcquad, const SDL_FRect* dstrect, const double angle, const SDL_FPoint* center, const SDL_FlipMode flip, float scale_x, float scale_y); +static bool NGAGE_QueueGeometry(SDL_Renderer* renderer, SDL_RenderCommand* cmd, SDL_Texture* texture, const float* xy, int xy_stride, const SDL_FColor* color, int color_stride, const float* uv, int uv_stride, int num_vertices, const void* indices, int num_indices, int size_indices, float scale_x, float scale_y); + +static void NGAGE_InvalidateCachedState(SDL_Renderer* renderer); +static bool NGAGE_RunCommandQueue(SDL_Renderer* renderer, SDL_RenderCommand* cmd, void* vertices, size_t vertsize); +static bool NGAGE_UpdateTexture(SDL_Renderer* renderer, SDL_Texture* texture, const SDL_Rect* rect, const void* pixels, int pitch); + +static bool NGAGE_LockTexture(SDL_Renderer* renderer, SDL_Texture* texture, const SDL_Rect* rect, void** pixels, int* pitch); +static void NGAGE_UnlockTexture(SDL_Renderer* renderer, SDL_Texture* texture); +static void NGAGE_SetTextureScaleMode(SDL_Renderer* renderer, SDL_Texture* texture, SDL_ScaleMode scaleMode); +static bool NGAGE_SetRenderTarget(SDL_Renderer* renderer, SDL_Texture* texture); +static SDL_Surface* NGAGE_RenderReadPixels(SDL_Renderer* renderer, const SDL_Rect* rect); +static bool NGAGE_RenderPresent(SDL_Renderer* renderer); +static void NGAGE_DestroyTexture(SDL_Renderer* renderer, SDL_Texture* texture); + +static void NGAGE_DestroyRenderer(SDL_Renderer* renderer); + +static bool NGAGE_SetVSync(SDL_Renderer* renderer, int vsync); + +static bool NGAGE_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL_PropertiesID create_props) +{ + SDL_SetupRendererColorspace(renderer, create_props); + + if (renderer->output_colorspace != SDL_COLORSPACE_RGB_DEFAULT) + { + return SDL_SetError("Unsupported output colorspace"); + } + + NGAGE_RendererData *phdata = SDL_calloc(1, sizeof(NGAGE_RendererData)); + if (!phdata) + { + SDL_OutOfMemory(); + return false; + } + + renderer->WindowEvent = NGAGE_WindowEvent; + renderer->GetOutputSize = NGAGE_GetOutputSize; + renderer->SupportsBlendMode = NGAGE_SupportsBlendMode; + renderer->CreateTexture = NGAGE_CreateTexture; + renderer->QueueSetViewport = NGAGE_QueueSetViewport; + renderer->QueueSetDrawColor = NGAGE_QueueSetDrawColor; + renderer->QueueDrawPoints = NGAGE_QueueDrawVertices; + renderer->QueueDrawLines = NGAGE_QueueDrawVertices; + renderer->QueueFillRects = NGAGE_QueueFillRects; + renderer->QueueCopy = NGAGE_QueueCopy; + renderer->QueueCopyEx = NGAGE_QueueCopyEx; + renderer->QueueGeometry = NGAGE_QueueGeometry; + + renderer->InvalidateCachedState = NGAGE_InvalidateCachedState; + renderer->RunCommandQueue = NGAGE_RunCommandQueue; + renderer->UpdateTexture = NGAGE_UpdateTexture; + renderer->LockTexture = NGAGE_LockTexture; + renderer->UnlockTexture = NGAGE_UnlockTexture; + renderer->SetTextureScaleMode = NGAGE_SetTextureScaleMode; + renderer->SetRenderTarget = NGAGE_SetRenderTarget; + renderer->RenderReadPixels = NGAGE_RenderReadPixels; + renderer->RenderPresent = NGAGE_RenderPresent; + renderer->DestroyTexture = NGAGE_DestroyTexture; + + renderer->DestroyRenderer = NGAGE_DestroyRenderer; + + renderer->SetVSync = NGAGE_SetVSync; + + renderer->name = NGAGE_RenderDriver.name; + renderer->window = window; + renderer->internal = phdata; + + SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_ARGB4444); + SDL_SetNumberProperty(SDL_GetRendererProperties(renderer), SDL_PROP_RENDERER_MAX_TEXTURE_SIZE_NUMBER, 256); + SDL_SetHintWithPriority(SDL_HINT_RENDER_LINE_METHOD, "2", SDL_HINT_OVERRIDE); + + return true; +} + +SDL_RenderDriver NGAGE_RenderDriver = +{ + NGAGE_CreateRenderer, + "N-Gage" +}; + +static void NGAGE_WindowEvent(SDL_Renderer* renderer, const SDL_WindowEvent* event) +{ + return; +} + +static bool NGAGE_GetOutputSize(SDL_Renderer* renderer, int* w, int* h) +{ + return true; +} + +static bool NGAGE_SupportsBlendMode(SDL_Renderer* renderer, SDL_BlendMode blendMode) +{ + switch (blendMode) + { + case SDL_BLENDMODE_NONE: + case SDL_BLENDMODE_MOD: + return true; + default: + return false; + } +} + +static bool NGAGE_CreateTexture(SDL_Renderer* renderer, SDL_Texture* texture, SDL_PropertiesID create_props) +{ + NGAGE_TextureData *data = (NGAGE_TextureData *)SDL_calloc(1, sizeof(*data)); + if (!data) + { + return false; + } + + if (!NGAGE_CreateTextureData(data, texture->w, texture->h)) + { + SDL_free(data); + return false; + } + + SDL_Surface* surface = SDL_CreateSurface(texture->w, texture->h, texture->format); + if (!surface) + { + SDL_free(data); + return false; + } + + data->surface = surface; + texture->internal = data; + + return true; +} + +static bool NGAGE_QueueSetViewport(SDL_Renderer* renderer, SDL_RenderCommand* cmd) +{ + if (!cmd->data.viewport.rect.w && !cmd->data.viewport.rect.h) + { + SDL_Rect viewport = { 0, 0, NGAGE_SCREEN_WIDTH, NGAGE_SCREEN_HEIGHT }; + SDL_SetRenderViewport(renderer, &viewport); + } + + return true; +} + +static bool NGAGE_QueueSetDrawColor(SDL_Renderer* renderer, SDL_RenderCommand* cmd) +{ + return true; +} + +static bool NGAGE_QueueDrawVertices(SDL_Renderer* renderer, SDL_RenderCommand* cmd, const SDL_FPoint* points, int count) +{ + NGAGE_Vertex *verts = (NGAGE_Vertex*)SDL_AllocateRenderVertices(renderer, count * sizeof(NGAGE_Vertex), 0, &cmd->data.draw.first); + if (!verts) + { + return false; + } + + cmd->data.draw.count = count; + + for (int i = 0; i < count; i++, points++) + { + int fixed_x = Real2Fix(points->x); + int fixed_y = Real2Fix(points->y); + + verts[i].x = Fix2Int(fixed_x); + verts[i].y = Fix2Int(fixed_y); + + Uint32 color = NGAGE_ConvertColor(cmd->data.draw.color.r, cmd->data.draw.color.g, cmd->data.draw.color.b, cmd->data.draw.color.a, cmd->data.draw.color_scale); + + verts[i].color.a = (Uint8)(color >> 24); + verts[i].color.b = (Uint8)(color >> 16); + verts[i].color.g = (Uint8)(color >> 8); + verts[i].color.r = (Uint8)color; + } + + return true; +} + +static bool NGAGE_QueueFillRects(SDL_Renderer* renderer, SDL_RenderCommand* cmd, const SDL_FRect* rects, int count) +{ + NGAGE_Vertex *verts = (NGAGE_Vertex*)SDL_AllocateRenderVertices(renderer, count * 2 * sizeof(NGAGE_Vertex), 0, &cmd->data.draw.first); + if (!verts) + { + return false; + } + + cmd->data.draw.count = count; + + for (int i = 0; i < count; i++, rects++) + { + verts[i * 2].x = Real2Fix(rects->x); + verts[i * 2].y = Real2Fix(rects->y); + verts[i * 2 + 1].x = Real2Fix(rects->w); + verts[i * 2 + 1].y = Real2Fix(rects->h); + + verts[i * 2].x = Fix2Int(verts[i * 2].x); + verts[i * 2].y = Fix2Int(verts[i * 2].y); + verts[i * 2 + 1].x = Fix2Int(verts[i * 2 + 1].x); + verts[i * 2 + 1].y = Fix2Int(verts[i * 2 + 1].y); + + Uint32 color = NGAGE_ConvertColor(cmd->data.draw.color.r, cmd->data.draw.color.g, cmd->data.draw.color.b, cmd->data.draw.color.a, cmd->data.draw.color_scale); + + verts[i * 2].color.a = (Uint8)(color >> 24); + verts[i * 2].color.b = (Uint8)(color >> 16); + verts[i * 2].color.g = (Uint8)(color >> 8); + verts[i * 2].color.r = (Uint8)color; + } + + return true; +} + +static bool NGAGE_QueueCopy(SDL_Renderer* renderer, SDL_RenderCommand* cmd, SDL_Texture* texture, const SDL_FRect* srcrect, const SDL_FRect* dstrect) +{ + SDL_Rect *verts = (SDL_Rect *)SDL_AllocateRenderVertices(renderer, 2 * sizeof(SDL_Rect), 0, &cmd->data.draw.first); + + if (!verts) + { + return false; + } + + cmd->data.draw.count = 1; + + verts->x = (int)srcrect->x; + verts->y = (int)srcrect->y; + verts->w = (int)srcrect->w; + verts->h = (int)srcrect->h; + + verts++; + + verts->x = (int)dstrect->x; + verts->y = (int)dstrect->y; + verts->w = (int)dstrect->w; + verts->h = (int)dstrect->h; + + return true; +} + +static bool NGAGE_QueueCopyEx(SDL_Renderer* renderer, SDL_RenderCommand* cmd, SDL_Texture* texture, const SDL_FRect *srcquad, const SDL_FRect *dstrect, const double angle, const SDL_FPoint* center, const SDL_FlipMode flip, float scale_x, float scale_y) +{ + NGAGE_CopyExData* verts = (NGAGE_CopyExData*)SDL_AllocateRenderVertices(renderer, sizeof(NGAGE_CopyExData), 0, &cmd->data.draw.first); + + if (!verts) + { + return false; + } + + cmd->data.draw.count = 1; + + verts->srcrect.x = (int)srcquad->x; + verts->srcrect.y = (int)srcquad->y; + verts->srcrect.w = (int)srcquad->w; + verts->srcrect.h = (int)srcquad->h; + verts->dstrect.x = (int)dstrect->x; + verts->dstrect.y = (int)dstrect->y; + verts->dstrect.w = (int)dstrect->w; + verts->dstrect.h = (int)dstrect->h; + + verts->angle = Real2Fix(angle); + verts->center.x = Real2Fix(center->x); + verts->center.y = Real2Fix(center->y); + verts->scale_x = Real2Fix(scale_x); + verts->scale_y = Real2Fix(scale_y); + + verts->flip = flip; + + return true; +} + +static bool NGAGE_QueueGeometry(SDL_Renderer* renderer, SDL_RenderCommand* cmd, SDL_Texture* texture, const float* xy, int xy_stride, const SDL_FColor* color, int color_stride, const float* uv, int uv_stride, int num_vertices, const void* indices, int num_indices, int size_indices, float scale_x, float scale_y) +{ + return true; +} + +static void NGAGE_InvalidateCachedState(SDL_Renderer* renderer) +{ + return; +} + +static bool NGAGE_RunCommandQueue(SDL_Renderer* renderer, SDL_RenderCommand* cmd, void* vertices, size_t vertsize) +{ + NGAGE_RendererData *phdata = (NGAGE_RendererData *)renderer->internal; + if (!phdata) + { + return false; + } + phdata->viewport = 0; + + while (cmd) + { + switch (cmd->command) + { + case SDL_RENDERCMD_NO_OP: + { + break; + } + + case SDL_RENDERCMD_SETVIEWPORT: + { + phdata->viewport = &cmd->data.viewport.rect; + break; + } + + case SDL_RENDERCMD_SETCLIPRECT: + { + const SDL_Rect* rect = &cmd->data.cliprect.rect; + + if (cmd->data.cliprect.enabled) + { + NGAGE_SetClipRect(rect); + } + + break; + } + + case SDL_RENDERCMD_SETDRAWCOLOR: + { + break; + } + + case SDL_RENDERCMD_CLEAR: + { + Uint32 color = NGAGE_ConvertColor(cmd->data.color.color.r, cmd->data.color.color.g, cmd->data.color.color.b, cmd->data.color.color.a, cmd->data.color.color_scale); + + NGAGE_Clear(color); + break; + } + + case SDL_RENDERCMD_DRAW_POINTS: + { + NGAGE_Vertex* verts = (NGAGE_Vertex*)(((Uint8 *)vertices) + cmd->data.draw.first); + const int count = cmd->data.draw.count; + + // Apply viewport. + if (phdata->viewport && (phdata->viewport->x || phdata->viewport->y)) + { + for (int i = 0; i < count; i++) + { + verts[i].x += phdata->viewport->x; + verts[i].y += phdata->viewport->y; + } + } + + NGAGE_DrawPoints(verts, count); + break; + } + case SDL_RENDERCMD_DRAW_LINES: + { + NGAGE_Vertex* verts = (NGAGE_Vertex*)(((Uint8 *)vertices) + cmd->data.draw.first); + const int count = cmd->data.draw.count; + + // Apply viewport. + if (phdata->viewport && (phdata->viewport->x || phdata->viewport->y)) + { + for (int i = 0; i < count; i++) + { + verts[i].x += phdata->viewport->x; + verts[i].y += phdata->viewport->y; + } + } + + NGAGE_DrawLines(verts, count); + break; + } + + case SDL_RENDERCMD_FILL_RECTS: + { + NGAGE_Vertex* verts = (NGAGE_Vertex*)(((Uint8 *)vertices) + cmd->data.draw.first); + const int count = cmd->data.draw.count; + + // Apply viewport. + if (phdata->viewport && (phdata->viewport->x || phdata->viewport->y)) + { + for (int i = 0; i < count; i++) + { + verts[i].x += phdata->viewport->x; + verts[i].y += phdata->viewport->y; + } + } + + NGAGE_FillRects(verts, count); + break; + } + + case SDL_RENDERCMD_COPY: + { + SDL_Rect* verts = (SDL_Rect*)(((Uint8*)vertices) + cmd->data.draw.first); + SDL_Rect* srcrect = verts; + SDL_Rect* dstrect = verts + 1; + SDL_Texture* texture = cmd->data.draw.texture; + + // Apply viewport. + if (phdata->viewport && (phdata->viewport->x || phdata->viewport->y)) + { + dstrect->x += phdata->viewport->x; + dstrect->y += phdata->viewport->y; + } + + NGAGE_Copy(renderer, texture, srcrect, dstrect); + break; + } + + case SDL_RENDERCMD_COPY_EX: + { + NGAGE_CopyExData* copydata = (NGAGE_CopyExData*)(((Uint8*)vertices) + cmd->data.draw.first); + SDL_Texture* texture = cmd->data.draw.texture; + + // Apply viewport. + if (phdata->viewport && (phdata->viewport->x || phdata->viewport->y)) + { + copydata->dstrect.x += phdata->viewport->x; + copydata->dstrect.y += phdata->viewport->y; + } + + NGAGE_CopyEx(renderer, texture, copydata); + break; + } + + case SDL_RENDERCMD_GEOMETRY: + { + break; + } + } + cmd = cmd->next; + } + + return true; +} + +static bool NGAGE_UpdateTexture(SDL_Renderer* renderer, SDL_Texture* texture, const SDL_Rect* rect, const void* pixels, int pitch) +{ + NGAGE_TextureData *phdata = (NGAGE_TextureData *)texture->internal; + + SDL_Surface *surface = phdata->surface; + Uint8 *src, *dst; + int row; + size_t length; + + if (SDL_MUSTLOCK(surface)) + { + if (!SDL_LockSurface(surface)) + { + return false; + } + } + src = (Uint8 *)pixels; + dst = (Uint8 *)surface->pixels + + rect->y * surface->pitch + + rect->x * surface->fmt->bytes_per_pixel; + + length = (size_t)rect->w * surface->fmt->bytes_per_pixel; + for (row = 0; row < rect->h; ++row) + { + SDL_memcpy(dst, src, length); + src += pitch; + dst += surface->pitch; + } + if (SDL_MUSTLOCK(surface)) + { + SDL_UnlockSurface(surface); + } + + return true; +} + +static bool NGAGE_LockTexture(SDL_Renderer* renderer, SDL_Texture* texture, const SDL_Rect* rect, void** pixels, int* pitch) +{ + NGAGE_TextureData *phdata = (NGAGE_TextureData *)texture->internal; + SDL_Surface *surface = phdata->surface; + + *pixels = + (void *)((Uint8 *)surface->pixels + rect->y * surface->pitch + + rect->x * surface->fmt->bytes_per_pixel); + *pitch = surface->pitch; + return true; +} + +static void NGAGE_UnlockTexture(SDL_Renderer* renderer, SDL_Texture* texture) +{ +} + +static void NGAGE_SetTextureScaleMode(SDL_Renderer* renderer, SDL_Texture* texture, SDL_ScaleMode scaleMode) +{ +} + +static bool NGAGE_SetRenderTarget(SDL_Renderer* renderer, SDL_Texture* texture) +{ + return true; +} + +static SDL_Surface* NGAGE_RenderReadPixels(SDL_Renderer* renderer, const SDL_Rect* rect) +{ + return (SDL_Surface*)0; +} + +static bool NGAGE_RenderPresent(SDL_Renderer* renderer) +{ + NGAGE_Flip(); + + return true; +} + +static void NGAGE_DestroyTexture(SDL_Renderer* renderer, SDL_Texture* texture) +{ + NGAGE_TextureData *data = (NGAGE_TextureData *)texture->internal; + if (data) + { + SDL_DestroySurface(data->surface); + NGAGE_DestroyTextureData(data); + SDL_free(data); + texture->internal = 0; + } +} + +static void NGAGE_DestroyRenderer(SDL_Renderer* renderer) +{ + NGAGE_RendererData *phdata = (NGAGE_RendererData *)renderer->internal; + if (phdata) + { + SDL_free(phdata); + renderer->internal = 0; + } +} + +static bool NGAGE_SetVSync(SDL_Renderer* renderer, int vsync) +{ + return true; +} + +#endif // SDL_VIDEO_RENDER_NGAGE diff --git a/src/render/ngage/SDL_render_ngage.cpp b/src/render/ngage/SDL_render_ngage.cpp new file mode 100644 index 0000000000000..886d910d77489 --- /dev/null +++ b/src/render/ngage/SDL_render_ngage.cpp @@ -0,0 +1,804 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + 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. +*/ +#ifdef __cplusplus +extern "C" { +#endif + +#include "SDL_internal.h" +#include "../SDL_sysrender.h" +#include "../../events/SDL_keyboard_c.h" +#include "SDL_render_ngage_c.h" + +#ifdef __cplusplus +} +#endif + +#ifdef SDL_VIDEO_RENDER_NGAGE + +#include "SDL_render_ngage_c.hpp" +#include "SDL_render_ops.hpp" + +const TUint32 WindowClientHandle = 0x571D0A; + +extern CRenderer* gRenderer; + +#ifdef __cplusplus +extern "C" { +#endif + + void NGAGE_Clear(const Uint32 color) + { + gRenderer->Clear(color); + } + + bool NGAGE_Copy(SDL_Renderer* renderer, SDL_Texture* texture, SDL_Rect* srcrect, SDL_Rect* dstrect) + { + return gRenderer->Copy(renderer, texture, srcrect, dstrect); + } + + bool NGAGE_CopyEx(SDL_Renderer* renderer, SDL_Texture* texture, NGAGE_CopyExData* copydata) + { + return gRenderer->CopyEx(renderer, texture, copydata); + } + + bool NGAGE_CreateTextureData(NGAGE_TextureData *data, const int width, const int height) + { + return gRenderer->CreateTextureData(data, width, height); + } + + void NGAGE_DestroyTextureData(NGAGE_TextureData *data) + { + if (data) + { + delete data->bitmap; + data->bitmap = NULL; + } + } + + void NGAGE_DrawLines(NGAGE_Vertex* verts, const int count) + { + gRenderer->DrawLines(verts, count); + } + + void NGAGE_DrawPoints(NGAGE_Vertex* verts, const int count) + { + gRenderer->DrawPoints(verts, count); + } + + void NGAGE_FillRects(NGAGE_Vertex* verts, const int count) + { + gRenderer->FillRects(verts, count); + } + + void NGAGE_Flip() + { + gRenderer->Flip(); + } + + void NGAGE_SetClipRect(const SDL_Rect* rect) + { + gRenderer->SetClipRect(rect->x, rect->y, rect->w, rect->h); + } + + void NGAGE_SetDrawColor(const Uint32 color) + { + if (gRenderer) + { + gRenderer->SetDrawColor(color); + } + } + + void NGAGE_PumpEventsInternal() + { + gRenderer->PumpEvents(); + } + + void NGAGE_SuspendScreenSaverInternal(bool suspend) + { + gRenderer->SuspendScreenSaver(suspend); + } + +#ifdef __cplusplus +} +#endif + +CRenderer* CRenderer::NewL() +{ + CRenderer* self = new (ELeave) CRenderer(); + CleanupStack::PushL(self); + self->ConstructL(); + CleanupStack::Pop(self); + return self; +} + +CRenderer::CRenderer() : iRenderer(0), iDirectScreen(0), iScreenGc(0), iWsSession(), iWsWindowGroup(), iWsWindowGroupID(0), iWsWindow(), iWsScreen(0), iWsEventStatus(), iWsEvent(), iShowFPS(EFalse), iFPS(0), iFont(0) {} + +CRenderer::~CRenderer() +{ + delete iRenderer; + iRenderer = 0; +} + +void CRenderer::ConstructL() +{ + TInt error = KErrNone; + + error = iWsSession.Connect(); + if (error != KErrNone) { + SDL_Log("Failed to connect to window server: %d", error); + User::Leave(error); + } + + iWsScreen = new(ELeave) CWsScreenDevice(iWsSession); + error = iWsScreen->Construct(); + if (error != KErrNone) { + SDL_Log("Failed to construct screen device: %d", error); + User::Leave(error); + } + + iWsWindowGroup = RWindowGroup(iWsSession); + error = iWsWindowGroup.Construct(WindowClientHandle); + if (error != KErrNone) { + SDL_Log("Failed to construct window group: %d", error); + User::Leave(error); + } + iWsWindowGroup.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 + iWsWindowGroup.SetName(winGroupName); + + iWsWindow = RWindow(iWsSession); + error = iWsWindow.Construct(iWsWindowGroup, WindowClientHandle - 1); + if (error != KErrNone) + { + SDL_Log("Failed to construct window: %d", error); + User::Leave(error); + } + iWsWindow.SetBackgroundColor(KRgbWhite); + iWsWindow.SetRequiredDisplayMode(EColor4K); + iWsWindow.Activate(); + iWsWindow.SetSize(iWsScreen->SizeInPixels()); + iWsWindow.SetVisible(ETrue); + + iWsWindowGroupID = iWsWindowGroup.Identifier(); + + TRAPD(errc, iRenderer = iRenderer->NewL()); + if (errc != KErrNone) + { + SDL_Log("Failed to create renderer: %d", errc); + return; + } + + iDirectScreen = CDirectScreenAccess::NewL( + iWsSession, + *(iWsScreen), + iWsWindow, *this); + + // Select font. + TFontSpec fontSpec(_L("LatinBold12"), 12); + TInt errd = iWsScreen->GetNearestFontInTwips((CFont*&)iFont, fontSpec); + if (errd != KErrNone) + { + SDL_Log("Failed to get font: %d", errd); + return; + } + + // Activate events. + iWsEventStatus = KRequestPending; + iWsSession.EventReady(&iWsEventStatus); + + DisableKeyBlocking(); + + iIsFocused = ETrue; + iShowFPS = EFalse; + iSuspendScreenSaver = EFalse; + + if (!iDirectScreen->IsActive()) + { + TRAPD(err, iDirectScreen->StartL()); + if (KErrNone != err) + { + return; + } + iDirectScreen->ScreenDevice()->SetAutoUpdate(ETrue); + } +} + +void CRenderer::Restart(RDirectScreenAccess::TTerminationReasons aReason) +{ + if (!iDirectScreen->IsActive()) + { + TRAPD(err, iDirectScreen->StartL()); + if (KErrNone != err) + { + return; + } + iDirectScreen->ScreenDevice()->SetAutoUpdate(ETrue); + } +} + +void CRenderer::AbortNow(RDirectScreenAccess::TTerminationReasons aReason) +{ + if (iDirectScreen->IsActive()) + { + iDirectScreen->Cancel(); + } +} + +void CRenderer::Clear(TUint32 iColor) +{ + if (iRenderer && iRenderer->Gc()) + { + iRenderer->Gc()->SetBrushColor(iColor); + iRenderer->Gc()->Clear(); + } +} + +#ifdef __cplusplus +extern "C" { +#endif + +Uint32 NGAGE_ConvertColor(float r, float g, float b, float a, float color_scale) +{ + TFixed ff = 255 << 16; // 255.f + + TFixed scalef = Real2Fix(color_scale); + TFixed rf = Real2Fix(r); + TFixed gf = Real2Fix(g); + TFixed bf = Real2Fix(b); + TFixed af = Real2Fix(a); + + rf = FixMul(rf, scalef); + gf = FixMul(gf, scalef); + bf = FixMul(bf, scalef); + + rf = SDL_clamp(rf, 0, ff); + gf = SDL_clamp(gf, 0, ff); + bf = SDL_clamp(bf, 0, ff); + af = SDL_clamp(af, 0, ff); + + rf = FixMul(rf, ff) >> 16; + gf = FixMul(gf, ff) >> 16; + bf = FixMul(bf, ff) >> 16; + af = FixMul(af, ff) >> 16; + + return (af << 24) | (bf << 16) | (gf << 8) | rf; +} + +#ifdef __cplusplus +} +#endif + +bool CRenderer::Copy(SDL_Renderer* renderer, SDL_Texture* texture, const SDL_Rect* srcrect, const SDL_Rect* dstrect) +{ + if (!texture) + { + return false; + } + + NGAGE_TextureData *phdata = (NGAGE_TextureData *)texture->internal; + if (!phdata) + { + return false; + } + + SDL_FColor* c = &texture->color; + int w = phdata->surface->w; + int h = phdata->surface->h; + int pitch = phdata->surface->pitch; + void* source = phdata->surface->pixels; + void* dest; + + if (!source) + { + return false; + } + + void* pixel_buffer_a = SDL_calloc(1, pitch * h); + if (!pixel_buffer_a) + { + return false; + } + dest = pixel_buffer_a; + + void* pixel_buffer_b = SDL_calloc(1, pitch * h); + if (!pixel_buffer_b) + { + SDL_free(pixel_buffer_a); + return false; + } + + if (c->a != 1.f || c->r != 1.f || c->g != 1.f || c->b != 1.f) + { + ApplyColorMod(dest, source, pitch, w, h, texture->color); + + source = dest; + } + + float sx; + float sy; + SDL_GetRenderScale(renderer, &sx, &sy); + + if (sx != 1.f || sy != 1.f) + { + TFixed scale_x = Real2Fix(sx); + TFixed scale_y = Real2Fix(sy); + TFixed center_x = Int2Fix(w / 2); + TFixed center_y = Int2Fix(h / 2); + + dest == pixel_buffer_a ? dest = pixel_buffer_b : dest = pixel_buffer_a; + + ApplyScale(dest, source, pitch, w, h, center_x, center_y, scale_x, scale_y); + + source = dest; + } + + Mem::Copy(phdata->bitmap->DataAddress(), source, pitch * h); + SDL_free(pixel_buffer_a); + SDL_free(pixel_buffer_b); + + if (phdata->bitmap) + { + TRect aSource(TPoint(srcrect->x, srcrect->y), TSize(srcrect->w, srcrect->h)); + TPoint aDest(dstrect->x, dstrect->y); + iRenderer->Gc()->BitBlt(aDest, phdata->bitmap, aSource); + } + + return true; +} + +bool CRenderer::CopyEx(SDL_Renderer* renderer, SDL_Texture* texture, const NGAGE_CopyExData* copydata) +{ + NGAGE_TextureData *phdata = (NGAGE_TextureData *)texture->internal; + if (!phdata) + { + return false; + } + + SDL_FColor* c = &texture->color; + int w = phdata->surface->w; + int h = phdata->surface->h; + int pitch = phdata->surface->pitch; + void* source = phdata->surface->pixels; + void* dest; + + if (!source) + { + return false; + } + + void* pixel_buffer_a = SDL_calloc(1, pitch * h); + if (!pixel_buffer_a) + { + return false; + } + dest = pixel_buffer_a; + + void* pixel_buffer_b = SDL_calloc(1, pitch * h); + if (!pixel_buffer_a) + { + SDL_free(pixel_buffer_a); + return false; + } + + if (copydata->flip) + { + ApplyFlip(dest, source, pitch, w, h, copydata->flip); + source = dest; + } + + if (copydata->scale_x != 1.f || copydata->scale_y != 1.f) + { + dest == pixel_buffer_a ? dest = pixel_buffer_b : dest = pixel_buffer_a; + ApplyScale(dest, source, pitch, w, h, copydata->center.x, copydata->center.y, copydata->scale_x, copydata->scale_y); + source = dest; + } + + if (copydata->angle) + { + dest == pixel_buffer_a ? dest = pixel_buffer_b : dest = pixel_buffer_a; + ApplyRotation(dest, source, pitch, w, h, copydata->center.x, copydata->center.y, copydata->angle); + source = dest; + } + + if (c->a != 1.f || c->r != 1.f || c->g != 1.f || c->b != 1.f) + { + dest == pixel_buffer_a ? dest = pixel_buffer_b : dest = pixel_buffer_a; + ApplyColorMod(dest, source, pitch, w, h, texture->color); + source = dest; + } + + Mem::Copy(phdata->bitmap->DataAddress(), source, pitch * h); + SDL_free(pixel_buffer_a); + SDL_free(pixel_buffer_b); + + if (phdata->bitmap) + { + TRect aSource(TPoint(copydata->srcrect.x, copydata->srcrect.y), TSize(copydata->srcrect.w, copydata->srcrect.h)); + TPoint aDest(copydata->dstrect.x, copydata->dstrect.y); + iRenderer->Gc()->BitBlt(aDest, phdata->bitmap, aSource); + } + + return true; +} + +bool CRenderer::CreateTextureData(NGAGE_TextureData* aTextureData, const TInt aWidth, const TInt aHeight) +{ + if (!aTextureData) + { + return false; + } + + aTextureData->bitmap = new CFbsBitmap(); + if (!aTextureData->bitmap) + { + return false; + } + + TInt error = aTextureData->bitmap->Create(TSize(aWidth, aHeight), EColor4K); + if (error != KErrNone) + { + delete aTextureData->bitmap; + aTextureData->bitmap = NULL; + return false; + } + + return true; +} + +void CRenderer::DrawLines(NGAGE_Vertex* aVerts, const TInt aCount) +{ + if (iRenderer && iRenderer->Gc()) + { + TPoint* aPoints = new TPoint[aCount]; + + for (TInt i = 0; i < aCount; i++) + { + aPoints[i] = TPoint(aVerts[i].x, aVerts[i].y); + } + + TUint32 aColor = ( + ((TUint8)aVerts->color.a << 24) | + ((TUint8)aVerts->color.b << 16) | + ((TUint8)aVerts->color.g << 8) | + (TUint8)aVerts->color.r); + + iRenderer->Gc()->SetPenColor(aColor); + iRenderer->Gc()->DrawPolyLineNoEndPoint(aPoints, aCount); + + delete[] aPoints; + } +} + +void CRenderer::DrawPoints(NGAGE_Vertex* aVerts, const TInt aCount) +{ + if (iRenderer && iRenderer->Gc()) + { + for (TInt i = 0; i < aCount; i++, aVerts++) + { + TUint32 aColor = ( + ((TUint8)aVerts->color.a << 24) | + ((TUint8)aVerts->color.b << 16) | + ((TUint8)aVerts->color.g << 8) | + (TUint8)aVerts->color.r); + + iRenderer->Gc()->SetPenColor(aColor); + iRenderer->Gc()->Plot(TPoint(aVerts->x, aVerts->y)); + } + } +} + +void CRenderer::FillRects(NGAGE_Vertex* aVerts, const TInt aCount) +{ + if (iRenderer && iRenderer->Gc()) + { + for (TInt i = 0; i < aCount; i++, aVerts++) + { + TPoint pos(aVerts[i].x, aVerts[i].y); + TSize size( + aVerts[i + 1].x, + aVerts[i + 1].y); + TRect rect(pos, size); + + TUint32 aColor = ( + ((TUint8)aVerts->color.a << 24) | + ((TUint8)aVerts->color.b << 16) | + ((TUint8)aVerts->color.g << 8) | + (TUint8)aVerts->color.r); + + iRenderer->Gc()->SetPenColor(aColor); + iRenderer->Gc()->SetBrushColor(aColor); + iRenderer->Gc()->DrawRect(rect); + } + } +} + +void CRenderer::Flip() +{ + if (!iRenderer) + { + SDL_Log("iRenderer is NULL."); + return; + } + + if (!iIsFocused) + { + return; + } + + iRenderer->Gc()->UseFont(iFont); + + if (iShowFPS && iRenderer->Gc()) + { + UpdateFPS(); + + TBuf<64> info; + + iRenderer->Gc()->SetPenStyle(CGraphicsContext::ESolidPen); + iRenderer->Gc()->SetBrushStyle(CGraphicsContext::ENullBrush); + iRenderer->Gc()->SetPenColor(KRgbCyan); + + TRect aTextRect(TPoint(3, 203 - iFont->HeightInPixels()), TSize(45, iFont->HeightInPixels() + 2)); + iRenderer->Gc()->SetBrushStyle(CGraphicsContext::ESolidBrush); + iRenderer->Gc()->SetBrushColor(KRgbBlack); + iRenderer->Gc()->DrawRect(aTextRect); + + // Draw messages. + info.Format(_L("FPS: %d"), iFPS); + iRenderer->Gc()->DrawText(info, TPoint(5, 203)); + } + else + { + // This is a workaround that helps regulating the FPS. + iRenderer->Gc()->DrawText(_L(""), TPoint(0, 0)); + } + iRenderer->Gc()->DiscardFont(); + iRenderer->Flip(iDirectScreen); + + // Keep the backlight on. + if (iSuspendScreenSaver) + { + User::ResetInactivityTime(); + } + // Suspend the current thread for a short while. + // Give some time to other threads and active objects. + User::After(0); +} + +void CRenderer::SetDrawColor(TUint32 iColor) +{ + if (iRenderer && iRenderer->Gc()) + { + iRenderer->Gc()->SetPenColor(iColor); + iRenderer->Gc()->SetBrushColor(iColor); + iRenderer->Gc()->SetBrushStyle(CGraphicsContext::ESolidBrush); + + TRAPD(err, iRenderer->SetCurrentColor(iColor)); + if (err != KErrNone) + { + return; + } + } +} + +void CRenderer::SetClipRect(TInt aX, TInt aY, TInt aWidth, TInt aHeight) +{ + if (iRenderer && iRenderer->Gc()) + { + TRect viewportRect(aX, aY, aX + aWidth, aY + aHeight); + iRenderer->Gc()->SetClippingRect(viewportRect); + } +} + +void CRenderer::UpdateFPS() +{ + static TTime lastTime; + static TInt frameCount = 0; + TTime currentTime; + const TUint KOneSecond = 1000000; // 1s in ms. + + currentTime.HomeTime(); + ++frameCount; + + TTimeIntervalMicroSeconds timeDiff = currentTime.MicroSecondsFrom(lastTime); + + if (timeDiff.Int64() >= KOneSecond) + { + // Calculate FPS. + iFPS = frameCount; + + // Reset frame count and last time. + frameCount = 0; + lastTime = currentTime; + } +} + +void CRenderer::SuspendScreenSaver(TBool aSuspend) +{ + iSuspendScreenSaver = aSuspend; +} + +static SDL_Scancode ConvertScancode(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_HASH; + break; + case EStdKeyDevice0: // Left softkey + keycode = SDLK_SOFTLEFT; + break; + case EStdKeyDevice1: // Right softkey + keycode = SDLK_SOFTRIGHT; + break; + case EStdKeyApplication0: // Call softkey + keycode = SDLK_CALL; + break; + case EStdKeyApplication1: // End call softkey + keycode = SDLK_ENDCALL; + break; + case EStdKeyDevice3: // Middle softkey + keycode = SDLK_SELECT; + 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, NULL); +} + +void CRenderer::HandleEvent(const TWsEvent& aWsEvent) +{ + Uint64 timestamp; + + switch (aWsEvent.Type()) + { + case EEventKeyDown: /* Key events */ + timestamp = SDL_GetPerformanceCounter(); + SDL_SendKeyboardKey(timestamp, 1, aWsEvent.Key()->iCode, ConvertScancode(aWsEvent.Key()->iScanCode), true); + + if (aWsEvent.Key()->iScanCode == EStdKeyHash) + { + if (iShowFPS) + { + iShowFPS = EFalse; + } + else + { + iShowFPS = ETrue; + } + } + + break; + case EEventKeyUp: /* Key events */ + timestamp = SDL_GetPerformanceCounter(); + SDL_SendKeyboardKey(timestamp, 1, aWsEvent.Key()->iCode, ConvertScancode(aWsEvent.Key()->iScanCode), false); + + case EEventFocusGained: + DisableKeyBlocking(); + if (!iDirectScreen->IsActive()) + { + TRAPD(err, iDirectScreen->StartL()); + if (KErrNone != err) + { + return; + } + iDirectScreen->ScreenDevice()->SetAutoUpdate(ETrue); + iIsFocused = ETrue; + } + Flip(); + break; + case EEventFocusLost: + { + if (iDirectScreen->IsActive()) + { + iDirectScreen->Cancel(); + } + + iIsFocused = EFalse; + break; + } + default: + break; + } +} + +void CRenderer::DisableKeyBlocking() +{ + TRawEvent aEvent; + + aEvent.Set((TRawEvent::TType) /*EDisableKeyBlock*/ 51); + iWsSession.SimulateRawEvent(aEvent); +} + +void CRenderer::PumpEvents() +{ + while (iWsEventStatus != KRequestPending) + { + iWsSession.GetEvent(iWsEvent); + HandleEvent(iWsEvent); + iWsEventStatus = KRequestPending; + iWsSession.EventReady(&iWsEventStatus); + } +} + +#endif // SDL_VIDEO_RENDER_NGAGE diff --git a/src/render/ngage/SDL_render_ngage_c.h b/src/render/ngage/SDL_render_ngage_c.h new file mode 100644 index 0000000000000..8093a06f53d9e --- /dev/null +++ b/src/render/ngage/SDL_render_ngage_c.h @@ -0,0 +1,105 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + 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 ngage_video_render_ngage_c_h +#define ngage_video_render_ngage_c_h + +#define NGAGE_SCREEN_WIDTH 176 +#define NGAGE_SCREEN_HEIGHT 208 + +#ifdef __cplusplus +extern "C" { +#endif + +#include "../SDL_sysrender.h" + + typedef struct NGAGE_RendererData + { + SDL_Rect* viewport; + + } NGAGE_RendererData; + + typedef struct NGAGE_Vertex + { + int x; + int y; + + struct + { + Uint8 a; + Uint8 r; + Uint8 g; + Uint8 b; + + } color; + + } NGAGE_Vertex; + + typedef struct CFbsBitmap CFbsBitmap; + + typedef struct NGAGE_TextureData + { + CFbsBitmap *bitmap; + SDL_Surface* surface; + + } NGAGE_TextureData; + + typedef struct NGAGE_CopyExData + { + SDL_Rect srcrect; + SDL_Rect dstrect; + + int angle; + + struct + { + int x; + int y; + + } center; + + SDL_FlipMode flip; + + int scale_x; + int scale_y; + + } NGAGE_CopyExData; + + void NGAGE_Clear(const Uint32 color); + Uint32 NGAGE_ConvertColor(float r, float g, float b, float a, float color_scale); + bool NGAGE_Copy(SDL_Renderer* renderer, SDL_Texture* texture, SDL_Rect* srcrect, SDL_Rect* dstrect); + bool NGAGE_CopyEx(SDL_Renderer *renderer, SDL_Texture *texture, NGAGE_CopyExData *copydata); + bool NGAGE_CreateTextureData(NGAGE_TextureData* data, const int width, const int height); + void NGAGE_DestroyTextureData(NGAGE_TextureData* data); + void NGAGE_DrawLines(NGAGE_Vertex* verts, const int count); + void NGAGE_DrawPoints(NGAGE_Vertex* verts, const int count); + void NGAGE_FillRects(NGAGE_Vertex* verts, const int count); + void NGAGE_Flip(); + void NGAGE_SetClipRect(const SDL_Rect* rect); + void NGAGE_SetDrawColor(const Uint32 color); + void NGAGE_PumpEventsInternal(); + void NGAGE_SuspendScreenSaverInternal(bool suspend); + +#ifdef __cplusplus +} +#endif + +#endif // ngage_video_render_ngage_c_h diff --git a/src/render/ngage/SDL_render_ngage_c.hpp b/src/render/ngage/SDL_render_ngage_c.hpp new file mode 100644 index 0000000000000..f0c2537e68551 --- /dev/null +++ b/src/render/ngage/SDL_render_ngage_c.hpp @@ -0,0 +1,91 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + 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 ngage_video_render_ngage_c_hpp +#define ngage_video_render_ngage_c_hpp + +#include +#include +#include +#include "SDL_render_ngage_c.h" + +class CRenderer: public MDirectScreenAccess +{ +public: + static CRenderer* NewL(); + virtual ~CRenderer(); + + // Rendering functions. + void Clear(TUint32 iColor); + bool Copy(SDL_Renderer* renderer, SDL_Texture* texture, const SDL_Rect* srcrect, const SDL_Rect* dstrect); + bool CopyEx(SDL_Renderer* renderer, SDL_Texture* texture, const NGAGE_CopyExData* copydata); + bool CreateTextureData(NGAGE_TextureData* aTextureData, const TInt aWidth, const TInt aHeight); + void DrawLines(NGAGE_Vertex* aVerts, const TInt aCount); + void DrawPoints(NGAGE_Vertex* aVerts, const TInt aCount); + void FillRects(NGAGE_Vertex* aVerts, const TInt aCount); + void Flip(); + void SetDrawColor(TUint32 iColor); + void SetClipRect(TInt aX, TInt aY, TInt aWidth, TInt aHeight); + void UpdateFPS(); + void SuspendScreenSaver(TBool aSuspend); + + // Event handling. + void DisableKeyBlocking(); + void HandleEvent(const TWsEvent& aWsEvent); + void PumpEvents(); + +private: + CRenderer(); + void ConstructL(void); + + // BackBuffer. + CNRenderer *iRenderer; + + // Direct screen access. + CDirectScreenAccess* iDirectScreen; + CFbsBitGc *iScreenGc; + TBool iIsFocused; + + // Window server session. + RWsSession iWsSession; + RWindowGroup iWsWindowGroup; + TInt iWsWindowGroupID; + RWindow iWsWindow; + CWsScreenDevice* iWsScreen; + + // Event handling. + TRequestStatus iWsEventStatus; + TWsEvent iWsEvent; + + // MDirectScreenAccess functions. + void Restart (RDirectScreenAccess::TTerminationReasons aReason); + void AbortNow (RDirectScreenAccess::TTerminationReasons aReason); + + // Frame per second. + TBool iShowFPS; + TUint iFPS; + const CFont* iFont; + + // Screen saver. + TBool iSuspendScreenSaver; +}; + +#endif // ngage_video_render_ngage_c_hpp diff --git a/src/render/ngage/SDL_render_ops.cpp b/src/render/ngage/SDL_render_ops.cpp new file mode 100644 index 0000000000000..4632bddcd0729 --- /dev/null +++ b/src/render/ngage/SDL_render_ops.cpp @@ -0,0 +1,152 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + 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 <3dtypes.h> +#include "SDL_render_ops.hpp" + +void ApplyColorMod(void *dest, void *source, int pitch, int width, int height, SDL_FColor color) +{ + TUint16 *src_pixels = static_cast(source); + TUint16 *dst_pixels = static_cast(dest); + + TFixed rf = Real2Fix(color.r); + TFixed gf = Real2Fix(color.g); + TFixed bf = Real2Fix(color.b); + + for (int y = 0; y < height; ++y) + { + for (int x = 0; x < width; ++x) + { + TUint16 pixel = src_pixels[y * pitch / 2 + x]; + TUint8 r = (pixel & 0xF800) >> 8; + TUint8 g = (pixel & 0x07E0) >> 3; + TUint8 b = (pixel & 0x001F) << 3; + r = FixMul(r, rf); + g = FixMul(g, gf); + b = FixMul(b, bf); + dst_pixels[y * pitch / 2 + x] = (r << 8) | (g << 3) | (b >> 3); + } + } +} + +void ApplyFlip(void* dest, void* source, int pitch, int width, int height, SDL_FlipMode flip) +{ + TUint16* src_pixels = static_cast(source); + TUint16* dst_pixels = static_cast(dest); + + for (int y = 0; y < height; ++y) + { + for (int x = 0; x < width; ++x) + { + int src_x = x; + int src_y = y; + + if (flip & SDL_FLIP_HORIZONTAL) + { + src_x = width - 1 - x; + } + + if (flip & SDL_FLIP_VERTICAL) + { + src_y = height - 1 - y; + } + + dst_pixels[y * pitch / 2 + x] = src_pixels[src_y * pitch / 2 + src_x]; + } + } +} + +void ApplyRotation(void* dest, void* source, int pitch, int width, int height, TFixed center_x, TFixed center_y, TFixed angle) +{ + TUint16* src_pixels = static_cast(source); + TUint16* dst_pixels = static_cast(dest); + + TFixed cos_angle = 0; + TFixed sin_angle = 0; + + if (angle != 0) + { + FixSinCos(angle, sin_angle, cos_angle); + } + + for (int y = 0; y < height; ++y) + { + for (int x = 0; x < width; ++x) + { + // Translate point to origin. + TFixed translated_x = Int2Fix(x) - center_x; + TFixed translated_y = Int2Fix(y) - center_y; + + // Rotate point (clockwise). + TFixed rotated_x = FixMul(translated_x, cos_angle) + FixMul(translated_y, sin_angle); + TFixed rotated_y = FixMul(translated_y, cos_angle) - FixMul(translated_x, sin_angle); + + // Translate point back. + int final_x = Fix2Int(rotated_x + center_x); + int final_y = Fix2Int(rotated_y + center_y); + + // Check bounds. + if (final_x >= 0 && final_x < width && final_y >= 0 && final_y < height) + { + dst_pixels[y * pitch / 2 + x] = src_pixels[final_y * pitch / 2 + final_x]; + } + else + { + dst_pixels[y * pitch / 2 + x] = 0; + } + } + } +} + +void ApplyScale(void* dest, void* source, int pitch, int width, int height, TFixed center_x, TFixed center_y, TFixed scale_x, TFixed scale_y) +{ + TUint16* src_pixels = static_cast(source); + TUint16* dst_pixels = static_cast(dest); + + for (int y = 0; y < height; ++y) + { + for (int x = 0; x < width; ++x) + { + // Translate point to origin. + TFixed translated_x = Int2Fix(x) - center_x; + TFixed translated_y = Int2Fix(y) - center_y; + + // Scale point. + TFixed scaled_x = FixDiv(translated_x, scale_x); + TFixed scaled_y = FixDiv(translated_y, scale_y); + + // Translate point back. + int final_x = Fix2Int(scaled_x + center_x); + int final_y = Fix2Int(scaled_y + center_y); + + // Check bounds. + if (final_x >= 0 && final_x < width && final_y >= 0 && final_y < height) + { + dst_pixels[y * pitch / 2 + x] = src_pixels[final_y * pitch / 2 + final_x]; + } + else + { + dst_pixels[y * pitch / 2 + x] = 0; + } + } + } +} diff --git a/src/render/ngage/SDL_render_ops.hpp b/src/render/ngage/SDL_render_ops.hpp new file mode 100644 index 0000000000000..734fc244eb438 --- /dev/null +++ b/src/render/ngage/SDL_render_ops.hpp @@ -0,0 +1,32 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + 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 ngage_video_render_ops_hpp +#define ngage_video_render_ops_hpp + +#include <3dtypes.h> + +void ApplyColorMod(void* dest, void* source, int pitch, int width, int height, SDL_FColor color); +void ApplyFlip(void* dest, void* source, int pitch, int width, int height, SDL_FlipMode flip); +void ApplyRotation(void* dest, void* source, int pitch, int width, int height, TFixed center_x, TFixed center_y, TFixed angle); +void ApplyScale(void* dest, void* source, int pitch, int width, int height, TFixed center_x, TFixed center_y, TFixed scale_x, TFixed scale_y); + +#endif // ngage_video_render_ops_hpp diff --git a/src/stdlib/SDL_string.c b/src/stdlib/SDL_string.c index 007719ef796f1..0228b612f636c 100644 --- a/src/stdlib/SDL_string.c +++ b/src/stdlib/SDL_string.c @@ -34,6 +34,8 @@ #if defined(__SIZEOF_WCHAR_T__) #define SDL_SIZEOF_WCHAR_T __SIZEOF_WCHAR_T__ +#elif defined(SDL_PLATFORM_NGAGE) +#define SDL_SIZEOF_WCHAR_T 2 #elif defined(SDL_PLATFORM_WINDOWS) #define SDL_SIZEOF_WCHAR_T 2 #else // assume everything else is UTF-32 (add more tests if compiler-assert fails below!) diff --git a/src/stdlib/SDL_vacopy.h b/src/stdlib/SDL_vacopy.h index fee560e4684cd..494409c125337 100644 --- a/src/stdlib/SDL_vacopy.h +++ b/src/stdlib/SDL_vacopy.h @@ -27,4 +27,7 @@ #elif defined(__GNUC__) && (__GNUC__ < 3) #define va_copy(dst, src) __va_copy(dst, src) + +#elif defined(__SYMBIAN32__) +extern void va_copy(char* dest, char* src); #endif diff --git a/src/time/ngage/SDL_systime.cpp b/src/time/ngage/SDL_systime.cpp new file mode 100644 index 0000000000000..5d8eaa4fe0847 --- /dev/null +++ b/src/time/ngage/SDL_systime.cpp @@ -0,0 +1,151 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2024 Sam Lantinga + + 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 +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +void SDL_GetSystemTimeLocalePreferences(SDL_DateFormat *df, SDL_TimeFormat *tf) +{ + TLanguage language = User::Language(); + + switch (language) { + case ELangFrench: + case ELangSwissFrench: + case ELangBelgianFrench: + case ELangInternationalFrench: + case ELangGerman: + case ELangSwissGerman: + case ELangAustrian: + case ELangSpanish: + case ELangInternationalSpanish: + case ELangLatinAmericanSpanish: + case ELangItalian: + case ELangSwissItalian: + case ELangSwedish: + case ELangFinlandSwedish: + case ELangDanish: + case ELangNorwegian: + case ELangNorwegianNynorsk: + case ELangFinnish: + case ELangPortuguese: + case ELangBrazilianPortuguese: + case ELangTurkish: + case ELangCyprusTurkish: + case ELangIcelandic: + case ELangRussian: + case ELangHungarian: + case ELangDutch: + case ELangBelgianFlemish: + case ELangCzech: + case ELangSlovak: + case ELangPolish: + case ELangSlovenian: + case ELangTaiwanChinese: + case ELangHongKongChinese: + case ELangPrcChinese: + case ELangJapanese: + case ELangThai: + case ELangAfrikaans: + case ELangAlbanian: + case ELangAmharic: + case ELangArabic: + case ELangArmenian: + case ELangAzerbaijani: + case ELangBelarussian: + case ELangBengali: + case ELangBulgarian: + case ELangBurmese: + case ELangCatalan: + case ELangCroatian: + case ELangEstonian: + case ELangFarsi: + case ELangScotsGaelic: + case ELangGeorgian: + case ELangGreek: + case ELangCyprusGreek: + case ELangGujarati: + case ELangHebrew: + case ELangHindi: + case ELangIndonesian: + case ELangIrish: + case ELangKannada: + case ELangKazakh: + case ELangKhmer: + case ELangKorean: + case ELangLao: + case ELangLatvian: + case ELangLithuanian: + case ELangMacedonian: + case ELangMalay: + case ELangMalayalam: + case ELangMarathi: + case ELangMoldavian: + case ELangMongolian: + case ELangPunjabi: + case ELangRomanian: + case ELangSerbian: + case ELangSinhalese: + case ELangSomali: + case ELangSwahili: + case ELangTajik: + case ELangTamil: + case ELangTelugu: + case ELangTibetan: + case ELangTigrinya: + case ELangTurkmen: + case ELangUkrainian: + case ELangUrdu: + case ELangUzbek: + case ELangVietnamese: + case ELangWelsh: + case ELangZulu: + *df = SDL_DATE_FORMAT_DDMMYYYY; + *tf = SDL_TIME_FORMAT_24HR; + break; + case ELangAmerican: + case ELangCanadianEnglish: + case ELangInternationalEnglish: + case ELangSouthAfricanEnglish: + case ELangAustralian: + case ELangNewZealand: + case ELangCanadianFrench: + *df = SDL_DATE_FORMAT_MMDDYYYY; + *tf = SDL_TIME_FORMAT_12HR; + break; + case ELangEnglish: + case ELangOther: + default: + *df = SDL_DATE_FORMAT_DDMMYYYY; + *tf = SDL_TIME_FORMAT_24HR; + break; + } +} + +#ifdef __cplusplus +} +#endif diff --git a/src/timer/ngage/SDL_systimer.cpp b/src/timer/ngage/SDL_systimer.cpp new file mode 100644 index 0000000000000..07b257e8eee69 --- /dev/null +++ b/src/timer/ngage/SDL_systimer.cpp @@ -0,0 +1,47 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + 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 +#include + +#ifdef __cplusplus +extern "C" { +#endif + + Uint64 SDL_GetPerformanceCounter(void) + { + return (Uint64)User::TickCount(); + } + + Uint64 SDL_GetPerformanceFrequency(void) + { + return (Uint64)1000000u; + } + + void SDL_SYS_DelayNS(Uint64 ns) + { + User::After(SDL_NS_TO_US(ns)); + } + +#ifdef __cplusplus +} +#endif diff --git a/src/video/SDL_sysvideo.h b/src/video/SDL_sysvideo.h index 6951d456424ae..a8f317b48b1c7 100644 --- a/src/video/SDL_sysvideo.h +++ b/src/video/SDL_sysvideo.h @@ -520,6 +520,7 @@ extern VideoBootStrap PSP_bootstrap; extern VideoBootStrap VITA_bootstrap; extern VideoBootStrap RISCOS_bootstrap; extern VideoBootStrap N3DS_bootstrap; +extern VideoBootStrap NGAGE_bootstrap; extern VideoBootStrap RPI_bootstrap; extern VideoBootStrap KMSDRM_bootstrap; extern VideoBootStrap DUMMY_bootstrap; diff --git a/src/video/SDL_video.c b/src/video/SDL_video.c index 3a400d8131b20..482e536b2bce3 100644 --- a/src/video/SDL_video.c +++ b/src/video/SDL_video.c @@ -119,6 +119,9 @@ static VideoBootStrap *bootstrap[] = { #ifdef SDL_VIDEO_DRIVER_N3DS &N3DS_bootstrap, #endif +#ifdef SDL_VIDEO_DRIVER_NGAGE + &NGAGE_bootstrap, +#endif #ifdef SDL_VIDEO_DRIVER_KMSDRM &KMSDRM_bootstrap, #endif diff --git a/src/video/ngage/SDL_ngagevideo.c b/src/video/ngage/SDL_ngagevideo.c new file mode 100644 index 0000000000000..a5a6ab1298b01 --- /dev/null +++ b/src/video/ngage/SDL_ngagevideo.c @@ -0,0 +1,183 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + 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_sysvideo.h" + +#ifdef SDL_VIDEO_DRIVER_NGAGE + +#include "SDL_ngagevideo.h" + +#define NGAGE_VIDEO_DRIVER_NAME "N-Gage" + +static void NGAGE_DeleteDevice(SDL_VideoDevice *device); +static bool NGAGE_VideoInit(SDL_VideoDevice *device); +static void NGAGE_VideoQuit(SDL_VideoDevice *device); + +static bool NGAGE_GetDisplayBounds(SDL_VideoDevice* device, SDL_VideoDisplay* display, SDL_Rect* rect); +static bool NGAGE_GetDisplayModes(SDL_VideoDevice* device, SDL_VideoDisplay* display); + +static void NGAGE_PumpEvents(SDL_VideoDevice* device); + +static bool NGAGE_SuspendScreenSaver(SDL_VideoDevice* device); + +static SDL_VideoDevice* NGAGE_CreateDevice(void) +{ + 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 (SDL_VideoDevice*)0; + } + + // Initialize internal N-Gage specific data. + phdata = (SDL_VideoData *)SDL_calloc(1, sizeof(SDL_VideoData)); + if (!phdata) + { + SDL_OutOfMemory(); + SDL_free(device); + return (SDL_VideoDevice*)0; + } + + device->internal = phdata; + + device->name = "Nokia N-Gage"; + + device->VideoInit = NGAGE_VideoInit; + device->VideoQuit = NGAGE_VideoQuit; + + device->GetDisplayBounds = NGAGE_GetDisplayBounds; + device->GetDisplayModes = NGAGE_GetDisplayModes; + + device->PumpEvents = NGAGE_PumpEvents; + + device->SuspendScreenSaver = NGAGE_SuspendScreenSaver; + + device->free = NGAGE_DeleteDevice; + + device->device_caps = VIDEO_DEVICE_CAPS_FULLSCREEN_ONLY; + + return device; +} + +VideoBootStrap NGAGE_bootstrap = +{ + NGAGE_VIDEO_DRIVER_NAME, + "N-Gage Video Driver", + NGAGE_CreateDevice, + 0 +}; + +static void NGAGE_DeleteDevice(SDL_VideoDevice *device) +{ + SDL_free(device->internal); + SDL_free(device); +} + +static bool NGAGE_VideoInit(SDL_VideoDevice *device) +{ + SDL_VideoData *phdata = (SDL_VideoData*)device->internal; + + if (!phdata) + { + return false; + } + + SDL_zero(phdata->mode); + SDL_zero(phdata->display); + + phdata->mode.w = 176; + phdata->mode.h = 208; + phdata->mode.refresh_rate = 60.0f; + phdata->mode.format = SDL_PIXELFORMAT_ARGB4444; + + phdata->display.name = "N-Gage"; + phdata->display.desktop_mode = phdata->mode; + + if (SDL_AddVideoDisplay(&phdata->display, false) == 0) + { + return false; + } + + return true; +} + +static void NGAGE_VideoQuit(SDL_VideoDevice *device) +{ + SDL_VideoData *phdata = (SDL_VideoData*)device->internal; + + if (phdata) + { + SDL_zero(phdata->mode); + SDL_zero(phdata->display); + } +} + +static bool NGAGE_GetDisplayBounds(SDL_VideoDevice* device, SDL_VideoDisplay* display, SDL_Rect* rect) +{ + if (!display) + { + return false; + } + + rect->x = 0; + rect->y = 0; + rect->w = display->current_mode->w; + rect->h = display->current_mode->h; + + return true; +} + +static bool NGAGE_GetDisplayModes(SDL_VideoDevice* device, SDL_VideoDisplay* display) +{ + SDL_VideoData *phdata = (SDL_VideoData*)device->internal; + SDL_DisplayMode mode; + + SDL_zero(mode); + mode.w = phdata->mode.w; + mode.h = phdata->mode.h; + mode.refresh_rate = phdata->mode.refresh_rate; + mode.format = phdata->mode.format; + + if (!SDL_AddFullscreenDisplayMode(display, &mode)) + { + return false; + } + + return true; +} + +#include "../../render/ngage/SDL_render_ngage_c.h" + +static void NGAGE_PumpEvents(SDL_VideoDevice* device) +{ + NGAGE_PumpEventsInternal(); +} + +static bool NGAGE_SuspendScreenSaver(SDL_VideoDevice* device) +{ + NGAGE_SuspendScreenSaverInternal(device->suspend_screensaver); + return true; +} + +#endif // SDL_VIDEO_DRIVER_NGAGE diff --git a/src/video/ngage/SDL_ngagevideo.h b/src/video/ngage/SDL_ngagevideo.h new file mode 100644 index 0000000000000..3153d6be80e08 --- /dev/null +++ b/src/video/ngage/SDL_ngagevideo.h @@ -0,0 +1,39 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "SDL_internal.h" + +#ifdef SDL_VIDEO_DRIVER_NGAGE + +#include "../SDL_sysvideo.h" + +#ifndef _SDL_ngagevideo_h +#define _SDL_ngagevideo_h + +typedef struct SDL_VideoData +{ + SDL_DisplayMode mode; + SDL_VideoDisplay display; + +} SDL_VideoData; + +#endif // _SDL_ngagevideo_h + +#endif // SDL_VIDEO_DRIVER_NGAGE