Просмотр исходного кода

visionos: persist all configurable window settings

Save the gaze indicator and dimmed mode setting as well as curvature
Sam Lantinga 1 день назад
Родитель
Сommit
33e237eb67

+ 2 - 2
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 the window. Otherwise, the handle has already been destroyed and all resources
                                              associated with it are invalid */
                                              associated with it are invalid */
     SDL_EVENT_WINDOW_HDR_STATE_CHANGED, /**< Window HDR properties have changed */
     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_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 */
     /* Keyboard events */
     SDL_EVENT_KEY_DOWN        = 0x300, /**< Key pressed */
     SDL_EVENT_KEY_DOWN        = 0x300, /**< Key pressed */

+ 4 - 11
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:
  * 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
  * If this window is being created to be used with an SDL_Renderer, you should
  * not add a graphics API specific property
  * 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_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_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_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.
  * Get the numeric ID of a window.
@@ -1635,10 +1631,7 @@ extern SDL_DECLSPEC SDL_Window * SDLCALL SDL_GetWindowParent(SDL_Window *window)
  *
  *
  * On visionOS:
  * 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.
  * \param window the window to query.
  * \returns a valid property ID on success or 0 on failure; call
  * \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_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_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_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.
  * Get the window flags.

+ 1 - 1
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_LEAVE_FULLSCREEN);
         SDL_WINDOWEVENT_CASE(SDL_EVENT_WINDOW_DESTROYED);
         SDL_WINDOWEVENT_CASE(SDL_EVENT_WINDOW_DESTROYED);
         SDL_WINDOWEVENT_CASE(SDL_EVENT_WINDOW_HDR_STATE_CHANGED);
         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
 #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)
 #define PRINT_KEYDEV_EVENT(event) (void)SDL_snprintf(details, sizeof(details), " (timestamp=%" SDL_PRIu64 " which=%u)", event->kdevice.timestamp, (uint)event->kdevice.which)

+ 69 - 13
src/video/uikit/SDL_CurvedContentHosting.swift

