···296296 list(APPEND ENABLED_DRIVERS euroc)
297297endif()
298298299299+# We always build the sample driver, to make sure it stays valid, but it never gets linked
300300+add_library(drv_sample STATIC
301301+ sample/sample_hmd.c
302302+ sample/sample_interface.h
303303+ sample/sample_prober.c)
304304+target_link_libraries(drv_sample PRIVATE xrt-interfaces aux_util)
305305+299306if(ENABLED_HEADSET_DRIVERS)
300307 set(ENABLED_DRIVERS ${ENABLED_HEADSET_DRIVERS} ${ENABLED_DRIVERS})
301308 list(SORT ENABLED_DRIVERS)
+192
src/xrt/drivers/sample/sample_hmd.c
···11+// Copyright 2020-2021, Collabora, Ltd.
22+// SPDX-License-Identifier: BSL-1.0
33+/*!
44+ * @file
55+ * @brief Sample HMD device, use as a starting point to make your own device driver.
66+ *
77+ *
88+ * Based largely on dummy_hmd.c
99+ *
1010+ * @author Jakob Bornecrantz <jakob@collabora.com>
1111+ * @author Ryan Pavlik <ryan.pavlik@collabora.com>
1212+ * @ingroup drv_sample
1313+ */
1414+1515+#include "xrt/xrt_device.h"
1616+1717+#include "os/os_time.h"
1818+1919+#include "math/m_api.h"
2020+#include "math/m_mathinclude.h"
2121+2222+#include "util/u_var.h"
2323+#include "util/u_misc.h"
2424+#include "util/u_time.h"
2525+#include "util/u_debug.h"
2626+#include "util/u_device.h"
2727+#include "util/u_logging.h"
2828+#include "util/u_distortion_mesh.h"
2929+3030+#include <stdio.h>
3131+3232+3333+/*
3434+ *
3535+ * Structs and defines.
3636+ *
3737+ */
3838+3939+/*!
4040+ * A sample HMD device.
4141+ *
4242+ * @implements xrt_device
4343+ */
4444+struct sample_hmd
4545+{
4646+ struct xrt_device base;
4747+4848+ struct xrt_pose pose;
4949+5050+ enum u_logging_level log_level;
5151+};
5252+5353+5454+/// Casting helper function
5555+static inline struct sample_hmd *
5656+sample_hmd(struct xrt_device *xdev)
5757+{
5858+ return (struct sample_hmd *)xdev;
5959+}
6060+6161+DEBUG_GET_ONCE_LOG_OPTION(sample_log, "SAMPLE_LOG", U_LOGGING_WARN)
6262+6363+#define SH_TRACE(p, ...) U_LOG_XDEV_IFL_T(&sh->base, sh->log_level, __VA_ARGS__)
6464+#define SH_DEBUG(p, ...) U_LOG_XDEV_IFL_D(&sh->base, sh->log_level, __VA_ARGS__)
6565+#define SH_ERROR(p, ...) U_LOG_XDEV_IFL_E(&sh->base, sh->log_level, __VA_ARGS__)
6666+6767+static void
6868+sample_hmd_destroy(struct xrt_device *xdev)
6969+{
7070+ struct sample_hmd *sh = sample_hmd(xdev);
7171+7272+ // Remove the variable tracking.
7373+ u_var_remove_root(sh);
7474+7575+ u_device_free(&sh->base);
7676+}
7777+7878+static void
7979+sample_hmd_update_inputs(struct xrt_device *xdev)
8080+{
8181+ // Empty, you should put code to update the attached input fields (if any)
8282+}
8383+8484+static void
8585+sample_hmd_get_tracked_pose(struct xrt_device *xdev,
8686+ enum xrt_input_name name,
8787+ uint64_t at_timestamp_ns,
8888+ struct xrt_space_relation *out_relation)
8989+{
9090+ struct sample_hmd *sh = sample_hmd(xdev);
9191+9292+ if (name != XRT_INPUT_GENERIC_HEAD_POSE) {
9393+ SH_ERROR(sh, "unknown input name");
9494+ return;
9595+ }
9696+9797+ // Estimate pose at timestamp at_timestamp_ns!
9898+ math_quat_normalize(&sh->pose.orientation);
9999+ out_relation->pose = sh->pose;
100100+ out_relation->relation_flags = (enum xrt_space_relation_flags)(XRT_SPACE_RELATION_ORIENTATION_VALID_BIT |
101101+ XRT_SPACE_RELATION_POSITION_VALID_BIT |
102102+ XRT_SPACE_RELATION_ORIENTATION_TRACKED_BIT);
103103+}
104104+105105+static void
106106+sample_hmd_get_view_pose(struct xrt_device *xdev,
107107+ const struct xrt_vec3 *eye_relation,
108108+ uint32_t view_index,
109109+ struct xrt_pose *out_pose)
110110+{
111111+ (void)xdev;
112112+ // This helper function assumes a symmetric IPD
113113+ u_device_get_view_pose(eye_relation, view_index, out_pose);
114114+}
115115+116116+struct xrt_device *
117117+sample_hmd_create(void)
118118+{
119119+ // This indicates you won't be using Monado's built-in tracking algorithms.
120120+ enum u_device_alloc_flags flags =
121121+ (enum u_device_alloc_flags)(U_DEVICE_ALLOC_HMD | U_DEVICE_ALLOC_TRACKING_NONE);
122122+123123+ struct sample_hmd *sh = U_DEVICE_ALLOCATE(struct sample_hmd, flags, 1, 0);
124124+ sh->base.update_inputs = sample_hmd_update_inputs;
125125+ sh->base.get_tracked_pose = sample_hmd_get_tracked_pose;
126126+ sh->base.get_view_pose = sample_hmd_get_view_pose;
127127+ sh->base.destroy = sample_hmd_destroy;
128128+ sh->base.name = XRT_DEVICE_GENERIC_HMD;
129129+ sh->base.device_type = XRT_DEVICE_TYPE_HMD;
130130+ sh->pose.orientation.w = 1.0f; // All other values set to zero by U_DEVICE_ALLOCATE (which calls U_CALLOC)
131131+ sh->log_level = debug_get_log_option_sample_log();
132132+133133+ // Print name.
134134+ snprintf(sh->base.str, XRT_DEVICE_NAME_LEN, "Sample HMD");
135135+ snprintf(sh->base.serial, XRT_DEVICE_NAME_LEN, "Sample HMD S/N");
136136+137137+ // Setup input.
138138+ sh->base.inputs[0].name = XRT_INPUT_GENERIC_HEAD_POSE;
139139+140140+ // Set up display details
141141+ // refresh rate
142142+ sh->base.hmd->screens[0].nominal_frame_interval_ns = time_s_to_ns(1.0f / 90.0f);
143143+144144+ const double hFOV = 90 * (M_PI / 180.0);
145145+ const double vFOV = 96.73 * (M_PI / 180.0);
146146+ // center of projection
147147+ const double hCOP = 0.529;
148148+ const double vCOP = 0.5;
149149+ if (
150150+ /* right eye */
151151+ !math_compute_fovs(1, hCOP, hFOV, 1, vCOP, vFOV, &sh->base.hmd->views[1].fov) ||
152152+ /*
153153+ * left eye - same as right eye, except the horizontal center of projection is moved in the opposite
154154+ * direction now
155155+ */
156156+ !math_compute_fovs(1, 1.0 - hCOP, hFOV, 1, vCOP, vFOV, &sh->base.hmd->views[0].fov)) {
157157+ // If those failed, it means our math was impossible.
158158+ SH_ERROR(sh, "Failed to setup basic device info");
159159+ sample_hmd_destroy(&sh->base);
160160+ return NULL;
161161+ }
162162+ const int panel_w = 1080;
163163+ const int panel_h = 1200;
164164+165165+ // Single "screen" (always the case)
166166+ sh->base.hmd->screens[0].w_pixels = panel_w * 2;
167167+ sh->base.hmd->screens[0].h_pixels = panel_h;
168168+169169+ // Left, Right
170170+ for (uint8_t eye = 0; eye < 2; ++eye) {
171171+ sh->base.hmd->views[eye].display.w_pixels = panel_w;
172172+ sh->base.hmd->views[eye].display.h_pixels = panel_h;
173173+ sh->base.hmd->views[eye].viewport.y_pixels = 0;
174174+ sh->base.hmd->views[eye].viewport.w_pixels = panel_w;
175175+ sh->base.hmd->views[eye].viewport.h_pixels = panel_h;
176176+ // if rotation is not identity, the dimensions can get more complex.
177177+ sh->base.hmd->views[eye].rot = u_device_rotation_ident;
178178+ }
179179+ // left eye starts at x=0, right eye starts at x=panel_width
180180+ sh->base.hmd->views[0].viewport.x_pixels = 0;
181181+ sh->base.hmd->views[1].viewport.x_pixels = panel_w;
182182+183183+ // Setup variable tracker: Optional but useful for debugging
184184+ u_var_add_root(sh, "Sample HMD", true);
185185+ u_var_add_pose(sh, &sh->pose, "pose");
186186+ u_var_add_log_level(sh, &sh->log_level, "log_level");
187187+188188+ // Distortion information, fills in xdev->compute_distortion().
189189+ u_distortion_mesh_set_none(&sh->base);
190190+191191+ return &sh->base;
192192+}
+81
src/xrt/drivers/sample/sample_interface.h
···11+// Copyright 2020-2021, Collabora, Ltd.
22+// SPDX-License-Identifier: BSL-1.0
33+/*!
44+ * @file
55+ * @brief Interface to sample driver.
66+ * @author Jakob Bornecrantz <jakob@collabora.com>
77+ * @author Ryan Pavlik <ryan.pavlik@collabora.com>
88+ * @ingroup drv_sample
99+ */
1010+1111+#pragma once
1212+1313+#ifdef __cplusplus
1414+extern "C" {
1515+#endif
1616+1717+/*!
1818+ * @defgroup drv_sample Sample driver
1919+ * @ingroup drv
2020+ *
2121+ * @brief Simple do-nothing sample driver, that cannot be detected by USB VID/PID
2222+ * and thus exposes an "auto-prober" to explicitly discover the device.
2323+ *
2424+ * This device has an implementation of @ref xrt_auto_prober to perform hardware
2525+ * detection, as well as an implementation of @ref xrt_device for the actual device.
2626+ *
2727+ * If your device is or has USB HID that **can** be detected based on USB VID/PID,
2828+ * you can skip the @ref xrt_auto_prober implementation, and instead implement a
2929+ * "found" function that matches the signature expected by xrt_prober_entry::found.
3030+ * See for example @ref hdk_found.
3131+ *
3232+ * After you copy and rename these files, you can customize them with the following,
3333+ * assuming your new device type is called `struct my_device` or `md` for short, and
3434+ * your auto-prober is called `struct my_device_auto_prober` or `mdap` for short:
3535+ *
3636+ * ```sh
3737+ * # First pattern is for renaming device types,
3838+ * # second is for renaming device variables,
3939+ * # third is for renaming device macros.
4040+ * # Fourth and fifth are for renaming auto prober types and variables, respectively.
4141+ * # The last two are for renaming the environment variable and function name
4242+ * # for the environment variable logging config.
4343+ * sed -r -e 's/sample_hmd/my_device/g' \
4444+ * -e 's/\bsh\b/md/g' \
4545+ * -e 's/sample_auto_prober/my_device_auto_prober/g' \
4646+ * -e 's/\bsap\b/mdap/g' \
4747+ * -e 's/\bSH_/MD_/g' \
4848+ * -e 's/sample/my_device/g' \
4949+ * -e 's/SAMPLE/MY_DEVICE/g' \
5050+ * -i *.c *.h
5151+ * ```
5252+ */
5353+5454+/*!
5555+ * Create a auto prober for a sample device.
5656+ *
5757+ * @ingroup drv_sample
5858+ */
5959+struct xrt_auto_prober *
6060+sample_create_auto_prober(void);
6161+6262+/*!
6363+ * Create a sample hmd.
6464+ *
6565+ * This is only exposed so that the prober (in one source file)
6666+ * can call the construction function (in another.)
6767+ * @ingroup drv_sample
6868+ */
6969+struct xrt_device *
7070+sample_hmd_create(void);
7171+7272+/*!
7373+ * @dir drivers/sample
7474+ *
7575+ * @brief @ref drv_sample files.
7676+ */
7777+7878+7979+#ifdef __cplusplus
8080+}
8181+#endif