Support multiple platform drivers (#444)

* Start of supporting multiple platform drivers

* Remove dependency of null platform on SDL2

* cmake: support loading SDL2 dynamically

* Move CMake options to project root for SDL2::SDL2 access

* Don't link to OpenGL

* Move common dynamic loading to header

* Add SDL 1.2 platform

* Add SDL1 platform backend

* Remove accidental addition

* Try multiple platform drivers until we find a good one

* Fix sdl1 mouse

* Use my BRender fork FIXME FIXME FIXME REMOVEME REMOVEME REMOVEME

* Build dethrace supporting SDL1 on ci

* use dethrace-labs BRender fork
This commit is contained in:
Anonymous Maarten 2025-05-03 23:44:46 +02:00 committed by GitHub
parent 33e49e8f0f
commit a2cdd1f061
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 1115 additions and 190 deletions

View File

@ -41,7 +41,7 @@ jobs:
if: ${{ runner.os == 'Linux' }}
run: |
sudo apt-get update
sudo apt-get install -y libgl-dev libxext-dev ${{ matrix.platform.apt-packages }}
sudo apt-get install -y libgl-dev libgl1-mesa-dev libglu1-mesa-dev libxext-dev
- name: Set up SDL
uses: libsdl-org/setup-sdl@main
with:
@ -52,6 +52,7 @@ jobs:
cmake-toolchain-file: ${{ matrix.platform.cmake-toolchain-file }}
discriminator: ${{ matrix.platform.arch }}
version: 2-latest
version-sdl12-compat: 1-head
- name: 'Prepare sources for release'
if: ${{ startsWith(github.ref, 'refs/tags/') }}
run: |
@ -68,6 +69,9 @@ jobs:
-DDETHRACE_PACKAGE_PLATFORM=${{ matrix.platform.name }} \
-DDETHRACE_PACKAGE_ARCH=${{ matrix.platform.arch }} \
-DCMAKE_TOOLCHAIN_FILE=${{ matrix.platform.cmake-toolchain-file }} \
-DDETHRACE_PLATFORM_SDL1=ON \
-DDETHRACE_PLATFORM_SDL2=ON \
-DDETHRACE_PLATFORM_SDL_DYNAMIC=ON \
${{ matrix.platform.cmake-args }}
- name: 'Build (CMake)'
run: |

View File

@ -14,6 +14,7 @@ if(MSVC)
endif()
include(CheckCCompilerFlag)
include(CMakeDependentOption)
include(TestBigEndian)
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/VERSION")
@ -61,11 +62,39 @@ endfunction()
test_big_endian(IS_BIGENDIAN)
find_package(SDL2 CONFIG)
if(NOT SDL2_FOUND)
find_package(SDL2 MODULE REQUIRED)
option(DETHRACE_PLATFORM_SDL1 "Support SDL 1.2 platform driver" OFF)
option(DETHRACE_PLATFORM_SDL2 "Support SDL 2 platform driver" ON)
set(count_sdl_platforms 0)
set(DETHRACE_PLATFORMS )
if(DETHRACE_PLATFORM_SDL1)
find_package(SDL REQUIRED)
list(APPEND DETHRACE_PLATFORMS SDL1)
math(EXPR count_sdl_platforms "${count_sdl_platforms} + 1")
endif()
if(DETHRACE_PLATFORM_SDL2)
find_package(SDL2 CONFIG)
if(NOT SDL2_FOUND)
find_package(SDL2 MODULE REQUIRED)
endif()
list(APPEND DETHRACE_PLATFORMS SDL2)
math(EXPR count_sdl_platforms "${count_sdl_platforms} + 1")
endif()
if(count_sdl_platforms GREATER 1)
# Force dynamic SDL when enabling 2 (or more) SDL platform backends
set(dynamic_sdl_force TRUE)
else()
# When no SDL backend is selected, disable dynamic SDL backend
# Enabling one SDL backend is a don-t-care
set(dynamic_sdl_force FALSE)
endif()
cmake_dependent_option(DETHRACE_PLATFORM_SDL_DYNAMIC "Load SDL dynamically" FALSE "count_sdl_platforms EQUAL 1" "${dynamic_sdl_force}")
if(NOT DETHRACE_PLATFORMS)
message(FATAL_ERROR "No platform drivers enabled")
endif()
add_subdirectory(lib/BRender-v1.3.2)
add_subdirectory(lib/libsmacker)

View File

@ -22,34 +22,58 @@ target_sources(harness PRIVATE
...
```
## IO Platform (windowing / input / rendering)
## Platform (windowing / input)
A `Platform` in _dethrace_ implements windowing, rendering and input handling.
A `Platform` in _dethrace_ implements windowing and input handling.
The default platform is `SDL_OpenGL`, which uses SDL for windowing and input, and OpenGL for rendering. See [platforms/sdl_opengl.c](https://github.com/dethrace-labs/dethrace/blob/main/src/harness/platforms/sdl_opengl.c).
The default platform is `SDL2`, which uses SDL2 for windowing and input. See [platforms/sdl_opengl.c](https://github.com/dethrace-labs/dethrace/blob/main/src/harness/platforms/sdl2.c).
To add a new `Platform`:
1. Create `platforms/my_platform.c` file and add a `Harness_Platform_Init` function. Hook up all the function pointers using the `sdl_opengl` [implementation](https://github.com/dethrace-labs/dethrace/blob/main/src/harness/platforms/sdl_opengl.h) as a guide.
1. Create a `src/harness/my_platform.c` file where you'll implement your platform-specific callbacks.
Define a public fully-initialized `const tPlatform_bootstrap MYPLATFORM_bootstrap ` variable in this file.
2. Add a new conditional section in `src/harness/CMakeLists.txt` for your new platform
2. Add the the following code fragments to appropriate locations in `src/harness/harness.c`:
```c
extern const tPlatform_bootstrap MYPLATFORM_bootstrap;
```
```c
#ifdef DETHRACE_PLATFORM_MYPLATFORM
&MYPLATFORM_bootstrap,
#endif
```
For example:
```
if (IO_PLATFORM STREQUAL "My_Platform")
target_sources(harness PRIVATE
io_platforms/my_platform.c
)
endif()
```
3. Add new conditionals to `CMakeLists.txt` and `src/harness/CMakeLists.txt` for your new platform
3. Run cmake to update your build with the new platform
```sh
cd build
cmake -DIO_PLATFORM=My_Platform ..
```
For example:
```cmake
# CMakeLists.txt
option(DETHRACE_PLATFORM_MYPLATFORM "Enable my platform" OFF)
if(DETHRACE_PLATFORM_MYPLATFORM)
find_package(MyPlatform REQUIRED)
endif()
```
```cmake
# src/harness/CMakeLists.txt
if(DETHRACE_PLATFORM_MYPLATFORM)
target_sources(harness PRIVATE
my_platform.c
)
target_compile_definitions(harness PRIVATE DETHRACE_PLATFORM_MYPLATFORM)
target_link_libraries(harness PRIVATE MyPlatform::MyPlatform)
endif()
```
4. Build
```
cmake --build .
```
4. Hook up all the function pointers using the [sdl2 platform](https://github.com/dethrace-labs/dethrace/blob/main/src/harness/platforms/sdl2.c) as a guide.
5. Run cmake to update your build with the new platform
```sh
cd build
cmake -DDETHRACE_PLATFORM_MYPLATFORM=ON ..
cmake --build .
```
6. Build
```
-cmake --build .
```

@ -1 +1 @@
Subproject commit 89861762c3e0743eed484ef5dace8e882ecd1289
Subproject commit fa8b5a90dff141f6e7e1f4f146ac593779fe6cea

View File

@ -15,7 +15,7 @@ if (DETHRACE_ASAN)
target_link_options(dethrace_obj PUBLIC -fsanitize=address)
endif()
target_link_libraries(dethrace_obj PUBLIC SDL2::SDL2 smackw32 harness BRender::Full BRender::DDI s3)
target_link_libraries(dethrace_obj PUBLIC smackw32 harness BRender::Full BRender::DDI s3)
if(MSVC)
target_compile_definitions(dethrace_obj PRIVATE -D_CRT_SECURE_NO_WARNINGS)
@ -177,6 +177,9 @@ add_executable(dethrace
${CMAKE_SOURCE_DIR}/packaging/windows/dethrace.rc
)
get_property(build_rpaths GLOBAL PROPERTY DETHRACE_BUILD_RPATHS)
set_property(TARGET dethrace APPEND PROPERTY BUILD_RPATH "${build_rpaths}")
target_link_libraries(dethrace PRIVATE dethrace_obj compile_with_werror)
target_sources(dethrace PRIVATE main.c)
@ -240,11 +243,25 @@ if(DETHRACE_INSTALL)
DESTINATION "."
OPTIONAL
)
endif()
if(WIN32)
install(FILES $<TARGET_RUNTIME_DLLS:dethrace>
DESTINATION "."
OPTIONAL
)
if(DETHRACE_PLATFORM_SDL_DYNAMIC)
if(DETHRACE_PLATFORM_SDL1)
get_filename_component(sdl1_lib_dir "${SDL_LIBRARY}" DIRECTORY)
get_filename_component(sdl1_root_dir "${SDL_LIBRARY}" PATH)
set(sdl1_bin_dir "${sdl1_root_dir}/bin" PATH)
find_file(SDL1DLL_PATH NAMES "SDL.dll" PATHS ${sdl1_lib_dir} ${sdl1_root_dir} PATH_SUFFIXES "bin" REQUIRED)
if(SDL1DLL_PATH)
install(FILES "${SDL1DLL_PATH}"
DESTINATION "."
OPTIONAL
)
endif()
endif()
if(DETHRACE_PLATFORM_SDL2)
install(FILES "$<TARGET_FILE:SDL2::SDL2>"
DESTINATION "."
OPTIONAL
)
endif()
endif()
endif()
endif()

View File

@ -9,7 +9,7 @@
#include "brender.h"
extern void Harness_Init(int* argc, char* argv[]);
extern int Harness_Init(int* argc, char* argv[]);
extern int original_main(int pArgc, char* pArgv[]);
void BR_CALLBACK _BrBeginHook(void) {
@ -28,6 +28,8 @@ void BR_CALLBACK _BrEndHook(void) {
}
int main(int argc, char* argv[]) {
int result;
#ifdef _WIN32
/* Attach to the console that started us if any */
if (AttachConsole(ATTACH_PARENT_PROCESS)) {
@ -46,7 +48,10 @@ int main(int argc, char* argv[]) {
}
#endif
Harness_Init(&argc, argv);
result = Harness_Init(&argc, argv);
if (result != 0) {
return result;
}
return original_main(argc, argv);
}

View File

@ -5,7 +5,7 @@ target_include_directories(s3
include
)
target_link_libraries(s3 PRIVATE brender SDL2::SDL2 harness compile_with_werror)
target_link_libraries(s3 PRIVATE brender harness compile_with_werror)
if(NOT MSVC)
target_link_libraries(s3 PUBLIC pthread m)

View File

@ -2,10 +2,6 @@ configure_file(version.h.in version.h @ONLY)
add_library(harness STATIC)
if (NOT DEFINED IO_PLATFORM)
set(IO_PLATFORM "SDL2")
endif()
target_include_directories(harness
PRIVATE
.
@ -62,12 +58,41 @@ target_sources(harness PRIVATE
"${CMAKE_CURRENT_BINARY_DIR}/version.h"
)
if (IO_PLATFORM STREQUAL "SDL2")
if(DETHRACE_PLATFORM_SDL1)
target_sources(harness PRIVATE
platforms/sdl1.c
platforms/sdl1_scancode_to_dinput.h
platforms/sdl1_syms.h
)
target_compile_definitions(harness PRIVATE DETHRACE_PLATFORM_SDL1)
if(DETHRACE_PLATFORM_SDL_DYNAMIC)
set_property(SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/platforms/sdl1.c" APPEND PROPERTY INCLUDE_DIRECTORIES "$<TARGET_PROPERTY:SDL::SDL,INTERFACE_INCLUDE_DIRECTORIES>")
get_filename_component(sdl_library_directory "${SDL_LIBRARY}" DIRECTORY)
set_property(GLOBAL APPEND PROPERTY DETHRACE_BUILD_RPATHS "${sdl_library_directory}")
else()
target_include_directories(harness PRIVATE ${SDL_INCLUDE_DIR})
target_link_libraries(harness PRIVATE ${SDL_LIBRARIES})
endif()
endif()
if(DETHRACE_PLATFORM_SDL2)
target_sources(harness PRIVATE
platforms/sdl2.c
platforms/sdl2_scancode_to_dinput.h
platforms/sdl2_syms.h
)
target_link_libraries(harness PRIVATE SDL2::SDL2)
target_compile_definitions(harness PRIVATE DETHRACE_PLATFORM_SDL2)
if(DETHRACE_PLATFORM_SDL_DYNAMIC)
set_property(SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/platforms/sdl2.c" APPEND PROPERTY INCLUDE_DIRECTORIES "$<TARGET_PROPERTY:SDL2::SDL2,INTERFACE_INCLUDE_DIRECTORIES>")
set_property(GLOBAL APPEND PROPERTY DETHRACE_BUILD_RPATHS "$<TARGET_FILE_DIR:SDL2::SDL2>")
else()
target_link_libraries(harness PRIVATE SDL2::SDL2)
endif()
endif()
if(DETHRACE_PLATFORM_SDL_DYNAMIC)
target_compile_definitions(harness PRIVATE DETHRACE_SDL_DYNAMIC)
target_link_libraries(harness PRIVATE ${CMAKE_DL_LIBS})
endif()
if(WIN32)

View File

@ -12,16 +12,22 @@
#include <string.h>
#include <sys/stat.h>
br_pixelmap* palette;
uint32_t* screen_buffer;
br_pixelmap* last_dst = NULL;
br_pixelmap* last_src = NULL;
int force_null_platform = 0;
extern uint32_t gI_am_cheating;
extern void Harness_Platform_Init(tHarness_platform* platform);
extern const tPlatform_bootstrap SDL1_bootstrap;
extern const tPlatform_bootstrap SDL2_bootstrap;
static const tPlatform_bootstrap *platform_bootstraps[] = {
#ifdef DETHRACE_PLATFORM_SDL2
&SDL2_bootstrap,
#endif
#ifdef DETHRACE_PLATFORM_SDL1
&SDL1_bootstrap,
#endif
};
// SplatPack or Carmageddon. This is where we represent the code differences between the two. For example, the intro smack file.
tHarness_game_info harness_game_info;
@ -31,9 +37,15 @@ tHarness_game_config harness_game_config;
// Platform hooks
tHarness_platform gHarness_platform;
extern void Harness_Platform_Init(tHarness_platform* platform);
static int force_null_platform = 0;
int Harness_ProcessCommandLine(int* argc, char* argv[]);
typedef struct {
const char *platform_name;
uint32_t platform_capabilityies;
int install_signalhandler;
} tArgument_config;
static int Harness_ProcessCommandLine(tArgument_config* argument_config, int* argc, char* argv[]);
static void Harness_DetectGameMode(void) {
if (access("DATA/RACES/CASTLE.TXT", F_OK) != -1) {
@ -138,7 +150,7 @@ static void Harness_DetectGameMode(void) {
}
}
void Harness_Init(int* argc, char* argv[]) {
int Harness_Init(int* argc, char* argv[]) {
int result;
printf("Dethrace version: %s\n", DETHRACE_VERSION);
@ -169,12 +181,21 @@ void Harness_Init(int* argc, char* argv[]) {
harness_game_config.no_bind = 0;
// Disable verbose logging
harness_game_config.verbose = 0;
// install signal handler by default
harness_game_config.install_signalhandler = 1;
Harness_ProcessCommandLine(argc, argv);
tArgument_config argument_config;
// don't require a particular platform
argument_config.platform_name = NULL;
// request software renderer capability
argument_config.platform_capabilityies = ePlatform_cap_software;
// install signal handler
argument_config.install_signalhandler = 1;
if (harness_game_config.install_signalhandler) {
if (Harness_ProcessCommandLine(&argument_config, argc, argv) != 0) {
fprintf(stderr, "Failed to parse harness command line\n");
return 1;
}
if (argument_config.install_signalhandler) {
OS_InstallSignalHandler(argv[0]);
}
@ -205,8 +226,54 @@ void Harness_Init(int* argc, char* argv[]) {
if (force_null_platform) {
Null_Platform_Init(&gHarness_platform);
} else {
Harness_Platform_Init(&gHarness_platform);
const tPlatform_bootstrap* selected_bootstrap = NULL;
if (argument_config.platform_name != NULL) {
size_t i;
for (i = 0; i < BR_ASIZE(platform_bootstraps); i++) {
if (strcasecmp(platform_bootstraps[i]->name, argument_config.platform_name) == 0) {
if ((platform_bootstraps[i]->capabilities & argument_config.platform_capabilityies) != argument_config.platform_capabilityies) {
fprintf(stderr, "Platform \"%s\" does not support requested capabilities. Try another video driver and/or add/remove --opengl\n", selected_bootstrap->name);
return 1;
}
selected_bootstrap = platform_bootstraps[i];
break;
}
}
if (selected_bootstrap == NULL) {
fprintf(stderr, "Could not find a platform named \"%s\"\n", argument_config.platform_name);
return 1;
}
if (selected_bootstrap->init(&gHarness_platform) != 0) {
fprintf(stderr, "%s initialization failed\n", selected_bootstrap->name);
return 1;
}
} else {
size_t i;
for (i = 0; i < BR_ASIZE(platform_bootstraps); i++) {
LOG_TRACE10("Attempting video driver \"%s\"", platform_bootstraps[i]->name);
if ((platform_bootstraps[i]->capabilities & argument_config.platform_capabilityies) != argument_config.platform_capabilityies) {
fprintf(stderr, "Skipping platform \"%s\". Does not support required capabilities.\n", platform_bootstraps[i]->name);
continue;
}
LOG_TRACE10("Try platform \"%s\"...");
if (platform_bootstraps[i]->init(&gHarness_platform) == 0) {
selected_bootstrap = platform_bootstraps[i];
break;
}
}
if (selected_bootstrap == NULL) {
fprintf(stderr, "Could not find a supported platform\n");
return 1;
}
}
if (selected_bootstrap == NULL) {
fprintf(stderr, "Could not find a supported platform\n");
return 1;
}
LOG_INFO("Platform: %s (%s)", selected_bootstrap->name, selected_bootstrap->description);
}
return 0;
}
// used by unit tests
@ -214,86 +281,94 @@ void Harness_ForceNullPlatform(void) {
force_null_platform = 1;
}
int Harness_ProcessCommandLine(int* argc, char* argv[]) {
for (int i = 1; i < *argc; i++) {
int handled = 0;
int Harness_ProcessCommandLine(tArgument_config* config, int* argc, char* argv[]) {
for (int i = 1; i < *argc;) {
int consumed = -1;
if (strcasecmp(argv[i], "--cdcheck") == 0) {
harness_game_config.enable_cd_check = 1;
handled = 1;
consumed = 1;
} else if (strstr(argv[i], "--debug=") != NULL) {
char* s = strstr(argv[i], "=");
harness_debug_level = atoi(s + 1);
LOG_INFO("debug level set to %d", harness_debug_level);
handled = 1;
consumed = 1;
} else if (strstr(argv[i], "--physics-step-time=") != NULL) {
char* s = strstr(argv[i], "=");
harness_game_config.physics_step_time = atoi(s + 1);
LOG_INFO("Physics step time set to %d", harness_game_config.physics_step_time);
handled = 1;
consumed = 1;
} else if (strstr(argv[i], "--fps=") != NULL) {
char* s = strstr(argv[i], "=");
harness_game_config.fps = atoi(s + 1);
LOG_INFO("FPS limiter set to %f", harness_game_config.fps);
handled = 1;
consumed = 1;
} else if (strcasecmp(argv[i], "--freeze-timer") == 0) {
LOG_INFO("Timer frozen");
harness_game_config.freeze_timer = 1;
handled = 1;
consumed = 1;
} else if (strcasecmp(argv[i], "--no-signal-handler") == 0) {
LOG_INFO("Don't install the signal handler");
harness_game_config.install_signalhandler = 0;
handled = 1;
config->install_signalhandler = 0;
consumed = 1;
} else if (strstr(argv[i], "--demo-timeout=") != NULL) {
char* s = strstr(argv[i], "=");
harness_game_config.demo_timeout = atoi(s + 1) * 1000;
LOG_INFO("Demo timeout set to %d milliseconds", harness_game_config.demo_timeout);
handled = 1;
consumed = 1;
} else if (strcasecmp(argv[i], "--i-am-cheating") == 0) {
gI_am_cheating = 0xa11ee75d;
handled = 1;
consumed = 1;
} else if (strcasecmp(argv[i], "--enable-diagnostics") == 0) {
harness_game_config.enable_diagnostics = 1;
handled = 1;
consumed = 1;
} else if (strstr(argv[i], "--volume-multiplier=") != NULL) {
char* s = strstr(argv[i], "=");
harness_game_config.volume_multiplier = atof(s + 1);
LOG_INFO("Volume multiplier set to %f", harness_game_config.volume_multiplier);
handled = 1;
consumed = 1;
} else if (strcasecmp(argv[i], "--full-screen") == 0) {
// option left for backwards compatibility
harness_game_config.start_full_screen = 1;
handled = 1;
consumed = 1;
} else if (strcasecmp(argv[i], "--window") == 0) {
harness_game_config.start_full_screen = 0;
handled = 1;
consumed = 1;
} else if (strcasecmp(argv[i], "--gore-check") == 0) {
harness_game_config.gore_check = 1;
handled = 1;
consumed = 1;
} else if (strcasecmp(argv[i], "--sound-options") == 0) {
harness_game_config.sound_options = 1;
handled = 1;
consumed = 1;
} else if (strcasecmp(argv[i], "--no-bind") == 0) {
harness_game_config.no_bind = 1;
handled = 1;
consumed = 1;
} else if (strcasecmp(argv[i], "--opengl") == 0) {
config->platform_capabilityies &= ~ePlatform_cap_video_mask;
config->platform_capabilityies |= ePlatform_cap_opengl;
harness_game_config.opengl_3dfx_mode = 1;
handled = 1;
consumed = 1;
} else if (strcasecmp(argv[i], "--no-music") == 0) {
harness_game_config.no_music = 1;
handled = 1;
consumed = 1;
} else if (strcasecmp(argv[i], "--game-completed") == 0) {
harness_game_config.game_completed = 1;
handled = 1;
consumed = 1;
} else if (strcmp(argv[i], "--platform") == 0) {
if (i < *argc + 1) {
config->platform_name = argv[i + 1];
consumed = 2;
}
}
if (handled) {
if (consumed > 0) {
// shift args downwards
for (int j = i; j < *argc - 1; j++) {
argv[j] = argv[j + 1];
for (int j = i; j < *argc - consumed; j++) {
argv[j] = argv[j + consumed];
}
(*argc)--;
i--;
*argc -= consumed;
} else {
i += 1;
}
}

View File

@ -50,8 +50,6 @@ typedef struct tHarness_game_config {
int verbose;
int opengl_3dfx_mode;
int game_completed;
int install_signalhandler;
} tHarness_game_config;
extern tHarness_game_info harness_game_info;

View File

@ -38,7 +38,7 @@ typedef struct tHarness_platform {
int (*ShowErrorMessage)(void* window, char* text, char* caption);
// Create a window. Uses an underscore to avoid name collisions with windows.h `CreateWindow` macro
void (*CreateWindow_)(char* title, int nWidth, int nHeight, tHarness_window_type window_type);
void (*CreateWindow_)(const char* title, int nWidth, int nHeight, tHarness_window_type window_type);
void (*Swap)(br_pixelmap* back_buffer);
void (*PaletteChanged)(br_colour entries[256]);
// If this platform supports OpenGL
@ -47,9 +47,22 @@ typedef struct tHarness_platform {
} tHarness_platform;
enum {
ePlatform_cap_software = 0x1,
ePlatform_cap_opengl = 0x2,
ePlatform_cap_video_mask = ePlatform_cap_software | ePlatform_cap_opengl,
};
typedef struct tPlatform_bootstrap {
const char *name;
const char *description;
uint32_t capabilities;
int (*init)(tHarness_platform* platform);
} tPlatform_bootstrap;
extern tHarness_platform gHarness_platform;
void Harness_Init(int* argc, char* argv[]);
int Harness_Init(int* argc, char* argv[]);
// Hooks are called from original game code.

View File

@ -1,45 +1,64 @@
#include "null.h"
// todo: shouldnt depend on sdl...
#include <SDL.h>
static uint32_t null_time;
static int null_set_window_pos(void* hWnd, int x, int y, int nWidth, int nHeight) {
null_time += 1;
return 0;
}
static void null_destroy_window(void* hWnd) {
null_time += 1;
}
static int null_show_error_message(void* window, char* text, char* caption) {
null_time += 1;
return 0;
}
static void null_get_and_handle_message(MSG_* msg) {
null_time += 1;
}
static void null_get_keyboard_state(unsigned int count, uint8_t* buffer) {
null_time += 1;
}
static int null_get_mouse_buttons(int* pButton1, int* pButton2) {
null_time += 1;
return 0;
}
static int null_get_mouse_position(int* pX, int* pY) {
null_time += 1;
return 0;
}
static int null_show_cursor(int show) {
null_time += 1;
return 0;
}
static void null_set_palette(PALETTEENTRY_* palette) {
null_time += 1;
}
static void null_sleep(uint32_t milliseconds) {
null_time += 1;
null_time += milliseconds;
}
static uint32_t null_getticks(void) {
null_time += 1;
return null_time;
}
void Null_Platform_Init(tHarness_platform* platform) {
null_time = 0;
platform->ProcessWindowMessages = null_get_and_handle_message;
// todo: shouldnt depend on sdl...
platform->Sleep = SDL_Delay;
platform->GetTicks = SDL_GetTicks;
platform->Sleep = null_sleep;
platform->GetTicks = null_getticks;
platform->ShowCursor = null_show_cursor;
platform->SetWindowPos = null_set_window_pos;
platform->DestroyWindow = null_destroy_window;

View File

@ -0,0 +1,315 @@
#include <SDL.h>
#include "harness.h"
#include "harness/config.h"
#include "harness/hooks.h"
#include "harness/trace.h"
#include "sdl1_scancode_to_dinput.h"
#include "sdl1_syms.h"
SDL_COMPILE_TIME_ASSERT(sdl1_platform_requires_SDL1, SDL_MAJOR_VERSION == 1);
static SDL_Surface* screen;
static uint32_t converted_palette[256];
static br_pixelmap* last_screen_src;
static Uint32 last_frame_time;
static uint8_t directinput_key_state[SDLK_LAST];
static struct {
int x, y;
float scale_x, scale_y;
} viewport;
// Callbacks back into original game code
extern void QuitGame(void);
extern uint32_t gKeyboard_bits[8];
extern br_pixelmap* gBack_screen;
static int window_width, window_height;
#ifdef DETHRACE_SDL_DYNAMIC
#ifdef _WIN32
static const char * const possible_locations[] = {
"SDL.dll",
};
#elif defined(__APPLE__)
#define SHARED_OBJECT_NAME "libSDL"
#define SDL1_LIBNAME "libSDL.dylib"
static const char * const possible_locations[] = {
"@loader_path/" SDL1_LIBNAME, /* MyApp.app/Contents/MacOS/libSDL2_dylib */
"@executable_path/" SDL1_LIBNAME, /* MyApp.app/Contents/MacOS/libSDL2_dylib */
SDL1_LIBNAME /* oh well, anywhere the system can see the .dylib (/usr/local/lib or whatever) */
};
#else
static const char * const possible_locations[] = {
"libSDL-1.2.so.0",
"libSDL-1.2.so",
};
#endif
#endif
#ifdef DETHRACE_SDL_DYNAMIC
static void *sdl1_so;
#endif
#define OBJECT_NAME sdl1_so
#define SYMBOL_PREFIX SDL1_
#define FOREACH_SDLX_SYM FOREACH_SDL1_SYM
#include "sdl_dyn_common.h"
static void calculate_viewport(int window_width, int window_height) {
int vp_width, vp_height;
float target_aspect_ratio;
float aspect_ratio;
aspect_ratio = (float)window_width / window_height;
target_aspect_ratio = (float)gBack_screen->width / gBack_screen->height;
vp_width = window_width;
vp_height = window_height;
if (aspect_ratio != target_aspect_ratio) {
if (aspect_ratio > target_aspect_ratio) {
vp_width = window_height * target_aspect_ratio + .5f;
} else {
vp_height = window_width / target_aspect_ratio + .5f;
}
}
viewport.x = (window_width - vp_width) / 2;
viewport.y = (window_height - vp_height) / 2;
viewport.scale_x = (float)vp_width / gBack_screen->width;
viewport.scale_y = (float)vp_height / gBack_screen->height;
}
static void recreate_screen(void) {
int video_flags = SDL_RESIZABLE | SDL_HWSURFACE | SDL_DOUBLEBUF;
if (harness_game_config.start_full_screen) {
video_flags |= SDL_FULLSCREEN;
}
screen = SDL1_SetVideoMode(window_width, window_height, 32, video_flags);
if (screen == NULL) {
LOG_PANIC("SDL_SetVideoMode failed (%s)", SDL1_GetError());
}
}
static void SDL1_Harness_CreateWindow(const char* title, int width, int height, tHarness_window_type window_type) {
window_width = width;
window_height = height;
initializeSDL1KeyNums();
if (SDL1_Init(SDL_INIT_VIDEO) != 0) {
LOG_PANIC("SDL_INIT_VIDEO error: %s", SDL1_GetError());
}
if (window_type == eWindow_type_software) {
recreate_screen();
} else {
LOG_PANIC("Unsupported window type (%d)", window_type);
}
SDL1_WM_SetCaption("Carmageddon", NULL);
SDL1_ShowCursor(SDL_DISABLE);
}
static int SDL1_Harness_SetWindowPos(void* hWnd, int x, int y, int nWidth, int nHeight) {
// SDL_SetWindowPosition(hWnd, x, y);
if (nWidth == 320 && nHeight == 200) {
nWidth = 640;
nHeight = 400;
}
window_width = nWidth;
window_height = nHeight;
recreate_screen();
SDL1_UpdateRect(hWnd, x, y, nWidth, nHeight);
return 0;
}
static void SDL1_Harness_DestroyWindow(void* hWnd) {
SDL1_FreeSurface(screen);
SDL1_Quit();
screen = NULL;
}
// Checks whether the `flag_check` is the only modifier applied.
// e.g. is_only_modifier(event.key.keysym.mod, KMOD_ALT) returns true when only the ALT key was pressed
static int is_only_key_modifier(int modifier_flags, int flag_check) {
return (modifier_flags & flag_check) && (modifier_flags & (KMOD_CTRL | KMOD_SHIFT | KMOD_ALT | KMOD_META)) == (modifier_flags & flag_check);
}
static void SDL1_Harness_ProcessWindowMessages(MSG_* msg) {
SDL_Event event;
int dinput_key;
while (SDL1_PollEvent(&event)) {
switch (event.type) {
case SDL_KEYDOWN:
case SDL_KEYUP:
if (event.key.keysym.sym == SDLK_RETURN) {
if (event.key.type == SDL_KEYDOWN) {
if ((event.key.keysym.mod & (KMOD_CTRL | KMOD_SHIFT | KMOD_ALT | KMOD_META))) {
// Ignore keydown of RETURN when used together with some modifier
return;
}
} else if (event.key.type == SDL_KEYUP) {
if (is_only_key_modifier(event.key.keysym.mod, KMOD_ALT)) {
SDL1_WM_ToggleFullScreen(screen);
}
}
}
// Map incoming SDL scancode to DirectInput DIK_* key code.
// https://github.com/DanielGibson/Snippets/blob/master/sdl2_scancode_to_dinput.h
dinput_key = sdl1KeyToDirectInputKeyNum[event.key.keysym.sym];
if (dinput_key == 0) {
LOG_WARN("unexpected key \"%s\" (0x%x)", SDL1_GetKeyName(event.key.keysym.sym), event.key.keysym.sym);
return;
}
// DInput expects high bit to be set if key is down
// https://learn.microsoft.com/en-us/previous-versions/windows/desktop/ee418261(v=vs.85)
directinput_key_state[dinput_key] = (event.type == SDL_KEYDOWN ? 0x80 : 0);
if (event.type == SDL_KEYDOWN) {
gKeyboard_bits[dinput_key >> 5] |= (1 << (dinput_key & 0x1F));
} else {
gKeyboard_bits[dinput_key >> 5] &= ~(1 << (dinput_key & 0x1F));
}
break;
case SDL_VIDEORESIZE:
calculate_viewport(event.resize.w, event.resize.h);
break;
case SDL_QUIT:
QuitGame();
return;
}
}
return;
}
static void get_keyboard_state(unsigned int count, uint8_t* buffer) {
memcpy(buffer, directinput_key_state, count);
}
static int get_mouse_buttons(int* pButton1, int* pButton2) {
int state = SDL1_GetMouseState(NULL, NULL);
*pButton1 = state & SDL_BUTTON_LMASK;
*pButton2 = state & SDL_BUTTON_RMASK;
return 0;
}
static int get_mouse_position(int* pX, int* pY) {
SDL1_GetMouseState(pX, pY);
return 0;
}
static void limit_fps(void) {
Uint32 now = SDL1_GetTicks();
if (last_frame_time != 0) {
unsigned int frame_time = now - last_frame_time;
last_frame_time = now;
if (frame_time < 100) {
int sleep_time = (1000 / harness_game_config.fps) - frame_time;
if (sleep_time > 5) {
gHarness_platform.Sleep(sleep_time);
}
}
}
last_frame_time = SDL1_GetTicks();
}
static void SDL1_Renderer_Present(br_pixelmap* src) {
// fastest way to convert 8 bit indexed to 32 bit
uint8_t* src_pixels = src->pixels;
uint32_t* dest_pixels;
SDL1_LockSurface(screen);
dest_pixels = screen->pixels;
for (int i = 0; i < src->height * src->width; i++) {
*dest_pixels = converted_palette[*src_pixels];
dest_pixels++;
src_pixels++;
}
SDL1_UnlockSurface(screen);
SDL1_Flip(screen);
last_screen_src = src;
if (harness_game_config.fps != 0) {
limit_fps();
}
}
static void SDL1_Harness_Swap(br_pixelmap* back_buffer) {
SDL1_Harness_ProcessWindowMessages(NULL);
if (0) {
SDL1_GL_SwapBuffers();
} else {
SDL1_Renderer_Present(back_buffer);
if (harness_game_config.fps != 0) {
limit_fps();
}
}
}
static void SDL1_Harness_PaletteChanged(br_colour entries[256]) {
for (int i = 0; i < 256; i++) {
converted_palette[i] = (0xff << 24 | BR_RED(entries[i]) << 16 | BR_GRN(entries[i]) << 8 | BR_BLU(entries[i]));
}
if (last_screen_src != NULL) {
SDL1_Harness_Swap(last_screen_src);
}
}
static int SDL1_Harness_ShowErrorMessage(void* window, char* text, char* caption) {
fprintf(stderr, "%s", text);
#ifdef _WIN32
MessageBoxA(NULL, text, caption, MB_ICONERROR);
#endif
return 0;
}
static void SDL1_Harness_GetViewport(int* x, int* y, float* width_multipler, float* height_multiplier) {
*x = viewport.x;
*y = viewport.y;
*width_multipler = viewport.scale_x;
*height_multiplier = viewport.scale_y;
}
static int SDL1_Harness_Platform_Init(tHarness_platform* platform) {
if (SDL1_LoadSymbols() != 0) {
return 1;
}
platform->ProcessWindowMessages = SDL1_Harness_ProcessWindowMessages;
platform->Sleep = SDL1_Delay;
platform->GetTicks = SDL1_GetTicks;
platform->ShowCursor = SDL1_ShowCursor;
platform->SetWindowPos = SDL1_Harness_SetWindowPos;
platform->DestroyWindow = SDL1_Harness_DestroyWindow;
platform->GetKeyboardState = get_keyboard_state;
platform->GetMousePosition = get_mouse_position;
platform->GetMouseButtons = get_mouse_buttons;
platform->ShowErrorMessage = SDL1_Harness_ShowErrorMessage;
platform->CreateWindow_ = SDL1_Harness_CreateWindow;
platform->Swap = SDL1_Harness_Swap;
platform->PaletteChanged = SDL1_Harness_PaletteChanged;
platform->GL_GetProcAddress = SDL1_GL_GetProcAddress;
platform->GetViewport = SDL1_Harness_GetViewport;
return 0;
}
const tPlatform_bootstrap SDL1_bootstrap = {
"sdl1",
"SDL1 video backend (libsdl.org)",
ePlatform_cap_software,
SDL1_Harness_Platform_Init,
};

View File

@ -0,0 +1,208 @@
/*
* Maps SDL1 scancodes to directinput keynums/scancodes.
* Useful if you're porting a game that uses dinput scancodes internally
* (for key bindings etc) or any other lib (like CEGUI) that uses them.
*
* (C) 2015 Daniel Gibson
*
* Homepage: https://github.com/DanielGibson/Snippets/
*
* License:
* This software is dual-licensed to the public domain and under the following
* license: you are granted a perpetual, irrevocable license to copy, modify,
* publish, and distribute this file as you see fit.
* No warranty implied; use at your own risk.
*
* So you can do whatever you want with this code, including copying it
* (or parts of it) into your own source.
* No need to mention me or this "license" in your code or docs, even though
* it would be appreciated, of course.
*
*/
#include <SDL.h>
#if 0 // Usage Example:
#include "sdl2_scancode_to_dinput.h"
static int SDLScanCodeToKeyNum(SDL_Scancode sc)
{
int idx = (int)sc;
assert(idx >= 0 && idx < SDL_NUM_SCANCODES);
return scanCodeToKeyNum[idx];
}
static SDL_Scancode KeyNumToSDLScanCode( int keyNum )
{
if( keyNum >= 0 && keyNum < 0xEF )
{
for(int i = 0; i < SDL_NUM_SCANCODES; ++i)
{
if(scanCodeToKeyNum[i] == keyNum) return (SDL_Scancode)i;
}
}
return SDL_SCANCODE_UNKNOWN;
}
#endif // 0
#ifndef _SDL1_SCANCODE_TO_DINPUT_H_
#define _SDL1_SCANCODE_TO_DINPUT_H_
// TODO: map the following keys, if possible:
// #define DIK_UNDERLINE 0x93 /* (NEC PC98) */
// #define DIK_KANJI 0x94 /* (Japanese keyboard) */
// #define DIK_AX 0x96 /* (Japan AX) */
// #define DIK_UNLABELED 0x97 /* (J3100) */
//
// #define DIK_WAKE 0xE3 /* System Wake */
//
// (#define DIK_ABNT_C2 0x7E /* Numpad . on Brazilian keyboard */ - system should map this to KP_COMMA or something,
// according to USB doc, so probably it doesn't need mapping here)
// maps SDL1 keysyms to directinput keynums/scancodes - dinput_key = sdlScanCodeToDirectInputKeyNum[(int)your_sdl2_scancode];
static int sdl1KeyToDirectInputKeyNum[SDLK_LAST];
static void initializeSDL1KeyNums(void) {
memset(sdl1KeyToDirectInputKeyNum, 0, sizeof(sdl1KeyToDirectInputKeyNum));
sdl1KeyToDirectInputKeyNum[SDLK_a] = 0x1E; /* DIK_A */
sdl1KeyToDirectInputKeyNum[SDLK_b] = 0x30; /* DIK_B */
sdl1KeyToDirectInputKeyNum[SDLK_c] = 0x2E; /* DIK_C */
sdl1KeyToDirectInputKeyNum[SDLK_d] = 0x20; /* DIK_D */
sdl1KeyToDirectInputKeyNum[SDLK_e] = 0x12; /* DIK_E */
sdl1KeyToDirectInputKeyNum[SDLK_f] = 0x21; /* DIK_F */
sdl1KeyToDirectInputKeyNum[SDLK_g] = 0x22; /* DIK_G */
sdl1KeyToDirectInputKeyNum[SDLK_h] = 0x23; /* DIK_H */
sdl1KeyToDirectInputKeyNum[SDLK_i] = 0x17; /* DIK_I */
sdl1KeyToDirectInputKeyNum[SDLK_j] = 0x24; /* DIK_J */
sdl1KeyToDirectInputKeyNum[SDLK_k] = 0x25; /* DIK_K */
sdl1KeyToDirectInputKeyNum[SDLK_l] = 0x26; /* DIK_L */
sdl1KeyToDirectInputKeyNum[SDLK_m] = 0x32; /* DIK_M */
sdl1KeyToDirectInputKeyNum[SDLK_n] = 0x31; /* DIK_N */
sdl1KeyToDirectInputKeyNum[SDLK_o] = 0x18; /* DIK_O */
sdl1KeyToDirectInputKeyNum[SDLK_p] = 0x19; /* DIK_P */
sdl1KeyToDirectInputKeyNum[SDLK_q] = 0x10; /* DIK_Q */
sdl1KeyToDirectInputKeyNum[SDLK_r] = 0x13; /* DIK_R */
sdl1KeyToDirectInputKeyNum[SDLK_s] = 0x1F; /* DIK_S */
sdl1KeyToDirectInputKeyNum[SDLK_t] = 0x14; /* DIK_T */
sdl1KeyToDirectInputKeyNum[SDLK_u] = 0x16; /* DIK_U */
sdl1KeyToDirectInputKeyNum[SDLK_v] = 0x2F; /* DIK_V */
sdl1KeyToDirectInputKeyNum[SDLK_w] = 0x11; /* DIK_W */
sdl1KeyToDirectInputKeyNum[SDLK_x] = 0x2D; /* DIK_X */
sdl1KeyToDirectInputKeyNum[SDLK_y] = 0x15; /* DIK_Y */
sdl1KeyToDirectInputKeyNum[SDLK_z] = 0x2C; /* DIK_Z */
sdl1KeyToDirectInputKeyNum[SDLK_0] = 0x2C; /* DIK_Z */
sdl1KeyToDirectInputKeyNum[SDLK_1] = 0x02; /* DIK_1 */
sdl1KeyToDirectInputKeyNum[SDLK_2] = 0x03; /* DIK_2 */
sdl1KeyToDirectInputKeyNum[SDLK_3] = 0x04; /* DIK_3 */
sdl1KeyToDirectInputKeyNum[SDLK_4] = 0x05; /* DIK_4 */
sdl1KeyToDirectInputKeyNum[SDLK_5] = 0x06; /* DIK_5 */
sdl1KeyToDirectInputKeyNum[SDLK_6] = 0x07; /* DIK_6 */
sdl1KeyToDirectInputKeyNum[SDLK_7] = 0x08; /* DIK_7 */
sdl1KeyToDirectInputKeyNum[SDLK_8] = 0x09; /* DIK_8 */
sdl1KeyToDirectInputKeyNum[SDLK_9] = 0x0A; /* DIK_9 */
sdl1KeyToDirectInputKeyNum[SDLK_0] = 0x0B; /* DIK_0 */
sdl1KeyToDirectInputKeyNum[SDLK_KP1] = 0x4F; /* DIK_NUMPAD1 */
sdl1KeyToDirectInputKeyNum[SDLK_KP2] = 0x50; /* DIK_NUMPAD2 */
sdl1KeyToDirectInputKeyNum[SDLK_KP3] = 0x51; /* DIK_NUMPAD3 */
sdl1KeyToDirectInputKeyNum[SDLK_KP4] = 0x4B; /* DIK_NUMPAD4 */
sdl1KeyToDirectInputKeyNum[SDLK_KP5] = 0x4C; /* DIK_NUMPAD5 */
sdl1KeyToDirectInputKeyNum[SDLK_KP6] = 0x4D; /* DIK_NUMPAD6 */
sdl1KeyToDirectInputKeyNum[SDLK_KP7] = 0x47; /* DIK_NUMPAD7 */
sdl1KeyToDirectInputKeyNum[SDLK_KP8] = 0x48; /* DIK_NUMPAD8 */
sdl1KeyToDirectInputKeyNum[SDLK_KP9] = 0x49; /* DIK_NUMPAD9 */
sdl1KeyToDirectInputKeyNum[SDLK_KP0] = 0x52; /* DIK_NUMPAD0 */
sdl1KeyToDirectInputKeyNum[SDLK_RETURN] = 0x1C; /* DIK_RETURN */
sdl1KeyToDirectInputKeyNum[SDLK_ESCAPE] = 0x01; /* DIK_ESCAPE */
sdl1KeyToDirectInputKeyNum[SDLK_BACKSPACE] = 0x0E; /* DIK_BACK */
sdl1KeyToDirectInputKeyNum[SDLK_TAB] = 0x0F; /* DIK_TAB */
sdl1KeyToDirectInputKeyNum[SDLK_SPACE] = 0x39; /* DIK_SPACE */
sdl1KeyToDirectInputKeyNum[SDLK_MINUS] = 0x0C; /* DIK_MINUS */
sdl1KeyToDirectInputKeyNum[SDLK_EQUALS] = 0x0D; /* DIK_EQUALS */
sdl1KeyToDirectInputKeyNum[SDLK_LEFTBRACKET] = 0x1A; /* DIK_LBRACKET */
sdl1KeyToDirectInputKeyNum[SDLK_RIGHTBRACKET] = 0x1B; /* DIK_RBRACKET */
sdl1KeyToDirectInputKeyNum[SDLK_BACKSLASH] = 0x2B; /* DIK_BACKSLASH */
sdl1KeyToDirectInputKeyNum[SDLK_HASH] = 0x2B; /* DIK_BACKSLASH */
sdl1KeyToDirectInputKeyNum[SDLK_SEMICOLON] = 0x27; /* DIK_SEMICOLON */
sdl1KeyToDirectInputKeyNum[SDLK_QUOTE] = 0x28; /* DIK_APOSTROPHE */
sdl1KeyToDirectInputKeyNum[SDLK_BACKQUOTE] = 0x29; /* DIK_GRAVE */
sdl1KeyToDirectInputKeyNum[SDLK_COMMA] = 0x33; /* DIK_COMMA */
sdl1KeyToDirectInputKeyNum[SDLK_PERIOD] = 0x34; /* DIK_PERIOD */
sdl1KeyToDirectInputKeyNum[SDLK_SLASH] = 0x35; /* DIK_SLASH */
sdl1KeyToDirectInputKeyNum[SDLK_CAPSLOCK] = 0x3A; /* DIK_CAPITAL */
sdl1KeyToDirectInputKeyNum[SDLK_F1] = 0x3A; /* DIK_CAPITAL */
sdl1KeyToDirectInputKeyNum[SDLK_F1] = 0x3B; /* DIK_F1 */
sdl1KeyToDirectInputKeyNum[SDLK_F2] = 0x3C; /* DIK_F2 */
sdl1KeyToDirectInputKeyNum[SDLK_F3] = 0x3D; /* DIK_F3 */
sdl1KeyToDirectInputKeyNum[SDLK_F4] = 0x3E; /* DIK_F4 */
sdl1KeyToDirectInputKeyNum[SDLK_F5] = 0x3F; /* DIK_F5 */
sdl1KeyToDirectInputKeyNum[SDLK_F6] = 0x40; /* DIK_F6 */
sdl1KeyToDirectInputKeyNum[SDLK_F7] = 0x41; /* DIK_F7 */
sdl1KeyToDirectInputKeyNum[SDLK_F8] = 0x42; /* DIK_F8 */
sdl1KeyToDirectInputKeyNum[SDLK_F9] = 0x43; /* DIK_F9 */
sdl1KeyToDirectInputKeyNum[SDLK_F10] = 0x44; /* DIK_F10 */
sdl1KeyToDirectInputKeyNum[SDLK_F11] = 0x57; /* DIK_F11 */
sdl1KeyToDirectInputKeyNum[SDLK_F12] = 0x58; /* DIK_F12 */
sdl1KeyToDirectInputKeyNum[SDLK_PRINT] = 0xB7; /* DIK_F12 */
sdl1KeyToDirectInputKeyNum[SDLK_SCROLLOCK] = 0x46; /* DIK_SCROLL */
sdl1KeyToDirectInputKeyNum[SDLK_PAUSE] = 0xC5; /* DIK_PAUSE */
sdl1KeyToDirectInputKeyNum[SDLK_INSERT] = 0xD2; /* DIK_INSERT */
sdl1KeyToDirectInputKeyNum[SDLK_HOME] = 0xC7; /* DIK_HOME */
sdl1KeyToDirectInputKeyNum[SDLK_PAGEUP] = 0xC9; /* DIK_PRIOR */
sdl1KeyToDirectInputKeyNum[SDLK_DELETE] = 0xD3; /* DIK_DELETE */
sdl1KeyToDirectInputKeyNum[SDLK_END] = 0xCF; /* DIK_END */
sdl1KeyToDirectInputKeyNum[SDLK_PAGEDOWN] = 0xD1; /* DIK_NEXT */
sdl1KeyToDirectInputKeyNum[SDLK_RIGHT] = 0xCD; /* DIK_RIGHT */
sdl1KeyToDirectInputKeyNum[SDLK_LEFT] = 0xCB; /* DIK_LEFT */
sdl1KeyToDirectInputKeyNum[SDLK_DOWN] = 0xD0; /* DIK_DOWN */
sdl1KeyToDirectInputKeyNum[SDLK_UP] = 0xC8; /* DIK_UP */
sdl1KeyToDirectInputKeyNum[SDLK_LCTRL] = 0x1D; /* DIK_LCONTROL */
sdl1KeyToDirectInputKeyNum[SDLK_LSHIFT] = 0x2A; /* DIK_LSHIFT */
sdl1KeyToDirectInputKeyNum[SDLK_LALT] = 0x38; /* DIK_LMENU */
sdl1KeyToDirectInputKeyNum[SDLK_LMETA] = 0xDB; /* DIK_LWIN */
sdl1KeyToDirectInputKeyNum[SDLK_RCTRL] = 0x9D; /* DIK_RCONTROL */
sdl1KeyToDirectInputKeyNum[SDLK_RSHIFT] = 0x36; /* DIK_RSHIFT */
sdl1KeyToDirectInputKeyNum[SDLK_RALT] = 0xB8; /* DIK_RMENU */
sdl1KeyToDirectInputKeyNum[SDLK_RMETA] = 0xDC; /* DIK_RWIN */
sdl1KeyToDirectInputKeyNum[SDLK_NUMLOCK] = 0x45; /* DIK_NUMLOCK */
sdl1KeyToDirectInputKeyNum[SDLK_KP_DIVIDE] = 0xB5; /* DIK_DIVIDE */
sdl1KeyToDirectInputKeyNum[SDLK_KP_MULTIPLY] = 0x37; /* DIK_MULTIPLY */
sdl1KeyToDirectInputKeyNum[SDLK_KP_MINUS] = 0x4A; /* DIK_SUBTRACT */
sdl1KeyToDirectInputKeyNum[SDLK_KP_PLUS] = 0x4E; /* DIK_ADD */
sdl1KeyToDirectInputKeyNum[SDLK_KP_ENTER] = 0x9C; /* DIK_NUMPADENTER */
sdl1KeyToDirectInputKeyNum[SDLK_KP1] = 0x4F; /* DIK_NUMPAD1 */
sdl1KeyToDirectInputKeyNum[SDLK_KP2] = 0x50; /* DIK_NUMPAD2 */
sdl1KeyToDirectInputKeyNum[SDLK_KP3] = 0x51; /* DIK_NUMPAD3 */
sdl1KeyToDirectInputKeyNum[SDLK_KP4] = 0x4B; /* DIK_NUMPAD4 */
sdl1KeyToDirectInputKeyNum[SDLK_KP5] = 0x4C; /* DIK_NUMPAD5 */
sdl1KeyToDirectInputKeyNum[SDLK_KP6] = 0x4D; /* DIK_NUMPAD6 */
sdl1KeyToDirectInputKeyNum[SDLK_KP7] = 0x47; /* DIK_NUMPAD7 */
sdl1KeyToDirectInputKeyNum[SDLK_KP8] = 0x48; /* DIK_NUMPAD8 */
sdl1KeyToDirectInputKeyNum[SDLK_KP9] = 0x49; /* DIK_NUMPAD9 */
sdl1KeyToDirectInputKeyNum[SDLK_KP0] = 0x52; /* DIK_NUMPAD0 */
sdl1KeyToDirectInputKeyNum[SDLK_KP_PERIOD] = 0x53; /* DIK_DECIMAL */
/* 0x56 - DIK_OEM_102 */
/* 0xDD - DIK_APPS */
sdl1KeyToDirectInputKeyNum[SDLK_POWER] = 0xDE; /* DIK_POWER */
sdl1KeyToDirectInputKeyNum[SDLK_KP_EQUALS] = 0x8D; /* DIK_NUMPADEQUALS */
sdl1KeyToDirectInputKeyNum[SDLK_F13] = 0x8D; /* DIK_F13 */
sdl1KeyToDirectInputKeyNum[SDLK_F14] = 0x64; /* DIK_F14 */
sdl1KeyToDirectInputKeyNum[SDLK_F15] = 0x65; /* DIK_F15 */
sdl1KeyToDirectInputKeyNum[SDLK_SYSREQ] = 0xB7; /* SDL_SCANCODE_SYSREQ */
}
#endif /* _SDL1_SCANCODE_TO_DINPUT_H_ */

View File

@ -0,0 +1,27 @@
#ifndef sdl1_syms_h
#define sdl1_syms_h
#include <SDL.h>
#define FOREACH_SDL1_SYM(X) \
X(Init, int, (Uint32)) \
X(Quit, void, (void)) \
X(Delay, void, (Uint32)) \
X(GetTicks, Uint32, (void)) \
X(GetError, char*, (void)) \
X(PollEvent, int, (SDL_Event*)) \
X(SetVideoMode, SDL_Surface*, (int, int, int, Uint32)) \
X(FreeSurface, void, (SDL_Surface*)) \
X(LockSurface, int, (SDL_Surface*)) \
X(UnlockSurface, void, (SDL_Surface*)) \
X(UpdateRect, void, (SDL_Surface*, Sint32, Sint32, Sint32, Sint32)) \
X(WM_SetCaption, void, (const char*, const char*)) \
X(WM_ToggleFullScreen, int, (SDL_Surface*)) \
X(Flip, int, (SDL_Surface*)) \
X(ShowCursor, int, (int)) \
X(GetMouseState, Uint8, (int*, int*)) \
X(GetKeyName, char*, (SDLKey)) \
X(GL_GetProcAddress, void*, (const char*)) \
X(GL_SwapBuffers, void, (void))
#endif /* sdl1_syms_h */

View File

@ -5,22 +5,25 @@
#include "harness/hooks.h"
#include "harness/trace.h"
#include "sdl2_scancode_to_dinput.h"
#include "sdl2_syms.h"
SDL_Window* window;
SDL_Renderer* renderer;
SDL_Texture* screen_texture;
uint32_t converted_palette[256];
br_pixelmap* last_screen_src;
SDL_COMPILE_TIME_ASSERT(sdl2_platform_requires_SDL2, SDL_MAJOR_VERSION == 2);
SDL_GLContext* gl_context;
static SDL_Window* window;
static SDL_Renderer* renderer;
static SDL_Texture* screen_texture;
static uint32_t converted_palette[256];
static br_pixelmap* last_screen_src;
int render_width, render_height;
static SDL_GLContext* gl_context;
Uint32 last_frame_time;
static int render_width, render_height;
uint8_t directinput_key_state[SDL_NUM_SCANCODES];
static Uint32 last_frame_time;
struct {
static uint8_t directinput_key_state[SDL_NUM_SCANCODES];
static struct {
int x, y;
float scale_x, scale_y;
} viewport;
@ -30,6 +33,42 @@ extern void QuitGame(void);
extern uint32_t gKeyboard_bits[8];
extern br_pixelmap* gBack_screen;
#ifdef DETHRACE_SDL_DYNAMIC
#ifdef _WIN32
static const char * const possible_locations[] = {
"SDL2.dll",
};
#elif defined(__APPLE__)
#define SHARED_OBJECT_NAME "libSDL2"
#define SDL2_LIBNAME "libSDL2.dylib"
#define SDL2_FRAMEWORK "SDL2.framework/Versions/A/SDL2"
static const char * const possible_locations[] = {
"@loader_path/" SDL2_LIBNAME, /* MyApp.app/Contents/MacOS/libSDL2_dylib */
"@loader_path/../Frameworks/" SDL2_FRAMEWORK, /* MyApp.app/Contents/Frameworks/SDL2_framework */
"@executable_path/" SDL2_LIBNAME, /* MyApp.app/Contents/MacOS/libSDL2_dylib */
"@executable_path/../Frameworks/" SDL2_FRAMEWORK, /* MyApp.app/Contents/Frameworks/SDL2_framework */
NULL, /* /Users/username/Library/Frameworks/SDL2_framework */
"/Library/Frameworks" SDL2_FRAMEWORK, /* /Library/Frameworks/SDL2_framework */
SDL2_LIBNAME /* oh well, anywhere the system can see the .dylib (/usr/local/lib or whatever) */
};
#else
static const char * const possible_locations[] = {
"libSDL2-2.0.so.0",
"libSDL2-2.0.so",
};
#endif
#endif
#ifdef DETHRACE_SDL_DYNAMIC
static void *sdl2_so;
#endif
#define OBJECT_NAME sdl2_so
#define SYMBOL_PREFIX SDL2_
#define FOREACH_SDLX_SYM FOREACH_SDL2_SYM
#include "sdl_dyn_common.h"
static void calculate_viewport(int window_width, int window_height) {
int vp_width, vp_height;
float target_aspect_ratio;
@ -53,20 +92,20 @@ static void calculate_viewport(int window_width, int window_height) {
viewport.scale_y = (float)vp_height / gBack_screen->height;
}
static int set_window_pos(void* hWnd, int x, int y, int nWidth, int nHeight) {
static int SDL2_Harness_SetWindowPos(void* hWnd, int x, int y, int nWidth, int nHeight) {
// SDL_SetWindowPosition(hWnd, x, y);
if (nWidth == 320 && nHeight == 200) {
nWidth = 640;
nHeight = 400;
}
SDL_SetWindowSize(hWnd, nWidth, nHeight);
SDL2_SetWindowSize(hWnd, nWidth, nHeight);
return 0;
}
static void destroy_window(void* hWnd) {
// SDL_GL_DeleteContext(context);
SDL_DestroyWindow(window);
SDL_Quit();
static void SDL2_Harness_DestroyWindow(void* hWnd) {
// SDL2_GL_DeleteContext(context);
SDL2_DestroyWindow(window);
SDL2_Quit();
window = NULL;
}
@ -76,15 +115,15 @@ static int is_only_key_modifier(int modifier_flags, int flag_check) {
return (modifier_flags & flag_check) && (modifier_flags & (KMOD_CTRL | KMOD_SHIFT | KMOD_ALT | KMOD_GUI)) == (modifier_flags & flag_check);
}
static void get_and_handle_message(MSG_* msg) {
static void SDL2_Harness_ProcessWindowMessages(MSG_* msg) {
SDL_Event event;
int dinput_key;
while (SDL_PollEvent(&event)) {
while (SDL2_PollEvent(&event)) {
switch (event.type) {
case SDL_KEYDOWN:
case SDL_KEYUP:
if (event.key.windowID != SDL_GetWindowID(window)) {
if (event.key.windowID != SDL2_GetWindowID(window)) {
continue;
}
if (event.key.keysym.sym == SDLK_RETURN) {
@ -95,7 +134,7 @@ static void get_and_handle_message(MSG_* msg) {
}
} else if (event.key.type == SDL_KEYUP) {
if (is_only_key_modifier(event.key.keysym.mod, KMOD_ALT)) {
SDL_SetWindowFullscreen(window, (SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN_DESKTOP) ? 0 : SDL_WINDOW_FULLSCREEN_DESKTOP);
SDL2_SetWindowFullscreen(window, (SDL2_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN_DESKTOP) ? 0 : SDL_WINDOW_FULLSCREEN_DESKTOP);
}
}
}
@ -104,7 +143,7 @@ static void get_and_handle_message(MSG_* msg) {
// https://github.com/DanielGibson/Snippets/blob/master/sdl2_scancode_to_dinput.h
dinput_key = sdlScanCodeToDirectInputKeyNum[event.key.keysym.scancode];
if (dinput_key == 0) {
LOG_WARN("unexpected scan code %s (%d)", SDL_GetScancodeName(event.key.keysym.scancode), event.key.keysym.scancode);
LOG_WARN("unexpected scan code %s (%d)", SDL2_GetScancodeName(event.key.keysym.scancode), event.key.keysym.scancode);
return;
}
// DInput expects high bit to be set if key is down
@ -129,35 +168,35 @@ static void get_and_handle_message(MSG_* msg) {
}
}
static void get_keyboard_state(unsigned int count, uint8_t* buffer) {
static void SDL2_Harness_GetKeyboardState(unsigned int count, uint8_t* buffer) {
memcpy(buffer, directinput_key_state, count);
}
static int get_mouse_buttons(int* pButton1, int* pButton2) {
if (SDL_GetMouseFocus() != window) {
static int SDL2_Harness_GetMouseButtons(int* pButton1, int* pButton2) {
if (SDL2_GetMouseFocus() != window) {
*pButton1 = 0;
*pButton2 = 0;
return 0;
}
int state = SDL_GetMouseState(NULL, NULL);
int state = SDL2_GetMouseState(NULL, NULL);
*pButton1 = state & SDL_BUTTON_LMASK;
*pButton2 = state & SDL_BUTTON_RMASK;
return 0;
}
static int get_mouse_position(int* pX, int* pY) {
static int SDL2_Harness_GetMousePosition(int* pX, int* pY) {
int window_width, window_height;
float lX, lY;
if (SDL_GetMouseFocus() != window) {
if (SDL2_GetMouseFocus() != window) {
return 0;
}
SDL_GetWindowSize(window, &window_width, &window_height);
SDL2_GetWindowSize(window, &window_width, &window_height);
SDL_GetMouseState(pX, pY);
SDL2_GetMouseState(pX, pY);
if (renderer != NULL) {
// software renderer
SDL_RenderWindowToLogical(renderer, *pX, *pY, &lX, &lY);
SDL2_RenderWindowToLogical(renderer, *pX, *pY, &lX, &lY);
} else {
// hardware renderer
// handle case where window is stretched larger than the pixel size
@ -170,7 +209,7 @@ static int get_mouse_position(int* pX, int* pY) {
}
static void limit_fps(void) {
Uint32 now = SDL_GetTicks();
Uint32 now = SDL2_GetTicks();
if (last_frame_time != 0) {
unsigned int frame_time = now - last_frame_time;
last_frame_time = now;
@ -181,16 +220,16 @@ static void limit_fps(void) {
}
}
}
last_frame_time = SDL_GetTicks();
last_frame_time = SDL2_GetTicks();
}
int show_error_message(void* window, char* text, char* caption) {
static int SDL2_Harness_ShowErrorMessage(void* window, char* text, char* caption) {
fprintf(stderr, "%s", text);
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, caption, text, window);
SDL2_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, caption, text, window);
return 0;
}
static void create_window(char* title, int width, int height, tHarness_window_type window_type) {
static void SDL2_Harness_CreateWindow(const char* title, int width, int height, tHarness_window_type window_type) {
int window_width, window_height;
render_width = width;
@ -205,68 +244,68 @@ static void create_window(char* title, int width, int height, tHarness_window_ty
window_height = 480;
}
if (SDL_Init(SDL_INIT_VIDEO) != 0) {
LOG_PANIC("SDL_INIT_VIDEO error: %s", SDL_GetError());
if (SDL2_Init(SDL_INIT_VIDEO) != 0) {
LOG_PANIC("SDL_INIT_VIDEO error: %s", SDL2_GetError());
}
if (window_type == eWindow_type_opengl) {
window = SDL_CreateWindow(title,
window = SDL2_CreateWindow(title,
SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED,
window_width, window_height,
SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
if (window == NULL) {
LOG_PANIC("Failed to create window: %s", SDL_GetError());
LOG_PANIC("Failed to create window: %s", SDL2_GetError());
}
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
gl_context = SDL_GL_CreateContext(window);
SDL2_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
SDL2_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL2_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
gl_context = SDL2_GL_CreateContext(window);
if (gl_context == NULL) {
LOG_WARN("Failed to create OpenGL core profile: %s. Trying OpenGLES...", SDL_GetError());
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
gl_context = SDL_GL_CreateContext(window);
LOG_WARN("Failed to create OpenGL core profile: %s. Trying OpenGLES...", SDL2_GetError());
SDL2_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
SDL2_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL2_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
gl_context = SDL2_GL_CreateContext(window);
}
if (gl_context == NULL) {
LOG_PANIC("Failed to create OpenGL context: %s", SDL_GetError());
LOG_PANIC("Failed to create OpenGL context: %s", SDL2_GetError());
}
SDL_GL_SetSwapInterval(1);
SDL2_GL_SetSwapInterval(1);
} else {
window = SDL_CreateWindow(title,
window = SDL2_CreateWindow(title,
SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED,
window_width, window_height,
SDL_WINDOW_RESIZABLE);
if (window == NULL) {
LOG_PANIC("Failed to create window: %s", SDL_GetError());
LOG_PANIC("Failed to create window: %s", SDL2_GetError());
}
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_PRESENTVSYNC);
renderer = SDL2_CreateRenderer(window, -1, SDL_RENDERER_PRESENTVSYNC);
if (renderer == NULL) {
LOG_PANIC("Failed to create renderer: %s", SDL_GetError());
LOG_PANIC("Failed to create renderer: %s", SDL2_GetError());
}
SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_NONE);
SDL_RenderSetLogicalSize(renderer, render_width, render_height);
SDL2_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_NONE);
SDL2_RenderSetLogicalSize(renderer, render_width, render_height);
screen_texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, width, height);
screen_texture = SDL2_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, width, height);
if (screen_texture == NULL) {
SDL_RendererInfo info;
SDL_GetRendererInfo(renderer, &info);
SDL2_GetRendererInfo(renderer, &info);
for (Uint32 i = 0; i < info.num_texture_formats; i++) {
LOG_INFO("%s\n", SDL_GetPixelFormatName(info.texture_formats[i]));
LOG_INFO("%s\n", SDL2_GetPixelFormatName(info.texture_formats[i]));
}
LOG_PANIC("Failed to create screen_texture: %s", SDL_GetError());
LOG_PANIC("Failed to create screen_texture: %s", SDL2_GetError());
}
}
SDL_ShowCursor(SDL_DISABLE);
SDL2_ShowCursor(SDL_DISABLE);
viewport.x = 0;
viewport.y = 0;
@ -274,30 +313,31 @@ static void create_window(char* title, int width, int height, tHarness_window_ty
viewport.scale_y = 1;
if (harness_game_config.start_full_screen) {
SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN_DESKTOP);
SDL2_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN_DESKTOP);
}
}
static void swap(br_pixelmap* back_buffer) {
uint8_t* src_pixels = back_buffer->pixels;
uint32_t* dest_pixels;
int dest_pitch;
static void SDL2_Harness_Swap(br_pixelmap* back_buffer) {
get_and_handle_message(NULL);
SDL2_Harness_ProcessWindowMessages(NULL);
if (gl_context != NULL) {
SDL_GL_SwapWindow(window);
SDL2_GL_SwapWindow(window);
} else {
SDL_LockTexture(screen_texture, NULL, (void**)&dest_pixels, &dest_pitch);
uint8_t* src_pixels = back_buffer->pixels;
uint32_t* dest_pixels;
int dest_pitch;
SDL2_LockTexture(screen_texture, NULL, (void**)&dest_pixels, &dest_pitch);
for (int i = 0; i < back_buffer->height * back_buffer->width; i++) {
*dest_pixels = converted_palette[*src_pixels];
dest_pixels++;
src_pixels++;
}
SDL_UnlockTexture(screen_texture);
SDL_RenderClear(renderer);
SDL_RenderCopy(renderer, screen_texture, NULL, NULL);
SDL_RenderPresent(renderer);
SDL2_UnlockTexture(screen_texture);
SDL2_RenderClear(renderer);
SDL2_RenderCopy(renderer, screen_texture, NULL, NULL);
SDL2_RenderPresent(renderer);
last_screen_src = back_buffer;
}
@ -306,37 +346,48 @@ static void swap(br_pixelmap* back_buffer) {
}
}
static void palette_changed(br_colour entries[256]) {
static void SDL2_Harness_PaletteChanged(br_colour entries[256]) {
for (int i = 0; i < 256; i++) {
converted_palette[i] = (0xff << 24 | BR_RED(entries[i]) << 16 | BR_GRN(entries[i]) << 8 | BR_BLU(entries[i]));
}
if (last_screen_src != NULL) {
swap(last_screen_src);
SDL2_Harness_Swap(last_screen_src);
}
}
static void get_viewport(int* x, int* y, float* width_multipler, float* height_multiplier) {
static void SDL2_Harness_GetViewport(int* x, int* y, float* width_multipler, float* height_multiplier) {
*x = viewport.x;
*y = viewport.y;
*width_multipler = viewport.scale_x;
*height_multiplier = viewport.scale_y;
}
void Harness_Platform_Init(tHarness_platform* platform) {
platform->ProcessWindowMessages = get_and_handle_message;
platform->Sleep = SDL_Delay;
platform->GetTicks = SDL_GetTicks;
platform->ShowCursor = SDL_ShowCursor;
platform->SetWindowPos = set_window_pos;
platform->DestroyWindow = destroy_window;
platform->GetKeyboardState = get_keyboard_state;
platform->GetMousePosition = get_mouse_position;
platform->GetMouseButtons = get_mouse_buttons;
platform->ShowErrorMessage = show_error_message;
static int SDL2_Harness_Platform_Init(tHarness_platform* platform) {
if (SDL2_LoadSymbols() != 0) {
return 1;
}
platform->ProcessWindowMessages = SDL2_Harness_ProcessWindowMessages;
platform->Sleep = SDL2_Delay;
platform->GetTicks = SDL2_GetTicks;
platform->ShowCursor = SDL2_ShowCursor;
platform->SetWindowPos = SDL2_Harness_SetWindowPos;
platform->DestroyWindow = SDL2_Harness_DestroyWindow;
platform->GetKeyboardState = SDL2_Harness_GetKeyboardState;
platform->GetMousePosition = SDL2_Harness_GetMousePosition;
platform->GetMouseButtons = SDL2_Harness_GetMouseButtons;
platform->ShowErrorMessage = SDL2_Harness_ShowErrorMessage;
platform->CreateWindow_ = create_window;
platform->Swap = swap;
platform->PaletteChanged = palette_changed;
platform->GL_GetProcAddress = SDL_GL_GetProcAddress;
platform->GetViewport = get_viewport;
}
platform->CreateWindow_ = SDL2_Harness_CreateWindow;
platform->Swap = SDL2_Harness_Swap;
platform->PaletteChanged = SDL2_Harness_PaletteChanged;
platform->GL_GetProcAddress = SDL2_GL_GetProcAddress;
platform->GetViewport = SDL2_Harness_GetViewport;
return 0;
};
const tPlatform_bootstrap SDL2_bootstrap = {
"sdl2",
"SDL2 video backend (libsdl.org)",
ePlatform_cap_software | ePlatform_cap_opengl,
SDL2_Harness_Platform_Init,
};

View File

@ -0,0 +1,45 @@
#ifndef sdl2_syms_h
#define sdl2_syms_h
#include <SDL.h>
#define FOREACH_SDL2_SYM(X) \
X(Init, int, (Uint32)) \
X(Quit, void, (void)) \
X(Delay, void, (Uint32)) \
X(GetTicks, Uint32, (void)) \
X(GetError, const char*, (void)) \
X(PollEvent, int, (SDL_Event*)) \
X(ShowSimpleMessageBox, int, (Uint32, const char*, const char *, SDL_Window*)) \
X(CreateWindow, SDL_Window*, (const char*, int, int, int, int, Uint32)) \
X(DestroyWindow, void, (SDL_Window*)) \
X(GetWindowFlags, Uint32, (SDL_Window*)) \
X(GetWindowID, Uint32, (SDL_Window*)) \
X(GetWindowSize, void, (SDL_Window*, int*, int*)) \
X(SetWindowFullscreen, int, (SDL_Window*, Uint32)) \
X(SetWindowSize, void, (SDL_Window*, int, int)) \
X(CreateRenderer, SDL_Renderer*, (SDL_Window*, int, Uint32)) \
X(RenderClear, int, (SDL_Renderer*)) \
X(RenderCopy, int, (SDL_Renderer*,SDL_Texture*, const SDL_Rect*, const SDL_Rect*)) \
X(RenderPresent, void, (SDL_Renderer*)) \
X(RenderWindowToLogical, void, (SDL_Renderer*, int, int, float*, float*)) \
X(GetRendererInfo, int, (SDL_Renderer*, SDL_RendererInfo*)) \
X(RenderSetLogicalSize, int, (SDL_Renderer*, int, int)) \
X(SetRenderDrawBlendMode, int, (SDL_Renderer*, SDL_BlendMode)) \
X(CreateTexture, SDL_Texture*, (SDL_Renderer*, Uint32, int, int, int)) \
X(LockTexture, int, (SDL_Texture*, const SDL_Rect*, void**, int*)) \
X(UnlockTexture, void, (SDL_Texture*)) \
X(GetMouseFocus, SDL_Window*, (void)) \
X(GetMouseState, Uint32, (int*, int*)) \
X(ShowCursor, int, (int)) \
X(GetPixelFormatName, const char*, (Uint32)) \
X(GetScancodeName, const char *, (SDL_Scancode)) \
X(GL_CreateContext, SDL_GLContext, (SDL_Window*)) \
X(GL_GetProcAddress, void*, (const char*)) \
X(GL_SetAttribute, int, (SDL_GLattr, int)) \
X(GL_SetSwapInterval, int, (int)) \
X(GL_SwapWindow, void, (SDL_Window*))
#undef SDL2_SYM
#endif /* sdl2_syms_h */

View File

@ -0,0 +1,72 @@
#ifndef sdl_dyn_common_h
#define sdl_dyn_common_h
#ifdef DETHRACE_SDL_DYNAMIC
#ifdef _WIN32
#include <windows.h>
#ifdef CreateWindow
#undef CreateWindow
#endif
static void *Harness_LoadObject(const char *name) {
return LoadLibraryA(name);
}
static void Harness_UnloadObject(void *obj) {
FreeLibrary(obj);
}
static void *Harness_LoadFunction(void *obj, const char *name) {
return GetProcAddress(obj, name);
}
#else
#include <dlfcn.h>
static void *Harness_LoadObject(const char *name) {
return dlopen(name, RTLD_NOW | RTLD_LOCAL);
}
static void Harness_UnloadObject(void *obj) {
dlclose(obj);
}
static void *Harness_LoadFunction(void *obj, const char *name) {
return dlsym(obj, name);
}
#endif
#endif
#define STR2_JOIN(A,B) A##B
#define STR_JOIN(A,B) STR2_JOIN(A, B)
#define X_TYPEDEF(name, ret, args) typedef ret SDLCALL t##SDL_##name##_fn args;
#define X_STATIC_SYMBOL(name, ret, args) static t##SDL_##name##_fn* STR_JOIN(SYMBOL_PREFIX, name);
#ifdef DETHRACE_SDL_DYNAMIC
#define X_LOAD_FUNCTION(name, ret, args) \
STR_JOIN(SYMBOL_PREFIX, name) = Harness_LoadFunction(OBJECT_NAME, "SDL_" #name); \
if (STR_JOIN(SYMBOL_PREFIX, name) == NULL) { \
goto failure; \
}
#else
#define X_LOAD_FUNCTION(name, ret, args) STR_JOIN(SYMBOL_PREFIX, name) = SDL_##name;
#endif
FOREACH_SDLX_SYM(X_TYPEDEF)
FOREACH_SDLX_SYM(X_STATIC_SYMBOL)
static int STR_JOIN(SYMBOL_PREFIX,LoadSymbols)(void) {
#ifdef DETHRACE_SDL_DYNAMIC
for (size_t i = 0; i < BR_ASIZE(possible_locations); i++) {
OBJECT_NAME = Harness_LoadObject(possible_locations[i]);
if (OBJECT_NAME != NULL) {
break;
}
}
if (OBJECT_NAME == NULL) {
return 1;
}
#endif
FOREACH_SDLX_SYM(X_LOAD_FUNCTION)
return 0;
#ifdef DETHRACE_SDL_DYNAMIC
failure:
Harness_UnloadObject(OBJECT_NAME);
return 1;
#endif
}
#endif /* sdl_dyn_common_h */

View File

@ -1,8 +1,6 @@
add_executable(dethrace_test)
add_test(NAME test_dethrace COMMAND dethrace_test)
set(IO_PLATFORM "SDL2")
target_link_libraries(dethrace_test PRIVATE dethrace_obj)
target_include_directories(dethrace_test PRIVATE