mirror of
https://github.com/dingusdev/dingusppc.git
synced 2024-12-23 21:29:28 +00:00
Merge pull request #55 from mihaip/upstreaming
Add Emscripten build target
This commit is contained in:
commit
8cad7ee509
@ -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}")
|
||||
@ -40,6 +43,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)
|
||||
|
||||
@ -83,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)
|
||||
|
||||
@ -101,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")
|
||||
|
||||
@ -118,6 +128,16 @@ 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
|
||||
${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})
|
||||
|
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