Browse Source

Merge e8ab73a31f into 5a08031caf

pull/8312/merge
Chris V. 4 days ago committed by GitHub
parent
commit
163ce18a55
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 4
      3rdParty/Lua/CMakeLists.txt
  2. 4
      CMake/platforms/emscripten.cmake
  3. 22
      CMakeLists.txt
  4. 138
      Packaging/emscripten/emscripten_pre.js
  5. 232
      Packaging/emscripten/file-manager.js
  6. 168
      Packaging/emscripten/index.html
  7. 14
      Source/engine/dx.cpp
  8. 2
      Source/interfac.cpp
  9. 9
      Source/loadsave.cpp
  10. 13
      Source/options.cpp
  11. 9
      Source/pfile.cpp
  12. 2
      Source/utils/sdl_mutex.h
  13. 44
      Source/utils/sdl_thread.cpp
  14. 4
      Source/utils/sdl_thread.h

4
3rdParty/Lua/CMakeLists.txt vendored

@ -26,6 +26,10 @@ if(CMAKE_SYSTEM_NAME MATCHES "Darwin" AND DARWIN_MAJOR_VERSION VERSION_EQUAL 8)
# localtime_r gmtime_r
find_package(MacportsLegacySupport REQUIRED)
target_link_libraries(lua_static PRIVATE MacportsLegacySupport::MacportsLegacySupport)
elseif(EMSCRIPTEN)
# Enable pthread support for Emscripten to match SDL2's USE_PTHREADS=1
target_compile_options(lua_static PUBLIC -pthread)
target_link_options(lua_static PUBLIC -pthread)
elseif(TARGET_PLATFORM STREQUAL "dos")
target_compile_definitions(lua_static PUBLIC -DLUA_USE_C89)
elseif(ANDROID AND ("${ANDROID_ABI}" STREQUAL "armeabi-v7a" OR "${ANDROID_ABI}" STREQUAL "x86"))

4
CMake/platforms/emscripten.cmake

@ -1,13 +1,15 @@
set(BUILD_TESTING OFF)
set(BUILD_ASSETS_MPQ OFF)
set(DISABLE_ZERO_TIER ON)
set(DISABLE_TCP ON)
set(DEVILUTIONX_SYSTEM_SDL_AUDIOLIB OFF)
set(DEVILUTIONX_SYSTEM_LIBSODIUM OFF)
set(DEVILUTIONX_SYSTEM_LIBFMT OFF)
set(NOEXIT ON)
# Emscripten ports do have a bzip2 but it fails to link with this error:
# warning: _BZ2_bzDecompress may need to be added to EXPORTED_FUNCTIONS if it arrives from a system library
# error: undefined symbol: BZ2_bzDecompressEnd (referenced by top-level compiled C/C++ code)
set(DEVILUTIONX_SYSTEM_BZIP2 OFF)
file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/Packaging/emscripten/index.html" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}")
file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/Packaging/emscripten/file-manager.js" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}")

22
CMakeLists.txt

