From 732977db277075b08392394bf9c132bff6154a93 Mon Sep 17 00:00:00 2001 From: Mihai Parparita Date: Wed, 30 Aug 2023 23:12:43 -0700 Subject: [PATCH 1/2] Minimal changes to get an Emscripten build Disable stack traces in loguru (excinfo.h is not available in Emscripten) and set Emscripten linker flags. --- CMakeLists.txt | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index a9fbba8..66f27d0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,6 +40,12 @@ else() # Windows build relies on vcpkg add_compile_definitions(SDL_MAIN_HANDLED) endif() +if (EMSCRIPTEN) + message(STATUS "Targeting Emscripten") + # loguru tries to include excinfo.h, which is not available under Emscripten. + add_compile_definitions(LOGURU_STACKTRACES=0) +endif() + option(DPPC_BUILD_PPC_TESTS "Build PowerPC tests" OFF) option(DPPC_BUILD_BENCHMARKS "Build benchmarking programs" OFF) @@ -118,6 +124,16 @@ add_executable(dingusppc ${SOURCES} $ if (WIN32) target_link_libraries(dingusppc PRIVATE SDL2::SDL2 SDL2::SDL2main cubeb) +elseif (EMSCRIPTEN) + target_link_libraries(dingusppc PRIVATE SDL2::SDL2 SDL2::SDL2main cubeb + ${CMAKE_DL_LIBS} ${CMAKE_THREAD_LIBS_INIT} + "-gsource-map" + # 256 MB max for emulated Mac RAM, plus 32 MB of emulator overhead + "-s INITIAL_MEMORY=301989888" + "-s MODULARIZE" + "-s EXPORT_ES6" + "-s EXPORT_NAME=emulator" + "-s 'EXTRA_EXPORTED_RUNTIME_METHODS=[\"FS\"]'") else() target_link_libraries(dingusppc PRIVATE SDL2::SDL2 SDL2::SDL2main cubeb ${CMAKE_DL_LIBS} ${CMAKE_THREAD_LIBS_INIT}) From 1f7edfdb3b9f513ab5e4271aedda85a65f7ee299 Mon Sep 17 00:00:00 2001 From: Mihai Parparita Date: Sun, 3 Sep 2023 17:59:35 -0700 Subject: [PATCH 2/2] Make Emscripten build not depend on SDL2 or cubeb While Emscripten has an SDL compabtility layer, it assumes that the code is executing in the main browser process (and thus has access to them DOM). The Infinite Mac project runs emulators in a worker thread (for better performance) and has a custom API for the display, sound, input, etc. Similarly, it does not need the cross-platform sound support from cubeb, there there is a sound API as well. This commit makes SDL (*_sdl.cpp) and cubeb-based (*_cubeb.cpp) code be skipped when targeting Emscripten, and instead *_js.cpp files are used instead (this is the cross-platform convention used by Chromium[^1], and could be extended for other targets). For hostevents.cpp and soundserver.cpp the entire file was replaced, whereas for videoctrl.cpp there was enough shared logic that it was kept, and the platform-specific bits were moved behind a Display class that can have per-platform implementations. For cases where we need additional private fields in the platform-specific classes, we use a PIMPL pattern. The *_js.cpp files with implementations are not included in this commit, since they are closely tied to the Infinite Mac project, and will live in its fork of DingusPPC. [^1]: https://www.chromium.org/developers/design-documents/conventions-and-patterns-for-multi-platform-development/ --- CMakeLists.txt | 28 ++-- cmake/PlatformGlob.cmake | 21 +++ core/CMakeLists.txt | 8 +- core/{hostevents.cpp => hostevents_sdl.cpp} | 0 devices/CMakeLists.txt | 39 +++-- devices/floppy/floppyimg.cpp | 2 +- devices/sound/soundserver.h | 23 +-- ...{soundserver.cpp => soundserver_cubeb.cpp} | 87 ++++++---- devices/video/display.h | 53 ++++++ devices/video/display_sdl.cpp | 157 ++++++++++++++++++ devices/video/videoctrl.cpp | 130 ++------------- devices/video/videoctrl.h | 10 +- machines/CMakeLists.txt | 2 +- main.cpp | 12 +- main.h | 25 +++ main_sdl.cpp | 38 +++++ utils/CMakeLists.txt | 2 +- 17 files changed, 429 insertions(+), 208 deletions(-) create mode 100644 cmake/PlatformGlob.cmake rename core/{hostevents.cpp => hostevents_sdl.cpp} (100%) rename devices/sound/{soundserver.cpp => soundserver_cubeb.cpp} (72%) create mode 100644 devices/video/display.h create mode 100644 devices/video/display_sdl.cpp create mode 100644 main.h create mode 100644 main_sdl.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 66f27d0..c54fe88 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,20 +1,23 @@ cmake_minimum_required(VERSION 3.1) project(dingusppc) +list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") +include(PlatformGlob) + set(CMAKE_CXX_STANDARD 20) set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}/lib) -if (NOT WIN32) +if (NOT WIN32 AND NOT EMSCRIPTEN) find_package(SDL2 REQUIRED) include_directories(${SDL2_INCLUDE_DIRS}) if (UNIX AND NOT APPLE) find_package (Threads) endif() -else() # Windows build relies on vcpkg +elseif (WIN32) # Windows build relies on vcpkg # pick up system wide vcpkg if exists if (DEFINED ENV{VCPKG_ROOT} AND EXISTS $ENV{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake) message(STATUS "Using system vcpkg at $ENV{VCPKG_ROOT}") @@ -89,11 +92,12 @@ add_subdirectory("${PROJECT_SOURCE_DIR}/machines/") add_subdirectory("${PROJECT_SOURCE_DIR}/utils/") add_subdirectory("${PROJECT_SOURCE_DIR}/thirdparty/loguru/") -set(BUILD_TESTS OFF CACHE BOOL "Build Cubeb tests") -set(BUILD_SHARED_LIBS OFF CACHE BOOL "Build shared libraries") -set(BUILD_TOOLS OFF CACHE BOOL "Build Cubeb tools") - -add_subdirectory(thirdparty/cubeb EXCLUDE_FROM_ALL) +if (NOT EMSCRIPTEN) + set(BUILD_TESTS OFF CACHE BOOL "Build Cubeb tests") + set(BUILD_SHARED_LIBS OFF CACHE BOOL "Build shared libraries") + set(BUILD_TOOLS OFF CACHE BOOL "Build Cubeb tools") + add_subdirectory(thirdparty/cubeb EXCLUDE_FROM_ALL) +endif() set(CLI11_ROOT ${PROJECT_SOURCE_DIR}/thirdparty/CLI11) @@ -107,10 +111,10 @@ include_directories("${PROJECT_SOURCE_DIR}" "${PROJECT_SOURCE_DIR}/thirdparty/CLI11/" "${PROJECT_SOURCE_DIR}/thirdparty/cubeb/include") -file(GLOB SOURCES "${PROJECT_SOURCE_DIR}/*.cpp" - "${PROJECT_SOURCE_DIR}/*.c" - "${PROJECT_SOURCE_DIR}/*.hpp" - "${PROJECT_SOURCE_DIR}/*.h") +platform_glob(SOURCES "${PROJECT_SOURCE_DIR}/*.cpp" + "${PROJECT_SOURCE_DIR}/*.c" + "${PROJECT_SOURCE_DIR}/*.hpp" + "${PROJECT_SOURCE_DIR}/*.h") file(GLOB TEST_SOURCES "${PROJECT_SOURCE_DIR}/cpu/ppc/test/*.cpp") @@ -125,7 +129,7 @@ add_executable(dingusppc ${SOURCES} $ if (WIN32) target_link_libraries(dingusppc PRIVATE SDL2::SDL2 SDL2::SDL2main cubeb) elseif (EMSCRIPTEN) - target_link_libraries(dingusppc PRIVATE SDL2::SDL2 SDL2::SDL2main cubeb + target_link_libraries(dingusppc PRIVATE ${CMAKE_DL_LIBS} ${CMAKE_THREAD_LIBS_INIT} "-gsource-map" # 256 MB max for emulated Mac RAM, plus 32 MB of emulator overhead diff --git a/cmake/PlatformGlob.cmake b/cmake/PlatformGlob.cmake new file mode 100644 index 0000000..11b3b03 --- /dev/null +++ b/cmake/PlatformGlob.cmake @@ -0,0 +1,21 @@ +# Detect platform suffix +if (EMSCRIPTEN) + set(PLATFORM_SUFFIX "_js$") +else() + set(PLATFORM_SUFFIX "_sdl|_cubeb$") +endif() + +# Function to perform a platform-specific glob +function(platform_glob RESULT_VAR) + set(PLATFORM_SOURCES) + foreach(GLOB_PATTERN ${ARGN}) + file(GLOB GLOB_RESULT ${GLOB_PATTERN}) + foreach(FILE_PATH ${GLOB_RESULT}) + get_filename_component(BASE_NAME ${FILE_PATH} NAME_WE) + if("${BASE_NAME}" MATCHES ${PLATFORM_SUFFIX} OR NOT "${BASE_NAME}" MATCHES "_js$|_sdl|_cubeb$") + list(APPEND PLATFORM_SOURCES ${FILE_PATH}) + endif() + endforeach() + endforeach() + set(${RESULT_VAR} ${PLATFORM_SOURCES} PARENT_SCOPE) +endfunction() diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index c109240..8b02212 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -2,7 +2,11 @@ include_directories("${PROJECT_SOURCE_DIR}" "${PROJECT_SOURCE_DIR}/thirdparty/loguru/" ) -file(GLOB SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp") +platform_glob(SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp") add_library(core OBJECT ${SOURCES}) -target_link_libraries(core PRIVATE SDL2::SDL2) +if (EMSCRIPTEN) + target_link_libraries(core PRIVATE) +else() + target_link_libraries(core PRIVATE SDL2::SDL2) +endif() diff --git a/core/hostevents.cpp b/core/hostevents_sdl.cpp similarity index 100% rename from core/hostevents.cpp rename to core/hostevents_sdl.cpp diff --git a/devices/CMakeLists.txt b/devices/CMakeLists.txt index 3639478..4cb4fd2 100644 --- a/devices/CMakeLists.txt +++ b/devices/CMakeLists.txt @@ -1,23 +1,30 @@ +list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") +include(PlatformGlob) + include_directories("${PROJECT_SOURCE_DIR}" "${PROJECT_SOURCE_DIR}/thirdparty/loguru/" ) -file(GLOB SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/common/*.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/common/adb/*.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/common/i2c/*.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/common/ata/*.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/common/pci/*.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/common/scsi/*.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/ethernet/*.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/floppy/*.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/ioctrl/*.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/memctrl/*.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/serial/*.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/sound/*.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/storage/*.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/video/*.cpp" +platform_glob(SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/common/*.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/common/adb/*.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/common/i2c/*.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/common/ata/*.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/common/pci/*.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/common/scsi/*.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/ethernet/*.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/floppy/*.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/ioctrl/*.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/memctrl/*.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/serial/*.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/sound/*.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/storage/*.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/video/*.cpp" ) add_library(devices OBJECT ${SOURCES}) -target_link_libraries(devices PRIVATE cubeb SDL2::SDL2) +if (EMSCRIPTEN) + target_link_libraries(devices PRIVATE) +else() + target_link_libraries(devices PRIVATE cubeb SDL2::SDL2) +endif() diff --git a/devices/floppy/floppyimg.cpp b/devices/floppy/floppyimg.cpp index 69c0343..59ab541 100644 --- a/devices/floppy/floppyimg.cpp +++ b/devices/floppy/floppyimg.cpp @@ -301,7 +301,7 @@ FloppyImgConverter* open_floppy_image(std::string& img_path) img_file.open(img_path, std::ios::in | std::ios::binary); if (img_file.fail()) { img_file.close(); - LOG_F(ERROR, "Could not open specified floppy image!"); + LOG_F(ERROR, "Could not open specified floppy image (%s)!", img_path.c_str()); return nullptr; } diff --git a/devices/sound/soundserver.h b/devices/sound/soundserver.h index 2b0dd7a..cd74e99 100644 --- a/devices/sound/soundserver.h +++ b/devices/sound/soundserver.h @@ -34,25 +34,14 @@ along with this program. If not, see . #ifndef SOUND_SERVER_H #define SOUND_SERVER_H -#include #include -enum { - SND_SERVER_DOWN = 0, - SND_API_READY, - SND_SERVER_UP, - SND_STREAM_OPENED, - SND_STREAM_CLOSED -}; - +#include class SoundServer : public HWComponent { public: - SoundServer() { - supports_types(HWCompType::SND_SERVER); - this->start(); - }; - ~SoundServer() { this->shutdown(); }; + SoundServer(); + ~SoundServer(); int start(); void shutdown(); @@ -61,10 +50,8 @@ public: void close_out_stream(); private: - int status; /* server status */ - cubeb *cubeb_ctx; - - cubeb_stream *out_stream; + class Impl; // Holds private fields + std::unique_ptr impl; }; #endif /* SOUND_SERVER_H */ diff --git a/devices/sound/soundserver.cpp b/devices/sound/soundserver_cubeb.cpp similarity index 72% rename from devices/sound/soundserver.cpp rename to devices/sound/soundserver_cubeb.cpp index ebcd2a9..fc22805 100644 --- a/devices/sound/soundserver.cpp +++ b/devices/sound/soundserver_cubeb.cpp @@ -29,60 +29,87 @@ along with this program. If not, see . #include #endif +enum { + SND_SERVER_DOWN = 0, + SND_API_READY, + SND_SERVER_UP, + SND_STREAM_OPENED, + SND_STREAM_CLOSED +}; + +class SoundServer::Impl { +public: + int status; /* server status */ + cubeb *cubeb_ctx; + + cubeb_stream *out_stream; +}; + +SoundServer::SoundServer(): impl(std::make_unique()) +{ + supports_types(HWCompType::SND_SERVER); + this->start(); +} + +SoundServer::~SoundServer() +{ + this->shutdown(); +} + #if 0 int SoundServer::start() { int err; - this->status = SND_SERVER_DOWN; + impl->status = SND_SERVER_DOWN; - this->soundio = soundio_create(); - if (!this->soundio) { + impl->soundio = soundio_create(); + if (!impl->soundio) { LOG_F(ERROR, "Sound Server: out of memory"); return -1; } - if ((err = soundio_connect(this->soundio))) { + if ((err = soundio_connect(impl->soundio))) { LOG_F(ERROR, "Unable to connect to backend: %s", soundio_strerror(err)); return -1; } LOG_F(INFO, "Connected to backend: %s", soundio_backend_name(soundio->current_backend)); - soundio_flush_events(this->soundio); + soundio_flush_events(impl->soundio); - this->status = SND_API_READY; + impl->status = SND_API_READY; - this->out_dev_index = soundio_default_output_device_index(this->soundio); - if (this->out_dev_index < 0) { + impl->out_dev_index = soundio_default_output_device_index(impl->soundio); + if (impl->out_dev_index < 0) { LOG_F(ERROR, "Sound Server: no output device found"); return -1; } - this->out_device = soundio_get_output_device(this->soundio, this->out_dev_index); - if (!this->out_device) { + impl->out_device = soundio_get_output_device(this->soundio, this->out_dev_index); + if (!impl->out_device) { LOG_F(ERROR, "Sound Server: out of memory"); return -1; } - LOG_F(INFO, "Sound Server output device: %s", this->out_device->name); + LOG_F(INFO, "Sound Server output device: %s", impl->out_device->name); - this->status = SND_SERVER_UP; + impl->status = SND_SERVER_UP; return 0; } void SoundServer::shutdown() { - switch (this->status) { + switch (impl->status) { case SND_SERVER_UP: - soundio_device_unref(this->out_device); + soundio_device_unref(impl->out_device); /* fall through */ case SND_API_READY: - soundio_destroy(this->soundio); + soundio_destroy(impl->soundio); } - this->status = SND_SERVER_DOWN; + impl->status = SND_SERVER_DOWN; LOG_F(INFO, "Sound Server shut down."); } @@ -96,24 +123,24 @@ int SoundServer::start() CoInitialize(nullptr); #endif - this->status = SND_SERVER_DOWN; + impl->status = SND_SERVER_DOWN; - res = cubeb_init(&this->cubeb_ctx, "Dingus sound server", NULL); + res = cubeb_init(&impl->cubeb_ctx, "Dingus sound server", NULL); if (res != CUBEB_OK) { LOG_F(ERROR, "Could not initialize Cubeb library"); return -1; } - LOG_F(INFO, "Connected to backend: %s", cubeb_get_backend_id(this->cubeb_ctx)); + LOG_F(INFO, "Connected to backend: %s", cubeb_get_backend_id(impl->cubeb_ctx)); - this->status = SND_API_READY; + impl->status = SND_API_READY; return 0; } void SoundServer::shutdown() { - switch (this->status) { + switch (impl->status) { case SND_STREAM_OPENED: close_out_stream(); /* fall through */ @@ -122,10 +149,10 @@ void SoundServer::shutdown() case SND_SERVER_UP: /* fall through */ case SND_API_READY: - cubeb_destroy(this->cubeb_ctx); + cubeb_destroy(impl->cubeb_ctx); } - this->status = SND_SERVER_DOWN; + impl->status = SND_SERVER_DOWN; LOG_F(INFO, "Sound Server shut down."); } @@ -189,7 +216,7 @@ int SoundServer::open_out_stream(uint32_t sample_rate, void *user_data) params.layout = CUBEB_LAYOUT_STEREO; params.prefs = CUBEB_STREAM_PREF_NONE; - res = cubeb_get_min_latency(this->cubeb_ctx, ¶ms, &latency_frames); + res = cubeb_get_min_latency(impl->cubeb_ctx, ¶ms, &latency_frames); if (res != CUBEB_OK) { LOG_F(ERROR, "Could not get minimum latency, error: %d", res); return -1; @@ -197,7 +224,7 @@ int SoundServer::open_out_stream(uint32_t sample_rate, void *user_data) LOG_F(9, "Minimum sound latency: %d frames", latency_frames); } - res = cubeb_stream_init(this->cubeb_ctx, &this->out_stream, "SndOut stream", + res = cubeb_stream_init(impl->cubeb_ctx, &impl->out_stream, "SndOut stream", NULL, NULL, NULL, ¶ms, latency_frames, sound_out_callback, status_callback, user_data); if (res != CUBEB_OK) { @@ -207,20 +234,20 @@ int SoundServer::open_out_stream(uint32_t sample_rate, void *user_data) LOG_F(9, "Sound output stream opened."); - this->status = SND_STREAM_OPENED; + impl->status = SND_STREAM_OPENED; return 0; } int SoundServer::start_out_stream() { - return cubeb_stream_start(this->out_stream); + return cubeb_stream_start(impl->out_stream); } void SoundServer::close_out_stream() { - cubeb_stream_stop(this->out_stream); - cubeb_stream_destroy(this->out_stream); - this->status = SND_STREAM_CLOSED; + cubeb_stream_stop(impl->out_stream); + cubeb_stream_destroy(impl->out_stream); + impl->status = SND_STREAM_CLOSED; LOG_F(9, "Sound output stream closed."); } diff --git a/devices/video/display.h b/devices/video/display.h new file mode 100644 index 0000000..435b329 --- /dev/null +++ b/devices/video/display.h @@ -0,0 +1,53 @@ +/* +DingusPPC - The Experimental PowerPC Macintosh emulator +Copyright (C) 2018-23 divingkatae and maximum + (theweirdo) spatium + +(Contact divingkatae#1017 or powermax#2286 on Discord for more info) + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +/** @file Display/screen abstraction (implemented on each platform). */ + +#ifndef DISPLAY_H +#define DISPLAY_H + +#include + +#include +#include + +class Display { +public: + Display(); + ~Display(); + + // Configures the display for the given width/height. + // Returns true if this is the first time the screen has been configured. + bool configure(int width, int height); + + // Clears the display + void blank(); + + void update(std::function convert_fb_cb, bool draw_hw_cursor, int cursor_x, int cursor_y); + + void handle_events(const WindowEvent& wnd_event); + void setup_hw_cursor(std::function draw_hw_cursor, int cursor_width, int cursor_height); +private: + class Impl; // Holds private fields + std::unique_ptr impl; +}; + +#endif // DISPLAY_H diff --git a/devices/video/display_sdl.cpp b/devices/video/display_sdl.cpp new file mode 100644 index 0000000..4655c5d --- /dev/null +++ b/devices/video/display_sdl.cpp @@ -0,0 +1,157 @@ +#include +#include +#include + +class Display::Impl { +public: + bool resizing = false; + uint32_t disp_wnd_id = 0; + SDL_Window* display_wnd = 0; + SDL_Renderer* renderer = 0; + SDL_Texture* disp_texture = 0; + SDL_Texture* cursor_texture = 0; + SDL_Rect cursor_rect; // destination rectangle for cursor drawing +}; + +Display::Display(): impl(std::make_unique()) +{ + +} + +Display::~Display() +{ + if (impl->cursor_texture) { + SDL_DestroyTexture(impl->cursor_texture); + } + + if (impl->disp_texture) { + SDL_DestroyTexture(impl->disp_texture); + } + + if (impl->renderer) { + SDL_DestroyRenderer(impl->renderer); + } + + if (impl->display_wnd) { + SDL_DestroyWindow(impl->display_wnd); + } +} + +bool Display::configure(int width, int height) +{ + bool is_initialization = false; + + if (!impl->display_wnd) { // create display window + impl->display_wnd = SDL_CreateWindow( + "DingusPPC Display", + SDL_WINDOWPOS_UNDEFINED, + SDL_WINDOWPOS_UNDEFINED, + width, height, + SDL_WINDOW_OPENGL + ); + + impl->disp_wnd_id = SDL_GetWindowID(impl->display_wnd); + if (impl->display_wnd == NULL) { + ABORT_F("Display: SDL_CreateWindow failed with %s", SDL_GetError()); + } + + impl->renderer = SDL_CreateRenderer(impl->display_wnd, -1, SDL_RENDERER_ACCELERATED); + if (impl->renderer == NULL) { + ABORT_F("Display: SDL_CreateRenderer failed with %s", SDL_GetError()); + } + + is_initialization = true; + } else { // resize display window + SDL_SetWindowSize(impl->display_wnd, width, height); + impl->resizing = true; + } + + if (impl->disp_texture) { + SDL_DestroyTexture(impl->disp_texture); + } + + impl->disp_texture = SDL_CreateTexture( + impl->renderer, + SDL_PIXELFORMAT_ARGB8888, + SDL_TEXTUREACCESS_STREAMING, + width, height + ); + + if (impl->disp_texture == NULL) { + ABORT_F("Display: SDL_CreateTexture failed with %s", SDL_GetError()); + } + + return is_initialization; +} + +void Display::handle_events(const WindowEvent& wnd_event) +{ + if (wnd_event.sub_type == SDL_WINDOWEVENT_SIZE_CHANGED && + wnd_event.window_id == impl->disp_wnd_id) + impl->resizing = false; +} + +void Display::blank() +{ + SDL_SetRenderDrawColor(impl->renderer, 0, 0, 0, 255); + SDL_RenderClear(impl->renderer); + SDL_RenderPresent(impl->renderer); +} + +void Display::update(std::function convert_fb_cb, bool draw_hw_cursor, int cursor_x, int cursor_y) +{ + if (impl->resizing) + return; + + uint8_t* dst_buf; + int dst_pitch; + + SDL_LockTexture(impl->disp_texture, NULL, (void **)&dst_buf, &dst_pitch); + + // texture update callback to get ARGB data from guest framebuffer + convert_fb_cb(dst_buf, dst_pitch); + + SDL_UnlockTexture(impl->disp_texture); + SDL_RenderClear(impl->renderer); + SDL_RenderCopy(impl->renderer, impl->disp_texture, NULL, NULL); + + // draw HW cursor if enabled + if (draw_hw_cursor) { + impl->cursor_rect.x = cursor_x; + impl->cursor_rect.y = cursor_y; + SDL_RenderCopy(impl->renderer, impl->cursor_texture, NULL, &impl->cursor_rect); + } + + SDL_RenderPresent(impl->renderer); +} + +void Display::setup_hw_cursor(std::function draw_hw_cursor, int cursor_width, int cursor_height) +{ + uint8_t* dst_buf; + int dst_pitch; + + if (impl->cursor_texture) { + SDL_DestroyTexture(impl->cursor_texture); + } + + impl->cursor_texture = SDL_CreateTexture( + impl->renderer, + SDL_PIXELFORMAT_ARGB8888, + SDL_TEXTUREACCESS_STREAMING, + cursor_width, cursor_height + ); + + if (impl->cursor_texture == NULL) { + ABORT_F("SDL_CreateTexture for HW cursor failed with %s", SDL_GetError()); + } + + SDL_LockTexture(impl->cursor_texture, NULL, (void **)&dst_buf, &dst_pitch); + SDL_SetTextureBlendMode(impl->cursor_texture, SDL_BLENDMODE_BLEND); + draw_hw_cursor(dst_buf, dst_pitch); + SDL_UnlockTexture(impl->cursor_texture); + + impl->cursor_rect.x = 0; + impl->cursor_rect.y = 0; + impl->cursor_rect.w = cursor_width; + impl->cursor_rect.h = cursor_height; +} diff --git a/devices/video/videoctrl.cpp b/devices/video/videoctrl.cpp index eaa91c0..1ea902f 100644 --- a/devices/video/videoctrl.cpp +++ b/devices/video/videoctrl.cpp @@ -23,11 +23,8 @@ along with this program. If not, see . #include #include -#include #include -#include -#include #include VideoCtrlBase::VideoCtrlBase(int width, int height) @@ -39,115 +36,45 @@ VideoCtrlBase::VideoCtrlBase(int width, int height) VideoCtrlBase::~VideoCtrlBase() { - if (this->cursor_texture) { - SDL_DestroyTexture(this->cursor_texture); - } - - if (this->disp_texture) { - SDL_DestroyTexture(this->disp_texture); - } - - if (this->renderer) { - SDL_DestroyRenderer(this->renderer); - } - - if (this->display_wnd) { - SDL_DestroyWindow(this->display_wnd); - } } void VideoCtrlBase::handle_events(const WindowEvent& wnd_event) { - if (wnd_event.sub_type == SDL_WINDOWEVENT_SIZE_CHANGED && - wnd_event.window_id == this->disp_wnd_id) - this->resizing = false; + this->display.handle_events(wnd_event); } +// TODO: consider renaming, since it's not always a window void VideoCtrlBase::create_display_window(int width, int height) { - if (!this->display_wnd) { // create display window - this->display_wnd = SDL_CreateWindow( - "DingusPPC Display", - SDL_WINDOWPOS_UNDEFINED, - SDL_WINDOWPOS_UNDEFINED, - width, height, - SDL_WINDOW_OPENGL - ); - - this->disp_wnd_id = SDL_GetWindowID(this->display_wnd); - - if (this->display_wnd == NULL) { - ABORT_F("Display: SDL_CreateWindow failed with %s", SDL_GetError()); - } - - this->renderer = SDL_CreateRenderer(this->display_wnd, -1, SDL_RENDERER_ACCELERATED); - if (this->renderer == NULL) { - ABORT_F("Display: SDL_CreateRenderer failed with %s", SDL_GetError()); - } - + bool is_initialization = this->display.configure(width, height); + if (is_initialization) { this->blank_on = true; // TODO: should be true! - this->blank_display(); - } else { // resize display window - SDL_SetWindowSize(this->display_wnd, width, height); - this->resizing = true; } this->active_width = width; this->active_height = height; - - if (this->disp_texture) { - SDL_DestroyTexture(this->disp_texture); - } - - this->disp_texture = SDL_CreateTexture( - this->renderer, - SDL_PIXELFORMAT_ARGB8888, - SDL_TEXTUREACCESS_STREAMING, - width, height - ); - - if (this->disp_texture == NULL) { - ABORT_F("Display: SDL_CreateTexture failed with %s", SDL_GetError()); - } } void VideoCtrlBase::blank_display() { - SDL_SetRenderDrawColor(this->renderer, 0, 0, 0, 255); - SDL_RenderClear(this->renderer); - SDL_RenderPresent(this->renderer); + this->display.blank(); } void VideoCtrlBase::update_screen() { - uint8_t* dst_buf; - int dst_pitch; - - //auto start_time = std::chrono::steady_clock::now(); - - if (this->resizing) - return; - if (this->blank_on) { - this->blank_display(); + this->display.blank(); return; } - SDL_LockTexture(this->disp_texture, NULL, (void **)&dst_buf, &dst_pitch); - - // texture update callback to get ARGB data from guest framebuffer - this->convert_fb_cb(dst_buf, dst_pitch); - - SDL_UnlockTexture(this->disp_texture); - SDL_RenderClear(this->renderer); - SDL_RenderCopy(this->renderer, this->disp_texture, NULL, NULL); - - // draw HW cursor if enabled + int cursor_x = 0; + int cursor_y = 0; if (this->cursor_on) { - this->get_cursor_position(cursor_rect.x, cursor_rect.y); - SDL_RenderCopy(this->renderer, this->cursor_texture, NULL, &cursor_rect); + this->get_cursor_position(cursor_x, cursor_y); } - SDL_RenderPresent(this->renderer); + this->display.update( + this->convert_fb_cb, + this->cursor_on, cursor_x, cursor_y); } void VideoCtrlBase::start_refresh_task() { @@ -200,34 +127,11 @@ void VideoCtrlBase::set_palette_color(uint8_t index, uint8_t r, uint8_t g, uint8 void VideoCtrlBase::setup_hw_cursor(int cursor_width, int cursor_height) { - uint8_t* dst_buf; - int dst_pitch; - - if (this->cursor_texture) { - SDL_DestroyTexture(this->cursor_texture); - } - - this->cursor_texture = SDL_CreateTexture( - this->renderer, - SDL_PIXELFORMAT_ARGB8888, - SDL_TEXTUREACCESS_STREAMING, - cursor_width, cursor_height - ); - - if (this->cursor_texture == NULL) { - ABORT_F("SDL_CreateTexture for HW cursor failed with %s", SDL_GetError()); - } - - SDL_LockTexture(this->cursor_texture, NULL, (void **)&dst_buf, &dst_pitch); - SDL_SetTextureBlendMode(this->cursor_texture, SDL_BLENDMODE_BLEND); - this->draw_hw_cursor(dst_buf, dst_pitch); - SDL_UnlockTexture(this->cursor_texture); - - this->cursor_rect.x = 0; - this->cursor_rect.y = 0; - this->cursor_rect.w = cursor_width; - this->cursor_rect.h = cursor_height; - + this->display.setup_hw_cursor( + [this](uint8_t *dst_buf, int dst_pitch) { + this->draw_hw_cursor(dst_buf, dst_pitch); + }, + cursor_width, cursor_height); this->cursor_on = true; } diff --git a/devices/video/videoctrl.h b/devices/video/videoctrl.h index 6ccb51b..6442fe8 100644 --- a/devices/video/videoctrl.h +++ b/devices/video/videoctrl.h @@ -26,7 +26,7 @@ along with this program. If not, see . #include #include -#include +#include #include #include @@ -61,7 +61,6 @@ protected: // CRT controller parameters bool crtc_on = false; bool blank_on = true; - bool resizing = false; bool cursor_on = false; int active_width; // width of the visible display area int active_height; // height of the visible display area @@ -88,12 +87,7 @@ protected: std::function convert_fb_cb; private: - uint32_t disp_wnd_id = 0; - SDL_Window* display_wnd = 0; - SDL_Renderer* renderer = 0; - SDL_Texture* disp_texture = 0; - SDL_Texture* cursor_texture = 0; - SDL_Rect cursor_rect; // destination rectangle for cursor drawing + Display display; }; #endif // VIDEO_CTRL_H diff --git a/machines/CMakeLists.txt b/machines/CMakeLists.txt index 9c71b2d..92a4ac7 100644 --- a/machines/CMakeLists.txt +++ b/machines/CMakeLists.txt @@ -7,4 +7,4 @@ include_directories("${PROJECT_SOURCE_DIR}" file(GLOB SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp") add_library(machines OBJECT ${SOURCES}) -target_link_libraries(machines PRIVATE cubeb SDL2::SDL2) +target_link_libraries(machines PRIVATE) diff --git a/main.cpp b/main.cpp index b85ea22..dad9947 100644 --- a/main.cpp +++ b/main.cpp @@ -29,6 +29,7 @@ along with this program. If not, see . #include #include #include +#include #include #include @@ -38,7 +39,6 @@ along with this program. If not, see . #include #include #include -#include #include using namespace std; @@ -49,7 +49,7 @@ void sigint_handler(int signum) { LOG_F(INFO, "Shutting down..."); delete gMachineObj.release(); - SDL_Quit(); + cleanup(); exit(0); } @@ -57,7 +57,7 @@ void sigabrt_handler(int signum) { LOG_F(INFO, "Shutting down..."); delete gMachineObj.release(); - SDL_Quit(); + cleanup(); } static string appDescription = string( @@ -166,8 +166,8 @@ int main(int argc, char** argv) { cout << "BootROM path: " << bootrom_path << endl; cout << "Execution mode: " << execution_mode << endl; - if (SDL_Init(SDL_INIT_VIDEO)) { - LOG_F(ERROR, "SDL_Init error: %s", SDL_GetError()); + if (!init()) { + LOG_F(ERROR, "Cannot initialize"); return 1; } @@ -217,7 +217,7 @@ bail: delete gMachineObj.release(); - SDL_Quit(); + cleanup(); return 0; } diff --git a/main.h b/main.h new file mode 100644 index 0000000..609e695 --- /dev/null +++ b/main.h @@ -0,0 +1,25 @@ +/* +DingusPPC - The Experimental PowerPC Macintosh emulator +Copyright (C) 2018-23 divingkatae and maximum + (theweirdo) spatium + +(Contact divingkatae#1017 or powermax#2286 on Discord for more info) + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +/** @file Platform-specific main functions. */ + +bool init(); +void cleanup(); diff --git a/main_sdl.cpp b/main_sdl.cpp new file mode 100644 index 0000000..69e93d9 --- /dev/null +++ b/main_sdl.cpp @@ -0,0 +1,38 @@ +/* +DingusPPC - The Experimental PowerPC Macintosh emulator +Copyright (C) 2018-23 divingkatae and maximum + (theweirdo) spatium + +(Contact divingkatae#1017 or powermax#2286 on Discord for more info) + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +/** @file SDL-specific main functions. */ + +#include +#include +#include + +bool init() { + if (SDL_Init(SDL_INIT_VIDEO)) { + LOG_F(ERROR, "SDL_Init error: %s", SDL_GetError()); + return false; + } + return true; +} + +void cleanup() { + SDL_Quit(); +} diff --git a/utils/CMakeLists.txt b/utils/CMakeLists.txt index 0866e1e..6010b1f 100644 --- a/utils/CMakeLists.txt +++ b/utils/CMakeLists.txt @@ -3,4 +3,4 @@ include_directories("${PROJECT_SOURCE_DIR}") file(GLOB SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp") add_library(utils OBJECT ${SOURCES}) -target_link_libraries(utils PRIVATE cubeb) +target_link_libraries(utils PRIVATE)