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:
Mihai Parparita 2023-09-03 17:59:35 -07:00
parent 732977db27
commit 1f7edfdb3b
17 changed files with 429 additions and 208 deletions

View File

@ -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
View 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()

View File

@ -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()

View File

@ -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()

View File

@ -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;
}

View File

@ -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 */

View File

@ -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, &params, &latency_frames);
res = cubeb_get_min_latency(impl->cubeb_ctx, &params, &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, &params, 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
View 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

View 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;
}

View File

@ -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;
}

View File

@ -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

View File

@ -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)

View File

@ -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
View 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
View 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();
}

View File

@ -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)