mirror of
https://github.com/dingusdev/dingusppc.git
synced 2024-12-22 00:29:18 +00:00
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/
This commit is contained in:
parent
732977db27
commit
1f7edfdb3b
@ -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} $<TARGET_OBJECTS:core>
|
||||
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
|
||||
|
21
cmake/PlatformGlob.cmake
Normal file
21
cmake/PlatformGlob.cmake
Normal file
@ -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()
|
@ -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()
|
||||
|
@ -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()
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -34,25 +34,14 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#ifndef SOUND_SERVER_H
|
||||
#define SOUND_SERVER_H
|
||||
|
||||
#include <cubeb/cubeb.h>
|
||||
#include <devices/common/hwcomponent.h>
|
||||
|
||||
enum {
|
||||
SND_SERVER_DOWN = 0,
|
||||
SND_API_READY,
|
||||
SND_SERVER_UP,
|
||||
SND_STREAM_OPENED,
|
||||
SND_STREAM_CLOSED
|
||||
};
|
||||
|
||||
#include <memory>
|
||||
|
||||
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> impl;
|
||||
};
|
||||
|
||||
#endif /* SOUND_SERVER_H */
|
||||
|
@ -29,60 +29,87 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#include <objbase.h>
|
||||
#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<Impl>())
|
||||
{
|
||||
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.");
|
||||
}
|
53
devices/video/display.h
Normal file
53
devices/video/display.h
Normal file
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file Display/screen abstraction (implemented on each platform). */
|
||||
|
||||
#ifndef DISPLAY_H
|
||||
#define DISPLAY_H
|
||||
|
||||
#include <core/hostevents.h>
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
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<void(uint8_t *dst_buf, int dst_pitch)> 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<void(uint8_t *dst_buf, int dst_pitch)> draw_hw_cursor, int cursor_width, int cursor_height);
|
||||
private:
|
||||
class Impl; // Holds private fields
|
||||
std::unique_ptr<Impl> impl;
|
||||
};
|
||||
|
||||
#endif // DISPLAY_H
|
157
devices/video/display_sdl.cpp
Normal file
157
devices/video/display_sdl.cpp
Normal file
@ -0,0 +1,157 @@
|
||||
#include <devices/video/display.h>
|
||||
#include <SDL.h>
|
||||
#include <loguru.hpp>
|
||||
|
||||
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<Impl>())
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
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<void(uint8_t *dst_buf, int dst_pitch)> 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<void(uint8_t *dst_buf, int dst_pitch)> 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;
|
||||
}
|
@ -23,11 +23,8 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <core/timermanager.h>
|
||||
#include <devices/video/videoctrl.h>
|
||||
#include <loguru.hpp>
|
||||
#include <memaccess.h>
|
||||
#include <SDL.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <cinttypes>
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -26,7 +26,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <core/hostevents.h>
|
||||
#include <devices/common/hwinterrupt.h>
|
||||
#include <SDL.h>
|
||||
#include <devices/video/display.h>
|
||||
|
||||
#include <cinttypes>
|
||||
#include <functional>
|
||||
@ -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<void(uint8_t *dst_buf, int dst_pitch)> 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
|
||||
|
@ -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)
|
||||
|
12
main.cpp
12
main.cpp
@ -29,6 +29,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#include <machines/machinefactory.h>
|
||||
#include <machines/machineproperties.h>
|
||||
#include <utils/profiler.h>
|
||||
#include <main.h>
|
||||
|
||||
#include <cinttypes>
|
||||
#include <csignal>
|
||||
@ -38,7 +39,6 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#include <sstream>
|
||||
#include <stdio.h>
|
||||
#include <CLI11.hpp>
|
||||
#include <SDL.h>
|
||||
#include <loguru.hpp>
|
||||
|
||||
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;
|
||||
}
|
||||
|
25
main.h
Normal file
25
main.h
Normal file
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file Platform-specific main functions. */
|
||||
|
||||
bool init();
|
||||
void cleanup();
|
38
main_sdl.cpp
Normal file
38
main_sdl.cpp
Normal file
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file SDL-specific main functions. */
|
||||
|
||||
#include <main.h>
|
||||
#include <loguru.hpp>
|
||||
#include <SDL.h>
|
||||
|
||||
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();
|
||||
}
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user