@ -271,7 +271,10 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang" AND NOT PS4)
if(APPLE)
add_link_options("$<$<NOT:$<CONFIG:Debug>>:LINKER:-dead_strip>")
else()
add_link_options("$<$<NOT:$<CONFIG:Debug>>:LINKER:--gc-sections,--as-needed>")
add_link_options("$<$<NOT:$<CONFIG:Debug>>:LINKER:--gc-sections>")
if(NOT EMSCRIPTEN)
add_link_options("$<$<NOT:$<CONFIG:Debug>>:LINKER:--as-needed>")
endif()
endif()
endif()
@ -301,7 +304,7 @@ endif()
# Not a genexp because CMake doesn't support it
# https://gitlab.kitware.com/cmake/cmake/-/issues/20546
if(NOT DISABLE_LTO)
if(NOT DISABLE_LTO AND NOT EMSCRIPTEN)
# LTO if supported:
include(CheckIPOSupported)
check_ipo_supported(RESULT is_ipo_supported OUTPUT lto_error)
@ -369,7 +372,7 @@ else()
Packaging/windows/devilutionx.rc
Packaging/apple/LaunchScreen.storyboard)
if(CMAKE_STRIP AND NOT DEVILUTIONX_DISABLE_STRIP)
if(CMAKE_STRIP AND NOT DEVILUTIONX_DISABLE_STRIP AND NOT EMSCRIPTEN)
add_custom_command(
TARGET ${BIN_TARGET} POST_BUILD
COMMAND $<$<OR:$<CONFIG:Release>,$<CONFIG:MinSizeRel>>:${CMAKE_STRIP}>
@ -398,7 +401,18 @@ include(Assets)
include(Mods)
if(EMSCRIPTEN)
target_link_options(${BIN_TARGET} PRIVATE --preload-file assets)
target_link_options(${BIN_TARGET} PRIVATE
--preload-file assets
-sFORCE_FILESYSTEM=1
-sALLOW_MEMORY_GROWTH=1
-sASYNCIFY
-lidbfs.js
--shell-file ${CMAKE_CURRENT_SOURCE_DIR}/Packaging/emscripten/index.html
)
# Add JavaScript to load MPQ files from the server directory at runtime
target_link_options(${BIN_TARGET} PRIVATE --pre-js ${CMAKE_CURRENT_SOURCE_DIR}/Packaging/emscripten/emscripten_pre.js)
# Disable fullscreen by default
target_compile_definitions(${BIN_TARGET} PRIVATE EMSCRIPTEN_NO_FULLSCREEN)
endif()
if(NOT USE_SDL1 AND NOT UWP_LIB)

138
Packaging/emscripten/emscripten_pre.js

@ -0,0 +1,138 @@
// Pre-load MPQ files from the server directory into Emscripten virtual filesystem
Module['preRun'] = Module['preRun'] || [];
// Mount IDBFS for persistent save files
Module['preRun'].push(function() {
console.log('Setting up IDBFS for persistent saves...');
// SDL uses //libsdl/ as the base path for Emscripten
// Save files are in //libsdl/diasurgical/devilution/
// Config files (diablo.ini) would be in //libsdl/diasurgical/
try {
// Helper function to create directory if it doesn't exist
function mkdirSafe(path) {
try {
// Check if path exists
var stat = FS.stat(path);
// If it exists and is a directory, we're good
if (FS.isDir(stat.mode)) {
return;
}
// If it exists but is not a directory, this is an error
console.error('Path exists but is not a directory: ' + path);
return;
} catch (e) {
// Path doesn't exist, try to create it
try {
FS.mkdir(path);
} catch (mkdirErr) {
// Only throw if it's not an "already exists" error
if (mkdirErr.errno !== 20 && mkdirErr.errno !== 17) {
throw mkdirErr;
}
}
}
}
// Create SDL directory hierarchy if needed
mkdirSafe('/libsdl');
mkdirSafe('/libsdl/diasurgical');
// Mount the diasurgical directory as IDBFS to persist saves AND settings
FS.mount(IDBFS, {}, '/libsdl/diasurgical');
console.log('IDBFS mounted successfully at /libsdl/diasurgical');
// Sync from IndexedDB to memory (load existing saves)
Module.addRunDependency('syncfs');
FS.syncfs(true, function(err) {
if (err) {
console.error('Error loading saves from IndexedDB:', err);
} else {
console.log('Existing saves loaded from IndexedDB');
}
Module.removeRunDependency('syncfs');
});
} catch (e) {
console.error('Error setting up IDBFS:', e);
}
});
// Load MPQ files from the server directory
Module['preRun'].push(function() {
// List of MPQ files to try loading (in priority order)
var mpqFiles = [
'spawn.mpq',
];
// Create a promise-based loading system
var loadPromises = mpqFiles.map(function(filename) {
return new Promise(function(resolve) {
fetch(filename)
.then(function(response) {
if (response.ok) {
return response.arrayBuffer();
}
throw new Error('File not found');
})
.then(function(data) {
console.log('Loading ' + filename + ' into virtual filesystem...');
FS.writeFile('/' + filename, new Uint8Array(data));
console.log('Successfully loaded ' + filename);
resolve();
})
.catch(function() {
// File doesn't exist, skip silently
resolve();
});
});
});
// Wait for all MPQ files to load before continuing
Module.addRunDependency('loadMPQs');
Promise.all(loadPromises).then(function() {
Module.removeRunDependency('loadMPQs');
});
});
// Track if a sync is in progress to prevent overlapping operations
var syncInProgress = false;
// Expose function to manually save to IndexedDB
Module['saveToIndexedDB'] = function() {
if (syncInProgress) {
return;
}
syncInProgress = true;
FS.syncfs(false, function(err) {
syncInProgress = false;
if (err) {
console.error('Error persisting saves to IndexedDB:', err);
}
});
};
// Auto-sync to IndexedDB every 30 seconds as a fallback
Module['postRun'] = Module['postRun'] || [];
Module['postRun'].push(function() {
setInterval(function() {
if (!syncInProgress) {
syncInProgress = true;
FS.syncfs(false, function(err) {
syncInProgress = false;
if (err) {
console.error('Auto-sync error:', err);
}
});
}
}, 30000);
// Sync when the page is about to close
window.addEventListener('beforeunload', function() {
if (!syncInProgress) {
FS.syncfs(false, function(err) {
if (err) console.error('Error syncing on page unload:', err);
});
}
});
});

232
Packaging/emscripten/file-manager.js

@ -0,0 +1,232 @@
// File Manager functionality
(function() {
const modal = document.getElementById('fileManagerModal');
const fileManagerBtn = document.getElementById('fileManagerBtn');
const closeModalBtn = document.getElementById('closeModal');
const dropZone = document.getElementById('dropZone');
const fileInput = document.getElementById('fileInput');
const browseBtn = document.getElementById('browseBtn');
const resetSettingsBtn = document.getElementById('resetSettingsBtn');
const mpqFilesList = document.getElementById('mpqFilesList');
// Open/close modal
fileManagerBtn.addEventListener('click', () => {
modal.classList.add('show');
refreshFileList();
});
closeModalBtn.addEventListener('click', () => {
modal.classList.remove('show');
});
modal.addEventListener('click', (e) => {
if (e.target === modal) {
modal.classList.remove('show');
}
});
// Browse button
browseBtn.addEventListener('click', () => {
fileInput.click();
});
// Drag and drop
dropZone.addEventListener('click', () => {
fileInput.click();
});
dropZone.addEventListener('dragover', (e) => {
e.preventDefault();
dropZone.classList.add('dragover');
});
dropZone.addEventListener('dragleave', () => {
dropZone.classList.remove('dragover');
});
dropZone.addEventListener('drop', (e) => {
e.preventDefault();
dropZone.classList.remove('dragover');
handleFiles(e.dataTransfer.files);
});
fileInput.addEventListener('change', (e) => {
handleFiles(e.target.files);
});
// Handle file upload
function handleFiles(files) {
if (!files || files.length === 0) return;
// Wait for Module and FS to be ready
if (typeof Module === 'undefined' || typeof FS === 'undefined') {
alert('Game is still loading. Please wait and try again.');
return;
}
const mpqFiles = Array.from(files).filter(f =>
f.name.toLowerCase().endsWith('.mpq')
);
if (mpqFiles.length === 0) {
alert('Please select MPQ files only.');
return;
}
let processed = 0;
mpqFiles.forEach(file => {
const reader = new FileReader();
reader.onload = function(e) {
try {
const data = new Uint8Array(e.target.result);
// Upload to the devilution subdirectory where the game searches
const path = '/libsdl/diasurgical/devilution/' + file.name; // Might want to make this dynamic later, since source mods might rename the paths
// Create directory if it doesn't exist
try {
FS.mkdir('/libsdl/diasurgical/devilution');
} catch (e) {
// Directory might already exist, ignore
}
// Write file to IDBFS-backed directory
FS.writeFile(path, data);
console.log('Uploaded:', file.name, '(' + formatBytes(file.size) + ')');
processed++;
if (processed === mpqFiles.length) {
// Sync to IndexedDB
FS.syncfs(false, function(err) {
if (err) {
console.error('Error syncing files:', err);
alert('Error saving files. Check console.');
} else {
alert('Files uploaded successfully! Reloading game...');
setTimeout(() => location.reload(), 500);
}
});
}
} catch (err) {
console.error('Error writing file:', err);
alert('Error uploading file: ' + file.name);
}
};
reader.readAsArrayBuffer(file);
});
}
// Refresh file list
function refreshFileList() {
if (typeof Module === 'undefined' || typeof FS === 'undefined') {
mpqFilesList.innerHTML = '<p class="info-text">Game is loading...</p>';
return;
}
try {
// Check if devilution directory exists
try {
FS.stat('/libsdl/diasurgical/devilution');
} catch (e) {
// Directory doesn't exist yet
mpqFilesList.innerHTML = '<p class="info-text">No MPQ files found.</p>';
return;
}
const files = FS.readdir('/libsdl/diasurgical/devilution');
const mpqFiles = files.filter(f =>
f.toLowerCase().endsWith('.mpq') && f !== '.' && f !== '..'
);
if (mpqFiles.length === 0) {
mpqFilesList.innerHTML = '<p class="info-text">No MPQ files found.</p>';
return;
}
mpqFilesList.innerHTML = mpqFiles.map(filename => {
const path = '/libsdl/diasurgical/devilution/' + filename;
const stat = FS.stat(path);
return `
<div class="file-item">
<span class="file-item-name">${filename}</span>
<span class="file-item-size">${formatBytes(stat.size)}</span>
<button class="btn btn-delete" onclick="deleteFile('${filename}')">Delete</button>
</div>
`;
}).join('');
} catch (err) {
console.error('Error reading files:', err);
mpqFilesList.innerHTML = '<p class="info-text">Error reading files.</p>';
}
}
// Delete file
window.deleteFile = function(filename) {
if (!confirm('Delete ' + filename + '? This will reload the game.')) {
return;
}
try {
const path = '/libsdl/diasurgical/devilution/' + filename;
FS.unlink(path);
// Sync deletion to IndexedDB
FS.syncfs(false, function(err) {
if (err) {
console.error('Error syncing deletion:', err);
alert('Error deleting file. Check console.');
} else {
alert('File deleted! Reloading game...');
setTimeout(() => location.reload(), 500);
}
});
} catch (err) {
console.error('Error deleting file:', err);
alert('Error deleting file: ' + filename);
}
};
// Reset settings
resetSettingsBtn.addEventListener('click', () => {
if (!confirm('Reset game settings? This will delete diablo.ini but keep your saves. The game will reload.')) {
return;
}
try {
const iniPath = '/libsdl/diasurgical/devilution/diablo.ini';
// Check if file exists
try {
FS.stat(iniPath);
// File exists, delete it
FS.unlink(iniPath);
console.log('Deleted diablo.ini');
} catch (e) {
// File doesn't exist, that's fine
console.log('diablo.ini not found (already reset)');
}
// Sync to IndexedDB
FS.syncfs(false, function(err) {
if (err) {
console.error('Error syncing settings reset:', err);
alert('Error resetting settings. Check console.');
} else {
alert('Settings reset! Reloading game...');
setTimeout(() => location.reload(), 500);
}
});
} catch (err) {
console.error('Error resetting settings:', err);
alert('Error resetting settings.');
}
});
// Helper function
function formatBytes(bytes) {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return Math.round(bytes / Math.pow(k, i) * 100) / 100 + ' ' + sizes[i];
}
})();

168
Packaging/emscripten/index.html

@ -25,12 +25,32 @@
div.emscripten_border {
border: 1px solid black;
margin: 20px auto;
width: min(90vw, calc((100vh - 200px) * 4 / 3));
max-width: 1280px;
aspect-ratio: 4 / 3;
display: flex;
justify-content: center;
align-items: center;
}
/* the canvas *must not* have any border or padding, or mouse coords will be wrong */
canvas.emscripten {
border: 0px none;
background-color: black;
width: 100%;
height: 100%;
max-width: 100%;
max-height: 100%;
object-fit: contain;
image-rendering: pixelated;
image-rendering: crisp-edges;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
-webkit-user-drag: none;
pointer-events: auto;
}
#emscripten_logo {
@ -138,10 +158,154 @@
font-family: 'Lucida Console', Monaco, monospace;
outline: none;
}
/* File Manager Styles */
#fileManagerBtn {
position: fixed;
top: 20px;
right: 20px;
cursor: pointer;
z-index: 1000;
}
#fileManagerModal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.5);
z-index: 2000;
justify-content: center;
align-items: center;
}
#fileManagerModal.show {
display: flex;
}
.modal-content {
background: white;
border: 1px solid black;
padding: 20px;
max-width: 600px;
width: 90%;
max-height: 80vh;
overflow-y: auto;
}
.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.modal-header h2 {
margin: 0;
}
.close-btn {
border: 1px solid black;
cursor: pointer;
padding: 5px 10px;
}
.drop-zone {
border: 1px solid black;
padding: 20px;
text-align: center;
margin: 20px 0;
cursor: pointer;
}
.drop-zone p {
margin: 10px 0;
}
.file-list {
margin: 20px 0;
}
.file-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px;
margin: 5px 0;
border: 1px solid black;
}
.file-item-name {
flex: 1;
}
.file-item-size {
margin: 0 15px;
}
.btn {
padding: 8px 16px;
border: 1px solid black;
cursor: pointer;
}
.section {
margin: 20px 0;
}
#fileInput {
display: none;
}
@media screen and (max-width: 700px) {
div.emscripten_border {
width: min(calc(100vw - 40px), calc((100vh - 200px) * 4 / 3));
}
}
</style>
</head>
<body>
<button id="fileManagerBtn" title="File Manager">File Manager</button>
<div id="fileManagerModal">
<div class="modal-content">
<div class="modal-header">
<h2>File Manager</h2>
<button class="close-btn" id="closeModal">&times;</button>
</div>
<div class="section">
<h3>Upload MPQ Files</h3>
<div class="info-text">
Upload DIABDAT.MPQ or SPAWN.MPQ or other game files. Files will persist across browser sessions.
</div>
<div class="drop-zone" id="dropZone">
<p>📁 Drag and drop MPQ files here</p>
<p>or</p>
<p><button class="btn" id="browseBtn">Browse Files</button></p>
</div>
<input type="file" id="fileInput" accept=".mpq,.MPQ" multiple>
</div>
<div class="section">
<div class="file-list">
<h3>Loaded MPQ Files</h3>
<div id="mpqFilesList"></div>
</div>
</div>
<div class="section">
<h3>Settings</h3>
<div class="info-text">
Reset game settings (diablo.ini) without deleting saves. Useful if settings are accidentally messed up or not working correctly.
</div>
<button class="btn btn-reset" id="resetSettingsBtn">Reset Game Settings</button>
</div>
</div>
</div>
<div class="spinner" id='spinner'></div>
<div class="emscripten" id="status">Downloading...</div>
@ -182,6 +346,9 @@
// See http://www.khronos.org/registry/webgl/specs/latest/1.0/#5.15.2
canvas.addEventListener("webglcontextlost", function (e) { alert('WebGL context lost. You will need to reload the page.'); e.preventDefault(); }, false);
// Prevent canvas from being dragged
canvas.addEventListener("dragstart", function (e) { e.preventDefault(); }, false);
return canvas;
})(),
setStatus: function (text) {
@ -222,6 +389,7 @@
};
};
</script>
<script type='text/javascript' src="file-manager.js"></script>
<script async type="text/javascript" src="devilutionx.js"></script>
</body>

