Skip to content

Commit 1f7edfd

Browse files
committed
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/
1 parent 732977d commit 1f7edfd

17 files changed

+429
-208
lines changed

CMakeLists.txt

+16-12
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,23 @@
11
cmake_minimum_required(VERSION 3.1)
22
project(dingusppc)
33

4+
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
5+
include(PlatformGlob)
6+
47
set(CMAKE_CXX_STANDARD 20)
58

69
set(CMAKE_INCLUDE_CURRENT_DIR ON)
710
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
811
set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}/lib)
912

10-
if (NOT WIN32)
13+
if (NOT WIN32 AND NOT EMSCRIPTEN)
1114
find_package(SDL2 REQUIRED)
1215
include_directories(${SDL2_INCLUDE_DIRS})
1316
if (UNIX AND NOT APPLE)
1417
find_package (Threads)
1518
endif()
1619

17-
else() # Windows build relies on vcpkg
20+
elseif (WIN32) # Windows build relies on vcpkg
1821
# pick up system wide vcpkg if exists
1922
if (DEFINED ENV{VCPKG_ROOT} AND EXISTS $ENV{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake)
2023
message(STATUS "Using system vcpkg at $ENV{VCPKG_ROOT}")
@@ -89,11 +92,12 @@ add_subdirectory("${PROJECT_SOURCE_DIR}/machines/")
8992
add_subdirectory("${PROJECT_SOURCE_DIR}/utils/")
9093
add_subdirectory("${PROJECT_SOURCE_DIR}/thirdparty/loguru/")
9194

92-
set(BUILD_TESTS OFF CACHE BOOL "Build Cubeb tests")
93-
set(BUILD_SHARED_LIBS OFF CACHE BOOL "Build shared libraries")
94-
set(BUILD_TOOLS OFF CACHE BOOL "Build Cubeb tools")
95-
96-
add_subdirectory(thirdparty/cubeb EXCLUDE_FROM_ALL)
95+
if (NOT EMSCRIPTEN)
96+
set(BUILD_TESTS OFF CACHE BOOL "Build Cubeb tests")
97+
set(BUILD_SHARED_LIBS OFF CACHE BOOL "Build shared libraries")
98+
set(BUILD_TOOLS OFF CACHE BOOL "Build Cubeb tools")
99+
add_subdirectory(thirdparty/cubeb EXCLUDE_FROM_ALL)
100+
endif()
97101

98102
set(CLI11_ROOT ${PROJECT_SOURCE_DIR}/thirdparty/CLI11)
99103

@@ -107,10 +111,10 @@ include_directories("${PROJECT_SOURCE_DIR}"
107111
"${PROJECT_SOURCE_DIR}/thirdparty/CLI11/"
108112
"${PROJECT_SOURCE_DIR}/thirdparty/cubeb/include")
109113

110-
file(GLOB SOURCES "${PROJECT_SOURCE_DIR}/*.cpp"
111-
"${PROJECT_SOURCE_DIR}/*.c"
112-
"${PROJECT_SOURCE_DIR}/*.hpp"
113-
"${PROJECT_SOURCE_DIR}/*.h")
114+
platform_glob(SOURCES "${PROJECT_SOURCE_DIR}/*.cpp"
115+
"${PROJECT_SOURCE_DIR}/*.c"
116+
"${PROJECT_SOURCE_DIR}/*.hpp"
117+
"${PROJECT_SOURCE_DIR}/*.h")
114118

115119
file(GLOB TEST_SOURCES "${PROJECT_SOURCE_DIR}/cpu/ppc/test/*.cpp")
116120

@@ -125,7 +129,7 @@ add_executable(dingusppc ${SOURCES} $<TARGET_OBJECTS:core>
125129
if (WIN32)
126130
target_link_libraries(dingusppc PRIVATE SDL2::SDL2 SDL2::SDL2main cubeb)
127131
elseif (EMSCRIPTEN)
128-
target_link_libraries(dingusppc PRIVATE SDL2::SDL2 SDL2::SDL2main cubeb
132+
target_link_libraries(dingusppc PRIVATE
129133
${CMAKE_DL_LIBS} ${CMAKE_THREAD_LIBS_INIT}
130134
"-gsource-map"
131135
# 256 MB max for emulated Mac RAM, plus 32 MB of emulator overhead

cmake/PlatformGlob.cmake

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Detect platform suffix
2+
if (EMSCRIPTEN)
3+
set(PLATFORM_SUFFIX "_js$")
4+
else()
5+
set(PLATFORM_SUFFIX "_sdl|_cubeb$")
6+
endif()
7+
8+
# Function to perform a platform-specific glob
9+
function(platform_glob RESULT_VAR)
10+
set(PLATFORM_SOURCES)
11+
foreach(GLOB_PATTERN ${ARGN})
12+
file(GLOB GLOB_RESULT ${GLOB_PATTERN})
13+
foreach(FILE_PATH ${GLOB_RESULT})
14+
get_filename_component(BASE_NAME ${FILE_PATH} NAME_WE)
15+
if("${BASE_NAME}" MATCHES ${PLATFORM_SUFFIX} OR NOT "${BASE_NAME}" MATCHES "_js$|_sdl|_cubeb$")
16+
list(APPEND PLATFORM_SOURCES ${FILE_PATH})
17+
endif()
18+
endforeach()
19+
endforeach()
20+
set(${RESULT_VAR} ${PLATFORM_SOURCES} PARENT_SCOPE)
21+
endfunction()

core/CMakeLists.txt

+6-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@ include_directories("${PROJECT_SOURCE_DIR}"
22
"${PROJECT_SOURCE_DIR}/thirdparty/loguru/"
33
)
44

5-
file(GLOB SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp")
5+
platform_glob(SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp")
66

77
add_library(core OBJECT ${SOURCES})
8-
target_link_libraries(core PRIVATE SDL2::SDL2)
8+
if (EMSCRIPTEN)
9+
target_link_libraries(core PRIVATE)
10+
else()
11+
target_link_libraries(core PRIVATE SDL2::SDL2)
12+
endif()
File renamed without changes.

devices/CMakeLists.txt

+23-16
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,30 @@
1+
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
2+
include(PlatformGlob)
3+
14
include_directories("${PROJECT_SOURCE_DIR}"
25
"${PROJECT_SOURCE_DIR}/thirdparty/loguru/"
36
)
47

5-
file(GLOB SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp"
6-
"${CMAKE_CURRENT_SOURCE_DIR}/common/*.cpp"
7-
"${CMAKE_CURRENT_SOURCE_DIR}/common/adb/*.cpp"
8-
"${CMAKE_CURRENT_SOURCE_DIR}/common/i2c/*.cpp"
9-
"${CMAKE_CURRENT_SOURCE_DIR}/common/ata/*.cpp"
10-
"${CMAKE_CURRENT_SOURCE_DIR}/common/pci/*.cpp"
11-
"${CMAKE_CURRENT_SOURCE_DIR}/common/scsi/*.cpp"
12-
"${CMAKE_CURRENT_SOURCE_DIR}/ethernet/*.cpp"
13-
"${CMAKE_CURRENT_SOURCE_DIR}/floppy/*.cpp"
14-
"${CMAKE_CURRENT_SOURCE_DIR}/ioctrl/*.cpp"
15-
"${CMAKE_CURRENT_SOURCE_DIR}/memctrl/*.cpp"
16-
"${CMAKE_CURRENT_SOURCE_DIR}/serial/*.cpp"
17-
"${CMAKE_CURRENT_SOURCE_DIR}/sound/*.cpp"
18-
"${CMAKE_CURRENT_SOURCE_DIR}/storage/*.cpp"
19-
"${CMAKE_CURRENT_SOURCE_DIR}/video/*.cpp"
8+
platform_glob(SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp"
9+
"${CMAKE_CURRENT_SOURCE_DIR}/common/*.cpp"
10+
"${CMAKE_CURRENT_SOURCE_DIR}/common/adb/*.cpp"
11+
"${CMAKE_CURRENT_SOURCE_DIR}/common/i2c/*.cpp"
12+
"${CMAKE_CURRENT_SOURCE_DIR}/common/ata/*.cpp"
13+
"${CMAKE_CURRENT_SOURCE_DIR}/common/pci/*.cpp"
14+
"${CMAKE_CURRENT_SOURCE_DIR}/common/scsi/*.cpp"
15+
"${CMAKE_CURRENT_SOURCE_DIR}/ethernet/*.cpp"
16+
"${CMAKE_CURRENT_SOURCE_DIR}/floppy/*.cpp"
17+
"${CMAKE_CURRENT_SOURCE_DIR}/ioctrl/*.cpp"
18+
"${CMAKE_CURRENT_SOURCE_DIR}/memctrl/*.cpp"
19+
"${CMAKE_CURRENT_SOURCE_DIR}/serial/*.cpp"
20+
"${CMAKE_CURRENT_SOURCE_DIR}/sound/*.cpp"
21+
"${CMAKE_CURRENT_SOURCE_DIR}/storage/*.cpp"
22+
"${CMAKE_CURRENT_SOURCE_DIR}/video/*.cpp"
2023
)
2124

2225
add_library(devices OBJECT ${SOURCES})
23-
target_link_libraries(devices PRIVATE cubeb SDL2::SDL2)
26+
if (EMSCRIPTEN)
27+
target_link_libraries(devices PRIVATE)
28+
else()
29+
target_link_libraries(devices PRIVATE cubeb SDL2::SDL2)
30+
endif()

devices/floppy/floppyimg.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,7 @@ FloppyImgConverter* open_floppy_image(std::string& img_path)
301301
img_file.open(img_path, std::ios::in | std::ios::binary);
302302
if (img_file.fail()) {
303303
img_file.close();
304-
LOG_F(ERROR, "Could not open specified floppy image!");
304+
LOG_F(ERROR, "Could not open specified floppy image (%s)!", img_path.c_str());
305305
return nullptr;
306306
}
307307

devices/sound/soundserver.h

+5-18
Original file line numberDiff line numberDiff line change
@@ -34,25 +34,14 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
3434
#ifndef SOUND_SERVER_H
3535
#define SOUND_SERVER_H
3636

37-
#include <cubeb/cubeb.h>
3837
#include <devices/common/hwcomponent.h>
3938

40-
enum {
41-
SND_SERVER_DOWN = 0,
42-
SND_API_READY,
43-
SND_SERVER_UP,
44-
SND_STREAM_OPENED,
45-
SND_STREAM_CLOSED
46-
};
47-
39+
#include <memory>
4840

4941
class SoundServer : public HWComponent {
5042
public:
51-
SoundServer() {
52-
supports_types(HWCompType::SND_SERVER);
53-
this->start();
54-
};
55-
~SoundServer() { this->shutdown(); };
43+
SoundServer();
44+
~SoundServer();
5645

5746
int start();
5847
void shutdown();
@@ -61,10 +50,8 @@ class SoundServer : public HWComponent {
6150
void close_out_stream();
6251

6352
private:
64-
int status; /* server status */
65-
cubeb *cubeb_ctx;
66-
67-
cubeb_stream *out_stream;
53+
class Impl; // Holds private fields
54+
std::unique_ptr<Impl> impl;
6855
};
6956

7057
#endif /* SOUND_SERVER_H */

devices/sound/soundserver.cpp renamed to devices/sound/soundserver_cubeb.cpp

+57-30
Original file line numberDiff line numberDiff line change
@@ -29,60 +29,87 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
2929
#include <objbase.h>
3030
#endif
3131

32+
enum {
33+
SND_SERVER_DOWN = 0,
34+
SND_API_READY,
35+
SND_SERVER_UP,
36+
SND_STREAM_OPENED,
37+
SND_STREAM_CLOSED
38+
};
39+
40+
class SoundServer::Impl {
41+
public:
42+
int status; /* server status */
43+
cubeb *cubeb_ctx;
44+
45+
cubeb_stream *out_stream;
46+
};
47+
48+
SoundServer::SoundServer(): impl(std::make_unique<Impl>())
49+
{
50+
supports_types(HWCompType::SND_SERVER);
51+
this->start();
52+
}
53+
54+
SoundServer::~SoundServer()
55+
{
56+
this->shutdown();
57+
}
58+
3259
#if 0
3360
int SoundServer::start()
3461
{
3562
int err;
3663

37-
this->status = SND_SERVER_DOWN;
64+
impl->status = SND_SERVER_DOWN;
3865

39-
this->soundio = soundio_create();
40-
if (!this->soundio) {
66+
impl->soundio = soundio_create();
67+
if (!impl->soundio) {
4168
LOG_F(ERROR, "Sound Server: out of memory");
4269
return -1;
4370
}
4471

45-
if ((err = soundio_connect(this->soundio))) {
72+
if ((err = soundio_connect(impl->soundio))) {
4673
LOG_F(ERROR, "Unable to connect to backend: %s", soundio_strerror(err));
4774
return -1;
4875
}
4976

5077
LOG_F(INFO, "Connected to backend: %s", soundio_backend_name(soundio->current_backend));
5178

52-
soundio_flush_events(this->soundio);
79+
soundio_flush_events(impl->soundio);
5380

54-
this->status = SND_API_READY;
81+
impl->status = SND_API_READY;
5582

56-
this->out_dev_index = soundio_default_output_device_index(this->soundio);
57-
if (this->out_dev_index < 0) {
83+
impl->out_dev_index = soundio_default_output_device_index(impl->soundio);
84+
if (impl->out_dev_index < 0) {
5885
LOG_F(ERROR, "Sound Server: no output device found");
5986
return -1;
6087
}
6188

62-
this->out_device = soundio_get_output_device(this->soundio, this->out_dev_index);
63-
if (!this->out_device) {
89+
impl->out_device = soundio_get_output_device(this->soundio, this->out_dev_index);
90+
if (!impl->out_device) {
6491
LOG_F(ERROR, "Sound Server: out of memory");
6592
return -1;
6693
}
6794

68-
LOG_F(INFO, "Sound Server output device: %s", this->out_device->name);
95+
LOG_F(INFO, "Sound Server output device: %s", impl->out_device->name);
6996

70-
this->status = SND_SERVER_UP;
97+
impl->status = SND_SERVER_UP;
7198

7299
return 0;
73100
}
74101

75102
void SoundServer::shutdown()
76103
{
77-
switch (this->status) {
104+
switch (impl->status) {
78105
case SND_SERVER_UP:
79-
soundio_device_unref(this->out_device);
106+
soundio_device_unref(impl->out_device);
80107
/* fall through */
81108
case SND_API_READY:
82-
soundio_destroy(this->soundio);
109+
soundio_destroy(impl->soundio);
83110
}
84111

85-
this->status = SND_SERVER_DOWN;
112+
impl->status = SND_SERVER_DOWN;
86113

87114
LOG_F(INFO, "Sound Server shut down.");
88115
}
@@ -96,24 +123,24 @@ int SoundServer::start()
96123
CoInitialize(nullptr);
97124
#endif
98125

99-
this->status = SND_SERVER_DOWN;
126+
impl->status = SND_SERVER_DOWN;
100127

101-
res = cubeb_init(&this->cubeb_ctx, "Dingus sound server", NULL);
128+
res = cubeb_init(&impl->cubeb_ctx, "Dingus sound server", NULL);
102129
if (res != CUBEB_OK) {
103130
LOG_F(ERROR, "Could not initialize Cubeb library");
104131
return -1;
105132
}
106133

107-
LOG_F(INFO, "Connected to backend: %s", cubeb_get_backend_id(this->cubeb_ctx));
134+
LOG_F(INFO, "Connected to backend: %s", cubeb_get_backend_id(impl->cubeb_ctx));
108135

109-
this->status = SND_API_READY;
136+
impl->status = SND_API_READY;
110137

111138
return 0;
112139
}
113140

114141
void SoundServer::shutdown()
115142
{
116-
switch (this->status) {
143+
switch (impl->status) {
117144
case SND_STREAM_OPENED:
118145
close_out_stream();
119146
/* fall through */
@@ -122,10 +149,10 @@ void SoundServer::shutdown()
122149
case SND_SERVER_UP:
123150
/* fall through */
124151
case SND_API_READY:
125-
cubeb_destroy(this->cubeb_ctx);
152+
cubeb_destroy(impl->cubeb_ctx);
126153
}
127154

128-
this->status = SND_SERVER_DOWN;
155+
impl->status = SND_SERVER_DOWN;
129156

130157
LOG_F(INFO, "Sound Server shut down.");
131158
}
@@ -189,15 +216,15 @@ int SoundServer::open_out_stream(uint32_t sample_rate, void *user_data)
189216
params.layout = CUBEB_LAYOUT_STEREO;
190217
params.prefs = CUBEB_STREAM_PREF_NONE;
191218

192-
res = cubeb_get_min_latency(this->cubeb_ctx, &params, &latency_frames);
219+
res = cubeb_get_min_latency(impl->cubeb_ctx, &params, &latency_frames);
193220
if (res != CUBEB_OK) {
194221
LOG_F(ERROR, "Could not get minimum latency, error: %d", res);
195222
return -1;
196223
} else {
197224
LOG_F(9, "Minimum sound latency: %d frames", latency_frames);
198225
}
199226

200-
res = cubeb_stream_init(this->cubeb_ctx, &this->out_stream, "SndOut stream",
227+
res = cubeb_stream_init(impl->cubeb_ctx, &impl->out_stream, "SndOut stream",
201228
NULL, NULL, NULL, &params, latency_frames,
202229
sound_out_callback, status_callback, user_data);
203230
if (res != CUBEB_OK) {
@@ -207,20 +234,20 @@ int SoundServer::open_out_stream(uint32_t sample_rate, void *user_data)
207234

208235
LOG_F(9, "Sound output stream opened.");
209236

210-
this->status = SND_STREAM_OPENED;
237+
impl->status = SND_STREAM_OPENED;
211238

212239
return 0;
213240
}
214241

215242
int SoundServer::start_out_stream()
216243
{
217-
return cubeb_stream_start(this->out_stream);
244+
return cubeb_stream_start(impl->out_stream);
218245
}
219246

220247
void SoundServer::close_out_stream()
221248
{
222-
cubeb_stream_stop(this->out_stream);
223-
cubeb_stream_destroy(this->out_stream);
224-
this->status = SND_STREAM_CLOSED;
249+
cubeb_stream_stop(impl->out_stream);
250+
cubeb_stream_destroy(impl->out_stream);
251+
impl->status = SND_STREAM_CLOSED;
225252
LOG_F(9, "Sound output stream closed.");
226253
}

0 commit comments

Comments
 (0)