From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Sat, 27 Mar 2021 22:05:09 +0100 Subject: [PATCH] Add SerenityOS platform support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Robin Burchell Co-Authored-By: tgsm Co-Authored-By: TheMorc Co-Authored-By: thatdutchguy Co-Authored-By: Oleg Sikorskiy Co-Authored-By: Gunnar Beutner Co-Authored-By: AnicJov Co-Authored-By: Jelle Raaijmakers Co-Authored-By: Kenneth Myhra Co-Authored-By: Stephan Unverwerth Co-Authored-By: Tim Schumacher Co-Authored-By: circl Co-Authored-By: kleines Filmröllchen Co-Authored-By: Linus Groh Co-Authored-By: Tim Ledbetter Co-Authored-By: Refrag --- CMakeLists.txt | 25 +- build-scripts/config.sub | 3 + cmake/sdlchecks.cmake | 20 + include/SDL_config.h.cmake | 2 + include/SDL_syswm.h | 3 +- src/SDL_error.c | 7 +- src/audio/SDL_audio.c | 3 + src/audio/SDL_sysaudio.h | 1 + src/audio/serenity/SDL_serenityaudio.cpp | 166 +++++ src/audio/serenity/SDL_serenityaudio.h | 38 + src/stdlib/SDL_stdlib.c | 2 +- src/video/SDL_sysvideo.h | 1 + src/video/SDL_video.c | 13 + src/video/serenity/SDL_serenityevents.cpp | 52 ++ src/video/serenity/SDL_serenityevents_c.h | 33 + src/video/serenity/SDL_serenitymessagebox.cpp | 47 ++ src/video/serenity/SDL_serenitymessagebox.h | 38 + src/video/serenity/SDL_serenitymouse.cpp | 169 +++++ src/video/serenity/SDL_serenitymouse.h | 39 ++ src/video/serenity/SDL_serenityvideo.cpp | 655 ++++++++++++++++++ src/video/serenity/SDL_serenityvideo.h | 101 +++ 21 files changed, 1392 insertions(+), 26 deletions(-) create mode 100644 src/audio/serenity/SDL_serenityaudio.cpp create mode 100644 src/audio/serenity/SDL_serenityaudio.h create mode 100644 src/video/serenity/SDL_serenityevents.cpp create mode 100644 src/video/serenity/SDL_serenityevents_c.h create mode 100644 src/video/serenity/SDL_serenitymessagebox.cpp create mode 100644 src/video/serenity/SDL_serenitymessagebox.h create mode 100644 src/video/serenity/SDL_serenitymouse.cpp create mode 100644 src/video/serenity/SDL_serenitymouse.h create mode 100644 src/video/serenity/SDL_serenityvideo.cpp create mode 100644 src/video/serenity/SDL_serenityvideo.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 00cf9b43fb496c339c2f1968862f9a1dd0ff7ee2..20164e2761d8848df6eb6c9736a4bd14d53c816b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,6 +29,9 @@ set(PKGCONFIG_DEPENDS) # etc. See https://github.com/libsdl-org/SDL/issues/4150 add_library(sdl-build-options INTERFACE) +set(UNIX 1) +set(VIDEO_WAYLAND OFF) + if(WINDOWS_STORE) target_compile_definitions(sdl-build-options INTERFACE "-DSDL_BUILDING_WINRT=1") target_compile_options(sdl-build-options INTERFACE "-ZW") @@ -416,7 +419,7 @@ set_option(SDL_SYSTEM_ICONV "Use iconv() from system-installed libraries" set_option(SDL_OPENGL "Include OpenGL support" ON) set_option(SDL_OPENGLES "Include OpenGL ES support" ON) set_option(SDL_PTHREADS "Use POSIX threads for multi-threading" ${SDL_PTHREADS_ENABLED_BY_DEFAULT}) -dep_option(SDL_PTHREADS_SEM "Use pthread semaphores" ON "SDL_PTHREADS" OFF) +dep_option(SDL_PTHREADS_SEM "Use pthread semaphores" OFF "SDL_PTHREADS" OFF) dep_option(SDL_OSS "Support the OSS audio API" ON "UNIX_SYS OR RISCOS" OFF) set_option(SDL_ALSA "Support the ALSA audio API" ${UNIX_SYS}) dep_option(SDL_ALSA_SHARED "Dynamically load ALSA audio support" ON "SDL_ALSA" OFF) @@ -1435,25 +1438,7 @@ elseif(UNIX AND NOT APPLE AND NOT RISCOS AND NOT HAIKU) CheckFusionSound() endif() - if(SDL_VIDEO) - # Need to check for Raspberry PI first and add platform specific compiler flags, otherwise the test for GLES fails! - CheckRPI() - CheckX11() - CheckDirectFB() - # Need to check for EGL first because KMSDRM and Wayland depends on it. - CheckEGL() - CheckKMSDRM() - CheckGLX() - CheckOpenGL() - CheckOpenGLES() - CheckWayland() - CheckVivante() - # FIXME: implement CheckVulkan() - if(SDL_VULKAN) - set(SDL_VIDEO_VULKAN 1) - set(HAVE_VULKAN TRUE) - endif() - endif() + CheckSerenity() if(UNIX) file(GLOB CORE_UNIX_SOURCES ${SDL2_SOURCE_DIR}/src/core/unix/*.c) diff --git a/build-scripts/config.sub b/build-scripts/config.sub index 312ad817c61e0dd18b57ecb8e1cd3be6edc924d7..c2582352995fafe32e0431b290390ee4c2c22598 100755 --- a/build-scripts/config.sub +++ b/build-scripts/config.sub @@ -1427,6 +1427,9 @@ case $os in os400*) os=os400 ;; + serenity*) + os=serenity + ;; sunos5*) os=`echo "$os" | sed -e 's|sunos5|solaris2|'` ;; diff --git a/cmake/sdlchecks.cmake b/cmake/sdlchecks.cmake index c4aec174e35ee653c4c4e0449939054e0f68ad9d..2f88b7f8790df95838e7de7a45789804e1eb9fde 100644 --- a/cmake/sdlchecks.cmake +++ b/cmake/sdlchecks.cmake @@ -801,6 +801,26 @@ macro(CheckDirectFB) endif() endmacro() +# Requires: +# - n/a +macro(CheckSerenity) + message_warn("Configuring SerenityOS!") + set(HAVE_VIDEO_SERENITY TRUE) + set(HAVE_AUDIO_SERENITY TRUE) + set(HAVE_SDL_VIDEO TRUE) + set(HAVE_SDL_AUDIO TRUE) + + set(CMAKE_CXX_FLAGS "-std=c++23 ${CMAKE_CXX_FLAGS}") + file(GLOB SERENITY_SOURCES ${SDL2_SOURCE_DIR}/src/video/serenity/*.cpp ${SDL2_SOURCE_DIR}/src/audio/serenity/*.cpp) + set(SOURCE_FILES ${SOURCE_FILES} ${SERENITY_SOURCES}) + set(SDL_VIDEO_DRIVER_SERENITY 1) + set(SDL_AUDIO_DRIVER_SERENITY 1) + set(SDL_THREAD_PTHREAD_RECURSIVE_MUTEX 1) + set(HAVE_VIDEO_OPENGL TRUE) + set(SDL_VIDEO_OPENGL 1) + list(APPEND EXTRA_LIBS ipc gui gfx gl core coreminimal) +endmacro(CheckSerenity) + # Requires: # - n/a macro(CheckVivante) diff --git a/include/SDL_config.h.cmake b/include/SDL_config.h.cmake index 7512bb1cc51e67350f63ed0f2074f7f44b6a1113..57e2a70937c3faf39b29e4ffe9dc08d97388e392 100644 --- a/include/SDL_config.h.cmake +++ b/include/SDL_config.h.cmake @@ -317,6 +317,7 @@ #cmakedefine SDL_AUDIO_DRIVER_PULSEAUDIO @SDL_AUDIO_DRIVER_PULSEAUDIO@ #cmakedefine SDL_AUDIO_DRIVER_PULSEAUDIO_DYNAMIC @SDL_AUDIO_DRIVER_PULSEAUDIO_DYNAMIC@ #cmakedefine SDL_AUDIO_DRIVER_QSA @SDL_AUDIO_DRIVER_QSA@ +#cmakedefine SDL_AUDIO_DRIVER_SERENITY @SDL_AUDIO_DRIVER_SERENITY@ #cmakedefine SDL_AUDIO_DRIVER_SNDIO @SDL_AUDIO_DRIVER_SNDIO@ #cmakedefine SDL_AUDIO_DRIVER_SNDIO_DYNAMIC @SDL_AUDIO_DRIVER_SNDIO_DYNAMIC@ #cmakedefine SDL_AUDIO_DRIVER_SUNAUDIO @SDL_AUDIO_DRIVER_SUNAUDIO@ @@ -410,6 +411,7 @@ #cmakedefine SDL_VIDEO_DRIVER_DIRECTFB_DYNAMIC @SDL_VIDEO_DRIVER_DIRECTFB_DYNAMIC@ #cmakedefine SDL_VIDEO_DRIVER_DUMMY @SDL_VIDEO_DRIVER_DUMMY@ #cmakedefine SDL_VIDEO_DRIVER_OFFSCREEN @SDL_VIDEO_DRIVER_OFFSCREEN@ +#cmakedefine SDL_VIDEO_DRIVER_SERENITY @SDL_VIDEO_DRIVER_SERENITY@ #cmakedefine SDL_VIDEO_DRIVER_WINDOWS @SDL_VIDEO_DRIVER_WINDOWS@ #cmakedefine SDL_VIDEO_DRIVER_WINRT @SDL_VIDEO_DRIVER_WINRT@ #cmakedefine SDL_VIDEO_DRIVER_WAYLAND @SDL_VIDEO_DRIVER_WAYLAND@ diff --git a/include/SDL_syswm.h b/include/SDL_syswm.h index b35734deb334508c6fbfcc0b635417a9d2841052..65a0c8173b376a94ee8650af44ab57dce55d3cb1 100644 --- a/include/SDL_syswm.h +++ b/include/SDL_syswm.h @@ -148,7 +148,8 @@ typedef enum SDL_SYSWM_OS2, SDL_SYSWM_HAIKU, SDL_SYSWM_KMSDRM, - SDL_SYSWM_RISCOS + SDL_SYSWM_RISCOS, + SDL_SYSWM_SERENITY } SDL_SYSWM_TYPE; /** diff --git a/src/SDL_error.c b/src/SDL_error.c index 501d3958457de93f75a8c68e1329964247319054..797267c8e24abc6b65f453c93f447e7239f4f8d7 100644 --- a/src/SDL_error.c +++ b/src/SDL_error.c @@ -51,10 +51,9 @@ int SDL_SetError(SDL_PRINTF_FORMAT_STRING const char *fmt, ...) } } - if (SDL_LogGetPriority(SDL_LOG_CATEGORY_ERROR) <= SDL_LOG_PRIORITY_DEBUG) { - /* If we are in debug mode, print out the error message */ - SDL_LogDebug(SDL_LOG_CATEGORY_ERROR, "%s", error->str); - } + // # HACK(SerenityOS): show everything that's going on + dbgputstr(error->str, strlen(error->str)); + dbgputstr("\n", 1); } return -1; diff --git a/src/audio/SDL_audio.c b/src/audio/SDL_audio.c index 60242e60a06de322281cc0b32b7d3dbc913a80b3..46ec7f9ef0375058e9f95cfea5c213a39b61346e 100644 --- a/src/audio/SDL_audio.c +++ b/src/audio/SDL_audio.c @@ -36,6 +36,9 @@ static SDL_AudioDevice *open_devices[16]; /* Available audio drivers */ static const AudioBootStrap *const bootstrap[] = { +#if SDL_AUDIO_DRIVER_SERENITY + &SERENITYAUDIO_bootstrap, +#endif #if SDL_AUDIO_DRIVER_PULSEAUDIO &PULSEAUDIO_bootstrap, #endif diff --git a/src/audio/SDL_sysaudio.h b/src/audio/SDL_sysaudio.h index c755d4341521104d369b4d5ff731617680c70215..3ee517796ab467ad5ac3debca738d1cb1cc54f1c 100644 --- a/src/audio/SDL_sysaudio.h +++ b/src/audio/SDL_sysaudio.h @@ -209,6 +209,7 @@ extern AudioBootStrap VITAAUD_bootstrap; extern AudioBootStrap N3DSAUDIO_bootstrap; extern AudioBootStrap EMSCRIPTENAUDIO_bootstrap; extern AudioBootStrap OS2AUDIO_bootstrap; +extern AudioBootStrap SERENITYAUDIO_bootstrap; #endif /* SDL_sysaudio_h_ */ diff --git a/src/audio/serenity/SDL_serenityaudio.cpp b/src/audio/serenity/SDL_serenityaudio.cpp new file mode 100644 index 0000000000000000000000000000000000000000..29fa1fcdb1bf018bae6129819648afccdf87c342 --- /dev/null +++ b/src/audio/serenity/SDL_serenityaudio.cpp @@ -0,0 +1,166 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2019 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" + +#if SDL_AUDIO_DRIVER_SERENITY +# define AK_DONT_REPLACE_STD + +extern "C" { + +# include "SDL_audio.h" + +# include "../SDL_audio_c.h" +# include "../SDL_audiodev_c.h" +} + +# include +# include +# include +# include +# include "SDL_serenityaudio.h" + +static Array output_buffer {}; +static size_t output_buffer_samples_remaining { 0 }; + +static void SERENITYAUDIO_CloseDevice(_THIS) +{ + dbgln("SERENITYAUDIO_CloseDevice"); + + struct SDL_PrivateAudioData* h = that->hidden; + if (h->client) + h->client->die(); + + SDL_free(that->hidden->mixbuf); + SDL_free(that->hidden); +} + +static int SERENITYAUDIO_OpenDevice(_THIS, char const*) +{ + /* Initialize all variables that we clean on shutdown */ + that->hidden = static_cast(SDL_malloc(sizeof *that->hidden)); + if (!that->hidden) + return SDL_OutOfMemory(); + SDL_zerop(that->hidden); + + that->spec.freq = 44100; + that->spec.format = AUDIO_S16LSB; + that->spec.channels = 2; + that->spec.samples = 1024; + + /* Calculate the final parameters for this audio specification */ + SDL_CalculateAudioSpec(&that->spec); + + /* Allocate mixing buffer */ + if (!that->iscapture) { + that->hidden->mixlen = that->spec.size; + that->hidden->mixbuf = (Uint8*)SDL_malloc(that->hidden->mixlen); + if (!that->hidden->mixbuf) + return SDL_OutOfMemory(); + SDL_memset(that->hidden->mixbuf, that->spec.silence, that->spec.size); + } + + /* We're ready to rock and roll. :-) */ + return 0; +} + +static void SERENITYAUDIO_PlayDevice(_THIS) +{ + struct SDL_PrivateAudioData* h = that->hidden; + + // We need to create our audio connection and event loop here, in order to register them with SDL's audio thread + if (!h->event_loop) + h->event_loop = make(); + if (!h->client) + h->client = MUST(Audio::ConnectionToServer::try_create()); + + h->client->async_start_playback(); + + auto convert_i16_to_double = [](i16 input) -> auto { + return (static_cast(input) - NumericLimits::min()) / NumericLimits::max() * 2. - 1.; + }; + + auto const sleep_spec = Duration::from_nanoseconds(100).to_timespec(); + auto input_buffer = reinterpret_cast(h->mixbuf); + auto input_samples = h->mixlen / that->spec.channels / sizeof(i16); + size_t input_position = 0; + + while (input_samples > 0) { + // Fill up the output buffer + auto const input_samples_to_process = min(input_samples, Audio::AUDIO_BUFFER_SIZE - output_buffer_samples_remaining); + for (size_t i = 0; i < input_samples_to_process; ++i) { + auto left = convert_i16_to_double(input_buffer[input_position]); + auto right = convert_i16_to_double(input_buffer[input_position + 1]); + output_buffer[output_buffer_samples_remaining + i] = Audio::Sample(left, right); + input_position += 2; + } + output_buffer_samples_remaining += input_samples_to_process; + input_samples -= input_samples_to_process; + + // Stop if we don't have enough samples to fill a buffer + if (output_buffer_samples_remaining < Audio::AUDIO_BUFFER_SIZE) + break; + + // Try to enqueue our output buffer + for (;;) { + auto enqueue_result = h->client->realtime_enqueue(output_buffer); + if (!enqueue_result.is_error()) + break; + if (enqueue_result.error() != Audio::AudioQueue::QueueStatus::Full) + return; + + nanosleep(&sleep_spec, nullptr); + } + output_buffer_samples_remaining = 0; + } + + // Pump our event loop - should just be the IPC call to start playback + for (;;) { + auto number_of_events_pumped = h->event_loop->pump(Core::EventLoop::WaitMode::PollForEvents); + if (number_of_events_pumped == 0) + break; + } +} + +static Uint8* SERENITYAUDIO_GetDeviceBuf(_THIS) +{ + return that->hidden->mixbuf; +} + +static SDL_bool SERENITYAUDIO_Init(SDL_AudioDriverImpl* impl) +{ + /* Set the function pointers */ + impl->OpenDevice = SERENITYAUDIO_OpenDevice; + impl->PlayDevice = SERENITYAUDIO_PlayDevice; + impl->GetDeviceBuf = SERENITYAUDIO_GetDeviceBuf; + impl->CloseDevice = SERENITYAUDIO_CloseDevice; + + impl->AllowsArbitraryDeviceNames = SDL_TRUE; + impl->HasCaptureSupport = SDL_FALSE; + impl->OnlyHasDefaultOutputDevice = SDL_TRUE; + + return SDL_TRUE; // this audio target is available. +} + +AudioBootStrap SERENITYAUDIO_bootstrap = { + "serenity", "Serenity using AudioServer", SERENITYAUDIO_Init, SDL_FALSE +}; + +#endif diff --git a/src/audio/serenity/SDL_serenityaudio.h b/src/audio/serenity/SDL_serenityaudio.h new file mode 100644 index 0000000000000000000000000000000000000000..8d152ca843e732d2d5a8127fc191ca22ce690a83 --- /dev/null +++ b/src/audio/serenity/SDL_serenityaudio.h @@ -0,0 +1,38 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2019 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" + +#pragma once + +#include "../SDL_sysaudio.h" +#include +#include +#include +#include + +#define _THIS SDL_AudioDevice* that + +struct SDL_PrivateAudioData { + RefPtr client; + OwnPtr event_loop; + Uint8* mixbuf { nullptr }; + size_t mixlen { 0 }; +}; diff --git a/src/stdlib/SDL_stdlib.c b/src/stdlib/SDL_stdlib.c index 8c9d196ac27cbb9db35faa03d29b9b301bf08622..3ab89a91506d6522f5dcd5a58cc77312a50f03e7 100644 --- a/src/stdlib/SDL_stdlib.c +++ b/src/stdlib/SDL_stdlib.c @@ -618,7 +618,7 @@ void *SDL_memset(SDL_OUT_BYTECAP(len) void *dst, int c, size_t len) #endif /* HAVE_MEMSET */ } -#if defined(HAVE_CTYPE_H) && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +#if defined(HAVE_CTYPE_H) && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L && !defined(__serenity__) int SDL_isblank(int x) { return isblank(x); diff --git a/src/video/SDL_sysvideo.h b/src/video/SDL_sysvideo.h index d7a42cc64d905cd1f8b3a03bf8ffd894c0c9b7ac..26e3b457f6fbb5d811d55254d1fb486aa6aa79e7 100644 --- a/src/video/SDL_sysvideo.h +++ b/src/video/SDL_sysvideo.h @@ -482,6 +482,7 @@ extern VideoBootStrap OFFSCREEN_bootstrap; extern VideoBootStrap NGAGE_bootstrap; extern VideoBootStrap OS2DIVE_bootstrap; extern VideoBootStrap OS2VMAN_bootstrap; +extern VideoBootStrap SERENITYVIDEO_bootstrap; /* Use SDL_OnVideoThread() sparingly, to avoid regressions in use cases that currently happen to work */ extern SDL_bool SDL_OnVideoThread(void); diff --git a/src/video/SDL_video.c b/src/video/SDL_video.c index 8065a0b8cfc71e8d4dadd7d40fbe15303ce7e05d..fdebdac17e72e280fbe55fbb6e8bc7f42e0fc154 100644 --- a/src/video/SDL_video.c +++ b/src/video/SDL_video.c @@ -65,6 +65,9 @@ /* Available video drivers */ static VideoBootStrap *bootstrap[] = { +#if SDL_VIDEO_DRIVER_SERENITY + &SERENITYVIDEO_bootstrap, +#endif #if SDL_VIDEO_DRIVER_COCOA &COCOA_bootstrap, #endif @@ -4240,6 +4243,10 @@ void SDL_WM_SetIcon(SDL_Surface * icon, Uint8 * mask) } } #endif +#if SDL_VIDEO_DRIVER_SERENITY +#include "serenity/SDL_serenitymessagebox.h" +#endif + SDL_bool SDL_GetWindowWMInfo(SDL_Window * window, struct SDL_SysWMinfo *info) { @@ -4522,6 +4529,12 @@ int SDL_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid) VITA_ShowMessageBox(messageboxdata, buttonid) == 0) { retval = 0; } +#endif +#if SDL_VIDEO_DRIVER_SERENITY + if (retval == -1 && + SERENITY_ShowMessageBox(messageboxdata, buttonid) == 0) { + retval = 0; + } #endif if (retval == -1) { const char *error = SDL_GetError(); diff --git a/src/video/serenity/SDL_serenityevents.cpp b/src/video/serenity/SDL_serenityevents.cpp new file mode 100644 index 0000000000000000000000000000000000000000..04cbf21ce74fb11e6713ba3a4beaad808751e80b --- /dev/null +++ b/src/video/serenity/SDL_serenityevents.cpp @@ -0,0 +1,52 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2021 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" + +#if SDL_VIDEO_DRIVER_SERENITY +#define AK_DONT_REPLACE_STD + +#include "../../events/SDL_events_c.h" + +#include "SDL_serenityvideo.h" +#include "SDL_serenityevents_c.h" +#include "SDL_timer.h" + +#include + +void +SERENITY_PumpEvents(_THIS) +{ + auto& loop = Core::EventLoop::current(); + if (loop.was_exit_requested()) + exit(0); + + auto const event_loop_timeout_ms = SDL_GetTicks() + 100; + while (loop.pump(Core::EventLoop::WaitMode::PollForEvents) > 0) { + if (SDL_TICKS_PASSED(SDL_GetTicks(), event_loop_timeout_ms)) + break; + } + +} + + +#endif /* SDL_VIDEO_DRIVER_SERENITY */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/video/serenity/SDL_serenityevents_c.h b/src/video/serenity/SDL_serenityevents_c.h new file mode 100644 index 0000000000000000000000000000000000000000..89e9e919e319c701144da8f6d73bbb2ea519758d --- /dev/null +++ b/src/video/serenity/SDL_serenityevents_c.h @@ -0,0 +1,33 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2019 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_serenityevents_c_h_ +#define SDL_serenityevents_c_h_ + +#include "../../SDL_internal.h" + +#include "SDL_serenityvideo.h" + +extern void SERENITY_PumpEvents(_THIS); + +#endif /* SDL_serenityevents_c_h_ */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/video/serenity/SDL_serenitymessagebox.cpp b/src/video/serenity/SDL_serenitymessagebox.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a0c7d0772be7198a521708abb9d69ef9cc413597 --- /dev/null +++ b/src/video/serenity/SDL_serenitymessagebox.cpp @@ -0,0 +1,47 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2019 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" + +#if SDL_VIDEO_DRIVER_SERENITY +# define AK_DONT_REPLACE_STD + +# include "SDL_messagebox.h" +# include "SDL_serenitymessagebox.h" + +# include +# include +# include + +extern "C" int SERENITY_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid) +{ + // From the docs: "[SDL_ShowMessageBox] may be called at any time, even before SDL_Init()." + AK::RefPtr app = GUI::Application::the(); + if (app == nullptr) + app = MUST(GUI::Application::create(Main::Arguments {})); + + GUI::MessageBox::show(nullptr, { messageboxdata->message, strlen(messageboxdata->message) }, { messageboxdata->title, strlen(messageboxdata->title) }); + return 0; +} + +#endif /* SDL_VIDEO_DRIVER_SERENITY */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/video/serenity/SDL_serenitymessagebox.h b/src/video/serenity/SDL_serenitymessagebox.h new file mode 100644 index 0000000000000000000000000000000000000000..34e607783310310ac8956bccf584b5ab12d45907 --- /dev/null +++ b/src/video/serenity/SDL_serenitymessagebox.h @@ -0,0 +1,38 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2019 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_serenitymessagebox_h_ +#define SDL_serenitymessagebox_h_ + +#ifdef __cplusplus +extern "C" { +#endif + +int SERENITY_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid); + +#ifdef __cplusplus +} +#endif + +#endif /* SDL_serenitymessagebox_h_ */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/video/serenity/SDL_serenitymouse.cpp b/src/video/serenity/SDL_serenitymouse.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c603fe8cedaae61ab8ae84407734d1c89f99ff06 --- /dev/null +++ b/src/video/serenity/SDL_serenitymouse.cpp @@ -0,0 +1,157 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2021 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" + +#if SDL_VIDEO_DRIVER_SERENITY +#define AK_DONT_REPLACE_STD + +/* + * SDL includes: + */ +extern "C" { +#include "../../events/SDL_mouse_c.h" +#include "../../events/SDL_touch_c.h" +#include "../SDL_sysvideo.h" +#include "SDL_events.h" +} + +#include "SDL_serenitymouse.h" +#include "SDL_serenityvideo.h" + +#include +#include + +struct SerenityCursorData final { + Gfx::StandardCursor cursor_type; +}; + +static SDL_Cursor* +SERENITY_CreateSystemCursor(SDL_SystemCursor id) +{ + auto cursor = static_cast(SDL_calloc(1, sizeof(SDL_Cursor))); + if (!cursor) + SDL_OutOfMemory(); + + auto cursor_data = static_cast(SDL_calloc(1, sizeof(SerenityCursorData))); + if (!cursor_data) + SDL_OutOfMemory(); + cursor->driverdata = static_cast(cursor_data); + + switch(id) + { + case SDL_SYSTEM_CURSOR_ARROW: cursor_data->cursor_type = Gfx::StandardCursor::Arrow; break; + case SDL_SYSTEM_CURSOR_IBEAM: cursor_data->cursor_type = Gfx::StandardCursor::IBeam; break; + case SDL_SYSTEM_CURSOR_WAIT: cursor_data->cursor_type = Gfx::StandardCursor::Wait; break; + case SDL_SYSTEM_CURSOR_CROSSHAIR: cursor_data->cursor_type = Gfx::StandardCursor::Crosshair; break; + case SDL_SYSTEM_CURSOR_WAITARROW: cursor_data->cursor_type = Gfx::StandardCursor::Wait; break; + case SDL_SYSTEM_CURSOR_SIZENWSE: cursor_data->cursor_type = Gfx::StandardCursor::ResizeDiagonalTLBR; break; + case SDL_SYSTEM_CURSOR_SIZENESW: cursor_data->cursor_type = Gfx::StandardCursor::ResizeDiagonalBLTR; break; + case SDL_SYSTEM_CURSOR_SIZEWE: cursor_data->cursor_type = Gfx::StandardCursor::ResizeHorizontal; break; + case SDL_SYSTEM_CURSOR_SIZENS: cursor_data->cursor_type = Gfx::StandardCursor::ResizeVertical; break; + case SDL_SYSTEM_CURSOR_SIZEALL: cursor_data->cursor_type = Gfx::StandardCursor::Move; break; + case SDL_SYSTEM_CURSOR_NO: cursor_data->cursor_type = Gfx::StandardCursor::Disallowed; break; + case SDL_SYSTEM_CURSOR_HAND: cursor_data->cursor_type = Gfx::StandardCursor::Hand; break; + default: + SDL_assert(0); + return nullptr; + } + return cursor; +} + +static SDL_Cursor* +SERENITY_CreateDefaultCursor() +{ + return SERENITY_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW); +} + +static void +SERENITY_FreeCursor(SDL_Cursor* cursor) +{ + if (cursor->driverdata) + SDL_free(cursor->driverdata); + SDL_free(cursor); +} + +static int +SERENITY_ShowCursor(SDL_Cursor* cursor) +{ + auto focused_window = SDL_GetMouseFocus(); + if (!focused_window) + return -1; + + auto platform_window = SerenityPlatformWindow::from_sdl_window(focused_window); + + // Hide the cursor + if (cursor == nullptr) { + platform_window->window()->set_cursor(Gfx::StandardCursor::Hidden); + return 0; + } + + auto cursor_data = static_cast(cursor->driverdata); + if (!cursor_data) + return -1; + + platform_window->window()->set_cursor(cursor_data->cursor_type); + return 0; +} + +static void +SERENITY_WarpMouse(SDL_Window *window, int x, int y) +{ + auto platform_window = SerenityPlatformWindow::from_sdl_window(window); + + auto window_position = platform_window->window()->position(); + auto warp_position = Gfx::Point(x, y); + + auto absolute_warp_position = window_position + warp_position; + + GUI::ConnectionToWindowServer::the().async_set_global_cursor_position(absolute_warp_position); + SDL_SendMouseMotion(window, 0, 0, warp_position.x(), warp_position.y()); +} + +void +SERENITY_InitMouse(_THIS) +{ + dbgln("SERENITY_InitMouse"); + + auto mouse = SDL_GetMouse(); + + mouse->CreateSystemCursor = SERENITY_CreateSystemCursor; + mouse->ShowCursor = SERENITY_ShowCursor; + mouse->FreeCursor = SERENITY_FreeCursor; + mouse->WarpMouse = SERENITY_WarpMouse; + + // FIXME: implement below methods + //mouse->CreateCursor = ...; + //mouse->SetRelativeMouseMode = ...; + + SDL_SetDefaultCursor(SERENITY_CreateDefaultCursor()); +} + +void +SERENITY_QuitMouse(_THIS) +{ + dbgln("SERENITY_QuitMouse"); +} + +#endif /* SDL_VIDEO_DRIVER_SERENITY */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/video/serenity/SDL_serenitymouse.h b/src/video/serenity/SDL_serenitymouse.h new file mode 100644 index 0000000000000000000000000000000000000000..039f0361b3d1b248e218ea69495f58e52ad532c6 --- /dev/null +++ b/src/video/serenity/SDL_serenitymouse.h @@ -0,0 +1,39 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2021 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_config.h" + +#ifndef SDL_serenitymouse_h_ +#define SDL_serenitymouse_h_ + +#ifdef __cplusplus +extern "C" { +#endif + +extern void SERENITY_InitMouse(_THIS); +extern void SERENITY_QuitMouse(_THIS); + +#ifdef __cplusplus +} +#endif + +#endif /* SDL_serenitymouse_h_ */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/video/serenity/SDL_serenityvideo.cpp b/src/video/serenity/SDL_serenityvideo.cpp new file mode 100644 index 0000000000000000000000000000000000000000..26ff18b1c878f7a14c32554859bd088522806b91 --- /dev/null +++ b/src/video/serenity/SDL_serenityvideo.cpp @@ -0,0 +1,655 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2019 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. + */ +extern "C" { + +#include "../../SDL_internal.h" + +#if SDL_VIDEO_DRIVER_SERENITY +# define AK_DONT_REPLACE_STD + +# include "../../events/SDL_events_c.h" +# include "../SDL_pixels_c.h" +# include "../SDL_sysvideo.h" +# include "SDL_mouse.h" +# include "SDL_syswm.h" +# include "SDL_video.h" +} + +# include "SDL_serenityevents_c.h" +# include "SDL_serenitymouse.h" +# include "SDL_serenityvideo.h" + +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include + +static consteval AK::Array generate_scancode_map() +{ + auto map = AK::Array::from_repeated_value(SDL_SCANCODE_UNKNOWN); + + // Below list is in order of Kernel/API/KeyCode.h; the shift modifier is applied by SDL + // and as such, unshifted scancodes must be used (e.g. dollar sign => 4). +# define MAP_KEYCODE(A,B) map[to_underlying(KeyCode::Key_##A)] = SDL_SCANCODE_##B; + MAP_KEYCODE(Escape, ESCAPE); + MAP_KEYCODE(Tab, TAB); + MAP_KEYCODE(Backspace, BACKSPACE); + MAP_KEYCODE(Return, RETURN); + MAP_KEYCODE(Insert, INSERT); + MAP_KEYCODE(Delete, DELETE); + MAP_KEYCODE(PrintScreen, PRINTSCREEN); + MAP_KEYCODE(PauseBreak, PAUSE); + MAP_KEYCODE(SysRq, SYSREQ); + MAP_KEYCODE(Home, HOME); + MAP_KEYCODE(End, END); + MAP_KEYCODE(Left, LEFT); + MAP_KEYCODE(Up, UP); + MAP_KEYCODE(Right, RIGHT); + MAP_KEYCODE(Down, DOWN); + MAP_KEYCODE(PageUp, PAGEUP); + MAP_KEYCODE(PageDown, PAGEDOWN); + MAP_KEYCODE(LeftShift, LSHIFT); + MAP_KEYCODE(RightShift, RSHIFT); + MAP_KEYCODE(Control, LCTRL); + MAP_KEYCODE(RightControl, RCTRL); + MAP_KEYCODE(Alt, LALT); + MAP_KEYCODE(RightAlt, RALT); + MAP_KEYCODE(CapsLock, CAPSLOCK); + MAP_KEYCODE(NumLock, NUMLOCKCLEAR); + MAP_KEYCODE(ScrollLock, SCROLLLOCK); + MAP_KEYCODE(F1, F1); + MAP_KEYCODE(F2, F2); + MAP_KEYCODE(F3, F3); + MAP_KEYCODE(F4, F4); + MAP_KEYCODE(F5, F5); + MAP_KEYCODE(F6, F6); + MAP_KEYCODE(F7, F7); + MAP_KEYCODE(F8, F8); + MAP_KEYCODE(F9, F9); + MAP_KEYCODE(F10, F10); + MAP_KEYCODE(F11, F11); + MAP_KEYCODE(F12, F12); + MAP_KEYCODE(Space, SPACE); + MAP_KEYCODE(ExclamationPoint, 1); + MAP_KEYCODE(DoubleQuote, APOSTROPHE); + MAP_KEYCODE(Hashtag, 3); + MAP_KEYCODE(Dollar, 4); + MAP_KEYCODE(Percent, 5); + MAP_KEYCODE(Ampersand, 7); + MAP_KEYCODE(Apostrophe, APOSTROPHE); + MAP_KEYCODE(LeftParen, 9); + MAP_KEYCODE(RightParen, 0); + MAP_KEYCODE(Asterisk, 8); + MAP_KEYCODE(Plus, EQUALS); + MAP_KEYCODE(Comma, COMMA); + MAP_KEYCODE(Minus, MINUS); + MAP_KEYCODE(Period, PERIOD); + MAP_KEYCODE(Slash, SLASH); + MAP_KEYCODE(0, 0); + MAP_KEYCODE(1, 1); + MAP_KEYCODE(2, 2); + MAP_KEYCODE(3, 3); + MAP_KEYCODE(4, 4); + MAP_KEYCODE(5, 5); + MAP_KEYCODE(6, 6); + MAP_KEYCODE(7, 7); + MAP_KEYCODE(8, 8); + MAP_KEYCODE(9, 9); + MAP_KEYCODE(Colon, SEMICOLON); + MAP_KEYCODE(Semicolon, SEMICOLON); + MAP_KEYCODE(LessThan, COMMA); + MAP_KEYCODE(Equal, EQUALS); + MAP_KEYCODE(GreaterThan, PERIOD); + MAP_KEYCODE(QuestionMark, SLASH); + MAP_KEYCODE(A, A); + MAP_KEYCODE(B, B); + MAP_KEYCODE(C, C); + MAP_KEYCODE(D, D); + MAP_KEYCODE(E, E); + MAP_KEYCODE(F, F); + MAP_KEYCODE(G, G); + MAP_KEYCODE(H, H); + MAP_KEYCODE(I, I); + MAP_KEYCODE(J, J); + MAP_KEYCODE(K, K); + MAP_KEYCODE(L, L); + MAP_KEYCODE(M, M); + MAP_KEYCODE(N, N); + MAP_KEYCODE(O, O); + MAP_KEYCODE(P, P); + MAP_KEYCODE(Q, Q); + MAP_KEYCODE(R, R); + MAP_KEYCODE(S, S); + MAP_KEYCODE(T, T); + MAP_KEYCODE(U, U); + MAP_KEYCODE(V, V); + MAP_KEYCODE(W, W); + MAP_KEYCODE(X, X); + MAP_KEYCODE(Y, Y); + MAP_KEYCODE(Z, Z); + MAP_KEYCODE(LeftBracket, LEFTBRACKET); + MAP_KEYCODE(RightBracket, RIGHTBRACKET); + MAP_KEYCODE(Backslash, BACKSLASH); + MAP_KEYCODE(Circumflex, 6); + MAP_KEYCODE(Underscore, MINUS); + MAP_KEYCODE(LeftBrace, LEFTBRACKET); + MAP_KEYCODE(RightBrace, RIGHTBRACKET); + MAP_KEYCODE(Pipe, BACKSLASH); + MAP_KEYCODE(Tilde, GRAVE); + MAP_KEYCODE(Backtick, GRAVE); + MAP_KEYCODE(Super, LGUI); + MAP_KEYCODE(BrowserSearch, AC_SEARCH); + MAP_KEYCODE(BrowserFavorites, AC_BOOKMARKS); + MAP_KEYCODE(BrowserHome, AC_HOME); + MAP_KEYCODE(PreviousTrack, AUDIOPREV); + MAP_KEYCODE(BrowserBack, AC_BACK); + MAP_KEYCODE(BrowserForward, AC_FORWARD); + MAP_KEYCODE(BrowserRefresh, AC_REFRESH); + MAP_KEYCODE(BrowserStop, AC_STOP); + MAP_KEYCODE(VolumeDown, VOLUMEDOWN); + MAP_KEYCODE(VolumeUp, VOLUMEUP); + // Unmapped: Wake + MAP_KEYCODE(Sleep, SLEEP); + MAP_KEYCODE(NextTrack, AUDIONEXT); + MAP_KEYCODE(MediaSelect, MEDIASELECT); + MAP_KEYCODE(Email, MAIL); + MAP_KEYCODE(MyComputer, COMPUTER); + MAP_KEYCODE(Power, POWER); + MAP_KEYCODE(Stop, STOP); + MAP_KEYCODE(LeftGUI, LGUI); + MAP_KEYCODE(Mute, MUTE); + MAP_KEYCODE(RightGUI, RGUI); + MAP_KEYCODE(Calculator, CALCULATOR); + // Unmapped: Apps + MAP_KEYCODE(PlayPause, AUDIOPLAY); + MAP_KEYCODE(Menu, APPLICATION); + + return map; +} + +static constexpr auto scancode_map = generate_scancode_map(); + +/* Initialization/Query functions */ +static int SERENITY_VideoInit(_THIS); +static int SERENITY_SetDisplayMode(_THIS, SDL_VideoDisplay* display, + SDL_DisplayMode* mode); +static void SERENITY_VideoQuit(_THIS); + +/* SERENITY driver bootstrap functions */ + +static int SERENITY_Available(void) { return (1); } + +static void SERENITY_DeleteDevice(SDL_VideoDevice* device) +{ + dbgln("SERENITY_DeleteDevice"); + SDL_free(device); +} + +extern int Serenity_CreateWindow(_THIS, SDL_Window* window); +extern void Serenity_ShowWindow(_THIS, SDL_Window* window); +extern void Serenity_HideWindow(_THIS, SDL_Window* window); +extern void Serenity_SetWindowTitle(_THIS, SDL_Window* window); +extern void Serenity_SetWindowSize(_THIS, SDL_Window* window); +extern void Serenity_SetWindowFullscreen(_THIS, SDL_Window* window, + SDL_VideoDisplay* display, + SDL_bool fullscreen); +extern void Serenity_SetWindowIcon(_THIS, SDL_Window* window, SDL_Surface* icon); +extern void Serenity_DestroyWindow(_THIS, SDL_Window* window); +extern SDL_bool Serenity_GetWindowWMInfo(_THIS, SDL_Window *window, struct SDL_SysWMinfo *info); +extern int Serenity_CreateWindowFramebuffer(_THIS, SDL_Window* window, + Uint32* format, void** pixels, + int* pitch); +extern int Serenity_UpdateWindowFramebuffer(_THIS, SDL_Window* window, + const SDL_Rect* rects, + int numrects); +extern void Serenity_DestroyWindowFramebuffer(_THIS, SDL_Window* window); +extern SDL_GLContext Serenity_GL_CreateContext(_THIS, SDL_Window* window); +extern void Serenity_GL_DeleteContext(_THIS, SDL_GLContext context); +extern void* Serenity_GL_GetProcAddress(_THIS, const char* proc); +extern int Serenity_GL_LoadLibrary(_THIS, const char* path); +extern int Serenity_GL_MakeCurrent(_THIS, SDL_Window* window, SDL_GLContext context); +extern int Serenity_GL_SwapWindow(_THIS, SDL_Window* window); + +static SDL_VideoDevice* SERENITY_CreateDevice(void) +{ + SDL_VideoDevice* device; + + /* Initialize all variables that we clean on shutdown */ + device = (SDL_VideoDevice*)SDL_calloc(1, sizeof(SDL_VideoDevice)); + if (!device) { + SDL_OutOfMemory(); + return (0); + } + device->is_dummy = SDL_FALSE; + + /* Set the function pointers */ + device->VideoInit = SERENITY_VideoInit; + device->VideoQuit = SERENITY_VideoQuit; + device->SetDisplayMode = SERENITY_SetDisplayMode; + device->PumpEvents = SERENITY_PumpEvents; + + device->CreateWindowFramebuffer = Serenity_CreateWindowFramebuffer; + device->UpdateWindowFramebuffer = Serenity_UpdateWindowFramebuffer; + device->DestroyWindowFramebuffer = Serenity_DestroyWindowFramebuffer; + device->CreateSDLWindow = Serenity_CreateWindow; + device->ShowWindow = Serenity_ShowWindow; + device->HideWindow = Serenity_HideWindow; + device->SetWindowTitle = Serenity_SetWindowTitle; + device->SetWindowSize = Serenity_SetWindowSize; + device->SetWindowFullscreen = Serenity_SetWindowFullscreen; + device->SetWindowIcon = Serenity_SetWindowIcon; + device->DestroyWindow = Serenity_DestroyWindow; + device->GetWindowWMInfo = Serenity_GetWindowWMInfo; + + device->GL_CreateContext = Serenity_GL_CreateContext; + device->GL_DeleteContext = Serenity_GL_DeleteContext; + device->GL_GetProcAddress = Serenity_GL_GetProcAddress; + device->GL_LoadLibrary = Serenity_GL_LoadLibrary; + device->GL_MakeCurrent = Serenity_GL_MakeCurrent; + device->GL_SwapWindow = Serenity_GL_SwapWindow; + + device->free = SERENITY_DeleteDevice; + + Serenity_GL_LoadLibrary(device, nullptr); + + return device; +} + +VideoBootStrap SERENITYVIDEO_bootstrap = { "serenity", "SDL serenity video driver", SERENITY_CreateDevice }; + +static RefPtr g_app; + +int SERENITY_VideoInit(_THIS) +{ + dbgln("{}: Initialising SDL application", __FUNCTION__); + + if (!g_app) { + g_app = MUST(GUI::Application::create(Main::Arguments {})); + g_app->set_quit_when_last_window_deleted(false); + } + + SERENITY_InitMouse(_this); + + // We only add the active desktop resolution until we properly support multiple + // fullscreen resolutions + auto desktop_rect = GUI::Desktop::the().rect(); + + SDL_DisplayMode mode; + mode.format = SDL_PIXELFORMAT_RGB888; + mode.w = desktop_rect.width(); + mode.h = desktop_rect.height(); + mode.refresh_rate = 60; + mode.driverdata = nullptr; + + if (SDL_AddBasicVideoDisplay(&mode) < 0) + return -1; + + SDL_AddDisplayMode(&_this->displays[0], &mode); + + /* We're done! */ + return 0; +} + +static int SERENITY_SetDisplayMode(_THIS, SDL_VideoDisplay* display, + SDL_DisplayMode* mode) +{ + dbgln("SERENITY_SetDisplayMode"); + return 0; +} + +void SERENITY_VideoQuit(_THIS) +{ + dbgln("SERENITY_VideoQuit"); + SERENITY_QuitMouse(_this); +} + +SerenitySDLWidget::SerenitySDLWidget(SDL_Window* sdl_window) + : m_sdl_window(sdl_window) +{ +} + +void SerenitySDLWidget::paint_event(GUI::PaintEvent& event) +{ + if (!m_buffer) + return; + + GUI::Painter painter(*this); + painter.add_clip_rect(event.rect()); + painter.blit(event.rect().location(), *m_buffer, event.rect()); +} + +void SerenitySDLWidget::resize_event(GUI::ResizeEvent&) +{ + SDL_SendWindowEvent(m_sdl_window, SDL_WINDOWEVENT_RESIZED, width(), height()); +} +void SerenitySDLWidget::show_event(GUI::ShowEvent&) +{ + SDL_SendWindowEvent(m_sdl_window, SDL_WINDOWEVENT_SHOWN, 0, 0); +} +void SerenitySDLWidget::hide_event(GUI::HideEvent&) +{ + SDL_SendWindowEvent(m_sdl_window, SDL_WINDOWEVENT_HIDDEN, 0, 0); +} + +static int map_button(GUI::MouseButton button) +{ + switch (button) { + case GUI::MouseButton::Primary: + return SDL_BUTTON_LEFT; + case GUI::MouseButton::Middle: + return SDL_BUTTON_MIDDLE; + case GUI::MouseButton::Secondary: + return SDL_BUTTON_RIGHT; + case GUI::MouseButton::Forward: + return SDL_BUTTON_X1; + case GUI::MouseButton::Backward: + return SDL_BUTTON_X2; + } + + VERIFY_NOT_REACHED(); + return 0; +} + +void SerenitySDLWidget::mousedown_event(GUI::MouseEvent& event) +{ + SDL_SendMouseMotion(m_sdl_window, 0, 0, event.x(), event.y()); + SDL_SendMouseButton(m_sdl_window, 0, SDL_PRESSED, map_button(event.button())); +} + +void SerenitySDLWidget::mousemove_event(GUI::MouseEvent& event) +{ + SDL_SendMouseMotion(m_sdl_window, 0, 0, event.x(), event.y()); +} + +void SerenitySDLWidget::mouseup_event(GUI::MouseEvent& event) +{ + SDL_SendMouseMotion(m_sdl_window, 0, 0, event.x(), event.y()); + SDL_SendMouseButton(m_sdl_window, 0, SDL_RELEASED, map_button(event.button())); +} + +void SerenitySDLWidget::mousewheel_event(GUI::MouseEvent& event) +{ + SDL_SendMouseMotion(m_sdl_window, 0, 0, event.x(), event.y()); + SDL_SendMouseWheel(m_sdl_window, 0, (float)event.wheel_raw_delta_x(), -(float)event.wheel_raw_delta_y(), SDL_MOUSEWHEEL_NORMAL); +} + +void SerenitySDLWidget::keydown_event(GUI::KeyEvent& event) +{ + SDL_SendKeyboardKey(SDL_PRESSED, scancode_map[event.key()]); + auto text = event.text(); + if (!text.is_empty()) + SDL_SendKeyboardText(text.characters()); +} + +void SerenitySDLWidget::keyup_event(GUI::KeyEvent& event) +{ + SDL_SendKeyboardKey(SDL_RELEASED, scancode_map[event.key()]); +} + +void SerenitySDLWidget::enter_event(Core::Event&) +{ + SDL_SetMouseFocus(m_sdl_window); +} + +void SerenitySDLWidget::leave_event(Core::Event&) +{ + SDL_SetMouseFocus(nullptr); +} + +int Serenity_CreateWindow(_THIS, SDL_Window* window) +{ + dbgln("{}: Creating new window of size {}x{}", __FUNCTION__, window->w, window->h); + + auto w = new SerenityPlatformWindow(window); + window->driverdata = w; + w->window()->set_double_buffering_enabled(false); + w->widget()->set_fill_with_background_color(false); + w->window()->set_main_widget(w->widget()); + w->window()->on_close_request = [window] { + SDL_SendWindowEvent(window, SDL_WINDOWEVENT_CLOSE, 0, 0); + return GUI::Window::CloseRequestDecision::StayOpen; + }; + w->window()->on_active_window_change = [window](bool is_active_window) { + SDL_SetKeyboardFocus(is_active_window ? window : nullptr); + }; + w->window()->on_input_preemption_change = [window](bool is_preempted) { + SDL_SetKeyboardFocus(is_preempted ? nullptr : window); + }; + SERENITY_PumpEvents(_this); + + return 0; +} + +void Serenity_ShowWindow(_THIS, SDL_Window* window) +{ + SerenityPlatformWindow::from_sdl_window(window)->window()->show(); +} + +void Serenity_HideWindow(_THIS, SDL_Window* window) +{ + SerenityPlatformWindow::from_sdl_window(window)->window()->hide(); +} + +void Serenity_SetWindowTitle(_THIS, SDL_Window* window) +{ + SerenityPlatformWindow::from_sdl_window(window)->window()->set_title(window->title); +} + +void Serenity_SetWindowSize(_THIS, SDL_Window* window) +{ + SerenityPlatformWindow::from_sdl_window(window)->window()->resize(window->w, window->h); +} + +void Serenity_SetWindowFullscreen(_THIS, SDL_Window* window, + SDL_VideoDisplay* display, + SDL_bool fullscreen) +{ + dbgln("Attempting to set SDL Window fullscreen to {}", (bool)fullscreen); + SerenityPlatformWindow::from_sdl_window(window)->window()->set_fullscreen(fullscreen); +} + +static Gfx::Color get_color_from_sdl_pixel(SDL_PixelFormat const& format, u32 pixel) +{ + u8 r = ((pixel & format.Rmask) >> format.Rshift) << format.Rloss; + u8 g = ((pixel & format.Gmask) >> format.Gshift) << format.Gloss; + u8 b = ((pixel & format.Bmask) >> format.Bshift) << format.Bloss; + u8 a = ((pixel & format.Amask) >> format.Ashift) << format.Aloss; + return {r, g, b, a}; +} + +static RefPtr create_bitmap_from_surface(SDL_Surface& icon) +{ + auto bitmap_or_error = Gfx::Bitmap::create(Gfx::BitmapFormat::BGRA8888, {icon.w, icon.h}); + if (bitmap_or_error.is_error()) + return {}; + auto bitmap = bitmap_or_error.release_value(); + + SDL_LockSurface(&icon); + + auto const& pixel_format = *icon.format; + auto pixels = static_cast(icon.pixels); + size_t offset = 0; + switch (pixel_format.format) { + case SDL_PIXELFORMAT_ARGB8888: + for (size_t y = 0; y < icon.h; ++y) { + for (size_t x = 0; x < icon.w; ++x) { + bitmap->set_pixel({x, y}, get_color_from_sdl_pixel(pixel_format, pixels[offset])); + ++offset; + } + } + break; + default: + warnln("Unable to convert SDL_Surface with format {} to bitmap", SDL_GetPixelFormatName(pixel_format.format)); + } + + SDL_UnlockSurface(&icon); + + return bitmap; +} + +void Serenity_SetWindowIcon(_THIS, SDL_Window* window, SDL_Surface* icon) +{ + auto serenity_window = SerenityPlatformWindow::from_sdl_window(window)->window(); + if (!icon) { + serenity_window->set_icon(nullptr); + return; + } + + auto icon_bitmap = create_bitmap_from_surface(*icon); + if (icon_bitmap) + serenity_window->set_icon(icon_bitmap); +} + +void Serenity_DestroyWindow(_THIS, SDL_Window* window) +{ + auto platform_window = SerenityPlatformWindow::from_sdl_window(window); + delete platform_window; + window->driverdata = nullptr; +} + +SDL_bool Serenity_GetWindowWMInfo(_THIS, SDL_Window *window, SDL_SysWMinfo *info) +{ + info->subsystem = SDL_SYSWM_SERENITY; + return SDL_TRUE; +} + +int Serenity_CreateWindowFramebuffer(_THIS, SDL_Window* window, Uint32* format, + void** pixels, int* pitch) +{ + dbgln("{}: Creating a new framebuffer of size {}x{}", __FUNCTION__, window->w, window->h); + auto win = SerenityPlatformWindow::from_sdl_window(window); + *format = SDL_PIXELFORMAT_RGB888; + auto bitmap_or_error = Gfx::Bitmap::create(Gfx::BitmapFormat::BGRx8888, {window->w, window->h}); + if (bitmap_or_error.is_error()) + return SDL_OutOfMemory(); + win->widget()->m_buffer = bitmap_or_error.release_value(); + *pitch = win->widget()->m_buffer->pitch(); + *pixels = win->widget()->m_buffer->scanline(0); + return 0; +} + +int Serenity_UpdateWindowFramebuffer(_THIS, SDL_Window* window, + const SDL_Rect* rects, int numrects) +{ + auto win = SerenityPlatformWindow::from_sdl_window(window); + for (int i = 0; i < numrects; i++) { + win->widget()->update(Gfx::IntRect(rects[i].x, rects[i].y, rects[i].w, rects[i].h)); + } + SERENITY_PumpEvents(_this); + + return 0; +} + +void Serenity_DestroyWindowFramebuffer(_THIS, SDL_Window* window) +{ + auto widget = SerenityPlatformWindow::from_sdl_window(window)->widget(); + dbgln("{}: Destroy framebuffer {}", __FUNCTION__, widget->m_buffer->size()); + widget->m_buffer = nullptr; +} + +SDL_GLContext Serenity_GL_CreateContext(_THIS, SDL_Window* window) +{ + auto platform_window = SerenityPlatformWindow::from_sdl_window(window); + + Uint32 format; + void* pixels; + int pitch; + Serenity_CreateWindowFramebuffer(_this, window, &format, &pixels, &pitch); + + auto gl_context_or_error = GL::create_context(*platform_window->widget()->m_buffer); + if (gl_context_or_error.is_error()) { + SDL_SetError("Failed to initialize the GL context"); + return nullptr; + } + auto gl_context = gl_context_or_error.release_value(); + auto serenity_gl_context = new SerenityGLContext(move(gl_context)); + platform_window->set_serenity_gl_context(serenity_gl_context); + GL::make_context_current(&serenity_gl_context->gl_context()); + return serenity_gl_context; +} + +void Serenity_GL_DeleteContext(_THIS, SDL_GLContext context) +{ + auto platform_context = static_cast(context); + delete platform_context; +} + +int Serenity_GL_LoadLibrary(_THIS, const char* path) +{ + if (_this->gl_config.driver_loaded) { + SDL_SetError("OpenGL library is already loaded"); + return -1; + } + + _this->gl_config.dll_handle = dlopen("libgl.so.serenity", RTLD_LAZY | RTLD_LOCAL); + if (!_this->gl_config.dll_handle) { + dbgln("Could not load OpenGL library: {}", dlerror()); + _this->gl_config.driver_loaded = SDL_FALSE; + return -1; + } + + _this->gl_config.driver_loaded = SDL_TRUE; + return 0; +} + +void* Serenity_GL_GetProcAddress(_THIS, const char* proc) +{ + if (!_this->gl_config.dll_handle) { + SDL_SetError("OpenGL library not available"); + return nullptr; + } + auto* res = dlsym(_this->gl_config.dll_handle, proc); + dbgln("GetProcAddress: {} -> {}", proc, res); + return res; +} + +int Serenity_GL_MakeCurrent(_THIS, SDL_Window* window, SDL_GLContext context) +{ + if (!window || !context) + return 0; + + auto platform_window = SerenityPlatformWindow::from_sdl_window(window); + auto serenity_gl_context = static_cast(context); + platform_window->set_serenity_gl_context(serenity_gl_context); + GL::make_context_current(&serenity_gl_context->gl_context()); + return 0; +} + +int Serenity_GL_SwapWindow(_THIS, SDL_Window* window) +{ + auto platform_window = SerenityPlatformWindow::from_sdl_window(window); + if (platform_window->serenity_gl_context()) + platform_window->serenity_gl_context()->gl_context().present(); + + platform_window->widget()->repaint(); + return 0; +} + +#endif /* SDL_VIDEO_DRIVER_SERENITY */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/video/serenity/SDL_serenityvideo.h b/src/video/serenity/SDL_serenityvideo.h new file mode 100644 index 0000000000000000000000000000000000000000..f74e1bd981734caf023b77a9b9fa30c46be40e4c --- /dev/null +++ b/src/video/serenity/SDL_serenityvideo.h @@ -0,0 +1,101 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2019 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_serenityvideo_h_ +#define SDL_serenityvideo_h_ + +#include "../SDL_sysvideo.h" + +#include +#include +#include + +class SerenitySDLWidget final : public GUI::Widget { + C_OBJECT(SerenitySDLWidget) +public: + explicit SerenitySDLWidget(SDL_Window*); + RefPtr m_buffer; + +protected: + void paint_event(GUI::PaintEvent&) override; + void resize_event(GUI::ResizeEvent&) override; + void show_event(GUI::ShowEvent&) override; + void hide_event(GUI::HideEvent&) override; + + void mousedown_event(GUI::MouseEvent&) override; + void mousemove_event(GUI::MouseEvent&) override; + void mouseup_event(GUI::MouseEvent&) override; + void mousewheel_event(GUI::MouseEvent&) override; + + void keydown_event(GUI::KeyEvent& event) override; + void keyup_event(GUI::KeyEvent& event) override; + + void enter_event(Core::Event&) override; + void leave_event(Core::Event&) override; + +private: + SDL_Window* m_sdl_window = nullptr; +}; + +class SerenityGLContext final { +public: + explicit SerenityGLContext(NonnullOwnPtr gl_context) + : m_gl_context(move(gl_context)) + { + } + + GL::GLContext& gl_context() { return *m_gl_context; } + +private: + NonnullOwnPtr m_gl_context; +}; + +class SerenityPlatformWindow final { +public: + explicit SerenityPlatformWindow(SDL_Window* sdl_window) + : m_window(MUST(GUI::Window::try_create())) + , m_widget(MUST(SerenitySDLWidget::try_create(sdl_window))) + { + m_window->resize(sdl_window->w, sdl_window->h); + m_window->set_resizable(false); + } + + static SerenityPlatformWindow* from_sdl_window(SDL_Window* window) + { + return static_cast(window->driverdata); + } + + SerenityGLContext* serenity_gl_context() { return m_serenity_gl_context; } + void set_serenity_gl_context(SerenityGLContext* serenity_gl_context) { m_serenity_gl_context = serenity_gl_context; } + + NonnullRefPtr window() { return m_window; } + NonnullRefPtr widget() { return m_widget; } + +private: + SerenityGLContext* m_serenity_gl_context; + NonnullRefPtr m_window; + NonnullRefPtr m_widget; +}; + +#endif /* SDL_serenityvideo_h_ */ + +/* vi: set ts=4 sw=4 expandtab: */