1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
|
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
// Qt-Security score:significant reason:default
import SwiftUI
import CompositorServices
import QIOSIntegrationPlugin
import RealityKit
struct QIOSSwiftApplication: App {
@UIApplicationDelegateAdaptor private var appDelegate: QIOSApplicationDelegate
@State private var immersionStyle:ImmersionStyle = .automatic
var body: some SwiftUI.Scene {
WindowGroup() {
ImmersiveSpaceControlView()
}
ImmersiveSpace(id: "QIOSImmersiveSpace") {
CompositorLayer(configuration: QIOSLayerConfiguration()) { layerRenderer in
QIOSIntegration.instance().renderCompositorLayer(layerRenderer)
// Handle any events in the scene.
layerRenderer.onSpatialEvent = { eventCollection in
QIOSIntegration.instance().handleSpatialEvents(jsonStringFromEventCollection(eventCollection))
}
}
}
.immersionStyle(selection: .constant(immersionStyle), in: immersionStyle)
}
}
public struct QIOSLayerConfiguration: CompositorLayerConfiguration {
public func makeConfiguration(capabilities: LayerRenderer.Capabilities,
configuration: inout LayerRenderer.Configuration) {
if #available(visionOS 2, *) {
QIOSIntegration.instance().configureCompositorLayer(
capabilities as cp_layer_renderer_capabilities_t,
configuration as cp_layer_renderer_configuration_t)
} else {
// Use reflection to pull out underlying C handles
let capabilitiesMirror = Mirror(reflecting: capabilities)
let configurationMirror = Mirror(reflecting: configuration)
QIOSIntegration.instance().configureCompositorLayer(
capabilitiesMirror.descendant("c_capabilities") as? cp_layer_renderer_capabilities_t,
configurationMirror.descendant("box", "value") as? cp_layer_renderer_configuration_t
)
}
}
}
public func runSwiftAppMain() {
QIOSSwiftApplication.main()
}
public class ImmersiveState: ObservableObject {
static let shared = ImmersiveState()
@Published var showImmersiveSpace: Bool = false
}
struct ImmersiveSpaceControlView: View {
@ObservedObject private var immersiveState = ImmersiveState.shared
@Environment(\.openImmersiveSpace) var openImmersiveSpace
@Environment(\.dismissImmersiveSpace) var dismissImmersiveSpace
var body: some View {
VStack {}
.onChange(of: immersiveState.showImmersiveSpace) { _, newValue in
Task {
if newValue {
await openImmersiveSpace(id: "QIOSImmersiveSpace")
} else {
await dismissImmersiveSpace()
}
}
}
}
}
public class ImmersiveSpaceManager : NSObject {
@objc public static func openImmersiveSpace() {
ImmersiveState.shared.showImmersiveSpace = true
}
@objc public static func dismissImmersiveSpace() {
ImmersiveState.shared.showImmersiveSpace = false
}
}
extension SpatialEventCollection.Event.Kind: Encodable {
enum CodingKeys: String, CodingKey {
case touch
case directPinch
case indirectPinch
case pointer
}
public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
switch self {
case .touch:
try container.encode("touch")
case .directPinch:
try container.encode("directPinch")
case .indirectPinch:
try container.encode("indirectPinch")
case .pointer:
try container.encode("pointer")
@unknown default:
try container.encode("unknown")
}
}
}
extension SpatialEventCollection.Event.Phase: Encodable {
enum CodingKeys: String, CodingKey {
case active
case ending
case cancled
}
public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
switch self {
case .active:
try container.encode("active")
case .ended:
try container.encode("ended")
case .cancelled:
try container.encode("canceled")
@unknown default:
try container.encode("unknown")
}
}
}
extension SpatialEventCollection.Event.InputDevicePose: Encodable {
enum CodingKeys: String, CodingKey {
case altitude
case azimuth
case pose3D
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(altitude.radians, forKey: .altitude)
try container.encode(azimuth.radians, forKey: .azimuth)
try container.encode(pose3D, forKey: .pose3D)
}
}
extension SpatialEventCollection.Event: Encodable {
enum CodingKeys: String, CodingKey {
case id
case timestamp
case kind
case location
case phase
case modifierKeys
case inputDevicePose
case location3D
case selectionRay
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(id.hashValue, forKey: .id)
try container.encode(timestamp, forKey: .timestamp)
try container.encode(kind, forKey: .kind)
try container.encode(location, forKey: .location)
try container.encode(phase, forKey: .phase)
try container.encode(modifierKeys.rawValue, forKey: .modifierKeys)
try container.encode(inputDevicePose, forKey: .inputDevicePose)
try container.encode(location3D, forKey: .location3D)
try container.encode(selectionRay, forKey: .selectionRay)
}
}
extension SpatialEventCollection: Encodable {
enum CodingKeys: String, CodingKey {
case events
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(Array(self), forKey: .events)
}
}
func jsonStringFromEventCollection(_ eventCollection: SpatialEventCollection) -> String {
let encoder = JSONEncoder()
encoder.dateEncodingStrategy = .iso8601
do {
let jsonData = try encoder.encode(eventCollection)
return String(data: jsonData, encoding: .utf8) ?? "{}"
} catch {
print("Failed to encode event collection: \(error)")
return "{}"
}
}
|