The open source OpenXR runtime
1// Copyright 2022-2023, Collabora, Ltd.
2// SPDX-License-Identifier: BSL-1.0
3/*!
4 * @file
5 * @brief Helpers for system objects like @ref xrt_system_devices.
6 * @author Jakob Bornecrantz <jakob@collabora.com>
7 * @ingroup aux_util
8 */
9
10#include "xrt/xrt_device.h"
11#include "xrt/xrt_prober.h"
12
13#include "util/u_misc.h"
14#include "util/u_device.h"
15#include "util/u_logging.h"
16#include "util/u_system_helpers.h"
17
18#include <assert.h>
19#include <limits.h>
20
21
22/*
23 *
24 * Helper functions.
25 *
26 */
27
28static int32_t
29get_index_for_device(const struct xrt_system_devices *xsysd, const struct xrt_device *xdev)
30{
31 assert(xsysd->xdev_count <= ARRAY_SIZE(xsysd->xdevs));
32 assert(xsysd->xdev_count < INT_MAX);
33
34 if (xdev == NULL) {
35 return -1;
36 }
37
38 for (int32_t i = 0; i < (int32_t)xsysd->xdev_count; i++) {
39 if (xsysd->xdevs[i] == xdev) {
40 return i;
41 }
42 }
43
44 return -1;
45}
46
47static const char *
48type_to_small_string(enum xrt_device_feature_type type)
49{
50 switch (type) {
51 case XRT_DEVICE_FEATURE_HAND_TRACKING_LEFT: return "hand_tracking_left";
52 case XRT_DEVICE_FEATURE_HAND_TRACKING_RIGHT: return "hand_tracking_right";
53 case XRT_DEVICE_FEATURE_EYE_TRACKING: return "eye_tracking";
54 default: return "invalid";
55 }
56}
57
58static inline void
59get_hand_tracking_devices(struct xrt_system_devices *xsysd, enum xrt_hand hand, struct xrt_device *out_ht_xdevs[2])
60{
61#define XRT_GET_U_HT(HAND) xsysd->static_roles.hand_tracking.unobstructed.HAND
62#define XRT_GET_C_HT(HAND) xsysd->static_roles.hand_tracking.conforming.HAND
63 if (hand == XRT_HAND_LEFT) {
64 out_ht_xdevs[0] = XRT_GET_U_HT(left);
65 out_ht_xdevs[1] = XRT_GET_C_HT(left);
66 } else {
67 out_ht_xdevs[0] = XRT_GET_U_HT(right);
68 out_ht_xdevs[1] = XRT_GET_C_HT(right);
69 }
70#undef XRT_GET_C_HT
71#undef XRT_GET_U_HT
72}
73
74static xrt_result_t
75set_hand_tracking_enabled(struct xrt_system_devices *xsysd, enum xrt_hand hand, bool enable)
76{
77 struct xrt_device *ht_sources[2] = {0};
78 get_hand_tracking_devices(xsysd, hand, ht_sources);
79
80 uint32_t ht_sources_size = ARRAY_SIZE(ht_sources);
81 // hand-tracking data-sources can all come from the same xrt-device instance
82 if (ht_sources[0] == ht_sources[1]) {
83 ht_sources_size = 1;
84 }
85
86 typedef xrt_result_t (*set_feature_t)(struct xrt_device *, enum xrt_device_feature_type);
87 const set_feature_t set_feature = enable ? xrt_device_begin_feature : xrt_device_end_feature;
88
89 const enum xrt_device_feature_type ht_feature =
90 (hand == XRT_HAND_LEFT) ? XRT_DEVICE_FEATURE_HAND_TRACKING_LEFT : XRT_DEVICE_FEATURE_HAND_TRACKING_RIGHT;
91
92 xrt_result_t xret = XRT_SUCCESS;
93 for (uint32_t i = 0; i < ht_sources_size; ++i) {
94 if (ht_sources[i]) {
95 xret = set_feature(ht_sources[i], ht_feature);
96 }
97 if (xret != XRT_SUCCESS) {
98 break;
99 }
100 }
101 return xret;
102}
103
104
105/*
106 *
107 * Internal functions.
108 *
109 */
110
111static void
112destroy(struct xrt_system_devices *xsysd)
113{
114 u_system_devices_close(xsysd);
115 free(xsysd);
116}
117
118static xrt_result_t
119get_roles(struct xrt_system_devices *xsysd, struct xrt_system_roles *out_roles)
120{
121 struct u_system_devices_static *usysds = u_system_devices_static(xsysd);
122
123 assert(usysds->cached.generation_id == 1);
124
125 *out_roles = usysds->cached;
126
127 return XRT_SUCCESS;
128}
129
130static xrt_result_t
131feature_inc(struct xrt_system_devices *xsysd, enum xrt_device_feature_type type)
132{
133 struct u_system_devices_static *usysds = u_system_devices_static(xsysd);
134
135 if (type >= XRT_DEVICE_FEATURE_MAX_ENUM) {
136 return XRT_ERROR_FEATURE_NOT_SUPPORTED;
137 }
138
139 // If it wasn't zero nothing to do.
140 if (!xrt_reference_inc_and_was_zero(&usysds->feature_use[type])) {
141 return XRT_SUCCESS;
142 }
143
144 xrt_result_t xret = XRT_SUCCESS;
145 if (type == XRT_DEVICE_FEATURE_HAND_TRACKING_LEFT) {
146 xret = set_hand_tracking_enabled(xsysd, XRT_HAND_LEFT, true);
147 } else if (type == XRT_DEVICE_FEATURE_HAND_TRACKING_RIGHT) {
148 xret = set_hand_tracking_enabled(xsysd, XRT_HAND_RIGHT, true);
149 } else if (type == XRT_DEVICE_FEATURE_EYE_TRACKING) {
150 xret = xrt_device_begin_feature(xsysd->static_roles.eyes, type);
151 } else {
152 xret = XRT_ERROR_FEATURE_NOT_SUPPORTED;
153 }
154 if (xret != XRT_SUCCESS) {
155 return xret;
156 }
157
158 U_LOG_D("Device-feature %s in use", type_to_small_string(type));
159
160 return XRT_SUCCESS;
161}
162
163static xrt_result_t
164feature_dec(struct xrt_system_devices *xsysd, enum xrt_device_feature_type type)
165{
166 struct u_system_devices_static *usysds = u_system_devices_static(xsysd);
167
168 if (type >= XRT_DEVICE_FEATURE_MAX_ENUM) {
169 return XRT_ERROR_FEATURE_NOT_SUPPORTED;
170 }
171
172 // If it is not zero we are done.
173 if (!xrt_reference_dec_and_is_zero(&usysds->feature_use[type])) {
174 return XRT_SUCCESS;
175 }
176
177 xrt_result_t xret;
178 if (type == XRT_DEVICE_FEATURE_HAND_TRACKING_LEFT) {
179 xret = set_hand_tracking_enabled(xsysd, XRT_HAND_LEFT, false);
180 } else if (type == XRT_DEVICE_FEATURE_HAND_TRACKING_RIGHT) {
181 xret = set_hand_tracking_enabled(xsysd, XRT_HAND_RIGHT, false);
182 } else if (type == XRT_DEVICE_FEATURE_EYE_TRACKING) {
183 xret = xrt_device_end_feature(xsysd->static_roles.eyes, type);
184 } else {
185 xret = XRT_ERROR_FEATURE_NOT_SUPPORTED;
186 }
187 if (xret != XRT_SUCCESS) {
188 return xret;
189 }
190
191 U_LOG_D("Device-feature %s no longer in use", type_to_small_string(type));
192
193 return XRT_SUCCESS;
194}
195
196
197/*
198 *
199 * 'Exported' functions.
200 *
201 */
202
203struct u_system_devices *
204u_system_devices_allocate(void)
205{
206 struct u_system_devices *usysd = U_TYPED_CALLOC(struct u_system_devices);
207 usysd->base.destroy = destroy;
208
209 return usysd;
210}
211
212void
213u_system_devices_close(struct xrt_system_devices *xsysd)
214{
215 struct u_system_devices *usysd = u_system_devices(xsysd);
216
217 for (uint32_t i = 0; i < ARRAY_SIZE(usysd->base.xdevs); i++) {
218 xrt_device_destroy(&usysd->base.xdevs[i]);
219 }
220
221 xrt_frame_context_destroy_nodes(&usysd->xfctx);
222}
223
224
225
226struct u_system_devices_static *
227u_system_devices_static_allocate(void)
228{
229 struct u_system_devices_static *usysds = U_TYPED_CALLOC(struct u_system_devices_static);
230 usysds->base.base.destroy = destroy;
231 usysds->base.base.get_roles = get_roles;
232 usysds->base.base.feature_inc = feature_inc;
233 usysds->base.base.feature_dec = feature_dec;
234
235 return usysds;
236}
237
238void
239u_system_devices_static_finalize(struct u_system_devices_static *usysds,
240 struct xrt_device *left,
241 struct xrt_device *right,
242 struct xrt_device *gamepad)
243{
244 struct xrt_system_devices *xsysd = &usysds->base.base;
245 int32_t left_index = get_index_for_device(xsysd, left);
246 int32_t right_index = get_index_for_device(xsysd, right);
247 int32_t gamepad_index = get_index_for_device(xsysd, gamepad);
248
249 U_LOG_D(
250 "Devices:"
251 "\n\t%i: %p"
252 "\n\t%i: %p"
253 "\n\t%i: %p",
254 left_index, (void *)left, //
255 right_index, (void *)right, //
256 gamepad_index, (void *)gamepad); //
257
258 // Consistency checking.
259 assert(usysds->cached.generation_id == 0);
260 assert(left_index < 0 || left != NULL);
261 assert(left_index >= 0 || left == NULL);
262 assert(right_index < 0 || right != NULL);
263 assert(right_index >= 0 || right == NULL);
264 assert(gamepad_index < 0 || gamepad != NULL);
265 assert(gamepad_index >= 0 || gamepad == NULL);
266
267 // Completely clear the struct.
268 usysds->cached = (struct xrt_system_roles)XRT_SYSTEM_ROLES_INIT;
269 usysds->cached.generation_id = 1;
270 usysds->cached.left = left_index;
271 usysds->cached.right = right_index;
272 usysds->cached.gamepad = gamepad_index;
273}
274
275
276/*
277 *
278 * Generic system devices helper.
279 *
280 */
281
282xrt_result_t
283u_system_devices_create_from_prober(struct xrt_instance *xinst,
284 struct xrt_session_event_sink *broadcast,
285 struct xrt_system_devices **out_xsysd,
286 struct xrt_space_overseer **out_xso)
287{
288 xrt_result_t xret;
289
290 assert(out_xsysd != NULL);
291 assert(*out_xsysd == NULL);
292
293
294 /*
295 * Create the devices.
296 */
297
298 struct xrt_prober *xp = NULL;
299 xret = xrt_instance_get_prober(xinst, &xp);
300 if (xret != XRT_SUCCESS) {
301 return xret;
302 }
303
304 xret = xrt_prober_probe(xp);
305 if (xret < 0) {
306 return xret;
307 }
308
309 return xrt_prober_create_system(xp, broadcast, out_xsysd, out_xso);
310}
311
312struct xrt_device *
313u_system_devices_get_ht_device(struct xrt_system_devices *xsysd, enum xrt_input_name name)
314{
315 for (uint32_t i = 0; i < xsysd->xdev_count; i++) {
316 struct xrt_device *xdev = xsysd->xdevs[i];
317
318 if (xdev == NULL || !xdev->supported.hand_tracking) {
319 continue;
320 }
321
322 for (uint32_t j = 0; j < xdev->input_count; j++) {
323 struct xrt_input *input = &xdev->inputs[j];
324
325 if (input->name == name) {
326 return xdev;
327 }
328 }
329 }
330
331 return NULL;
332}