diff --git a/include/SDL3/SDL_events.h b/include/SDL3/SDL_events.h index dd822ecc71..0a3827571f 100644 --- a/include/SDL3/SDL_events.h +++ b/include/SDL3/SDL_events.h @@ -163,9 +163,9 @@ typedef enum SDL_EventType associated with the window. Otherwise, the handle has already been destroyed and all resources associated with it are invalid */ SDL_EVENT_WINDOW_HDR_STATE_CHANGED, /**< Window HDR properties have changed */ - SDL_EVENT_WINDOW_CURVATURE_CHANGED, /**< Window curvature has changed to data1 (on visionOS) */ + SDL_EVENT_WINDOW_SETTINGS_CHANGED, /**< Window settings have changed (on visionOS) */ SDL_EVENT_WINDOW_FIRST = SDL_EVENT_WINDOW_SHOWN, - SDL_EVENT_WINDOW_LAST = SDL_EVENT_WINDOW_CURVATURE_CHANGED, + SDL_EVENT_WINDOW_LAST = SDL_EVENT_WINDOW_SETTINGS_CHANGED, /* Keyboard events */ SDL_EVENT_KEY_DOWN = 0x300, /**< Key pressed */ diff --git a/include/SDL3/SDL_video.h b/include/SDL3/SDL_video.h index 821c176101..1eeb7db6cd 100644 --- a/include/SDL3/SDL_video.h +++ b/include/SDL3/SDL_video.h @@ -1386,11 +1386,7 @@ extern SDL_DECLSPEC SDL_Window * SDLCALL SDL_CreatePopupWindow(SDL_Window *paren * * These are additional supported properties with visionOS: * - * - `SDL_PROP_WINDOW_CREATE_CURVATURE_FLOAT`: the curvature of the window on - * visionOS. Curved windows have square corners and additional controls for - * more immersive gaming. This can be -1 (disabled), which is the default, 0 - * (no curve), or set to a specific curvature radius in millimeters. A - * common value for a gaming monitor is 1000. + * - `SDL_PROP_WINDOW_CREATE_VISIONOS_SETTINGS_STRING`: the settings of the window in JSON format. If this isn't set, the window will have standard UIKit behavior. If this is set to "" or a valid setting string then the window is created with enhanced features allowing curved display. The curvature in the settings is defined as a radius in millimeters. A common value for a gaming monitor is 1000 and a setting string for that would be "{\"curvatureRadius\":1000}". * * If this window is being created to be used with an SDL_Renderer, you should * not add a graphics API specific property @@ -1454,7 +1450,7 @@ extern SDL_DECLSPEC SDL_Window * SDLCALL SDL_CreateWindowWithProperties(SDL_Prop #define SDL_PROP_WINDOW_CREATE_X11_WINDOW_NUMBER "SDL.window.create.x11.window" #define SDL_PROP_WINDOW_CREATE_EMSCRIPTEN_CANVAS_ID_STRING "SDL.window.create.emscripten.canvas_id" #define SDL_PROP_WINDOW_CREATE_EMSCRIPTEN_KEYBOARD_ELEMENT_STRING "SDL.window.create.emscripten.keyboard_element" -#define SDL_PROP_WINDOW_CREATE_CURVATURE_FLOAT "SDL.window.create.curvature" +#define SDL_PROP_WINDOW_CREATE_VISIONOS_SETTINGS_STRING "SDL.window.create.visionos.settings" /** * Get the numeric ID of a window. @@ -1635,10 +1631,7 @@ extern SDL_DECLSPEC SDL_Window * SDLCALL SDL_GetWindowParent(SDL_Window *window) * * On visionOS: * - * - `SDL_PROP_WINDOW_CURVATURE_FLOAT`: the curvature of the window in curved - * mode on visionOS. This value is updated dynamically when changed via the - * screen ornaments. This can be 0 (no curve), or a specific curvature - * radius in millimeters. A common value for a gaming monitor is 1000. + * - `SDL_PROP_WINDOW_VISIONOS_SETTINGS_STRING`: the current settings of the window in JSON format, or NULL if the window has standard UIKit behavior. SDL_EVENT_WINDOW_SETTINGS_CHANGED is sent when this value changes. * * \param window the window to query. * \returns a valid property ID on success or 0 on failure; call @@ -1689,7 +1682,7 @@ extern SDL_DECLSPEC SDL_PropertiesID SDLCALL SDL_GetWindowProperties(SDL_Window #define SDL_PROP_WINDOW_X11_WINDOW_NUMBER "SDL.window.x11.window" #define SDL_PROP_WINDOW_EMSCRIPTEN_CANVAS_ID_STRING "SDL.window.emscripten.canvas_id" #define SDL_PROP_WINDOW_EMSCRIPTEN_KEYBOARD_ELEMENT_STRING "SDL.window.emscripten.keyboard_element" -#define SDL_PROP_WINDOW_CURVATURE_FLOAT "SDL.window.curvature" +#define SDL_PROP_WINDOW_VISIONOS_SETTINGS_STRING "SDL.window.visionos.settings" /** * Get the window flags. diff --git a/src/events/SDL_events.c b/src/events/SDL_events.c index 19f5a85adc..e2ef1a35f3 100644 --- a/src/events/SDL_events.c +++ b/src/events/SDL_events.c @@ -565,7 +565,7 @@ int SDL_GetEventDescription(const SDL_Event *event, char *buf, int buflen) SDL_WINDOWEVENT_CASE(SDL_EVENT_WINDOW_LEAVE_FULLSCREEN); SDL_WINDOWEVENT_CASE(SDL_EVENT_WINDOW_DESTROYED); SDL_WINDOWEVENT_CASE(SDL_EVENT_WINDOW_HDR_STATE_CHANGED); - SDL_WINDOWEVENT_CASE(SDL_EVENT_WINDOW_CURVATURE_CHANGED); + SDL_WINDOWEVENT_CASE(SDL_EVENT_WINDOW_SETTINGS_CHANGED); #undef SDL_WINDOWEVENT_CASE #define PRINT_KEYDEV_EVENT(event) (void)SDL_snprintf(details, sizeof(details), " (timestamp=%" SDL_PRIu64 " which=%u)", event->kdevice.timestamp, (uint)event->kdevice.which) diff --git a/src/video/uikit/SDL_CurvedContentHosting.swift b/src/video/uikit/SDL_CurvedContentHosting.swift index d88b43680e..d8dccaf3bd 100644 --- a/src/video/uikit/SDL_CurvedContentHosting.swift +++ b/src/video/uikit/SDL_CurvedContentHosting.swift @@ -93,7 +93,7 @@ internal class SDL_ClearHostingController: UIHostingController SDL_CurvedContentSettings { + let settings = SDL_CurvedContentSettings() + if let json = SDL_VisionOS_GetWindowSettings() { + if json != "", let data = json.data(using: .utf8) { + do { + let values = try JSONDecoder().decode(SDL_CurvedContentPersistentSettings.self, from:data) + if let inputType = values.inputType { + settings.inputType = inputType + } + if let showHover = values.showHover { + settings.showHover = showHover + } + if let isDimmed = values.isDimmed { + settings.isDimmed = isDimmed + } + if let curvatureRadius = values.curvatureRadius { + settings.curvatureRadius = curvatureRadius + } + } catch { + NSLog("Couldn't parse window settings: %@", error.localizedDescription) + } + } + } + return settings } - enum InputType { - case eyes - case pointer + func save() { + let values = SDL_CurvedContentPersistentSettings() + values.inputType = inputType + values.showHover = showHover + values.isDimmed = isDimmed + values.curvatureRadius = curvatureRadius + + do { + let data = try JSONEncoder().encode(values) + let json = String(data: data, encoding: String.Encoding.utf8) + SDL_VisionOS_SendWindowSettings(json) + } catch { + NSLog("Couldn't encode window settings: %@", error.localizedDescription) + } } var inputType: InputType = .eyes var showHover: Bool = true var isDimmed: Bool = false - var curvatureRadius: Float = SDL_VisionOS_GetCurvature() + var curvatureRadius: Float = 0.0 var sceneState: SceneState = .interactive var isSnapped: Bool = false var settingsExpanded: Bool = false @@ -330,6 +380,9 @@ struct SDL_SettingsPanelView: View { Toggle(isOn: $settings.showHover) { } + .onChange(of: settings.showHover) { + settings.save() + } .labelsHidden() .tint(.secondary) @@ -341,6 +394,9 @@ struct SDL_SettingsPanelView: View { Toggle(isOn: $settings.isDimmed) { } + .onChange(of: settings.isDimmed) { + settings.save() + } .labelsHidden() .tint(.secondary) @@ -383,7 +439,7 @@ struct SDL_SettingsPanelView: View { + (1.0 - curvatureSlider) * Self.maximumCurvatureRadius) settings.curvatureRadius = radius } - SDL_VisionOS_SendCurvatureChanged(settings.curvatureRadius) + settings.save() } CurviestButtonIcon() diff --git a/src/video/uikit/SDL_UIKitBridge-swift.h b/src/video/uikit/SDL_UIKitBridge-swift.h index e63dc6c42b..a59414b4d3 100644 --- a/src/video/uikit/SDL_UIKitBridge-swift.h +++ b/src/video/uikit/SDL_UIKitBridge-swift.h @@ -23,11 +23,11 @@ // Called from Swift scene delegates when window size changes void SDL_VisionOS_SendSizeChanged(long width, long height); -// Called from Swift scene delegates to get the initial curvature -float SDL_VisionOS_GetCurvature(); +// Called from Swift scene delegates to get the initial window settings +NSString *SDL_VisionOS_GetWindowSettings(); -// Called from Swift scene delegates when window curvature changes -void SDL_VisionOS_SendCurvatureChanged(float curvature); +// Called from Swift scene delegates when window settings change +void SDL_VisionOS_SendWindowSettings(NSString *settings); // Called from Swift scene delegates when pointer mode changes void SDL_VisionOS_SendPointerMode(bool enabled); diff --git a/src/video/uikit/SDL_UIKitBridge.m b/src/video/uikit/SDL_UIKitBridge.m index b5c552f51a..e869d4b22c 100644 --- a/src/video/uikit/SDL_UIKitBridge.m +++ b/src/video/uikit/SDL_UIKitBridge.m @@ -57,27 +57,27 @@ void SDL_VisionOS_SendSizeChanged(long width, long height) } } -// Called from Swift scene delegates to get the initial curvature -float SDL_VisionOS_GetCurvature() +// Called from Swift scene delegates to get the initial window settings +NSString *SDL_VisionOS_GetWindowSettings() { SDL_Window *window = SDL_GetToplevelForKeyboardFocus(); if (window) { SDL_UIKitWindowData *data = (__bridge SDL_UIKitWindowData *)window->internal; - return data.curvature; + return data.settings; } - return 0.0f; + return nil; } // Called from Swift scene delegates when window curvature changes -void SDL_VisionOS_SendCurvatureChanged(float curvature) +void SDL_VisionOS_SendWindowSettings(NSString *settings) { SDL_Window *window = SDL_GetToplevelForKeyboardFocus(); if (window) { SDL_UIKitWindowData *data = (__bridge SDL_UIKitWindowData *)window->internal; - if (curvature != data.curvature) { - data.curvature = curvature; - SDL_SetFloatProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_CURVATURE_FLOAT, curvature); - SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_CURVATURE_CHANGED, (int)curvature, 0); + if (![settings isEqualToString:data.settings]) { + data.settings = settings; + SDL_SetStringProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_VISIONOS_SETTINGS_STRING, settings.UTF8String); + SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_SETTINGS_CHANGED, 0, 0); } } } diff --git a/src/video/uikit/SDL_uikitviewcontroller.m b/src/video/uikit/SDL_uikitviewcontroller.m index dc776741d9..7da8731699 100644 --- a/src/video/uikit/SDL_uikitviewcontroller.m +++ b/src/video/uikit/SDL_uikitviewcontroller.m @@ -127,7 +127,7 @@ static void SDLCALL SDL_HideHomeIndicatorHintChanged(void *userdata, const char #ifdef SDL_PLATFORM_VISIONOS if (@available(visionOS 26.0, *)) { SDL_UIKitWindowData *data = (__bridge SDL_UIKitWindowData *)self.window->internal; - if (data.curvature >= 0.0f) { + if (data.settings != nil) { [self initializeVisionOSCurvedUI]; } } diff --git a/src/video/uikit/SDL_uikitwindow.h b/src/video/uikit/SDL_uikitwindow.h index b9077ad36d..42a531cd65 100644 --- a/src/video/uikit/SDL_uikitwindow.h +++ b/src/video/uikit/SDL_uikitwindow.h @@ -55,7 +55,7 @@ extern NSUInteger UIKit_GetSupportedOrientations(SDL_Window *window); #ifdef SDL_PLATFORM_VISIONOS // Hosting controller for curved content mode (UIHostingController-based) @property(nonatomic, strong) id curvedContentHosting; -@property(nonatomic, assign) CGFloat curvature; +@property(nonatomic, strong) NSString *settings; #endif @end diff --git a/src/video/uikit/SDL_uikitwindow.m b/src/video/uikit/SDL_uikitwindow.m index 3c3b4fd926..5523d6c55f 100644 --- a/src/video/uikit/SDL_uikitwindow.m +++ b/src/video/uikit/SDL_uikitwindow.m @@ -106,18 +106,19 @@ static bool SetupWindowData(SDL_VideoDevice *_this, SDL_Window *window, UIWindow #endif window->w = width; window->h = height; - + SDL_PropertiesID props = SDL_GetWindowProperties(window); SDL_SetPointerProperty(props, SDL_PROP_WINDOW_UIKIT_WINDOW_POINTER, (__bridge void *)data.uiwindow); SDL_SetNumberProperty(props, SDL_PROP_WINDOW_UIKIT_METAL_VIEW_TAG_NUMBER, SDL_METALVIEW_TAG); #ifdef SDL_PLATFORM_VISIONOS - float curvature = SDL_GetFloatProperty(create_props, SDL_PROP_WINDOW_CREATE_CURVATURE_FLOAT, -1.0f); - if (curvature > 0.0f && curvature <= 1.0f) { - curvature = 0.0f; + const char *settings = SDL_GetStringProperty(create_props, SDL_PROP_WINDOW_CREATE_VISIONOS_SETTINGS_STRING, NULL); + if (settings) { + data.settings = [NSString stringWithUTF8String:settings]; + } else { + data.settings = nil; } - data.curvature = curvature; - SDL_SetFloatProperty(props, SDL_PROP_WINDOW_CURVATURE_FLOAT, curvature); + SDL_SetStringProperty(props, SDL_PROP_WINDOW_VISIONOS_SETTINGS_STRING, settings); #endif /* The View Controller will handle rotating the view when the device