14
Source/engine/dx.cpp

@ -17,6 +17,10 @@
#include <SDL.h>
#endif
#ifdef __EMSCRIPTEN__
#include <emscripten.h>
#endif
#include "controls/control_mode.hpp"
#include "controls/plrctrls.h"
#include "engine/render/primitive_render.hpp"
@ -236,7 +240,12 @@ void RenderPresent()
SDL_Surface *surface = GetOutputSurface();
if (!gbActive) {
#ifdef __EMSCRIPTEN__
// Just yield to browser when inactive instead of blocking
emscripten_sleep(1);
#else
LimitFrameRate();
#endif
return;
}
@ -259,6 +268,11 @@ void RenderPresent()
}
SDL_RenderPresent(renderer);
#ifdef __EMSCRIPTEN__
// Yield to browser to allow rendering
emscripten_sleep(1);
#endif
if (*GetOptions().Graphics.frameRateControl != FrameRateControl::VerticalSync) {
LimitFrameRate();
}

2
Source/interfac.cpp

@ -48,7 +48,7 @@
#include "controls/touch/renderers.h"
#endif
#ifdef __DJGPP__
#if defined(__DJGPP__) || defined(__EMSCRIPTEN__)
#define LOAD_ON_MAIN_THREAD
#endif

9
Source/loadsave.cpp

