mirror of
https://github.com/libsdl-org/SDL.git
synced 2026-05-24 22:09:54 +00:00
visionos: persist all configurable window settings
Save the gaze indicator and dimmed mode setting as well as curvature
This commit is contained in:
@@ -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 */
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user