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 04f4c10923976..b711c02edefd8 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): @@ -139,11 +140,12 @@ 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", ), } class StaticLibType(Enum): - MSVC = "SDL3-static.lib" + STATIC_LIB = "SDL3-static.lib" A = "libSDL3.a" @@ -223,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 = { @@ -290,6 +293,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 != ""} @@ -365,7 +369,7 @@ def spec_to_job(spec: JobSpec, key: str, trackmem_symbol_names: bool) -> JobDeta job.msvc_project_flags.append("-p:TreatWarningsAsError=true") job.test_pkg_config = False job.shared_lib = SharedLibType.WIN32 - job.static_lib = StaticLibType.MSVC + job.static_lib = StaticLibType.STATIC_LIB job.cmake_arguments.extend(( "-DCMAKE_MSVC_DEBUG_INFORMATION_FORMAT=ProgramDatabase", "-DCMAKE_EXE_LINKER_FLAGS=-DEBUG", @@ -740,6 +744,19 @@ 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: + build_parallel = False + job.cmake_build_type = "Release" + job.setup_ninja = True + job.static_lib = StaticLibType.STATIC_LIB + 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" + job.test_pkg_config = False case _: raise ValueError(f"Unsupported platform={spec.platform}") diff --git a/.github/workflows/generic.yml b/.github/workflows/generic.yml index 9776431e3da83..083859b341625 100644 --- a/.github/workflows/generic.yml +++ b/.github/workflows/generic.yml @@ -93,6 +93,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 a36d82acdd50c..ed14a33470e4c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -76,6 +76,7 @@ include("${SDL3_SOURCE_DIR}/cmake/GetGitRevisionDescription.cmake") include("${SDL3_SOURCE_DIR}/cmake/3rdparty.cmake") include("${SDL3_SOURCE_DIR}/cmake/PreseedMSVCCache.cmake") include("${SDL3_SOURCE_DIR}/cmake/PreseedEmscriptenCache.cmake") +include("${SDL3_SOURCE_DIR}/cmake/PreseedNokiaNGageCache.cmake") SDL_DetectCompiler() SDL_DetectTargetCPUArchitectures(SDL_CPUS) @@ -155,7 +156,7 @@ endif() # The hidraw support doesn't catch Xbox, PS4 and Nintendo controllers, # so we'll just use libusb when it's available. libusb does not support iOS, # so we default to yes on iOS. -if(IOS OR TVOS OR VISIONOS OR WATCHOS OR ANDROID) +if(IOS OR TVOS OR VISIONOS OR WATCHOS OR ANDROID OR NGAGE) set(SDL_HIDAPI_LIBUSB_AVAILABLE FALSE) else() set(SDL_HIDAPI_LIBUSB_AVAILABLE TRUE) @@ -219,7 +220,7 @@ if(EMSCRIPTEN) set(SDL_SHARED_AVAILABLE OFF) endif() -if(VITA OR PSP OR PS2 OR N3DS OR RISCOS) +if(VITA OR PSP OR PS2 OR N3DS OR RISCOS OR NGAGE) set(SDL_SHARED_AVAILABLE OFF) endif() @@ -414,6 +415,24 @@ if(VITA) set_option(VIDEO_VITA_PVR "Build with PSVita PVR gles/gles2 support" OFF) endif() +if (NGAGE) + set(SDL_GPU OFF) + set(SDL_CAMERA OFF) + set(SDL_JOYSTICK OFF) + set(SDL_HAPTIC OFF) + set(SDL_HIDAPI OFF) + set(SDL_POWER OFF) + set(SDL_SENSOR OFF) + set(SDL_DIALOG OFF) + set(SDL_DISKAUDIO OFF) + set(SDL_DUMMYAUDIO OFF) + set(SDL_DUMMYCAMERA OFF) + set(SDL_DUMMYVIDEO OFF) + set(SDL_OFFSCREEN OFF) + set(SDL_RENDER_GPU OFF) + set(SDL_VIRTUAL_JOYSTICK OFF) +endif() + if(NOT (SDL_SHARED OR SDL_STATIC)) message(FATAL_ERROR "SDL_SHARED and SDL_STATIC cannot both be disabled") endif() @@ -2931,6 +2950,73 @@ elseif(N3DS) set(HAVE_SDL_LOCALE TRUE) sdl_glob_sources("${SDL3_SOURCE_DIR}/src/io/n3ds/*.c") + +elseif(NGAGE) + + enable_language(CXX) + + set(SDL_MAIN_USE_CALLBACKS 1) + sdl_glob_sources("${SDL3_SOURCE_DIR}/src/main/ngage/*.c") + sdl_glob_sources("${SDL3_SOURCE_DIR}/src/main/ngage/*.cpp") + sdl_glob_sources("${SDL3_SOURCE_DIR}/src/core/ngage/*.cpp") + set(HAVE_SDL_MAIN_CALLBACKS TRUE) + + if(SDL_AUDIO) + set(SDL_AUDIO_DRIVER_NGAGE 1) + sdl_glob_sources("${SDL3_SOURCE_DIR}/src/audio/ngage/*.c") + sdl_glob_sources("${SDL3_SOURCE_DIR}/src/audio/ngage/*.cpp") + set(HAVE_SDL_AUDIO TRUE) + endif() + + set(SDL_FILESYSTEM_NGAGE 1) + sdl_glob_sources("${SDL3_SOURCE_DIR}/src/filesystem/ngage/*.c") + sdl_glob_sources("${SDL3_SOURCE_DIR}/src/filesystem/ngage/*.cpp") + sdl_glob_sources("${SDL3_SOURCE_DIR}/src/filesystem/posix/*.c") + set(HAVE_SDL_FILESYSTEM TRUE) + + sdl_glob_sources("${SDL3_SOURCE_DIR}/src/locale/ngage/*.cpp") + + if(SDL_RENDER) + set(SDL_VIDEO_RENDER_NGAGE 1) + sdl_glob_sources("${SDL3_SOURCE_DIR}/src/render/ngage/*.c") + endif() + + sdl_glob_sources("${SDL3_SOURCE_DIR}/src/time/ngage/*.cpp") + set(SDL_TIME_NGAGE 1) + + sdl_glob_sources("${SDL3_SOURCE_DIR}/src/render/ngage/*.cpp") + sdl_glob_sources("${SDL3_SOURCE_DIR}/src/time/unix/*.c") + + set(SDL_TIMER_NGAGE 1) + sdl_glob_sources("${SDL3_SOURCE_DIR}/src/timer/ngage/*.cpp") + + set(SDL_VIDEO_DRIVER_NGAGE 1) + sdl_glob_sources("${SDL3_SOURCE_DIR}/src/video/ngage/*.c") + set(HAVE_SDL_TIMERS TRUE) + + set(SDL_FSOPS_POSIX 1) + set(SDL_LEAN_AND_MEAN 1) + sdl_link_dependency(ngage + LINK_OPTIONS "SHELL:-s MAIN_COMPAT=0" + PKG_CONFIG_LINK_OPTIONS "-s;MAIN_COMPAT=0" + LIBS + NRenderer + 3dtypes + cone + libgcc + libgcc_ngage + mediaclientaudiostream + charconv + bitgdi + euser + estlib + ws32 + hal + fbscli + efsrv + scdv + gdi + ) endif() sdl_sources(${SDL3_SOURCE_DIR}/src/dialog/SDL_dialog.c) @@ -3111,8 +3197,8 @@ endif() # We always need to have threads and timers around if(NOT HAVE_SDL_THREADS) - # The emscripten platform has been carefully vetted to work without threads - if(EMSCRIPTEN) + # The Emscripten and N-Gage platform has been carefully vetted to work without threads + if(EMSCRIPTEN OR NGAGE) set(SDL_THREADS_DISABLED 1) sdl_glob_sources("${SDL3_SOURCE_DIR}/src/thread/generic/*.c") else() diff --git a/cmake/PreseedNokiaNGageCache.cmake b/cmake/PreseedNokiaNGageCache.cmake new file mode 100644 index 0000000000000..298177ba15d59 --- /dev/null +++ b/cmake/PreseedNokiaNGageCache.cmake @@ -0,0 +1,189 @@ +if(NGAGESDK) + function(SDL_Preseed_CMakeCache) + set(COMPILER_SUPPORTS_ARMNEON "" CACHE INTERNAL "Test COMPILER_SUPPORTS_ARMNEON") + set(COMPILER_SUPPORTS_FDIAGNOSTICS_COLOR_ALWAYS "" CACHE INTERNAL "Test COMPILER_SUPPORTS_FDIAGNOSTICS_COLOR_ALWAYS") + set(COMPILER_SUPPORTS_SYNC_LOCK_TEST_AND_SET "" CACHE INTERNAL "Test COMPILER_SUPPORTS_SYNC_LOCK_TEST_AND_SET") + set(HAVE_CLANG_COMMENT_BLOCK_COMMANDS "" CACHE INTERNAL "Test HAVE_CLANG_COMMENT_BLOCK_COMMANDS") + set(HAVE_ALLOCA_H "" CACHE INTERNAL "Have include alloca.h") + set(HAVE_LIBM "1" CACHE INTERNAL "Have library m") + set(HAVE_POSIX_SPAWN "" CACHE INTERNAL "Have symbol posix_spawn") + set(HAVE_MALLOC "1" CACHE INTERNAL "Have include malloc.h") + set(LIBC_HAS_ABS "1" CACHE INTERNAL "Have symbol abs") + set(LIBC_HAS_ACOS "1" CACHE INTERNAL "Have symbol acos") + set(LIBC_HAS_ACOSF "" CACHE INTERNAL "Have symbol acosf") + set(LIBC_HAS_ASIN "1" CACHE INTERNAL "Have symbol asin") + set(LIBC_HAS_ASINF "" CACHE INTERNAL "Have symbol asinf") + set(LIBC_HAS_ATAN "1" CACHE INTERNAL "Have symbol atan") + set(LIBC_HAS_ATAN2 "1" CACHE INTERNAL "Have symbol atan2") + set(LIBC_HAS_ATAN2F "" CACHE INTERNAL "Have symbol atan2f") + set(LIBC_HAS_ATANF "" CACHE INTERNAL "Have symbol atanf") + set(LIBC_HAS_ATOF "" CACHE INTERNAL "Have symbol atof") + set(LIBC_HAS_ATOI "" CACHE INTERNAL "Have symbol atoi") + set(LIBC_HAS_BCOPY "1" CACHE INTERNAL "Have symbol bcopy") + set(LIBC_HAS_CALLOC "" CACHE INTERNAL "Have symbol calloc") + set(LIBC_HAS_CEIL "1" CACHE INTERNAL "Have symbol ceil") + set(LIBC_HAS_CEILF "" CACHE INTERNAL "Have symbol ceilf") + set(LIBC_HAS_COPYSIGN "1" CACHE INTERNAL "Have symbol copysign") + set(LIBC_HAS_COPYSIGNF "1" CACHE INTERNAL "Have symbol copysignf") + set(LIBC_HAS_COS "1" CACHE INTERNAL "Have symbol cos") + set(LIBC_HAS_COSF "" CACHE INTERNAL "Have symbol cosf") + set(LIBC_HAS_EXP "1" CACHE INTERNAL "Have symbol exp") + set(LIBC_HAS_EXPF "" CACHE INTERNAL "Have symbol expf") + set(LIBC_HAS_FABS "1" CACHE INTERNAL "Have symbol fabs") + set(LIBC_HAS_FABSF "1" CACHE INTERNAL "Have symbol fabsf") + set(LIBC_HAS_FLOAT_H "1" CACHE INTERNAL "Have include float.h") + set(LIBC_HAS_FLOOR "1" CACHE INTERNAL "Have symbol floor") + set(LIBC_HAS_FLOORF "" CACHE INTERNAL "Have symbol floorf") + set(LIBC_HAS_FMOD "" CACHE INTERNAL "Have symbol fmod") + set(LIBC_HAS_FMODF "" CACHE INTERNAL "Have symbol fmodf") + set(LIBC_HAS_FOPEN64 "" CACHE INTERNAL "Have symbol fopen64") + set(LIBC_HAS_FREE "1" CACHE INTERNAL "Have symbol free") + set(LIBC_HAS_FSEEKO "" CACHE INTERNAL "Have symbol fseeko") + set(LIBC_HAS_FSEEKO64 "" CACHE INTERNAL "Have symbol fseeko64") + set(LIBC_HAS_GETENV "" CACHE INTERNAL "Have symbol getenv") + set(LIBC_HAS_ICONV_H "" CACHE INTERNAL "Have include iconv.h") + set(LIBC_HAS_INDEX "1" CACHE INTERNAL "Have symbol index") + set(LIBC_HAS_INTTYPES_H "1" CACHE INTERNAL "Have include inttypes.h") + set(LIBC_HAS_ISINF "1" CACHE INTERNAL "Have include isinf(double)") + set(LIBC_ISINF_HANDLES_FLOAT "1" CACHE INTERNAL "Have include isinf(float)") + set(LIBC_HAS_ISINFF "1" CACHE INTERNAL "Have include isinff(float)") + set(LIBC_HAS_ISNAN "1" CACHE INTERNAL "Have include isnan(double)") + set(LIBC_ISNAN_HANDLES_FLOAT "1" CACHE INTERNAL "Have include isnan(float)") + set(LIBC_HAS_ISNANF "1" CACHE INTERNAL "Have include isnanf(float)") + set(LIBC_HAS_ITOA "" CACHE INTERNAL "Have symbol itoa") + set(LIBC_HAS_LIMITS_H "1" CACHE INTERNAL "Have include limits.h") + set(LIBC_HAS_LOG "1" CACHE INTERNAL "Have symbol log") + set(LIBC_HAS_LOG10 "" CACHE INTERNAL "Have symbol log10") + set(LIBC_HAS_LOG10F "" CACHE INTERNAL "Have symbol log10f") + set(LIBC_HAS_LOGF "" CACHE INTERNAL "Have symbol logf") + set(LIBC_HAS_LROUND "" CACHE INTERNAL "Have symbol lround") + set(LIBC_HAS_LROUNDF "" CACHE INTERNAL "Have symbol lroundf") + set(LIBC_HAS_MALLOC "1" CACHE INTERNAL "Have symbol malloc") + set(LIBC_HAS_MALLOC_H "" CACHE INTERNAL "Have include malloc.h") + set(LIBC_HAS_MATH_H "1" CACHE INTERNAL "Have include math.h") + set(LIBC_HAS_MEMCMP "1" CACHE INTERNAL "Have symbol memcmp") + set(LIBC_HAS_MEMCPY "" CACHE INTERNAL "Have symbol memcpy") + set(LIBC_HAS_MEMMOVE "" CACHE INTERNAL "Have symbol memmove") + set(LIBC_HAS_MEMORY_H "" CACHE INTERNAL "Have include memory.h") + set(LIBC_HAS_MEMSET "" CACHE INTERNAL "Have symbol memset") + set(LIBC_HAS_MODF "1" CACHE INTERNAL "Have symbol modf") + set(LIBC_HAS_MODFF "" CACHE INTERNAL "Have symbol modff") + set(LIBC_HAS_POW "1" CACHE INTERNAL "Have symbol pow") + set(LIBC_HAS_POWF "" CACHE INTERNAL "Have symbol powf") + set(LIBC_HAS_PUTENV "" CACHE INTERNAL "Have symbol putenv") + set(LIBC_HAS_REALLOC "" CACHE INTERNAL "Have symbol realloc") + set(LIBC_HAS_RINDEX "1" CACHE INTERNAL "Have symbol rindex") + set(LIBC_HAS_ROUND "" CACHE INTERNAL "Have symbol round") + set(LIBC_HAS_ROUNDF "" CACHE INTERNAL "Have symbol roundf") + set(LIBC_HAS_SCALBN "1" CACHE INTERNAL "Have symbol scalbn") + set(LIBC_HAS_SCALBNF "" CACHE INTERNAL "Have symbol scalbnf") + set(LIBC_HAS_SETENV "" CACHE INTERNAL "Have symbol setenv") + set(LIBC_HAS_SIGNAL_H "" CACHE INTERNAL "Have include signal.h") + set(LIBC_HAS_SIN "1" CACHE INTERNAL "Have symbol sin") + set(LIBC_HAS_SINF "" CACHE INTERNAL "Have symbol sinf") + set(LIBC_HAS_SQR "" CACHE INTERNAL "Have symbol sqr") + set(LIBC_HAS_SQRT "1" CACHE INTERNAL "Have symbol sqrt") + set(LIBC_HAS_SQRTF "" CACHE INTERNAL "Have symbol sqrtf") + set(LIBC_HAS_SSCANF "1" CACHE INTERNAL "Have symbol sscanf") + set(LIBC_HAS_STDARG_H "1" CACHE INTERNAL "Have include stdarg.h") + set(LIBC_HAS_STDBOOL_H "1" CACHE INTERNAL "Have include stdbool.h") + set(LIBC_HAS_STDDEF_H "1" CACHE INTERNAL "Have include stddef.h") + set(LIBC_HAS_STDINT_H "1" CACHE INTERNAL "Have include stdint.h") + set(LIBC_HAS_STDIO_H "1" CACHE INTERNAL "Have include stdio.h") + set(LIBC_HAS_STDLIB_H "1" CACHE INTERNAL "Have include stdlib.h") + set(LIBC_HAS_STRCASESTR "" CACHE INTERNAL "Have symbol strcasestr") + set(LIBC_HAS_STRCHR "1" CACHE INTERNAL "Have symbol strchr") + set(LIBC_HAS_STRCMP "1" CACHE INTERNAL "Have symbol strcmp") + set(LIBC_HAS_STRINGS_H "" CACHE INTERNAL "Have include strings.h") + set(LIBC_HAS_STRING_H "1" CACHE INTERNAL "Have include string.h") + set(LIBC_HAS_STRLCAT "" CACHE INTERNAL "Have symbol strlcat") + set(LIBC_HAS_STRLCPY "" CACHE INTERNAL "Have symbol strlcpy") + set(LIBC_HAS_STRLEN "1" CACHE INTERNAL "Have symbol strlen") + set(LIBC_HAS_STRNCMP "1" CACHE INTERNAL "Have symbol strncmp") + set(LIBC_HAS_STRNLEN "" CACHE INTERNAL "Have symbol strnlen") + set(LIBC_HAS_STRNSTR "" CACHE INTERNAL "Have symbol strnstr") + set(LIBC_HAS_STRPBRK "1" CACHE INTERNAL "Have symbol strpbrk") + set(LIBC_HAS_STRRCHR "1" CACHE INTERNAL "Have symbol strrchr") + set(LIBC_HAS_STRSTR "1" CACHE INTERNAL "Have symbol strstr") + set(LIBC_HAS_STRTOD "" CACHE INTERNAL "Have symbol strtod") + set(LIBC_HAS_STRTOK_R "" CACHE INTERNAL "Have symbol strtok_r") + set(LIBC_HAS_STRTOL "" CACHE INTERNAL "Have symbol strtol") + set(LIBC_HAS_STRTOLL "" CACHE INTERNAL "Have symbol strtoll") + set(LIBC_HAS_STRTOUL "" CACHE INTERNAL "Have symbol strtoul") + set(LIBC_HAS_STRTOULL "" CACHE INTERNAL "Have symbol strtoull") + set(LIBC_HAS_SYS_TYPES_H "1" CACHE INTERNAL "Have include sys/types.h") + set(LIBC_HAS_TAN "1" CACHE INTERNAL "Have symbol tan") + set(LIBC_HAS_TANF "" CACHE INTERNAL "Have symbol tanf") + set(LIBC_HAS_TIME_H "1" CACHE INTERNAL "Have include time.h") + set(LIBC_HAS_TRUNC "" CACHE INTERNAL "Have symbol trunc") + set(LIBC_HAS_TRUNCF "" CACHE INTERNAL "Have symbol truncf") + set(LIBC_HAS_UNSETENV "" CACHE INTERNAL "Have symbol unsetenv") + set(LIBC_HAS_VSNPRINTF "" CACHE INTERNAL "Have symbol vsnprintf") + set(LIBC_HAS_VSSCANF "" CACHE INTERNAL "Have symbol vsscanf") + set(LIBC_HAS_WCHAR_H "1" CACHE INTERNAL "Have include wchar.h") + set(LIBC_HAS_WCSCMP "" CACHE INTERNAL "Have symbol wcscmp") + set(LIBC_HAS_WCSDUP "" CACHE INTERNAL "Have symbol wcsdup") + set(LIBC_HAS_WCSLCAT "" CACHE INTERNAL "Have symbol wcslcat") + set(LIBC_HAS_WCSLCPY "" CACHE INTERNAL "Have symbol wcslcpy") + set(LIBC_HAS_WCSLEN "" CACHE INTERNAL "Have symbol wcslen") + set(LIBC_HAS_WCSNCMP "" CACHE INTERNAL "Have symbol wcsncmp") + set(LIBC_HAS_WCSNLEN "" CACHE INTERNAL "Have symbol wcsnlen") + set(LIBC_HAS_WCSSTR "" CACHE INTERNAL "Have symbol wcsstr") + set(LIBC_HAS_WCSTOL "" CACHE INTERNAL "Have symbol wcstol") + set(LIBC_HAS__EXIT "" CACHE INTERNAL "Have symbol _Exit") + set(LIBC_HAS__I64TOA "" CACHE INTERNAL "Have symbol _i64toa") + set(LIBC_HAS__LTOA "" CACHE INTERNAL "Have symbol _ltoa") + set(LIBC_HAS__STRREV "" CACHE INTERNAL "Have symbol _strrev") + set(LIBC_HAS__UI64TOA "" CACHE INTERNAL "Have symbol _ui64toa") + set(LIBC_HAS__UITOA "" CACHE INTERNAL "Have symbol _uitoa") + set(LIBC_HAS__ULTOA "" CACHE INTERNAL "Have symbol _ultoa") + set(LIBC_HAS__WCSDUP "" CACHE INTERNAL "Have symbol _wcsdup") + set(LIBC_IS_GLIBC "" CACHE INTERNAL "Have symbol __GLIBC__") + set(_ALLOCA_IN_MALLOC_H "" CACHE INTERNAL "Have symbol _alloca") + set(HAVE_GCC_WALL "1" CACHE INTERNAL "Test HAVE_GCC_WALL") + set(HAVE_GCC_WUNDEF "1" CACHE INTERNAL "Test HAVE_GCC_WUNDEF") + set(HAVE_GCC_WFLOAT_CONVERSION "" CACHE INTERNAL "Test HAVE_GCC_WFLOAT_CONVERSION") + set(HAVE_GCC_NO_STRICT_ALIASING "1" CACHE INTERNAL "Test HAVE_GCC_NO_STRICT_ALIASING") + set(HAVE_GCC_WDOCUMENTATION "" CACHE INTERNAL "Test HAVE_GCC_WDOCUMENTATION") + set(HAVE_GCC_WDOCUMENTATION_UNKNOWN_COMMAND "" CACHE INTERNAL "Test HAVE_GCC_WDOCUMENTATION_UNKNOWN_COMMAND") + set(HAVE_GCC_COMMENT_BLOCK_COMMANDS "" CACHE INTERNAL "Test HAVE_GCC_COMMENT_BLOCK_COMMANDS") + set(HAVE_GCC_WSHADOW "1" CACHE INTERNAL "Test HAVE_GCC_WSHADOW") + set(HAVE_GCC_WUNUSED_LOCAL_TYPEDEFS "" CACHE INTERNAL "Test HAVE_GCC_WUNUSED_LOCAL_TYPEDEFS") + set(HAVE_GCC_WIMPLICIT_FALLTHROUGH "" CACHE INTERNAL "Test HAVE_GCC_WIMPLICIT_FALLTHROUGH") + set(HAVE_GCC_FVISIBILITY "" CACHE INTERNAL "Test HAVE_GCC_FVISIBILITY") + set(HAVE_ST_MTIM "" CACHE INTERNAL "Test HAVE_ST_MTIM") + #set(HAVE_O_CLOEXEC "" CACHE INTERNAL "Test HAVE_O_CLOEXEC") + #set(COMPILER_SUPPORTS_FDIAGNOSTICS_COLOR "" CACHE INTERNAL "Test COMPILER_SUPPORTS_FDIAGNOSTICS_COLOR") + set(COMPILER_SUPPORTS_GCC_ATOMICS "" CACHE INTERNAL "Test COMPILER_SUPPORTS_GCC_ATOMICS") + set(LINKER_SUPPORTS_VERSION_SCRIPT "" CACHE INTERNAL "Test LINKER_SUPPORTS_VERSION_SCRIPT") + set(LINKER_SUPPORTS_WL_NO_UNDEFINED "" CACHE INTERNAL "Test LINKER_SUPPORTS_WL_NO_UNDEFINED") + set(ICONV_IN_LIBC "" CACHE INTERNAL "Test ICONV_IN_LIBC") + set(ICONV_IN_LIBICONV "" CACHE INTERNAL "Test ICONV_IN_LIBICONV") + #set(LIBC_HAS_WORKING_LIBUNWIND "" CACHE INTERNAL "Test LIBC_HAS_WORKING_LIBUNWIND") + #set(LIBUNWIND_HAS_WORKINGLIBUNWIND "" CACHE INTERNAL "Test LIBUNWIND_HAS_WORKINGLIBUNWIND") + set(HAVE_GETPAGESIZE "" CACHE INTERNAL "Have symbol getpagesize") + set(HAVE_SIGACTION "" CACHE INTERNAL "Have symbol sigaction") + set(HAVE_SA_SIGACTION "" CACHE INTERNAL "Have symbol sa_sigaction") + set(HAVE_SETJMP "" CACHE INTERNAL "Have symbol setjmp") + set(HAVE_NANOSLEEP "" CACHE INTERNAL "Have symbol nanosleep") + set(HAVE_GMTIME_R "" CACHE INTERNAL "Have symbol gmtime_r") + set(HAVE_LOCALTIME_R "" CACHE INTERNAL "Have symbol localtime_r") + set(HAVE_NL_LANGINFO "" CACHE INTERNAL "Have symbol nl_langinfo") + set(HAVE_SYSCONF "" CACHE INTERNAL "Have symbol sysconf") + set(HAVE_SYSCTLBYNAME "" CACHE INTERNAL "Have symbol sysctlbyname") + set(HAVE_GETAUXVAL "" CACHE INTERNAL "Have symbol getauxval") + set(HAVE_ELF_AUX_INFO "" CACHE INTERNAL "Have symbol elf_aux_info") + set(HAVE_POLL "" CACHE INTERNAL "Have symbol poll") + set(HAVE_MEMFD_CREATE "" CACHE INTERNAL "Have symbol memfd_create") + set(HAVE_POSIX_FALLOCATE "" CACHE INTERNAL "Have symbol posix_fallocate") + set(HAVE_DLOPEN_IN_LIBC "" CACHE INTERNAL "Have symbol dlopen") + + set(HAVE_GETHOSTNAME "" CACHE INTERNAL "Have symbol gethostname") + set(HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCHDIR "" CACHE INTERNAL "Have symbol addchdir") + set(HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCHDIR_NP "" CACHE INTERNAL "Have symbol addchdir_np") + set(HAVE_FDATASYNC "" CACHE INTERNAL "Have symbol fdatasync") + + set(HAVE_SDL_FSOPS "1" CACHE INTERNAL "Enable SDL_FSOPS") + set(HAVE_SDL_LOCALE "1" CACHE INTERNAL "Enable SDL_LOCALE") + endfunction() +endif() diff --git a/cmake/sdlplatform.cmake b/cmake/sdlplatform.cmake index 677b187073ad2..60e0e77fd907a 100644 --- a/cmake/sdlplatform.cmake +++ b/cmake/sdlplatform.cmake @@ -22,6 +22,8 @@ function(SDL_DetectCMakePlatform) set(sdl_cmake_platform Haiku) elseif(NINTENDO_3DS) set(sdl_cmake_platform n3ds) + elseif(NGAGESDK) + set(sdl_cmake_platform ngage) elseif(PS2) set(sdl_cmake_platform ps2) elseif(VITA) diff --git a/cmake/test/CMakeLists.txt b/cmake/test/CMakeLists.txt index e3766f0e16033..ffaa1977a00fc 100644 --- a/cmake/test/CMakeLists.txt +++ b/cmake/test/CMakeLists.txt @@ -96,12 +96,14 @@ if(TEST_STATIC) add_executable(gui-static WIN32 main_gui.c) target_link_libraries(gui-static PRIVATE SDL3::SDL3-static) - # Assume SDL library has been built with `set(CMAKE_POSITION_INDEPENDENT_CODE ON)` - add_library(sharedlib-static SHARED main_lib.c) - target_link_libraries(sharedlib-static PRIVATE SDL3::SDL3-static) - generate_export_header(sharedlib-static EXPORT_MACRO_NAME MYLIBRARY_EXPORT) - target_compile_definitions(sharedlib-static PRIVATE "EXPORT_HEADER=\"${CMAKE_CURRENT_BINARY_DIR}/sharedlib-static_export.h\"") - set_target_properties(sharedlib-static PROPERTIES C_VISIBILITY_PRESET "hidden") + if(TEST_SHARED) + # Assume SDL library has been built with `set(CMAKE_POSITION_INDEPENDENT_CODE ON)` + add_library(sharedlib-static SHARED main_lib.c) + target_link_libraries(sharedlib-static PRIVATE SDL3::SDL3-static) + generate_export_header(sharedlib-static EXPORT_MACRO_NAME MYLIBRARY_EXPORT) + target_compile_definitions(sharedlib-static PRIVATE "EXPORT_HEADER=\"${CMAKE_CURRENT_BINARY_DIR}/sharedlib-static_export.h\"") + set_target_properties(sharedlib-static PROPERTIES C_VISIBILITY_PRESET "hidden") + endif() if(TEST_TEST) add_executable(sdltest-static sdltest.c) diff --git a/cmake/test/main_gui.c b/cmake/test/main_gui.c index 18ed10107c417..c0c4f9010cc24 100644 --- a/cmake/test/main_gui.c +++ b/cmake/test/main_gui.c @@ -1,24 +1,37 @@ -#include +#define SDL_MAIN_USE_CALLBACKS #include +#include + +static SDL_Window *window; -int main(int argc, char *argv[]) +SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event) +{ + return SDL_APP_CONTINUE; +} + +SDL_AppResult SDL_AppIterate(void *appstate) { - SDL_Window *window = NULL; SDL_Surface *screenSurface = NULL; + screenSurface = SDL_GetWindowSurface(window); + SDL_FillSurfaceRect(screenSurface, NULL, SDL_MapSurfaceRGB(screenSurface, 0xff, 0xff, 0xff)); + SDL_UpdateWindowSurface(window); + return SDL_APP_CONTINUE; +} + +SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[]) +{ if (!SDL_Init(SDL_INIT_VIDEO)) { SDL_Log("Could not initialize SDL: %s", SDL_GetError()); - return 1; + return SDL_APP_FAILURE; } window = SDL_CreateWindow("Hello SDL", 640, 480, 0); if (!window) { SDL_Log("could not create window: %s", SDL_GetError()); - return 1; + return SDL_APP_FAILURE; } - screenSurface = SDL_GetWindowSurface(window); - SDL_FillSurfaceRect(screenSurface, NULL, SDL_MapSurfaceRGB(screenSurface, 0xff, 0xff, 0xff)); - SDL_UpdateWindowSurface(window); - SDL_Delay(100); + return SDL_APP_CONTINUE; +} + +void SDL_AppQuit(void *appstate, SDL_AppResult result) { SDL_DestroyWindow(window); - SDL_Quit(); - return 0; } diff --git a/cmake/test/sdltest.c b/cmake/test/sdltest.c index f598a98c5cb8a..baf8e9b5f10ea 100644 --- a/cmake/test/sdltest.c +++ b/cmake/test/sdltest.c @@ -1,9 +1,24 @@ +#define SDL_MAIN_USE_CALLBACKS #include +#include #include +SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event) +{ + return SDL_APP_SUCCESS; +} + +SDL_AppResult SDL_AppIterate(void *appstate) +{ + return SDL_APP_SUCCESS; +} -int main(int argc, char *argv[]) { +SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[]) +{ SDLTest_CommonState state; SDLTest_CommonDefaultArgs(&state, argc, argv); - return 0; + return SDL_APP_SUCCESS; +} + +void SDL_AppQuit(void *appstate, SDL_AppResult result) { } diff --git a/docs/README-ngage.md b/docs/README-ngage.md index 84192b01abaee..72d656c4bce35 100644 --- a/docs/README-ngage.md +++ b/docs/README-ngage.md @@ -1,5 +1,64 @@ -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) + +- [Anonymous Maarten](https://github.com/madebr) + +Many thanks to: + +- icculus and slouken for always making room for us — even when we show up in 2025 + still waving the N-Gage flag. + +- The Nokia N-Gage [Discord community](https://discord.gg/dbUzqJ26vs) + who keeps the platform alive. + +- The staff and supporters of the + [Suomen pelimuseo](https://www.vapriikki.fi/nayttelyt/fantastinen-floppi/), and + to Heikki Jungmann, for their ongoing love and dedication for the Nokia N-Gage — you + guys are awesome! + +## History + +When SDL support was discontinued due to the lack of C99 support at the time, +this version was rebuilt from the ground up after resolving the compiler issues. + +In contrast to the earlier SDL2 port, this version features a dedicated rendering +backend and a functional, albeit limited, audio interface. Support for the +software renderer has been removed. + +The outcome is a significantly leaner and more efficient SDL port, which we hope +will breathe new life into this beloved yet obscure platform. + +## To the Stubborn Legends of the DC Scene + +This port is lovingly dedicated to the ever-nostalgic Dreamcast homebrew scene — +because if we managed to pull this off for the N-Gage (yes, the N-Gage), surely +you guys can stop clinging to SDL2 like it's a rare Shenmue prototype and finally +make the leap to SDL3. It’s 2025, not 1999 — and let’s be honest, you’re rocking +a state-of-the-art C23 compiler. The irony writes itself. + +## Existing Issues and Limitations + +- For now, the new + [SDL3 main callbacks](https://wiki.libsdl.org/SDL3/README/main-functions#how-to-use-main-callbacks-in-sdl3) + are not optional and must be used. This is important as the callbacks + are optional on other platforms. + +- 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. + +- Because the audio sample rate can change during phone calls, the sample + rate is currently fixed at 8kHz to ensure stable behavior. Although + dynamically adjusting the sample rate is theoretically possible, the + current implementation doesn't support it yet. This limitation is + expected to be resolved in a future update. + +- Dependency tracking is currently non-functional. diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 6efc021546f96..1c5a40e69e50c 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -111,6 +111,10 @@ macro(add_sdl_example_executable TARGET) elseif(EMSCRIPTEN) set_property(TARGET ${TARGET} PROPERTY SUFFIX ".html") target_link_options(${TARGET} PRIVATE -sALLOW_MEMORY_GROWTH=1) + elseif(NGAGE) + string(MD5 TARGET_MD5 "${TARGET}") + string(SUBSTRING "${TARGET_MD5}" 0 8 TARGET_MD5_8) + target_link_options(${TARGET} PRIVATE "SHELL:-s UID3=0x${TARGET_MD5_8}") endif() if(OPENGL_FOUND) 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..8f8c096a00f64 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.4.0. + */ +#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.h.cmake b/include/build_config/SDL_build_config.h.cmake index c8c02894a13fb..4d013c108c3ed 100644 --- a/include/build_config/SDL_build_config.h.cmake +++ b/include/build_config/SDL_build_config.h.cmake @@ -277,6 +277,7 @@ #cmakedefine SDL_AUDIO_DRIVER_PSP 1 #cmakedefine SDL_AUDIO_DRIVER_PS2 1 #cmakedefine SDL_AUDIO_DRIVER_N3DS 1 +#cmakedefine SDL_AUDIO_DRIVER_NGAGE 1 #cmakedefine SDL_AUDIO_DRIVER_QNX 1 #cmakedefine SDL_AUDIO_DRIVER_PRIVATE 1 @@ -365,6 +366,7 @@ #cmakedefine SDL_TIME_PSP 1 #cmakedefine SDL_TIME_PS2 1 #cmakedefine SDL_TIME_N3DS 1 +#cmakedefine SDL_TIME_NGAGE 1 /* Enable various timer systems */ #cmakedefine SDL_TIMER_HAIKU 1 @@ -387,6 +389,7 @@ #cmakedefine SDL_VIDEO_DRIVER_KMSDRM_DYNAMIC @SDL_VIDEO_DRIVER_KMSDRM_DYNAMIC@ #cmakedefine SDL_VIDEO_DRIVER_KMSDRM_DYNAMIC_GBM @SDL_VIDEO_DRIVER_KMSDRM_DYNAMIC_GBM@ #cmakedefine SDL_VIDEO_DRIVER_N3DS 1 +#cmakedefine SDL_VIDEO_DRIVER_NGAGE 1 #cmakedefine SDL_VIDEO_DRIVER_OFFSCREEN 1 #cmakedefine SDL_VIDEO_DRIVER_PS2 1 #cmakedefine SDL_VIDEO_DRIVER_PSP 1 @@ -438,6 +441,7 @@ #cmakedefine SDL_VIDEO_RENDER_VULKAN 1 #cmakedefine SDL_VIDEO_RENDER_OGL 1 #cmakedefine SDL_VIDEO_RENDER_OGL_ES2 1 +#cmakedefine SDL_VIDEO_RENDER_NGAGE 1 #cmakedefine SDL_VIDEO_RENDER_PS2 1 #cmakedefine SDL_VIDEO_RENDER_PSP 1 #cmakedefine SDL_VIDEO_RENDER_VITA_GXM 1 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..0343b992f995c --- /dev/null +++ b/include/build_config/SDL_build_config_ngage.h @@ -0,0 +1,71 @@ +/* + 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_FILESYSTEM_DUMMY 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_TIME_NGAGE 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 + +#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 9dff4ca9999d3..f10598c32a3ca 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 3e2936cbae267..ad1ea7e2ae61c 100644 --- a/src/audio/SDL_sysaudio.h +++ b/src/audio/SDL_sysaudio.h @@ -393,6 +393,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..3595bd702cd05 --- /dev/null +++ b/src/audio/ngage/SDL_ngageaudio.c @@ -0,0 +1,103 @@ +/* + 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..9acf030c38ab3 --- /dev/null +++ b/src/audio/ngage/SDL_ngageaudio.cpp @@ -0,0 +1,368 @@ +/* + 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_ngageaudio.h" +#include "../SDL_sysaudio.h" +#include "SDL_internal.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..dda2c91e92e67 --- /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..e0635f5a75738 --- /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_sysaudio.h" +#include "SDL_ngageaudio.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.cpp b/src/core/ngage/SDL_ngage.cpp new file mode 100644 index 0000000000000..5f14af1ddc7f6 --- /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..66c4c60ec192d --- /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 b836532800e3c..fc747c1944268 100644 --- a/src/cpuinfo/SDL_cpuinfo.c +++ b/src/cpuinfo/SDL_cpuinfo.c @@ -421,6 +421,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) { @@ -468,6 +474,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..4ab0eb737c7cb --- /dev/null +++ b/src/locale/ngage/SDL_syslocale.cpp @@ -0,0 +1,307 @@ +/* + 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_syslocale.h" +#include "SDL_internal.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: + locale = "fr_CH"; + break; + case ELangBelgianFrench: + locale = "fr_BE"; + break; + 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: + locale = "pt_PT"; + break; + case ELangBrazilianPortuguese: + locale = "pt_BR"; + 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: + locale = "nl_NL"; + break; + case ELangBelgianFlemish: + locale = "nl_BE"; + 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..bb6ceac09b671 --- /dev/null +++ b/src/main/ngage/SDL_sysmain_main.cpp @@ -0,0 +1,199 @@ +/* + 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" + +extern SDL_AppResult SDL_AppInit(void** appstate, int argc, char* argv[]); +extern SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event); +extern SDL_AppResult SDL_AppIterate(void* appstate); +extern void SDL_AppQuit(void* appstate, SDL_AppResult result); + +#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..4730dc7d867ef --- /dev/null +++ b/src/main/ngage/SDL_sysmain_main.hpp @@ -0,0 +1,46 @@ +/* + 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_sysmain_main_hpp_ +#define SDL_sysmain_main_hpp_ + +#include + +class CSDLmain : public CActive +{ +public: + static CSDLmain* NewL(); + ~CSDLmain(); + + void Start(); + +protected: + void DoCancel() ; + void RunL(); + +private: + CSDLmain(); + void ConstructL(); + SDL_AppResult iResult; +}; + +#endif // SDL_sysmain_main_hpp_ diff --git a/src/render/SDL_render.c b/src/render/SDL_render.c index 38ee1ae3be3f3..0a088a8b43fc3 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 diff --git a/src/render/SDL_sysrender.h b/src/render/SDL_sysrender.h index 5109b93451648..d42f536294559 100644 --- a/src/render/SDL_sysrender.h +++ b/src/render/SDL_sysrender.h @@ -357,6 +357,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..b78a230bb82ba --- /dev/null +++ b/src/render/ngage/SDL_render_ngage.c @@ -0,0 +1,544 @@ +/* + 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_sysrender.h" +#include "SDL_render_ngage_c.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..1717f3635b414 --- /dev/null +++ b/src/render/ngage/SDL_render_ngage.cpp @@ -0,0 +1,744 @@ +/* + 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 "../../events/SDL_keyboard_c.h" +#include "../SDL_sysrender.h" +#include "SDL_internal.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..2adab73398d74 --- /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); +void NGAGE_SetClipRect(const SDL_Rect *rect); +void NGAGE_SetDrawColor(const Uint32 color); +void NGAGE_PumpEventsInternal(void); +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..958901f288aba --- /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 "SDL_render_ngage_c.h" +#include +#include +#include + +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 79679a1f44405..77b99b5f312c1 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..0c4efc1ac3772 100644 --- a/src/stdlib/SDL_vacopy.h +++ b/src/stdlib/SDL_vacopy.h @@ -20,11 +20,12 @@ */ // Do our best to make sure va_copy is working -#if defined(_MSC_VER) && _MSC_VER <= 1800 +#if (defined(_MSC_VER) && _MSC_VER <= 1800) || defined(__SYMBIAN32__) // Visual Studio 2013 tries to link with _vacopy in the C runtime. Newer versions do an inline assignment #undef va_copy #define va_copy(dst, src) dst = src #elif defined(__GNUC__) && (__GNUC__ < 3) #define va_copy(dst, src) __va_copy(dst, src) + #endif diff --git a/src/time/ngage/SDL_systime.cpp b/src/time/ngage/SDL_systime.cpp new file mode 100644 index 0000000000000..e8c40f0fd7ab9 --- /dev/null +++ b/src/time/ngage/SDL_systime.cpp @@ -0,0 +1,184 @@ +/* + 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" + +#ifdef SDL_TIME_NGAGE + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +static TTime UnixEpoch(); + +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; + } +} + +bool SDL_GetCurrentTime(SDL_Time *ticks) +{ + if (!ticks) { + return SDL_InvalidParamError("ticks"); + } + + TTime now; + now.UniversalTime(); + + TTimeIntervalMicroSeconds interval = now.MicroSecondsFrom(UnixEpoch()); + TInt64 interval_ns = interval.Int64() * 1000; + Uint32 ns_low = interval_ns.Low(); + Uint32 ns_high = interval_ns.High(); + + *ticks = ((Uint64)ns_high << 32) | ns_low; + + return true; +} + +static TTime UnixEpoch() +{ + _LIT(KUnixEpoch, "19700101:000000.000000"); + TTime epochTime; + epochTime.Set(KUnixEpoch); + return epochTime; +} + +#ifdef __cplusplus +} +#endif + +#endif // SDL_TIME_NGAGE diff --git a/src/timer/ngage/SDL_systimer.cpp b/src/timer/ngage/SDL_systimer.cpp new file mode 100644 index 0000000000000..d2e1cce9049ca --- /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 f04c91436ac85..d4cbd66460dba 100644 --- a/src/video/SDL_sysvideo.h +++ b/src/video/SDL_sysvideo.h @@ -531,6 +531,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 294fad5d10e51..8cd1a4154e741 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..6ae9a75dd5977 --- /dev/null +++ b/src/video/ngage/SDL_ngagevideo.c @@ -0,0 +1,175 @@ +/* + 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 diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 164e2a142369a..0b46d9391abe0 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -32,8 +32,6 @@ if(NOT (MSVC AND SDL_CPU_ARM64)) find_package(OpenGL) endif() -set(SDL_TEST_EXECUTABLES) - add_library(sdltests_utils OBJECT testutils.c ) @@ -101,7 +99,7 @@ define_property(TARGET PROPERTY SDL_NONINTERACTIVE BRIEF_DOCS "If true, target i define_property(TARGET PROPERTY SDL_NONINTERACTIVE_ARGUMENTS BRIEF_DOCS "Argument(s) to run executable in non-interactive mode." FULL_DOCS "Argument(s) to run executable in non-interactive mode.") define_property(TARGET PROPERTY SDL_NONINTERACTIVE_TIMEOUT BRIEF_DOCS "Timeout for noninteractive executable." FULL_DOCS "Timeout for noninteractive executable.") -macro(add_sdl_test_executable TARGET) +function(add_sdl_test_executable TARGET) cmake_parse_arguments(AST "BUILD_DEPENDENT;NONINTERACTIVE;NEEDS_RESOURCES;TESTUTILS;THREADS;NO_C90;MAIN_CALLBACKS;NOTRACKMEM" "" "DEPENDS;DISABLE_THREADS_ARGS;NONINTERACTIVE_TIMEOUT;NONINTERACTIVE_ARGS;INSTALLED_ARGS;SOURCES" ${ARGN}) if(AST_UNPARSED_ARGUMENTS) message(FATAL_ERROR "Unknown argument(s): ${AST_UNPARSED_ARGUMENTS}") @@ -133,7 +131,8 @@ macro(add_sdl_test_executable TARGET) add_dependencies(${TARGET} ${AST_DEPENDS}) endif() - list(APPEND SDL_TEST_EXECUTABLES ${TARGET}) + set_propertY(TARGET ${TARGET} PROPERTY SDL_INSTALL "1") + set_property(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" APPEND PROPERTY SDL_TEST_EXECUTABLES "${TARGET}") set_property(TARGET ${TARGET} PROPERTY SDL_NOTRACKMEM ${AST_NOTRACKMEM}) if(AST_NONINTERACTIVE) set_property(TARGET ${TARGET} PROPERTY SDL_NONINTERACTIVE 1) @@ -192,6 +191,14 @@ macro(add_sdl_test_executable TARGET) target_link_options(${TARGET} PRIVATE "SHELL:--pre-js ${CMAKE_CURRENT_SOURCE_DIR}/emscripten/pre.js") target_link_options(${TARGET} PRIVATE "-sEXIT_RUNTIME=1") set_property(TARGET ${TARGET} APPEND PROPERTY LINK_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/emscripten/pre.js") + elseif(NGAGE) + string(MD5 TARGET_MD5 "${TARGET}") + string(SUBSTRING "${TARGET_MD5}" 0 8 TARGET_MD5_8) + target_link_options(${TARGET} PRIVATE "SHELL:-s UID3=0x${TARGET_MD5_8}") + if(NOT AST_MAIN_CALLBACKS) + set_property(TARGET ${TARGET} PROPERTY "EXCLUDE_FROM_ALL" "1") + set_propertY(TARGET ${TARGET} PROPERTY SDL_INSTALL "0") + endif() endif() if(OPENGL_FOUND) @@ -200,10 +207,10 @@ macro(add_sdl_test_executable TARGET) # FIXME: only add "${SDL3_BINARY_DIR}/include-config-$>" + include paths of external dependencies target_include_directories(${TARGET} PRIVATE "$") -endmacro() +endfunction() -check_include_file(signal.h HAVE_SIGNAL_H) -if(HAVE_SIGNAL_H) +check_include_file(signal.h LIBC_HAS_SIGNAL_H) +if(LIBC_HAS_SIGNAL_H) add_definitions(-DHAVE_SIGNAL_H) endif() @@ -425,6 +432,8 @@ add_sdl_test_executable(testprocess add_sdl_test_executable(childprocess SOURCES childprocess.c) add_dependencies(testprocess childprocess) +get_property(SDL_TEST_EXECUTABLES DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" PROPERTY SDL_TEST_EXECUTABLES) + if (HAVE_WAYLAND) # Set the GENERATED property on the protocol file, since it is first created at build time set_property(SOURCE ${SDL3_BINARY_DIR}/wayland-generated-protocols/xdg-shell-protocol.c PROPERTY GENERATED 1) @@ -654,10 +663,15 @@ if(SDL_INSTALL_TESTS) DESTINATION ${CMAKE_INSTALL_LIBEXECDIR}/installed-tests/SDL3 ) else() - install( - TARGETS ${SDL_TEST_EXECUTABLES} - DESTINATION ${CMAKE_INSTALL_LIBEXECDIR}/installed-tests/SDL3 - ) + foreach(test IN LISTS SDL_TEST_EXECUTABLES) + get_property(install_target TARGET ${test} PROPERTY "SDL_INSTALL") + if(install_target) + install( + TARGETS ${test} + DESTINATION ${CMAKE_INSTALL_LIBEXECDIR}/installed-tests/SDL3 + ) + endif() + endforeach() endif() if(MSVC) foreach(test IN LISTS SDL_TEST_EXECUTABLES)