@@ -93,7 +93,7 @@ internal class SDL_ClearHostingController<Content: View>: UIHostingController<Co
 @MainActor
 @MainActor
 @objc(SDL_CurvedContentHosting)
 @objc(SDL_CurvedContentHosting)
 internal class SDL_CurvedContentHosting: NSObject {
 internal class SDL_CurvedContentHosting: NSObject {
-    private let settings = SDL_CurvedContentSettings()
+    private let settings = SDL_CurvedContentSettings.load()
 
 
     private let helper = SDL_RealityKitHelper()
     private let helper = SDL_RealityKitHelper()
 
 
@@ -127,7 +127,7 @@ internal class SDL_CurvedContentHosting: NSObject {
         // Spin up an async task to present / dismiss ornaments when there are updates to the scene state.
         // Spin up an async task to present / dismiss ornaments when there are updates to the scene state.
         let settings = self.settings
         let settings = self.settings
         let sceneStateObservations = Observations { [weak settings] in
         let sceneStateObservations = Observations { [weak settings] in
-            guard let settings else { return nil as (SDL_CurvedContentSettings.SceneState, SDL_CurvedContentSettings.InputType, Bool, Bool)? }
+            guard let settings else { return nil as (SceneState, InputType, Bool, Bool)? }
             return (settings.sceneState, settings.inputType, settings.isSnapped, settings.settingsExpanded)
             return (settings.sceneState, settings.inputType, settings.isSnapped, settings.settingsExpanded)
         }
         }
         Task { [weak self] in
         Task { [weak self] in
@@ -195,26 +195,76 @@ internal class SDL_CurvedContentHosting: NSObject {
 
 
 // MARK: - Settings Panel
 // MARK: - Settings Panel
 
 
+/// State of the app user interface, determined by the content view's state.
+enum SceneState: String, Codable {
+    /// A state which allows the user to configure the scene.  Ornaments should be visible.
+    case interactive
+
+    /// A state which hides all UI except for the game itself.  Ornaments should not be visible.
+    case cinematic
+}
+
+enum InputType: String, Codable {
+    case eyes
+    case pointer
+}
+
+internal class SDL_CurvedContentPersistentSettings: Codable {
+    var inputType: InputType?
+    var showHover: Bool?
+    var isDimmed: Bool?
+    var curvatureRadius: Float?
+}
+
 @Observable
 @Observable
 internal class SDL_CurvedContentSettings {
 internal class SDL_CurvedContentSettings {
-    /// State of the app user interface, determined by the content view's state.
-    enum SceneState {
-        /// A state which allows the user to configure the scene.  Ornaments should be visible.
-        case interactive
 
 
-        /// A state which hides all UI except for the game itself.  Ornaments should not be visible.
-        case cinematic
+    static func load() -> 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 inputType: InputType = .eyes
     var showHover: Bool = true
     var showHover: Bool = true
     var isDimmed: Bool = false
     var isDimmed: Bool = false
-    var curvatureRadius: Float = SDL_VisionOS_GetCurvature()
+    var curvatureRadius: Float = 0.0
     var sceneState: SceneState = .interactive
     var sceneState: SceneState = .interactive
     var isSnapped: Bool = false
     var isSnapped: Bool = false
     var settingsExpanded: Bool = false
     var settingsExpanded: Bool = false
@@ -330,6 +380,9 @@ struct SDL_SettingsPanelView: View {
 
 
                 Toggle(isOn: $settings.showHover) {
                 Toggle(isOn: $settings.showHover) {
                 }
                 }
+                .onChange(of: settings.showHover) {
+                    settings.save()
+                }
                 .labelsHidden()
                 .labelsHidden()
                 .tint(.secondary)
                 .tint(.secondary)
 
 
@@ -341,6 +394,9 @@ struct SDL_SettingsPanelView: View {
 
 
                 Toggle(isOn: $settings.isDimmed) {
                 Toggle(isOn: $settings.isDimmed) {
                 }
                 }
+                .onChange(of: settings.isDimmed) {
+                    settings.save()
+                }
                 .labelsHidden()
                 .labelsHidden()
                 .tint(.secondary)
                 .tint(.secondary)
 
 
@@ -383,7 +439,7 @@ struct SDL_SettingsPanelView: View {
                                                 + (1.0 - curvatureSlider) * Self.maximumCurvatureRadius)
                                                 + (1.0 - curvatureSlider) * Self.maximumCurvatureRadius)
                             settings.curvatureRadius = radius
                             settings.curvatureRadius = radius
                         }
                         }
-                        SDL_VisionOS_SendCurvatureChanged(settings.curvatureRadius)
+                        settings.save()
                     }
                     }
 
 
                     CurviestButtonIcon()
                     CurviestButtonIcon()

+ 4 - 4
src/video/uikit/SDL_UIKitBridge-swift.h

@@ -23,11 +23,11 @@
 // Called from Swift scene delegates when window size changes
 // Called from Swift scene delegates when window size changes
 void SDL_VisionOS_SendSizeChanged(long width, long height);
 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
 // Called from Swift scene delegates when pointer mode changes
 void SDL_VisionOS_SendPointerMode(bool enabled);
 void SDL_VisionOS_SendPointerMode(bool enabled);

+ 9 - 9
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();
     SDL_Window *window = SDL_GetToplevelForKeyboardFocus();
     if (window) {
     if (window) {
         SDL_UIKitWindowData *data = (__bridge SDL_UIKitWindowData *)window->internal;
         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
 // 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();
     SDL_Window *window = SDL_GetToplevelForKeyboardFocus();
     if (window) {
     if (window) {
         SDL_UIKitWindowData *data = (__bridge SDL_UIKitWindowData *)window->internal;
         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);
         }
         }
     }
     }
 }
 }

+ 1 - 1
src/video/uikit/SDL_uikitviewcontroller.m

@@ -127,7 +127,7 @@ static void SDLCALL SDL_HideHomeIndicatorHintChanged(void *userdata, const char
 #ifdef SDL_PLATFORM_VISIONOS
 #ifdef SDL_PLATFORM_VISIONOS
     if (@available(visionOS 26.0, *)) {
     if (@available(visionOS 26.0, *)) {
         SDL_UIKitWindowData *data = (__bridge SDL_UIKitWindowData *)self.window->internal;
         SDL_UIKitWindowData *data = (__bridge SDL_UIKitWindowData *)self.window->internal;
-        if (data.curvature >= 0.0f) {
+        if (data.settings != nil) {
             [self initializeVisionOSCurvedUI];
             [self initializeVisionOSCurvedUI];
         }
         }
     }
     }

+ 1 - 1
src/video/uikit/SDL_uikitwindow.h

@@ -55,7 +55,7 @@ extern NSUInteger UIKit_GetSupportedOrientations(SDL_Window *window);
 #ifdef SDL_PLATFORM_VISIONOS
 #ifdef SDL_PLATFORM_VISIONOS
 // Hosting controller for curved content mode (UIHostingController-based)
 // Hosting controller for curved content mode (UIHostingController-based)
 @property(nonatomic, strong) id curvedContentHosting;
 @property(nonatomic, strong) id curvedContentHosting;
-@property(nonatomic, assign) CGFloat curvature;
+@property(nonatomic, strong) NSString *settings;
 #endif
 #endif
 
 
 @end
 @end

+ 7 - 6
src/video/uikit/SDL_uikitwindow.m

@@ -106,18 +106,19 @@ static bool SetupWindowData(SDL_VideoDevice *_this, SDL_Window *window, UIWindow
 #endif
 #endif
     window->w = width;
     window->w = width;
     window->h = height;
     window->h = height;
-    
+
     SDL_PropertiesID props = SDL_GetWindowProperties(window);
     SDL_PropertiesID props = SDL_GetWindowProperties(window);
     SDL_SetPointerProperty(props, SDL_PROP_WINDOW_UIKIT_WINDOW_POINTER, (__bridge void *)data.uiwindow);
     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);
     SDL_SetNumberProperty(props, SDL_PROP_WINDOW_UIKIT_METAL_VIEW_TAG_NUMBER, SDL_METALVIEW_TAG);
 
 
 #ifdef SDL_PLATFORM_VISIONOS
 #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
 #endif
 
 
     /* The View Controller will handle rotating the view when the device
     /* The View Controller will handle rotating the view when the device