From 077e71850f89fb4585f80a09231218da64f8a165 Mon Sep 17 00:00:00 2001 From: Hougant Chen Date: Thu, 4 Dec 2025 20:38:31 -0500 Subject: [PATCH 1/2] Add OpenXR loader init properties support - Support custom loader properties via lovr.conf initproperties table - Fix null terminator buffer overflow in extension string allocation - LOVR_BUILD_WITH_SYMBOLS now exports all symbols on Windows - Add NVIDIA to contributors --- CMakeLists.txt | 3 +++ README.md | 1 + src/api/l_headset.c | 33 +++++++++++++++++++++++-- src/modules/headset/headset.c | 45 +++++++++++++++++++++++++++++++++++ src/modules/headset/headset.h | 23 ++++++++++++++++++ 5 files changed, 103 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 654814bfb..aad846290 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -289,6 +289,9 @@ endif() if(LOVR_BUILD_WITH_SYMBOLS) set_target_properties(lovr PROPERTIES C_VISIBILITY_PRESET "default") + if(WIN32) + set_target_properties(lovr PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON) + endif() else() set_target_properties(lovr PROPERTIES C_VISIBILITY_PRESET "hidden") endif() diff --git a/README.md b/README.md index 77b46af14..8412dce3b 100644 --- a/README.md +++ b/README.md @@ -132,6 +132,7 @@ Contributors - [@porglezomp](https://github.com/porglezomp) - [@jmiskovic](https://github.com/jmiskovic) - [@wallbraker](https://github.com/wallbraker) +- NVIDIA Corporation & Affiliates License --- diff --git a/src/api/l_headset.c b/src/api/l_headset.c index f20c90469..32ad306c0 100644 --- a/src/api/l_headset.c +++ b/src/api/l_headset.c @@ -1126,8 +1126,8 @@ int luaopen_lovr_headset(lua_State* L) { lua_call(L, 2, 1); size_t length; const char* string = lua_tolstring(L, -1, &length); - char* extensions = lovrMalloc(length); - memcpy(extensions, string, length); + char* extensions = lovrMalloc(length + 1); + memcpy(extensions, string, length + 1); config.extensionCount = count; config.extensions = extensions; } @@ -1135,6 +1135,35 @@ int luaopen_lovr_headset(lua_State* L) { } } lua_pop(L, 1); + + lua_getfield(L, -1, "initproperties"); + if (lua_istable(L, -1)) { + int count = luax_len(L, -1); + if (count > 0) { + InitProperty* properties = lovrMalloc(count * sizeof(InitProperty)); + for (int i = 0; i < count; i++) { + lua_rawgeti(L, -1, i + 1); + if (lua_istable(L, -1)) { + lua_getfield(L, -1, "name"); + const char* name = lua_tostring(L, -1); + properties[i].name = name ? lovrStrdup(name) : NULL; + lua_pop(L, 1); + + lua_getfield(L, -1, "value"); + const char* value = lua_tostring(L, -1); + properties[i].value = value ? lovrStrdup(value) : NULL; + lua_pop(L, 1); + } else { + properties[i].name = NULL; + properties[i].value = NULL; + } + lua_pop(L, 1); + } + config.initPropertyCount = count; + config.initProperties = properties; + } + } + lua_pop(L, 1); } lua_pop(L, 1); } diff --git a/src/modules/headset/headset.c b/src/modules/headset/headset.c index c26faa229..5cebc7462 100644 --- a/src/modules/headset/headset.c +++ b/src/modules/headset/headset.c @@ -370,6 +370,13 @@ static bool loadVisibilityMask(void); bool lovrHeadsetInit(HeadsetConfig* config) { if (state.initialized) { lovrFree(config->extensions); + if (config->initProperties) { + for (uint32_t i = 0; i < config->initPropertyCount; i++) { + lovrFree(config->initProperties[i].name); + lovrFree(config->initProperties[i].value); + } + lovrFree(config->initProperties); + } return true; } @@ -397,6 +404,13 @@ bool lovrHeadsetInit(HeadsetConfig* config) { void lovrHeadsetDestroy(void) { disconnect(); lovrFree(state.config.extensions); + if (state.config.initProperties) { + for (uint32_t i = 0; i < state.config.initPropertyCount; i++) { + lovrFree(state.config.initProperties[i].name); + lovrFree(state.config.initProperties[i].value); + } + lovrFree(state.config.initProperties); + } Simulator simulator = state.simulator; // Keep simulator state between restarts, for convenience memset(&state, 0, sizeof(state)); state.simulator = simulator; @@ -437,6 +451,37 @@ bool lovrHeadsetConnect(void) { } #endif + // Initialize loader with properties (for custom runtime paths, etc.) +#if defined(__linux__) || defined(__APPLE__) || defined(_WIN32) + if (state.config.initPropertyCount > 0 && state.config.initProperties) { + static PFN_xrInitializeLoaderKHR xrInitializeLoaderKHR; + XR_LOAD(xrInitializeLoaderKHR); + lovrAssert(xrInitializeLoaderKHR, "Failed to initialize loader"); + + // Build property list from config + XrLoaderInitPropertyValueEXT* propertyList = lovrMalloc(state.config.initPropertyCount * sizeof(XrLoaderInitPropertyValueEXT)); + for (uint32_t i = 0; i < state.config.initPropertyCount; i++) { + propertyList[i].name = state.config.initProperties[i].name; + propertyList[i].value = state.config.initProperties[i].value; + } + + XrLoaderInitInfoPropertiesEXT loaderProperties = { + .type = XR_TYPE_LOADER_INIT_INFO_PROPERTIES_EXT, + .next = NULL, + .propertyValueCount = state.config.initPropertyCount, + .propertyValues = propertyList + }; + + XrResult initResult = xrInitializeLoaderKHR((XrLoaderInitInfoBaseHeaderKHR*) &loaderProperties); + lovrFree(propertyList); + + if (XR_FAILED(initResult)) { + lovrLog(LOG_ERROR, "OpenXR", "Failed to initialize loader with properties: %d", initResult); + return false; + } + } +#endif + // Extensions uint32_t extensionCount = 0; diff --git a/src/modules/headset/headset.h b/src/modules/headset/headset.h index 94e822987..39944ae1a 100644 --- a/src/modules/headset/headset.h +++ b/src/modules/headset/headset.h @@ -4,6 +4,17 @@ #pragma once +// Export/import macros for plugin functions +#ifdef _WIN32 + #ifdef LOVR_BUILDING_PLUGIN + #define LOVR_PLUGIN_IMPORT __declspec(dllimport) + #else + #define LOVR_PLUGIN_IMPORT __declspec(dllexport) + #endif +#else + #define LOVR_PLUGIN_IMPORT __attribute__((visibility("default"))) +#endif + #define HAND_JOINT_COUNT 26 #define MAX_LAYERS 10 @@ -20,6 +31,11 @@ typedef enum { SKELETON_NATURAL } ControllerSkeletonMode; +typedef struct { + char* name; + char* value; +} InitProperty; + typedef struct { float supersample; bool debug; @@ -33,6 +49,8 @@ typedef struct { ControllerSkeletonMode controllerSkeleton; uint32_t extensionCount; char* extensions; + uint32_t initPropertyCount; + InitProperty* initProperties; } HeadsetConfig; typedef struct { @@ -252,3 +270,8 @@ bool lovrHeadsetIsSupported(void); void lovrHeadsetGetVulkanPhysicalDevice(void* instance, uintptr_t physicalDevice); uint32_t lovrHeadsetCreateVulkanInstance(void* instanceCreateInfo, void* allocator, uintptr_t instance, void* getInstanceProcAddr); uint32_t lovrHeadsetCreateVulkanDevice(void* instance, void* deviceCreateInfo, void* allocatoor, uintptr_t device, void* getInstanceProcAddr); + +// OpenXR handle accessors (for plugins) +LOVR_PLUGIN_IMPORT uintptr_t xr_get_instance(void); +LOVR_PLUGIN_IMPORT uintptr_t xr_get_system(void); +LOVR_PLUGIN_IMPORT uintptr_t xr_get_session(void); From b48e63f1108e7941fe8e4609b84a78b2cdcc9d5f Mon Sep 17 00:00:00 2001 From: Hougant Chen Date: Thu, 4 Dec 2025 21:08:49 -0500 Subject: [PATCH 2/2] Make symbol visibility consistent --- src/modules/headset/headset.c | 12 +++--------- src/modules/headset/headset.h | 16 ++++++++-------- 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/src/modules/headset/headset.c b/src/modules/headset/headset.c index 5cebc7462..0af58786b 100644 --- a/src/modules/headset/headset.c +++ b/src/modules/headset/headset.c @@ -4214,20 +4214,14 @@ static bool loadVisibilityMask(void) { return true; } -#ifdef _WIN32 -#define EXPORT __declspec(dllexport) -#else -#define EXPORT __attribute__((visibility("default"))) -#endif - -EXPORT uintptr_t xr_get_instance(void) { +LOVR_API uintptr_t xr_get_instance(void) { return (uintptr_t) state.instance; } -EXPORT uintptr_t xr_get_system(void) { +LOVR_API uintptr_t xr_get_system(void) { return (uintptr_t) state.system; } -EXPORT uintptr_t xr_get_session(void) { +LOVR_API uintptr_t xr_get_session(void) { return (uintptr_t) state.session; } diff --git a/src/modules/headset/headset.h b/src/modules/headset/headset.h index 39944ae1a..cc3098ba2 100644 --- a/src/modules/headset/headset.h +++ b/src/modules/headset/headset.h @@ -4,15 +4,15 @@ #pragma once -// Export/import macros for plugin functions +// API export/import macro for plugin functions #ifdef _WIN32 - #ifdef LOVR_BUILDING_PLUGIN - #define LOVR_PLUGIN_IMPORT __declspec(dllimport) + #ifdef LOVR_API_IMPORT + #define LOVR_API __declspec(dllimport) #else - #define LOVR_PLUGIN_IMPORT __declspec(dllexport) + #define LOVR_API __declspec(dllexport) #endif #else - #define LOVR_PLUGIN_IMPORT __attribute__((visibility("default"))) + #define LOVR_API __attribute__((visibility("default"))) #endif #define HAND_JOINT_COUNT 26 @@ -272,6 +272,6 @@ uint32_t lovrHeadsetCreateVulkanInstance(void* instanceCreateInfo, void* allocat uint32_t lovrHeadsetCreateVulkanDevice(void* instance, void* deviceCreateInfo, void* allocatoor, uintptr_t device, void* getInstanceProcAddr); // OpenXR handle accessors (for plugins) -LOVR_PLUGIN_IMPORT uintptr_t xr_get_instance(void); -LOVR_PLUGIN_IMPORT uintptr_t xr_get_system(void); -LOVR_PLUGIN_IMPORT uintptr_t xr_get_session(void); +LOVR_API uintptr_t xr_get_instance(void); +LOVR_API uintptr_t xr_get_system(void); +LOVR_API uintptr_t xr_get_session(void);