st/oxr: Add interaction profile code

Not a complete implementation but gets us far enough to
respect the bindings that a application gives us.
This commit is contained in:
Jakob Bornecrantz 2019-09-02 22:04:13 +01:00
parent 2337299279
commit 79ad85e9b7
6 changed files with 1219 additions and 261 deletions

View file

@ -18,6 +18,7 @@ set(OXR_SOURCE_FILES
oxr_api_swapchain.c
oxr_api_system.c
oxr_api_verify.h
oxr_binding.c
oxr_chain.h
oxr_event.cpp
oxr_extension_support.h

View file

@ -382,7 +382,7 @@ oxr_xrEnumerateBoundSourcesForAction(
XR_TYPE_BOUND_SOURCES_FOR_ACTION_ENUMERATE_INFO);
OXR_VERIFY_ACTION_NOT_NULL(&log, enumerateInfo->action, act);
return oxr_action_get_bound_sources(&log, sess, act->key,
return oxr_action_enumerate_bound_sources(&log, sess, act->key,
sourceCapacityInput,
sourceCountOutput, sources);
}

View file

@ -0,0 +1,821 @@
// Copyright 2018-2019, Collabora, Ltd.
// SPDX-License-Identifier: BSL-1.0
/*!
* @file
* @brief Holds binding related functions.
* @author Jakob Bornecrantz <jakob@collabora.com>
* @ingroup oxr_main
*/
#include "util/u_misc.h"
#include "xrt/xrt_compiler.h"
#include "oxr_objects.h"
#include "oxr_logger.h"
#include <stdio.h>
struct binding_template
{
const char *paths[8];
enum xrt_input_name inputs[8];
enum xrt_output_name outputs[8];
enum oxr_sub_action_path sub_path;
};
struct profile_template
{
const char *path;
struct binding_template *bindings;
size_t num_bindings;
};
static struct binding_template khr_simple_controller_bindings[10];
static struct binding_template google_daydream_controller_bindings[12];
static struct profile_template profiles[2] = {
{
.path = "/interaction_profiles/khr/simple_controller",
.bindings = khr_simple_controller_bindings,
.num_bindings = ARRAY_SIZE(khr_simple_controller_bindings),
},
{
.path = "/interaction_profiles/google/daydream_controller",
.bindings = google_daydream_controller_bindings,
.num_bindings = ARRAY_SIZE(google_daydream_controller_bindings),
},
};
static void
setup_paths(struct oxr_logger *log,
struct oxr_instance *inst,
struct binding_template *templ,
struct oxr_binding *binding)
{
size_t count = 0;
while (templ->paths[count] != NULL) {
count++;
}
binding->num_paths = count;
binding->paths = U_TYPED_ARRAY_CALLOC(XrPath, count);
for (size_t x = 0; x < binding->num_paths; x++) {
const char *str = templ->paths[x];
size_t len = strlen(str);
oxr_path_get_or_create(log, inst, str, len, &binding->paths[x]);
}
}
static void
setup_inputs(struct oxr_logger *log,
struct oxr_instance *inst,
struct binding_template *templ,
struct oxr_binding *binding)
{
size_t count = 0;
while (templ->inputs[count] != 0) {
count++;
}
if (count == 0) {
return;
}
binding->num_inputs = count;
binding->inputs = U_TYPED_ARRAY_CALLOC(enum xrt_input_name, count);
for (size_t x = 0; x < binding->num_inputs; x++) {
binding->inputs[x] = templ->inputs[x];
}
}
static void
setup_outputs(struct oxr_logger *log,
struct oxr_instance *inst,
struct binding_template *templ,
struct oxr_binding *binding)
{
size_t count = 0;
while (templ->outputs[count] != 0) {
count++;
}
if (count == 0) {
return;
}
binding->num_outputs = count;
binding->outputs = U_TYPED_ARRAY_CALLOC(enum xrt_output_name, count);
for (size_t x = 0; x < binding->num_outputs; x++) {
binding->outputs[x] = templ->outputs[x];
}
}
static bool
is_valid_interaction_profile(struct oxr_instance *inst, XrPath path)
{
return inst->path_cache.khr_simple_controller == path ||
inst->path_cache.google_daydream_controller == path ||
inst->path_cache.htc_vive_controller == path ||
inst->path_cache.htc_vive_pro == path ||
inst->path_cache.microsoft_motion_controller == path ||
inst->path_cache.microsoft_xbox_controller == path ||
inst->path_cache.oculus_go_controller == path ||
inst->path_cache.oculus_touch_controller == path ||
inst->path_cache.valve_index_controller == path;
}
static bool
interaction_profile_find(struct oxr_logger *log,
struct oxr_instance *inst,
XrPath path,
struct oxr_interaction_profile **out_p)
{
for (size_t x = 0; x < inst->num_profiles; x++) {
struct oxr_interaction_profile *p = inst->profiles[x];
if (p->path != path) {
continue;
}
*out_p = p;
return true;
}
return false;
}
static bool
interaction_profile_find_or_create(struct oxr_logger *log,
struct oxr_instance *inst,
XrPath path,
struct oxr_interaction_profile **out_p)
{
if (interaction_profile_find(log, inst, path, out_p)) {
return true;
}
struct profile_template *templ = NULL;
for (size_t x = 0; x < ARRAY_SIZE(profiles); x++) {
templ = &profiles[x];
XrPath t_path = XR_NULL_PATH;
oxr_path_get_or_create(log, inst, templ->path,
strlen(templ->path), &t_path);
if (t_path == path) {
break;
} else {
templ = NULL;
}
}
if (templ == NULL) {
*out_p = NULL;
return false;
}
struct oxr_interaction_profile *p =
U_TYPED_CALLOC(struct oxr_interaction_profile);
p->num_bindings = templ->num_bindings;
p->bindings = U_TYPED_ARRAY_CALLOC(struct oxr_binding, p->num_bindings);
p->path = path;
for (size_t x = 0; x < templ->num_bindings; x++) {
struct binding_template *t = &templ->bindings[x];
struct oxr_binding *b = &p->bindings[x];
b->sub_path = t->sub_path;
setup_paths(log, inst, t, b);
setup_inputs(log, inst, t, b);
setup_outputs(log, inst, t, b);
}
// Add to the list of currently created interaction profiles.
size_t size =
sizeof(struct oxr_interaction_profile) * (inst->num_profiles + 1);
inst->profiles = realloc(inst->profiles, size);
inst->profiles[inst->num_profiles++] = p;
*out_p = p;
return true;
}
static void
reset_all_keys(struct oxr_binding *bindings, size_t num_bindings)
{
for (size_t x = 0; x < num_bindings; x++) {
free(bindings[x].keys);
bindings[x].keys = NULL;
bindings[x].num_keys = 0;
}
}
static void
add_key_to_matching_bindings(struct oxr_binding *bindings,
size_t num_bindings,
XrPath path,
uint32_t key)
{
for (size_t x = 0; x < num_bindings; x++) {
struct oxr_binding *b = &bindings[x];
bool found = false;
for (size_t y = 0; y < b->num_paths; y++) {
if (b->paths[y] == path) {
found = true;
break;
}
}
if (!found) {
continue;
}
size_t size = sizeof(uint32_t) * (b->num_keys + 1);
b->keys = realloc(b->keys, size);
b->keys[b->num_keys++] = key;
}
}
/*
*
* 'Exported' functions.
*
*/
void
oxr_find_profile_for_device(struct oxr_logger *log,
struct oxr_instance *inst,
struct xrt_device *xdev,
struct oxr_interaction_profile **out_p)
{
if (xdev == NULL) {
return;
}
enum xrt_device_name name = xdev->name;
//! @todo A lot more clever selecting the profile here.
switch (name) {
case XRT_DEVICE_HYDRA:
// clang-format off
interaction_profile_find(log, inst, inst->path_cache.khr_simple_controller, out_p);
// clang-format on
return;
case XRT_DEVICE_PSMV:
// clang-format off
interaction_profile_find(log, inst, inst->path_cache.khr_simple_controller, out_p);
// clang-format on
return;
default: return;
}
}
void
oxr_binding_find_bindings_from_key(struct oxr_logger *log,
struct oxr_interaction_profile *p,
uint32_t key,
struct oxr_binding *bindings[32],
size_t *num_bindings)
{
if (p == NULL) {
*num_bindings = 0;
return;
}
//! @todo This function should be a two call function, or handle more
//! then 32 bindings.
size_t num = 0;
for (size_t y = 0; y < p->num_bindings; y++) {
struct oxr_binding *b = &p->bindings[y];
for (size_t z = 0; z < b->num_keys; z++) {
if (b->keys[z] == key) {
bindings[num++] = b;
break;
}
}
if (num >= 32) {
*num_bindings = num;
return;
}
}
*num_bindings = num;
}
void
oxr_binding_destroy_all(struct oxr_logger *log, struct oxr_instance *inst)
{
for (size_t x = 0; x < inst->num_profiles; x++) {
struct oxr_interaction_profile *p = inst->profiles[x];
for (size_t y = 0; y < p->num_bindings; y++) {
struct oxr_binding *b = &p->bindings[y];
free(b->paths);
free(b->inputs);
free(b->outputs);
b->paths = NULL;
b->inputs = NULL;
b->outputs = NULL;
b->num_paths = 0;
b->num_inputs = 0;
b->num_outputs = 0;
}
free(p->bindings);
p->bindings = NULL;
p->num_bindings = 0;
}
free(inst->profiles);
inst->profiles = NULL;
inst->num_profiles = 0;
}
/*
*
* Client functions.
*
*/
XrResult
oxr_action_suggest_interaction_profile_bindings(
struct oxr_logger *log,
struct oxr_instance *inst,
const XrInteractionProfileSuggestedBinding *suggestedBindings)
{
struct oxr_interaction_profile *p = NULL;
XrPath path = suggestedBindings->interactionProfile;
const char *str;
size_t length;
// Check if this profile is valid.
if (!is_valid_interaction_profile(inst, path)) {
oxr_path_get_string(log, inst, path, &str, &length);
return oxr_error(log, XR_ERROR_PATH_UNSUPPORTED,
"(suggestedBindings->interactionProfile) "
"non-supported profile '%s'",
str);
}
interaction_profile_find_or_create(log, inst, path, &p);
// Valid path, but not used.
//! @todo Still needs to validate the paths.
if (p == NULL) {
return XR_SUCCESS;
}
struct oxr_binding *bindings = p->bindings;
size_t num_bindings = p->num_bindings;
//! @todo Validate keys **FIRST** then reset.
reset_all_keys(bindings, num_bindings);
for (size_t i = 0; i < suggestedBindings->countSuggestedBindings; i++) {
const XrActionSuggestedBinding *s =
&suggestedBindings->suggestedBindings[i];
struct oxr_action *act = (struct oxr_action *)s->action;
#if 0
oxr_path_get_string(log, inst, s->binding, &str, &length);
fprintf(stderr, "\t\t%s %i -> %s\n", act->name, act->key, str);
#endif
add_key_to_matching_bindings(bindings, num_bindings, s->binding,
act->key);
}
return XR_SUCCESS;
}
XrResult
oxr_action_get_current_interaction_profile(
struct oxr_logger *log,
struct oxr_session *sess,
XrPath topLevelUserPath,
XrInteractionProfileState *interactionProfile)
{
struct oxr_instance *inst = sess->sys->inst;
if (!sess->actionsAttached) {
return oxr_error(log, XR_ERROR_ACTIONSET_NOT_ATTACHED,
" xrAttachSessionActionSets has not been "
"called on this session.");
}
if (topLevelUserPath == inst->path_cache.head) {
return sess->head;
} else if (topLevelUserPath == inst->path_cache.left) {
return sess->left;
} else if (topLevelUserPath == inst->path_cache.right) {
return sess->right;
} else if (topLevelUserPath == inst->path_cache.gamepad) {
return sess->gamepad;
} else {
return oxr_error(log, XR_ERROR_HANDLE_INVALID,
" not implemented");
}
}
XrResult
oxr_action_get_input_source_localized_name(
struct oxr_logger *log,
struct oxr_session *sess,
const XrInputSourceLocalizedNameGetInfo *getInfo,
uint32_t bufferCapacityInput,
uint32_t *bufferCountOutput,
char *buffer)
{
//! @todo Implement
return oxr_error(log, XR_ERROR_HANDLE_INVALID, " not implemented");
}
XrResult
oxr_action_enumerate_bound_sources(struct oxr_logger *log,
struct oxr_session *sess,
uint64_t key,
uint32_t sourceCapacityInput,
uint32_t *sourceCountOutput,
XrPath *sources)
{
//! @todo Implement
return oxr_error(log, XR_ERROR_HANDLE_INVALID, " not implemented");
}
/*
*
* Shipped bindings.
*
*/
/*
*
*
*
* KHR Simple Controller
*
*
*
*/
static struct binding_template khr_simple_controller_bindings[10] = {
{
.sub_path = OXR_SUB_ACTION_PATH_LEFT,
.paths =
{
"/user/hand/left/input/select/click",
"/user/hand/left/input/select",
NULL,
},
.inputs =
{
XRT_INPUT_PSMV_TRIGGER_VALUE,
XRT_INPUT_HYDRA_TRIGGER_VALUE,
0,
},
},
{
.sub_path = OXR_SUB_ACTION_PATH_LEFT,
.paths =
{
"/user/hand/left/input/menu/click",
"/user/hand/left/input/menu",
NULL,
},
.inputs =
{
XRT_INPUT_PSMV_MOVE_CLICK,
XRT_INPUT_HYDRA_MIDDLE_CLICK,
0,
},
},
{
.sub_path = OXR_SUB_ACTION_PATH_LEFT,
.paths =
{
"/user/hand/left/input/grip/pose",
"/user/hand/left/input/grip",
NULL,
},
.inputs =
{
XRT_INPUT_PSMV_BODY_CENTER_POSE,
XRT_INPUT_HYDRA_POSE,
0,
},
},
{
.sub_path = OXR_SUB_ACTION_PATH_LEFT,
.paths =
{
"/user/hand/left/input/aim/pose",
"/user/hand/left/input/aim",
NULL,
},
.inputs =
{
XRT_INPUT_PSMV_BALL_TIP_POSE,
XRT_INPUT_HYDRA_POSE,
0,
},
},
{
.sub_path = OXR_SUB_ACTION_PATH_LEFT,
.paths =
{
"/user/hand/left/output/haptic",
NULL,
},
.outputs =
{
XRT_OUTPUT_NAME_PSMV_RUMBLE_VIBRATION,
0,
},
},
{
.sub_path = OXR_SUB_ACTION_PATH_RIGHT,
.paths =
{
"/user/hand/right/input/select/click",
"/user/hand/right/input/select",
NULL,
},
.inputs =
{
XRT_INPUT_PSMV_TRIGGER_VALUE,
XRT_INPUT_HYDRA_TRIGGER_VALUE,
0,
},
},
{
.sub_path = OXR_SUB_ACTION_PATH_RIGHT,
.paths =
{
"/user/hand/right/input/menu/click",
"/user/hand/right/input/menu",
NULL,
},
.inputs =
{
XRT_INPUT_PSMV_MOVE_CLICK,
XRT_INPUT_HYDRA_MIDDLE_CLICK,
0,
},
},
{
.sub_path = OXR_SUB_ACTION_PATH_RIGHT,
.paths =
{
"/user/hand/right/input/grip/pose",
"/user/hand/right/input/grip",
NULL,
},
.inputs =
{
XRT_INPUT_PSMV_BODY_CENTER_POSE,
XRT_INPUT_HYDRA_POSE,
0,
},
},
{
.sub_path = OXR_SUB_ACTION_PATH_RIGHT,
.paths =
{
"/user/hand/right/input/aim/pose",
"/user/hand/right/input/aim",
NULL,
},
.inputs =
{
XRT_INPUT_PSMV_BALL_TIP_POSE,
XRT_INPUT_HYDRA_POSE,
0,
},
},
{
.sub_path = OXR_SUB_ACTION_PATH_RIGHT,
.paths =
{
"/user/hand/right/output/haptic",
NULL,
},
.outputs =
{
XRT_OUTPUT_NAME_PSMV_RUMBLE_VIBRATION,
0,
},
},
};
/*
*
*
*
* Google Daydream Controller
*
*
*
*/
static struct binding_template google_daydream_controller_bindings[12] = {
{
.sub_path = OXR_SUB_ACTION_PATH_LEFT,
.paths =
{
"/user/hand/left/input/select/click",
"/user/hand/left/input/select",
NULL,
},
.inputs =
{
#if 0
XRT_INPUT_PSMV_TRIGGER_VALUE,
XRT_INPUT_HYDRA_TRIGGER_VALUE,
#endif
0,
},
},
{
//! @todo Flag that this is a trackpad
.sub_path = OXR_SUB_ACTION_PATH_LEFT,
.paths =
{
"/user/hand/left/input/trackpad",
"/user/hand/left/input/trackpad/x",
"/user/hand/left/input/trackpad/y",
NULL,
},
.inputs =
{
0,
},
},
{
.sub_path = OXR_SUB_ACTION_PATH_LEFT,
.paths =
{
"/user/hand/left/input/trackpad/click",
"/user/hand/left/input/trackpad",
NULL,
},
.inputs =
{
0,
},
},
{
.sub_path = OXR_SUB_ACTION_PATH_LEFT,
.paths =
{
"/user/hand/left/input/trackpad/touch",
NULL,
},
.inputs =
{
0,
},
},
{
.sub_path = OXR_SUB_ACTION_PATH_LEFT,
.paths =
{
"/user/hand/left/input/grip/pose",
"/user/hand/left/input/grip",
NULL,
},
.inputs =
{
#if 0
XRT_INPUT_PSMV_BODY_CENTER_POSE,
XRT_INPUT_HYDRA_POSE,
#endif
0,
},
},
{
.sub_path = OXR_SUB_ACTION_PATH_LEFT,
.paths =
{
"/user/hand/left/input/aim/pose",
"/user/hand/left/input/aim",
NULL,
},
.inputs =
{
#if 0
XRT_INPUT_PSMV_BALL_TIP_POSE,
XRT_INPUT_HYDRA_POSE,
#endif
0,
},
},
{
.sub_path = OXR_SUB_ACTION_PATH_RIGHT,
.paths =
{
"/user/hand/right/input/select/click",
"/user/hand/right/input/select",
NULL,
},
.inputs =
{
#if 0
XRT_INPUT_PSMV_TRIGGER_VALUE,
XRT_INPUT_HYDRA_TRIGGER_VALUE,
#endif
0,
},
},
{
.sub_path = OXR_SUB_ACTION_PATH_RIGHT,
//! @todo Flag that this is a trackpad
.paths =
{
"/user/hand/right/input/trackpad",
"/user/hand/right/input/trackpad/x",
"/user/hand/right/input/trackpad/y",
NULL,
},
.inputs =
{
0,
},
},
{
.sub_path = OXR_SUB_ACTION_PATH_RIGHT,
.paths =
{
"/user/hand/right/input/trackpad/click",
"/user/hand/right/input/trackpad",
NULL,
},
.inputs =
{
0,
},
},
{
.sub_path = OXR_SUB_ACTION_PATH_RIGHT,
.paths =
{
"/user/hand/right/input/trackpad/touch",
NULL,
},
.inputs =
{
0,
},
},
{
.sub_path = OXR_SUB_ACTION_PATH_RIGHT,
.paths =
{
"/user/hand/right/input/grip/pose",
"/user/hand/right/input/grip",
NULL,
},
.inputs =
{
#if 0
XRT_INPUT_PSMV_BODY_CENTER_POSE,
XRT_INPUT_HYDRA_POSE,
#endif
0,
},
},
{
.sub_path = OXR_SUB_ACTION_PATH_RIGHT,
.paths =
{
"/user/hand/right/input/aim/pose",
"/user/hand/right/input/aim",
NULL,
},
.inputs =
{
#if 0
XRT_INPUT_PSMV_BALL_TIP_POSE,
XRT_INPUT_HYDRA_POSE,
#endif
0,
},
},
};

View file

@ -23,24 +23,6 @@
#include "oxr_handle.h"
/*
*
* Struct and defines.
*
*/
enum sub_action_path
{
// clang-format off
OXR_SUB_ACTION_PATH_USER = 1 << 0,
OXR_SUB_ACTION_PATH_HEAD = 1 << 1,
OXR_SUB_ACTION_PATH_LEFT = 1 << 2,
OXR_SUB_ACTION_PATH_RIGHT = 1 << 3,
OXR_SUB_ACTION_PATH_GAMEPAD = 1 << 4,
// clang-format on
};
/*
*
* Pre declare functions.
@ -75,19 +57,24 @@ oxr_source_update(struct oxr_logger* log,
static void
oxr_source_bind_inputs(struct oxr_logger* log,
struct oxr_sink_logger* slog,
struct oxr_session* sess,
struct oxr_action* act,
struct oxr_source_cache* cache,
enum sub_action_path sub_path);
struct oxr_interaction_profile* profile,
enum oxr_sub_action_path sub_path);
static XrResult
oxr_source_destroy_cb(struct oxr_logger* log, struct oxr_handle_base* hb);
static XrResult
oxr_source_create(struct oxr_logger* log,
struct oxr_session* sess,
struct oxr_source_set* src_set,
struct oxr_action* act,
struct oxr_source** out_src);
struct oxr_interaction_profile* head,
struct oxr_interaction_profile* left,
struct oxr_interaction_profile* right,
struct oxr_interaction_profile* gamepad);
/*
@ -122,8 +109,10 @@ oxr_action_set_create(struct oxr_logger* log,
oxr_action_set_destroy_cb, &inst->handle);
act_set->key = key_gen++;
act_set->generation = 1;
act_set->inst = inst;
strncpy(act_set->name, createInfo->actionSetName,
sizeof(act_set->name));
*out_act_set = act_set;
@ -143,10 +132,6 @@ oxr_action_destroy_cb(struct oxr_logger* log, struct oxr_handle_base* hb)
//! @todo Move to oxr_objects.h
struct oxr_action* act = (struct oxr_action*)hb;
// Need to keep track of the generation of this
// action set to rebuild bindings when used.
act->act_set->generation++;
free(act);
return XR_SUCCESS;
@ -178,10 +163,6 @@ oxr_action_create(struct oxr_logger* log,
strncpy(act->name, createInfo->actionName, sizeof(act->name));
// Need to keep track of the generation of this
// action set to rebuild bindings when used.
act_set->generation++;
*out_act = act;
return XR_SUCCESS;
@ -280,65 +261,164 @@ oxr_source_get_pose_input(struct oxr_logger* log,
/*
*
* MEGA HACK Functions!
* Not so hack functions.
*
*/
static void
MEGA_HACK_get_binding(struct oxr_logger* log,
struct oxr_session* sess,
static bool
do_inputs(struct oxr_binding* bind,
struct xrt_device* xdev,
struct oxr_source_input inputs[16],
uint32_t* num_inputs)
{
struct xrt_input* input = NULL;
bool found = false;
for (size_t i = 0; i < bind->num_inputs; i++) {
if (oxr_xdev_find_input(xdev, bind->inputs[i], &input)) {
uint32_t index = (*num_inputs)++;
inputs[index].input = input;
inputs[index].xdev = xdev;
found = true;
}
}
return found;
}
static bool
do_outputs(struct oxr_binding* bind,
struct xrt_device* xdev,
struct oxr_source_output outputs[16],
uint32_t* num_outputs)
{
struct xrt_output* output = NULL;
bool found = false;
for (size_t i = 0; i < bind->num_outputs; i++) {
if (oxr_xdev_find_output(xdev, bind->outputs[i], &output)) {
uint32_t index = (*num_outputs)++;
outputs[index].name = output->name;
outputs[index].xdev = xdev;
found = true;
}
}
return found;
}
static bool
do_io_bindings(struct oxr_binding* b,
struct oxr_action* act,
enum sub_action_path sub_path,
struct xrt_device* xdev,
struct oxr_source_input inputs[16],
uint32_t* num_inputs,
struct oxr_source_output outputs[16],
uint32_t* num_outputs)
{
struct xrt_input* input = NULL;
struct xrt_output* output = NULL;
struct xrt_device* xdev = NULL;
bool found = false;
if (act->action_type == XR_ACTION_TYPE_VIBRATION_OUTPUT) {
found |= do_outputs(b, xdev, outputs, num_outputs);
} else {
found |= do_inputs(b, xdev, inputs, num_inputs);
}
return found;
}
static void
get_binding(struct oxr_logger* log,
struct oxr_sink_logger* slog,
struct oxr_session* sess,
struct oxr_action* act,
struct oxr_interaction_profile* profile,
enum oxr_sub_action_path sub_path,
struct oxr_source_input inputs[16],
uint32_t* num_inputs,
struct oxr_source_output outputs[16],
uint32_t* num_outputs)
{
struct xrt_device* xdev = NULL;
struct oxr_binding* bindings[32];
const char* profile_str;
const char* user_path_str;
size_t length;
//! @todo This probably falls on its head if the application doesn't use
//! sub action paths.
switch (sub_path) {
case OXR_SUB_ACTION_PATH_USER: xdev = NULL; break;
case OXR_SUB_ACTION_PATH_HEAD: xdev = sess->sys->head; break;
case OXR_SUB_ACTION_PATH_LEFT: xdev = sess->sys->left; break;
case OXR_SUB_ACTION_PATH_RIGHT: xdev = sess->sys->right; break;
case OXR_SUB_ACTION_PATH_GAMEPAD: xdev = NULL; break;
case OXR_SUB_ACTION_PATH_USER:
user_path_str = "/user";
xdev = NULL;
break;
case OXR_SUB_ACTION_PATH_HEAD:
user_path_str = "/user/head";
xdev = sess->sys->head;
break;
case OXR_SUB_ACTION_PATH_LEFT:
user_path_str = "/user/hand/left";
xdev = sess->sys->left;
break;
case OXR_SUB_ACTION_PATH_RIGHT:
user_path_str = "/user/hand/right";
xdev = sess->sys->right;
break;
case OXR_SUB_ACTION_PATH_GAMEPAD:
user_path_str = "/user/hand/gamepad";
xdev = NULL;
break;
default: break;
}
bool bound = false;
if (strcmp(act->name, "grab_object") == 0) {
bound = oxr_xdev_find_input(xdev, XRT_INPUT_PSMV_TRIGGER_VALUE,
&input) ||
oxr_xdev_find_input(xdev, XRT_INPUT_HYDRA_TRIGGER_VALUE,
&input);
} else if (strcmp(act->name, "hand_pose") == 0) {
bound = oxr_xdev_find_input(
xdev, XRT_INPUT_PSMV_BODY_CENTER_POSE, &input) ||
oxr_xdev_find_input(xdev, XRT_INPUT_HYDRA_POSE, &input);
} else if (strcmp(act->name, "quit_session") == 0) {
bound =
oxr_xdev_find_input(xdev, XRT_INPUT_PSMV_PS_CLICK, &input);
} else if (strcmp(act->name, "vibrate_hand") == 0) {
bound = oxr_xdev_find_output(
xdev, XRT_OUTPUT_NAME_PSMV_RUMBLE_VIBRATION, &output);
oxr_slog(slog, "\tFor: %s\n", user_path_str);
if (xdev == NULL) {
oxr_slog(slog, "\t\tNo xdev!\n");
return;
}
if (input != NULL) {
uint32_t index = (*num_inputs)++;
inputs[index].input = input;
inputs[index].xdev = xdev;
if (profile == NULL) {
oxr_slog(slog, "\t\tNo profile!\n");
return;
}
if (output != NULL) {
uint32_t index = (*num_outputs)++;
outputs[index].name = output->name;
outputs[index].xdev = xdev;
oxr_path_get_string(log, sess->sys->inst, profile->path, &profile_str,
&length);
oxr_slog(slog, "\t\tProfile: %s\n", profile_str);
size_t num = 0;
oxr_binding_find_bindings_from_key(log, profile, act->key, bindings,
&num);
if (num == 0) {
oxr_slog(slog, "\t\tNo bindings\n");
return;
}
//! @todo preserve this value and return it.
(void)bound;
for (size_t i = 0; i < num; i++) {
const char* str = NULL;
struct oxr_binding* b = bindings[i];
// Just pick the first path.
oxr_path_get_string(log, sess->sys->inst, b->paths[0], &str,
&length);
oxr_slog(slog, "\t\t\tBinding: %s\n", str);
if (b->sub_path != sub_path) {
oxr_slog(slog, "\t\t\t\tRejected! (SUB PATH)\n");
continue;
}
bool found = do_io_bindings(b, act, xdev, inputs, num_inputs,
outputs, num_outputs);
if (found) {
oxr_slog(slog, "\t\t\t\tBound!\n");
} else {
oxr_slog(slog, "\t\t\t\tRejected! (NO XDEV MAPPING)\n");
}
}
}
@ -369,8 +449,7 @@ oxr_source_set_create(struct oxr_logger* log,
OXR_ALLOCATE_HANDLE_OR_RETURN(log, src_set, OXR_XR_DEBUG_SOURCESET,
oxr_source_set_destroy_cb, &sess->handle);
src_set->generation = act_set->generation;
src_set->sess = sess;
u_hashmap_int_insert(sess->act_sets, act_set->key, src_set);
*out_src_set = src_set;
@ -408,42 +487,55 @@ oxr_source_destroy_cb(struct oxr_logger* log, struct oxr_handle_base* hb)
static XrResult
oxr_source_create(struct oxr_logger* log,
struct oxr_session* sess,
struct oxr_source_set* src_set,
struct oxr_action* act,
struct oxr_source** out_src)
struct oxr_interaction_profile* head,
struct oxr_interaction_profile* left,
struct oxr_interaction_profile* right,
struct oxr_interaction_profile* gamepad)
{
struct oxr_session* sess = src_set->sess;
struct oxr_source* src = NULL;
struct oxr_sink_logger slog = {0};
OXR_ALLOCATE_HANDLE_OR_RETURN(log, src, OXR_XR_DEBUG_SOURCE,
oxr_source_destroy_cb, &sess->handle);
oxr_source_destroy_cb, &src_set->handle);
u_hashmap_int_insert(sess->sources, act->key, src);
u_hashmap_int_insert(src_set->sess->sources, act->key, src);
// Start logging into a single buffer.
oxr_slog(&slog, ": Binding %s/%s\n", act->act_set->name, act->name);
if (act->sub_paths.user || act->sub_paths.any) {
oxr_source_bind_inputs(log, sess, act, &src->user,
#if 0
oxr_source_bind_inputs(log, slog, sess, act, &src->user, user,
OXR_SUB_ACTION_PATH_USER);
#endif
}
if (act->sub_paths.head || act->sub_paths.any) {
oxr_source_bind_inputs(log, sess, act, &src->head,
oxr_source_bind_inputs(log, &slog, sess, act, &src->head, head,
OXR_SUB_ACTION_PATH_HEAD);
}
if (act->sub_paths.left || act->sub_paths.any) {
oxr_source_bind_inputs(log, sess, act, &src->left,
oxr_source_bind_inputs(log, &slog, sess, act, &src->left, left,
OXR_SUB_ACTION_PATH_LEFT);
}
if (act->sub_paths.right || act->sub_paths.any) {
oxr_source_bind_inputs(log, sess, act, &src->right,
OXR_SUB_ACTION_PATH_RIGHT);
oxr_source_bind_inputs(log, &slog, sess, act, &src->right,
right, OXR_SUB_ACTION_PATH_RIGHT);
}
if (act->sub_paths.gamepad || act->sub_paths.any) {
oxr_source_bind_inputs(log, sess, act, &src->gamepad,
OXR_SUB_ACTION_PATH_GAMEPAD);
oxr_source_bind_inputs(log, &slog, sess, act, &src->gamepad,
gamepad, OXR_SUB_ACTION_PATH_GAMEPAD);
}
*out_src = src;
oxr_slog(&slog, "\tDone");
// Also frees all data.
oxr_log_slog(log, &slog);
return XR_SUCCESS;
}
@ -520,10 +612,7 @@ oxr_source_update(struct oxr_logger* log,
struct oxr_source* src = NULL;
oxr_session_get_source(sess, act->key, &src);
if (src == NULL) {
oxr_source_create(log, sess, act, &src);
}
// This really shouldn't be happening.
if (src == NULL) {
return;
}
@ -543,18 +632,20 @@ oxr_source_update(struct oxr_logger* log,
static void
oxr_source_bind_inputs(struct oxr_logger* log,
struct oxr_sink_logger* slog,
struct oxr_session* sess,
struct oxr_action* act,
struct oxr_source_cache* cache,
enum sub_action_path sub_path)
struct oxr_interaction_profile* profile,
enum oxr_sub_action_path sub_path)
{
struct oxr_source_input inputs[16] = {0};
uint32_t num_inputs = 0;
struct oxr_source_output outputs[16] = {0};
uint32_t num_outputs = 0;
MEGA_HACK_get_binding(log, sess, act, sub_path, inputs, &num_inputs,
outputs, &num_outputs);
get_binding(log, slog, sess, act, profile, sub_path, inputs,
&num_inputs, outputs, &num_outputs);
cache->current.active = false;
@ -614,41 +705,53 @@ oxr_session_get_source(struct oxr_session* sess,
}
}
/*!
* Loop over all actions in a action set and destroy the sources linked to those
* actions, this is so we can regenerate the bindings.
*/
static void
oxr_session_destroy_all_sources(struct oxr_logger* log,
struct oxr_session* sess,
struct oxr_action_set* act_set)
{
for (uint32_t k = 0; k < XRT_MAX_HANDLE_CHILDREN; k++) {
// This assumes that all children are actions.
struct oxr_action* act =
(struct oxr_action*)act_set->handle.children[k];
if (act == NULL) {
continue;
}
struct oxr_source* src = NULL;
oxr_session_get_source(sess, act->key, &src);
if (src == NULL) {
continue;
}
u_hashmap_int_erase(sess->sources, act->key);
oxr_handle_destroy(log, &src->handle);
}
}
XrResult
oxr_session_attach_action_sets(struct oxr_logger* log,
struct oxr_session* sess,
const XrSessionActionSetsAttachInfo* bindInfo)
{
//! @todo not implementeds
struct oxr_instance* inst = sess->sys->inst;
struct oxr_interaction_profile* head = NULL;
struct oxr_interaction_profile* left = NULL;
struct oxr_interaction_profile* right = NULL;
struct oxr_action_set* act_set = NULL;
struct oxr_source_set* src_set = NULL;
struct oxr_action* act = NULL;
oxr_find_profile_for_device(log, inst, sess->sys->head, &head);
oxr_find_profile_for_device(log, inst, sess->sys->left, &left);
oxr_find_profile_for_device(log, inst, sess->sys->right, &right);
// Has any of the bound action sets been updated.
for (uint32_t i = 0; i < bindInfo->countActionSets; i++) {
act_set = (struct oxr_action_set*)bindInfo->actionSets[i];
act_set->attached = true;
oxr_source_set_create(log, sess, act_set, &src_set);
for (uint32_t k = 0; k < XRT_MAX_HANDLE_CHILDREN; k++) {
act = (struct oxr_action*)act_set->handle.children[k];
if (act == NULL) {
continue;
}
oxr_source_create(log, src_set, act, head, left, right,
NULL);
}
}
if (head != NULL) {
sess->head = head->path;
}
if (left != NULL) {
sess->left = left->path;
}
if (right != NULL) {
sess->right = right->path;
}
sess->actionsAttached = true;
return XR_SUCCESS;
}
@ -661,6 +764,18 @@ oxr_action_sync_data(struct oxr_logger* log,
struct oxr_action_set* act_set = NULL;
struct oxr_source_set* src_set = NULL;
// Check that all action sets has been attached.
for (uint32_t i = 0; i < countActionSets; i++) {
oxr_session_get_source_set(sess, actionSets[i].actionSet,
&src_set, &act_set);
if (src_set == NULL) {
return oxr_error(
log, XR_ERROR_ACTIONSET_NOT_ATTACHED,
"(actionSets[%i].actionSet) has not been attached",
i);
}
}
// Synchronize outputs to this time.
int64_t now = time_state_get_now(sess->sys->inst->timekeeping);
@ -670,31 +785,6 @@ oxr_action_sync_data(struct oxr_logger* log,
sess->sys->inst->timekeeping);
}
//! @todo These semantics below are all wrong!
// Has any of the bound action sets been updated.
for (uint32_t i = 0; i < countActionSets; i++) {
oxr_session_get_source_set(sess, actionSets[i].actionSet,
&src_set, &act_set);
// No source set found, create it. This will set it to the
// current action set generation, that's okay since we will
// be creating the sources for the actions below.
if (src_set == NULL) {
oxr_source_set_create(log, sess, act_set, &src_set);
continue;
}
// Generations match, nothing to do.
if (src_set->generation == act_set->generation) {
continue;
}
// Found possible out of date bindings, we need to redo these
// bindings. Destroy the actions currently on this action set.
oxr_session_destroy_all_sources(log, sess, act_set);
}
// Go over all action sets and update them.
for (uint32_t i = 0; i < countActionSets; i++) {
struct oxr_sub_paths sub_paths;
@ -723,66 +813,6 @@ oxr_action_sync_data(struct oxr_logger* log,
return XR_SUCCESS;
}
XrResult
oxr_action_suggest_interaction_profile_bindings(
struct oxr_logger* log,
struct oxr_instance* inst,
const XrInteractionProfileSuggestedBinding* suggestedBindings)
{
//! @todo Implement
const char* str;
size_t length;
#if 0
fprintf(stderr, "%s\n", __func__);
oxr_path_get_string(log, inst, suggestedBindings->interactionProfile,
&str, &length);
fprintf(stderr, "\tinteractionProfile: %s\n", str);
#endif
for (size_t i = 0; i < suggestedBindings->countSuggestedBindings; i++) {
const XrActionSuggestedBinding* s =
&suggestedBindings->suggestedBindings[i];
struct oxr_action* act = (struct oxr_action*)s->action;
#if 0
oxr_path_get_string(log, inst, s->binding, &str, &length);
fprintf(stderr, "\t\t%s -> %s\n", act->name, str);
#else
(void)inst;
(void)act;
(void)str;
(void)length;
#endif
}
return XR_SUCCESS;
}
XrResult
oxr_action_get_current_interaction_profile(
struct oxr_logger* log,
struct oxr_session* sess,
XrPath topLevelUserPath,
XrInteractionProfileState* interactionProfile)
{
//! @todo Implement
return oxr_error(log, XR_ERROR_HANDLE_INVALID, " not implemented");
}
XrResult
oxr_action_get_input_source_localized_name(
struct oxr_logger* log,
struct oxr_session* sess,
const XrInputSourceLocalizedNameGetInfo* getInfo,
uint32_t bufferCapacityInput,
uint32_t* bufferCountOutput,
char* buffer)
{
//! @todo Implement
return oxr_error(log, XR_ERROR_HANDLE_INVALID, " not implemented");
}
/*
*
@ -995,18 +1025,6 @@ oxr_action_get_pose(struct oxr_logger* log,
return XR_SUCCESS;
}
XrResult
oxr_action_get_bound_sources(struct oxr_logger* log,
struct oxr_session* sess,
uint64_t key,
uint32_t sourceCapacityInput,
uint32_t* sourceCountOutput,
XrPath* sources)
{
//! @todo Implement
return oxr_error(log, XR_ERROR_HANDLE_INVALID, " not implemented");
}
/*
*

View file

@ -43,6 +43,7 @@ oxr_instance_destroy(struct oxr_logger *log, struct oxr_handle_base *hb)
u_var_remove_root((void *)inst);
oxr_binding_destroy_all(log, inst);
oxr_path_destroy_all(log, inst);
if (inst->path_store != NULL) {
@ -94,11 +95,22 @@ oxr_instance_create(struct oxr_logger *log,
}
// Cache certain often looked up paths.
// clang-format off
cache_path(log, inst, "/user", &inst->path_cache.user);
cache_path(log, inst, "/user/hand/head", &inst->path_cache.head);
cache_path(log, inst, "/user/hand/left", &inst->path_cache.left);
cache_path(log, inst, "/user/hand/right", &inst->path_cache.right);
cache_path(log, inst, "/user/hand/gamepad", &inst->path_cache.gamepad);
cache_path(log, inst, "/interaction_profiles/khr/simple_controller", &inst->path_cache.khr_simple_controller);
cache_path(log, inst, "/interaction_profiles/google/daydream_controller", &inst->path_cache.google_daydream_controller);
cache_path(log, inst, "/interaction_profiles/htc/vive_controller", &inst->path_cache.htc_vive_controller);
cache_path(log, inst, "/interaction_profiles/htc/vive_pro", &inst->path_cache.htc_vive_pro);
cache_path(log, inst, "/interaction_profiles/microsoft/motion_controller", &inst->path_cache.microsoft_motion_controller);
cache_path(log, inst, "/interaction_profiles/microsoft/xbox_controller", &inst->path_cache.microsoft_xbox_controller);
cache_path(log, inst, "/interaction_profiles/oculus/go_controller", &inst->path_cache.oculus_go_controller);
cache_path(log, inst, "/interaction_profiles/oculus/touch_controller", &inst->path_cache.oculus_touch_controller);
cache_path(log, inst, "/interaction_profiles/valve/index_controller", &inst->path_cache.valve_index_controller);
// clang-format on
p_ret = xrt_prober_create(&inst->prober);
if (p_ret != 0) {

View file

@ -79,6 +79,8 @@ struct oxr_source;
struct oxr_source_set;
struct oxr_source_input;
struct oxr_source_output;
struct oxr_binding;
struct oxr_interaction_profile;
#define XRT_MAX_HANDLE_CHILDREN 256
@ -103,6 +105,18 @@ enum oxr_handle_state
OXR_HANDLE_STATE_DESTROYED,
};
/*!
* Sub action paths.
*/
enum oxr_sub_action_path
{
OXR_SUB_ACTION_PATH_USER,
OXR_SUB_ACTION_PATH_HEAD,
OXR_SUB_ACTION_PATH_LEFT,
OXR_SUB_ACTION_PATH_RIGHT,
OXR_SUB_ACTION_PATH_GAMEPAD,
};
/*
*
@ -284,28 +298,6 @@ oxr_action_sync_data(struct oxr_logger *log,
uint32_t countActionSets,
const XrActiveActionSet *actionSets);
XrResult
oxr_action_suggest_interaction_profile_bindings(
struct oxr_logger *log,
struct oxr_instance *inst,
const XrInteractionProfileSuggestedBinding *suggestedBindings);
XrResult
oxr_action_get_current_interaction_profile(
struct oxr_logger *log,
struct oxr_session *sess,
XrPath topLevelUserPath,
XrInteractionProfileState *interactionProfile);
XrResult
oxr_action_get_input_source_localized_name(
struct oxr_logger *log,
struct oxr_session *sess,
const XrInputSourceLocalizedNameGetInfo *getInfo,
uint32_t bufferCapacityInput,
uint32_t *bufferCountOutput,
char *buffer);
XrResult
oxr_action_get_boolean(struct oxr_logger *log,
struct oxr_session *sess,
@ -335,14 +327,6 @@ oxr_action_get_pose(struct oxr_logger *log,
struct oxr_sub_paths sub_paths,
XrActionStatePose *data);
XrResult
oxr_action_get_bound_sources(struct oxr_logger *log,
struct oxr_session *sess,
uint64_t key,
uint32_t sourceCapacityInput,
uint32_t *sourceCountOutput,
XrPath *sources);
XrResult
oxr_action_apply_haptic_feedback(struct oxr_logger *log,
struct oxr_session *sess,
@ -357,6 +341,70 @@ oxr_action_stop_haptic_feedback(struct oxr_logger *log,
struct oxr_sub_paths sub_paths);
/*
*
* oxr_binding.c
*
*/
/*!
* Find the best matching profile for the given @ref xrt_device.
*
* @param xdev Can be null.
*/
void
oxr_find_profile_for_device(struct oxr_logger *log,
struct oxr_instance *inst,
struct xrt_device *xdev,
struct oxr_interaction_profile **out_p);
/*!
* Free all memory allocated by the binding system.
*/
void
oxr_binding_destroy_all(struct oxr_logger *log, struct oxr_instance *inst);
/*!
* Find all bindings that is the given action key is bound to.
*/
void
oxr_binding_find_bindings_from_key(struct oxr_logger *log,
struct oxr_interaction_profile *profile,
uint32_t key,
struct oxr_binding *bindings[32],
size_t *num_bindings);
XrResult
oxr_action_suggest_interaction_profile_bindings(
struct oxr_logger *log,
struct oxr_instance *inst,
const XrInteractionProfileSuggestedBinding *suggestedBindings);
XrResult
oxr_action_get_current_interaction_profile(
struct oxr_logger *log,
struct oxr_session *sess,
XrPath topLevelUserPath,
XrInteractionProfileState *interactionProfile);
XrResult
oxr_action_get_input_source_localized_name(
struct oxr_logger *log,
struct oxr_session *sess,
const XrInputSourceLocalizedNameGetInfo *getInfo,
uint32_t bufferCapacityInput,
uint32_t *bufferCountOutput,
char *buffer);
XrResult
oxr_action_enumerate_bound_sources(struct oxr_logger *log,
struct oxr_session *sess,
uint64_t key,
uint32_t sourceCapacityInput,
uint32_t *sourceCountOutput,
XrPath *sources);
/*
*
* oxr_session.c
@ -825,6 +873,9 @@ struct oxr_instance
struct oxr_event *last_event;
struct oxr_event *next_event;
struct oxr_interaction_profile **profiles;
size_t num_profiles;
struct
{
@ -833,6 +884,16 @@ struct oxr_instance
XrPath left;
XrPath right;
XrPath gamepad;
XrPath khr_simple_controller;
XrPath google_daydream_controller;
XrPath htc_vive_controller;
XrPath htc_vive_pro;
XrPath microsoft_motion_controller;
XrPath microsoft_xbox_controller;
XrPath oculus_go_controller;
XrPath oculus_touch_controller;
XrPath valve_index_controller;
} path_cache;
//! Debug messengers
@ -857,6 +918,21 @@ struct oxr_session
struct u_hashmap_int *act_sets;
struct u_hashmap_int *sources;
//! Has xrAttachSessionActionSets been called?
bool actionsAttached;
/*!
* Currently bound interaction profile.
* @{
*/
XrPath left;
XrPath right;
XrPath head;
XrPath gamepad;
/*!
* @}
*/
/*!
* IPD, to be expanded to a proper 3D relation.
*/
@ -873,6 +949,36 @@ struct oxr_session
struct oxr_swapchain **);
};
/*!
* A single interaction profile.
*/
struct oxr_interaction_profile
{
XrPath path;
struct oxr_binding *bindings;
size_t num_bindings;
};
/*!
* Interaction profile binding state.
*/
struct oxr_binding
{
XrPath *paths;
size_t num_paths;
enum oxr_sub_action_path sub_path;
uint32_t *keys;
size_t num_keys;
enum xrt_input_name *inputs;
size_t num_inputs;
enum xrt_output_name *outputs;
size_t num_outputs;
};
/*!
* To carry around a sementic selection of sub action paths.
*/
@ -896,8 +1002,8 @@ struct oxr_source_set
//! Common structure for things referred to by OpenXR handles.
struct oxr_handle_base handle;
//! Which generation of the XrActionSet was this created from.
uint32_t generation;
//! Owning session.
struct oxr_session *sess;
};
/*!
@ -1072,11 +1178,11 @@ struct oxr_action_set
//! Onwer of this messenger.
struct oxr_instance *inst;
/*!
* Every change that is done to a action set will increment this
* counter and trigger a rebinding of inputs when syncing actions.
*/
uint32_t generation;
//! Application supplied name of this action.
char name[XR_MAX_ACTION_SET_NAME_SIZE];
//! Has this action set been attached.
bool attached;
//! Unique key for the session hashmap.
uint32_t key;