@ -44,6 +44,10 @@
#include "utils/language.h"
#include "utils/status_macros.hpp"
#ifdef __EMSCRIPTEN__
#include <emscripten.h>
#endif
namespace devilution {
bool gbIsHellfireSaveGame;
@ -2931,6 +2935,11 @@ void SaveGame()
gbValidSaveFile = true;
pfile_write_hero(/*writeGameData=*/true);
sfile_write_stash();
#ifdef __EMSCRIPTEN__
// Persist saves to IndexedDB for browser storage
emscripten_run_script("if (typeof Module !== 'undefined' && Module.saveToIndexedDB) Module.saveToIndexedDB();");
#endif
}
void SaveLevel(SaveWriter &saveWriter)

13
Source/options.cpp

@ -185,8 +185,9 @@ void SaveIni()
#if SDL_VERSION_ATLEAST(2, 0, 0)
bool HardwareCursorDefault()
{
#if defined(__ANDROID__) || (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1)
#if defined(__ANDROID__) || (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1) || defined(__EMSCRIPTEN__)
// See https://github.com/diasurgical/devilutionX/issues/2502
// Emscripten: Software cursor works better in browsers
return false;
#else
return HardwareCursorSupported();
@ -739,10 +740,16 @@ SDL_AudioDeviceID OptionEntryAudioDevice::id() const
GraphicsOptions::GraphicsOptions()
: OptionCategoryBase("Graphics", N_("Graphics"), N_("Graphics Settings"))
, fullscreen("Fullscreen", OnlyIfSupportsWindowed | OptionEntryFlags::CantChangeInGame | OptionEntryFlags::RecreateUI, N_("Fullscreen"), N_("Display the game in windowed or fullscreen mode."), true)
, fullscreen("Fullscreen", OnlyIfSupportsWindowed | OptionEntryFlags::CantChangeInGame | OptionEntryFlags::RecreateUI, N_("Fullscreen"), N_("Display the game in windowed or fullscreen mode."),
#ifdef __EMSCRIPTEN__
false // Default to windowed mode for browser
#else
true
#endif
)
#if !defined(USE_SDL1) || defined(__3DS__)
, fitToScreen("Fit to Screen", OptionEntryFlags::CantChangeInGame | OptionEntryFlags::RecreateUI, N_("Fit to Screen"), N_("Automatically adjust the game window to your current desktop screen aspect ratio and resolution."),
#ifdef __DJGPP__
#if defined(__DJGPP__) || defined(__EMSCRIPTEN__)
false
#else
true

9
Source/pfile.cpp

@ -47,6 +47,10 @@
#include "mpq/mpq_reader.hpp"
#endif
#ifdef __EMSCRIPTEN__
#include <emscripten.h>
#endif
namespace devilution {
#define PASSWORD_SPAWN_SINGLE "adslhfb1"
@ -627,6 +631,11 @@ void pfile_write_hero(bool writeGameData)
{
SaveWriter saveWriter = GetSaveWriter(gSaveNumber);
pfile_write_hero(saveWriter, writeGameData);
#ifdef __EMSCRIPTEN__
// Persist saves to IndexedDB for browser storage
emscripten_run_script("if (typeof Module !== 'undefined' && Module.saveToIndexedDB) Module.saveToIndexedDB();");
#endif
}
#ifndef DISABLE_DEMOMODE

2
Source/utils/sdl_mutex.h

@ -18,7 +18,7 @@ namespace devilution {
* RAII wrapper for SDL_mutex. Satisfies std's "Lockable" (SDL 2) or "BasicLockable" (SDL 1)
* requirements so it can be used with std::lock_guard and friends.
*/
#ifdef __DJGPP__
#if defined(__DJGPP__) || defined(__EMSCRIPTEN__)
class SdlMutex final {
public:
SdlMutex() noexcept { }

44
Source/utils/sdl_thread.cpp

@ -1,22 +1,22 @@
#include "utils/sdl_thread.h"
namespace devilution {
#ifndef __DJGPP__
int SDLCALL SdlThread::ThreadTranslate(void *ptr)
{
auto handler = (void (*)())ptr;
handler();
return 0;
}
void SdlThread::ThreadDeleter(SDL_Thread *thread)
{
if (thread != nullptr)
app_fatal("Joinable thread destroyed");
}
#endif
} // namespace devilution
#include "utils/sdl_thread.h"
namespace devilution {
#ifndef __DJGPP__
int SDLCALL SdlThread::ThreadTranslate(void *ptr)
{
auto handler = (void (*)())ptr;
handler();
return 0;
}
void SdlThread::ThreadDeleter(SDL_Thread *thread)
{
if (thread != nullptr)
app_fatal("Joinable thread destroyed");
}
#endif
} // namespace devilution

4
Source/utils/sdl_thread.h

@ -24,7 +24,7 @@ inline SDL_ThreadID get_id()
inline SDL_threadID get_id()
#endif
{
#if defined(__DJGPP__)
#if defined(__DJGPP__) || defined(__EMSCRIPTEN__)
return 1;
#else
return SDL_GetThreadID(nullptr);
@ -32,7 +32,7 @@ inline SDL_threadID get_id()
}
} // namespace this_sdl_thread
#if defined(__DJGPP__)
#if defined(__DJGPP__) || defined(__EMSCRIPTEN__)
class SdlThread final {
public:
SdlThread(int(SDLCALL *handler)(void *), void *data)

Loading…
Cancel
Save