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

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 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 */

+ 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:
  *
- * - `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.

+ 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_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)

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

@@ -93,7 +93,7 @@ internal class SDL_ClearHostingController<Content: View>: UIHostingController<Co
 @MainActor
 @objc(SDL_CurvedContentHosting)
 internal class SDL_CurvedContentHosting: NSObject {
-    private let settings = SDL_CurvedContentSettings()
+    private let settings = SDL_CurvedContentSettings.load()
 
     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.
         let settings = self.settings
         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)
         }
         Task { [weak self] in
@@ -195,26 +195,76 @@ internal class SDL_CurvedContentHosting: NSObject {
 
 // 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
 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 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()

+ 4 - 4
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);

+ 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();
     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);
         }
     }
 }

+ 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
     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];
         }
     }

+ 1 - 1
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

+ 7 - 6
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