The open source OpenXR runtime
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

t/libmonado: Add libmonado implementation

Work done by lots of different authors that did various pieces of the library.

Co-authored-by: Korcan Hussein <korcan.hussein@collabora.com>
Co-authored-by: Jakob Bornecrantz <jakob@collabora.com>
Co-authored-by: Ryan Pavlik <ryan.pavlik@collabora.com>

+1284
+5
src/xrt/targets/CMakeLists.txt
··· 41 41 ) 42 42 add_subdirectory(sdl_test) 43 43 endif() 44 + 45 + # Monado management library 46 + if(XRT_FEATURE_SERVICE AND XRT_HAVE_LINUX) 47 + add_subdirectory(libmonado) 48 + endif()
+16
src/xrt/targets/libmonado/CMakeLists.txt
··· 1 + # Copyright 2019-2023, Collabora, Ltd. 2 + # SPDX-License-Identifier: BSL-1.0 3 + 4 + add_library(monado SHARED monado.c libmonado.def) 5 + set(LIBMONADO_HEADER_DIR ${CMAKE_INSTALL_INCLUDEDIR}/monado) 6 + target_link_libraries(monado PRIVATE aux_util ipc_client) 7 + target_include_directories( 8 + monado INTERFACE $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}> 9 + $<INSTALL_INTERFACE:${LIBMONADO_HEADER_DIR}> 10 + ) 11 + 12 + install(TARGETS monado RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) 13 + install(FILES monado.h DESTINATION ${LIBMONADO_HEADER_DIR}) 14 + 15 + add_executable(libmonado-example example.c) 16 + target_link_libraries(libmonado-example PRIVATE monado)
+200
src/xrt/targets/libmonado/example.c
··· 1 + // Copyright 2020-2023, Collabora, Ltd. 2 + // SPDX-License-Identifier: BSL-1.0 3 + /*! 4 + * @file 5 + * @brief Small cli application to demonstrate use of libmonado. 6 + * @author Ryan Pavlik <ryan.pavlik@collabora.com> 7 + * @author Pete Black <pblack@collabora.com> 8 + */ 9 + 10 + #include "monado.h" 11 + 12 + #include <ctype.h> 13 + #include <limits.h> 14 + #include <stddef.h> 15 + #include <stdio.h> 16 + #include <stdlib.h> 17 + #include <getopt.h> 18 + #include <stdbool.h> 19 + 20 + 21 + #define P(...) fprintf(stdout, __VA_ARGS__) 22 + #define PE(...) fprintf(stderr, __VA_ARGS__) 23 + #define CHECK_ID_EXIT(ID) \ 24 + do { \ 25 + if (ID < 0 || ID > INT_MAX) { \ 26 + PE("Invalid client index %i.\n", s_val); \ 27 + exit(1); \ 28 + } \ 29 + } while (false) 30 + 31 + typedef enum op_mode 32 + { 33 + MODE_GET, 34 + MODE_SET_PRIMARY, 35 + MODE_SET_FOCUSED, 36 + MODE_TOGGLE_IO, 37 + } op_mode_t; 38 + 39 + int 40 + get_mode(mnd_root_t *root) 41 + { 42 + mnd_result_t mret = mnd_root_update_client_list(root); 43 + if (mret != MND_SUCCESS) { 44 + PE("Failed to get client list.\n"); 45 + exit(1); 46 + } 47 + 48 + uint32_t num_clients = 0; 49 + mret = mnd_root_get_number_clients(root, &num_clients); 50 + if (mret != MND_SUCCESS) { 51 + PE("Failed to get client count.\n"); 52 + exit(1); 53 + } 54 + 55 + P("Clients: (%d)\n", num_clients); 56 + for (uint32_t i = 0; i < num_clients; i++) { 57 + uint32_t client_id = 0; 58 + uint32_t flags = 0; 59 + const char *name = NULL; 60 + 61 + mret = mnd_root_get_client_id_at_index(root, i, &client_id); 62 + if (mret != MND_SUCCESS) { 63 + PE("Failed to get client id for index %u", i); 64 + continue; 65 + } 66 + 67 + mret = mnd_root_get_client_state(root, client_id, &flags); 68 + if (mret != MND_SUCCESS) { 69 + PE("Failed to get client state for client id: %u (index: %u)", client_id, i); 70 + continue; 71 + } 72 + 73 + mret = mnd_root_get_client_name(root, client_id, &name); 74 + if (mret != MND_SUCCESS) { 75 + PE("Failed to get client name for client id: %u (index: %u)", client_id, i); 76 + continue; 77 + } 78 + 79 + P("\tid: % 8d" 80 + "\tact: %d" 81 + "\tdisp: %d" 82 + "\tfoc: %d" 83 + "\tio: %d" 84 + "\tovly: %d" 85 + "\t%s\n", 86 + client_id, // 87 + (flags & MND_CLIENT_SESSION_ACTIVE) != 0 ? 1 : 0, // 88 + (flags & MND_CLIENT_SESSION_VISIBLE) != 0 ? 1 : 0, // 89 + (flags & MND_CLIENT_SESSION_FOCUSED) != 0 ? 1 : 0, // 90 + (flags & MND_CLIENT_IO_ACTIVE) != 0 ? 1 : 0, // 91 + (flags & MND_CLIENT_SESSION_OVERLAY) != 0 ? 1 : 0, // 92 + name); 93 + } 94 + 95 + return 0; 96 + } 97 + 98 + int 99 + set_primary(mnd_root_t *root, int client_index) 100 + { 101 + mnd_result_t mret = mnd_root_set_client_primary(root, client_index); 102 + if (mret != MND_SUCCESS) { 103 + PE("Failed to set active client to index %d.\n", client_index); 104 + return 1; 105 + } 106 + 107 + return 0; 108 + } 109 + 110 + int 111 + set_focused(mnd_root_t *root, int client_index) 112 + { 113 + mnd_result_t mret = mnd_root_set_client_focused(root, client_index); 114 + if (mret != MND_SUCCESS) { 115 + PE("Failed to set focused client to index %d.\n", client_index); 116 + return 1; 117 + } 118 + 119 + return 0; 120 + } 121 + 122 + int 123 + toggle_io(mnd_root_t *root, int client_index) 124 + { 125 + mnd_result_t mret = mnd_root_toggle_client_io_active(root, client_index); 126 + if (mret != MND_SUCCESS) { 127 + PE("Failed to toggle io for client index %d.\n", client_index); 128 + return 1; 129 + } 130 + return 0; 131 + } 132 + 133 + int 134 + main(int argc, char *argv[]) 135 + { 136 + op_mode_t op_mode = MODE_GET; 137 + 138 + // parse arguments 139 + int c; 140 + int s_val = 0; 141 + 142 + opterr = 0; 143 + while ((c = getopt(argc, argv, "p:f:i:")) != -1) { 144 + switch (c) { 145 + case 'p': 146 + s_val = atoi(optarg); 147 + CHECK_ID_EXIT(s_val); 148 + op_mode = MODE_SET_PRIMARY; 149 + break; 150 + case 'f': 151 + s_val = atoi(optarg); 152 + CHECK_ID_EXIT(s_val); 153 + op_mode = MODE_SET_FOCUSED; 154 + break; 155 + case 'i': 156 + s_val = atoi(optarg); 157 + CHECK_ID_EXIT(s_val); 158 + op_mode = MODE_TOGGLE_IO; 159 + break; 160 + case '?': 161 + if (optopt == 's') { 162 + PE("Option -s requires a client index to set.\n"); 163 + } else if (isprint(optopt)) { 164 + PE("Option `-%c' unknown. Usage:\n", optopt); 165 + PE(" -f <index>: Set focused client\n"); 166 + PE(" -p <index>: Set primary client\n"); 167 + PE(" -i <index>: Toggle whether client receives input\n"); 168 + } else { 169 + PE("Option `\\x%x' unknown.\n", optopt); 170 + } 171 + exit(1); 172 + default: exit(0); 173 + } 174 + } 175 + 176 + mnd_root_t *root = NULL; 177 + mnd_result_t mret; 178 + 179 + mret = mnd_root_create(&root); 180 + if (mret != MND_SUCCESS) { 181 + PE("Failed to connect."); 182 + return 1; 183 + } 184 + 185 + mret = mnd_root_update_client_list(root); 186 + if (mret != MND_SUCCESS) { 187 + PE("Failed to update client list."); 188 + return 1; 189 + } 190 + 191 + switch (op_mode) { 192 + case MODE_GET: exit(get_mode(root)); break; 193 + case MODE_SET_PRIMARY: exit(set_primary(root, s_val)); break; 194 + case MODE_SET_FOCUSED: exit(set_focused(root, s_val)); break; 195 + case MODE_TOGGLE_IO: exit(toggle_io(root, s_val)); break; 196 + default: P("Unrecognised operation mode.\n"); exit(1); 197 + } 198 + 199 + return 0; 200 + }
+189
src/xrt/targets/libmonado/example.lua
··· 1 + #!/usr/bin/env luajit 2 + -- Copyright 2020-2023, Collabora, Ltd. 3 + -- SPDX-License-Identifier: BSL-1.0 4 + -- Author: Lubosz Sarnecki <lubosz.sarnecki@collabora.com> 5 + 6 + local ffi = require("ffi") 7 + 8 + ffi.cdef[[ 9 + typedef enum mnd_result 10 + { 11 + MND_SUCCESS = 0, 12 + MND_ERROR_INVALID_VERSION = -1, 13 + MND_ERROR_INVALID_VALUE = -2, 14 + MND_ERROR_CONNECTING_FAILED = -3, 15 + MND_ERROR_OPERATION_FAILED = -4, 16 + } mnd_result_t; 17 + 18 + typedef enum mnd_client_flags 19 + { 20 + MND_CLIENT_PRIMARY_APP = (1u << 0u), 21 + MND_CLIENT_SESSION_ACTIVE = (1u << 1u), 22 + MND_CLIENT_SESSION_VISIBLE = (1u << 2u), 23 + MND_CLIENT_SESSION_FOCUSED = (1u << 3u), 24 + MND_CLIENT_SESSION_OVERLAY = (1u << 4u), 25 + MND_CLIENT_IO_ACTIVE = (1u << 5u), 26 + } mnd_client_flags_t; 27 + 28 + typedef struct mnd_root mnd_root_t; 29 + 30 + void 31 + mnd_api_get_version(uint32_t *out_major, uint32_t *out_minor, uint32_t *out_patch); 32 + 33 + mnd_result_t 34 + mnd_root_create(mnd_root_t **out_root); 35 + 36 + void 37 + mnd_root_destroy(mnd_root_t **root_ptr); 38 + 39 + mnd_result_t 40 + mnd_root_update_client_list(mnd_root_t *root); 41 + 42 + mnd_result_t 43 + mnd_root_get_number_clients(mnd_root_t *root, uint32_t *out_num); 44 + 45 + mnd_result_t 46 + mnd_root_get_client_id_at_index(mnd_root_t *root, uint32_t index, uint32_t *out_client_id); 47 + 48 + mnd_result_t 49 + mnd_root_get_client_name(mnd_root_t *root, uint32_t client_id, const char **out_name); 50 + 51 + mnd_result_t 52 + mnd_root_get_client_state(mnd_root_t *root, uint32_t client_id, uint32_t *out_flags); 53 + 54 + mnd_result_t 55 + mnd_root_set_client_primary(mnd_root_t *root, uint32_t client_id); 56 + 57 + mnd_result_t 58 + mnd_root_set_client_focused(mnd_root_t *root, uint32_t client_id); 59 + 60 + mnd_result_t 61 + mnd_root_toggle_client_io_active(mnd_root_t *root, uint32_t client_id); 62 + ]] 63 + 64 + local status, lib = pcall(ffi.load, "libmonado.so") 65 + if not status then 66 + print("Could not find an installed libmonado.so in your path.") 67 + print("Add the Monado build directory to your LD_LIBRARY_PATH:") 68 + print(string.format("LD_LIBRARY_PATH=/home/user/monado/build/src/xrt/targets/libmonado/ %s", arg[0])) 69 + os.exit(1) 70 + end 71 + 72 + -- Parse arguments 73 + local args = {...} 74 + for i=1, #args, 1 do 75 + if args[i] == "-f" or args[i] == "--focused" then 76 + args_focused = tonumber(args[i+1]) 77 + elseif args[i] == "-p" or args[i] == "--primary" then 78 + args_primary = tonumber(args[i+1]) 79 + elseif args[i] == "-i" or args[i] == "--input" then 80 + args_input = tonumber(args[i+1]) 81 + end 82 + end 83 + 84 + -- Using ** doesn't work here, it hits an assertion in moando due being NULL 85 + local root_ptr = ffi.new("mnd_root_t*[1]") 86 + 87 + function create_root() 88 + local ret = lib.mnd_root_create(root_ptr) 89 + if ret ~= 0 then 90 + error("Could not create root") 91 + end 92 + return root_ptr[0] 93 + end 94 + 95 + local root = create_root() 96 + 97 + function update_clients() 98 + local ret = lib.mnd_root_update_client_list(root) 99 + if ret ~= 0 then 100 + error("Could not update clients") 101 + end 102 + end 103 + 104 + function get_client_count() 105 + client_count_ptr = ffi.new("uint32_t[1]") 106 + ret = lib.mnd_root_get_number_clients(root, client_count_ptr) 107 + if ret ~= 0 then 108 + error("Could not get number of clients") 109 + end 110 + return client_count_ptr[0] 111 + end 112 + 113 + function get_client_id_at_index(index) 114 + client_id_ptr = ffi.new("uint32_t[1]") 115 + local ret = lib.mnd_root_get_client_id_at_index(root, index, client_id_ptr) 116 + if ret ~= 0 then 117 + error("Could not get client id at index.") 118 + end 119 + return client_id_ptr[0] 120 + end 121 + 122 + function get_client_name(client_id) 123 + name_ptr = ffi.new("const char*[1]") 124 + local ret = lib.mnd_root_get_client_name(root, client_id, name_ptr) 125 + if ret ~= 0 then 126 + error("Could not get client name.") 127 + end 128 + return name_ptr[0] 129 + end 130 + 131 + function get_client_flags(client_id) 132 + flags_ptr = ffi.new("uint32_t[1]") 133 + local ret = lib.mnd_root_get_client_state(root, client_id, flags_ptr) 134 + if ret ~= 0 then 135 + error("Could not get client flags.") 136 + end 137 + return flags_ptr[0] 138 + end 139 + 140 + function set_primary(client_id) 141 + local ret = lib.mnd_root_set_client_primary(root, client_id) 142 + if ret ~= 0 then 143 + error("Failed to set primary client to client id", client_id) 144 + end 145 + end 146 + 147 + function set_focused(client_id) 148 + ret = lib.mnd_root_set_client_focused(root, client_id) 149 + if ret ~= 0 then 150 + error("Failed to set focused client to client id", client_id) 151 + end 152 + end 153 + 154 + function toggle_io(client_id) 155 + ret = lib.mnd_root_toggle_client_io_active(root, client_id) 156 + if ret ~= 0 then 157 + error("Failed to toggle io for client client id", client_id) 158 + end 159 + end 160 + 161 + if args_primary then 162 + set_primary(args_primary) 163 + end 164 + if args_focused then 165 + set_focused(args_focused) 166 + end 167 + if args_input then 168 + toggle_io(args_input) 169 + end 170 + 171 + update_clients(root) 172 + print("Client count:", get_client_count()) 173 + 174 + for i = 0, get_client_count() - 1 do 175 + local client_id = get_client_id_at_index(i) 176 + local name = get_client_name(client_id) 177 + local flags = get_client_flags(client_id) 178 + local primary = bit.band(flags, lib.MND_CLIENT_PRIMARY_APP) ~= 0 179 + local active = bit.band(flags, lib.MND_CLIENT_SESSION_ACTIVE) ~= 0 180 + local visible = bit.band(flags, lib.MND_CLIENT_SESSION_VISIBLE) ~= 0 181 + local focused = bit.band(flags, lib.MND_CLIENT_SESSION_FOCUSED) ~= 0 182 + local overlay = bit.band(flags, lib.MND_CLIENT_SESSION_OVERLAY) ~= 0 183 + local io_active = bit.band(flags, lib.MND_CLIENT_IO_ACTIVE) ~= 0 184 + print(string.format("id: %d primary: %5s active: %5s visible: %5s focused: %5s io: %5s overlay: %5s name: %s", 185 + client_id, tostring(primary), tostring(active), tostring(visible), tostring(focused), 186 + tostring(io_active), tostring(overlay), ffi.string(name))) 187 + end 188 + 189 + lib.mnd_root_destroy(root_ptr)
+65
src/xrt/targets/libmonado/example.py
··· 1 + #!/bin/env python3 2 + # Copyright 2020-2023, Collabora, Ltd. 3 + # SPDX-License-Identifier: BSL-1.0 4 + # Author: Jakob Bornecrantz <jakob@collabora.com> 5 + # Author: Lubosz Sarnecki <lubosz.sarnecki@collabora.com> 6 + # Author: Korcan Hussein <korcan.hussein@collabora.com> 7 + 8 + import argparse 9 + from monado import Monado, MonadoLibraryNotFoundError, MonadoHeaderNotFoundError 10 + 11 + 12 + def main(): 13 + parser = argparse.ArgumentParser(description='libmonado Python example.') 14 + parser.add_argument("-f", "--focused", type=int, metavar='CLIENT_ID', 15 + help="Set focused client") 16 + parser.add_argument("-p", "--primary", type=int, metavar='CLIENT_ID', 17 + help="Set primary client") 18 + parser.add_argument("-i", "--input", type=int, metavar='CLIENT_ID', 19 + help="Toggle whether client receives input") 20 + args = parser.parse_args() 21 + 22 + try: 23 + m = Monado() 24 + except MonadoLibraryNotFoundError: 25 + print("Could not find an installed libmonado.so in your path.") 26 + print("Add the Monado build directory to your LD_LIBRARY_PATH:") 27 + print(f"LD_LIBRARY_PATH=/home/user/monado/build/src/xrt/targets/libmonado/ ./{parser.prog}") 28 + return 29 + except MonadoHeaderNotFoundError: 30 + print("Could not find an installed monado.h.") 31 + print("Try setting the MONADO_HEADER_PATH manually:") 32 + print(f"MONADO_HEADER_PATH=/home/user/monado/src/xrt/targets/libmonado/monado.h ./{parser.prog}") 33 + return 34 + 35 + if args.focused: 36 + m.set_focused(args.focused) 37 + if args.primary: 38 + m.set_primary(args.primary) 39 + if args.input: 40 + m.toggle_io(args.input) 41 + 42 + m.update_clients() 43 + 44 + print(f"Clients: {m.client_count}") 45 + for x in range(m.client_count): 46 + c = m.snapshot_client(x) 47 + print(f"\tid: {c.ident:4d}, primary: {c.primary:d}, active: {c.active:d}, " 48 + f"visible: {c.visible:d}, focused: {c.focused:d}, io: {c.io_active:d}, " 49 + f"overlay: {c.overlay:d}, name: {c.name}") 50 + 51 + devices = m.get_devices() 52 + print(f"Devices: {len(devices)}") 53 + for dev in devices: 54 + print(f"\tid: {dev.ident:4d}, name: {dev.name}") 55 + 56 + roles_map = m.get_device_roles() 57 + print(f"Roles: {len(roles_map)}") 58 + for role_name, dev_id in roles_map.items(): 59 + print(f"\trole: {role_name},\tdevice-index: {dev_id:4d}") 60 + 61 + m.destroy() 62 + 63 + 64 + if __name__ == "__main__": 65 + main()
+15
src/xrt/targets/libmonado/libmonado.def
··· 1 + ; Copyright 2019-2023, Collabora, Ltd. 2 + ; SPDX-License-Identifier: BSL-1.0 3 + EXPORTS 4 + mnd_root_create 5 + mnd_root_destroy 6 + mnd_root_update_client_list 7 + mnd_root_get_number_clients 8 + mnd_root_get_client_name 9 + mnd_root_get_client_state 10 + mnd_root_set_client_primary 11 + mnd_root_set_client_focused 12 + mnd_root_toggle_client_io_active 13 + mnd_root_get_device_count 14 + mnd_root_get_device_info 15 + mnd_root_get_device_from_role
+336
src/xrt/targets/libmonado/monado.c
··· 1 + // Copyright 2019-2023, Collabora, Ltd. 2 + // SPDX-License-Identifier: BSL-1.0 3 + /*! 4 + * @file 5 + * @brief Implementation of libmonado 6 + * @author Jakob Bornecrantz <jakob@collabora.com> 7 + * @author Pete Black <pblack@collabora.com> 8 + * @author Ryan Pavlik <ryan.pavlik@collabora.com> 9 + */ 10 + 11 + #include "monado.h" 12 + 13 + #include "xrt/xrt_results.h" 14 + 15 + #include "util/u_misc.h" 16 + #include "util/u_file.h" 17 + #include "util/u_logging.h" 18 + 19 + #include "shared/ipc_protocol.h" 20 + 21 + #include "client/ipc_client_connection.h" 22 + #include "client/ipc_client.h" 23 + #include "ipc_client_generated.h" 24 + 25 + #include <limits.h> 26 + #include <stdint.h> 27 + #include <assert.h> 28 + 29 + 30 + struct mnd_root 31 + { 32 + struct ipc_connection ipc_c; 33 + 34 + //! List of clients. 35 + struct ipc_client_list clients; 36 + 37 + /// State of most recent app asked about 38 + struct ipc_app_state app_state; 39 + }; 40 + 41 + #define P(...) fprintf(stdout, __VA_ARGS__) 42 + #define PE(...) fprintf(stderr, __VA_ARGS__) 43 + 44 + 45 + /* 46 + * 47 + * Helper functions. 48 + * 49 + */ 50 + 51 + #define CHECK_NOT_NULL(ARG) \ 52 + do { \ 53 + if (ARG == NULL) { \ 54 + PE("Argument '" #ARG "' can not be null!"); \ 55 + return MND_ERROR_INVALID_VALUE; \ 56 + } \ 57 + } while (false) 58 + 59 + #define CHECK_CLIENT_ID(ID) \ 60 + do { \ 61 + if (ID == 0 && ID > INT_MAX) { \ 62 + PE("Invalid client id (%u)", ID); \ 63 + return MND_ERROR_INVALID_VALUE; \ 64 + } \ 65 + } while (false) 66 + 67 + #define CHECK_CLIENT_INDEX(INDEX) \ 68 + do { \ 69 + if (INDEX >= root->clients.id_count) { \ 70 + PE("Invalid client index, too large (%u)", INDEX); \ 71 + return MND_ERROR_INVALID_VALUE; \ 72 + } \ 73 + } while (false) 74 + 75 + static int 76 + get_client_info(mnd_root_t *root, uint32_t client_id) 77 + { 78 + assert(root != NULL); 79 + 80 + xrt_result_t r = ipc_call_system_get_client_info(&root->ipc_c, client_id, &root->app_state); 81 + if (r != XRT_SUCCESS) { 82 + PE("Failed to get client info for client id: %u.\n", client_id); 83 + return MND_ERROR_INVALID_VALUE; 84 + } 85 + 86 + return MND_SUCCESS; 87 + } 88 + 89 + 90 + /* 91 + * 92 + * API API. 93 + * 94 + */ 95 + 96 + void 97 + mnd_api_get_version(uint32_t *out_major, uint32_t *out_minor, uint32_t *out_patch) 98 + { 99 + *out_major = MND_API_VERSION_MAJOR; 100 + *out_minor = MND_API_VERSION_MINOR; 101 + *out_patch = MND_API_VERSION_PATCH; 102 + } 103 + 104 + 105 + /* 106 + * 107 + * Root API 108 + * 109 + */ 110 + 111 + mnd_result_t 112 + mnd_root_create(mnd_root_t **out_root) 113 + { 114 + CHECK_NOT_NULL(out_root); 115 + 116 + mnd_root_t *r = U_TYPED_CALLOC(mnd_root_t); 117 + 118 + struct xrt_instance_info info = {0}; 119 + snprintf(info.application_name, sizeof(info.application_name), "%s", "libmonado"); 120 + 121 + xrt_result_t xret = ipc_client_connection_init(&r->ipc_c, U_LOGGING_INFO, &info); 122 + if (xret != XRT_SUCCESS) { 123 + PE("Connection init error '%i'!\n", xret); 124 + free(r); 125 + return MND_ERROR_CONNECTING_FAILED; 126 + } 127 + 128 + *out_root = r; 129 + 130 + return MND_SUCCESS; 131 + } 132 + 133 + void 134 + mnd_root_destroy(mnd_root_t **root_ptr) 135 + { 136 + if (root_ptr == NULL) { 137 + return; 138 + } 139 + 140 + mnd_root_t *r = *root_ptr; 141 + if (r == NULL) { 142 + return; 143 + } 144 + 145 + ipc_client_connection_fini(&r->ipc_c); 146 + free(r); 147 + 148 + *root_ptr = NULL; 149 + 150 + return; 151 + } 152 + 153 + mnd_result_t 154 + mnd_root_update_client_list(mnd_root_t *root) 155 + { 156 + CHECK_NOT_NULL(root); 157 + 158 + xrt_result_t r = ipc_call_system_get_clients(&root->ipc_c, &root->clients); 159 + if (r != XRT_SUCCESS) { 160 + PE("Failed to get client list.\n"); 161 + return MND_ERROR_OPERATION_FAILED; 162 + } 163 + 164 + return MND_SUCCESS; 165 + } 166 + 167 + mnd_result_t 168 + mnd_root_get_number_clients(mnd_root_t *root, uint32_t *out_num) 169 + { 170 + CHECK_NOT_NULL(root); 171 + CHECK_NOT_NULL(out_num); 172 + 173 + *out_num = root->clients.id_count; 174 + 175 + return MND_SUCCESS; 176 + } 177 + 178 + mnd_result_t 179 + mnd_root_get_client_id_at_index(mnd_root_t *root, uint32_t index, uint32_t *out_client_id) 180 + { 181 + CHECK_NOT_NULL(root); 182 + CHECK_CLIENT_INDEX(index); 183 + 184 + *out_client_id = root->clients.ids[index]; 185 + 186 + return MND_SUCCESS; 187 + } 188 + 189 + mnd_result_t 190 + mnd_root_get_client_name(mnd_root_t *root, uint32_t client_id, const char **out_name) 191 + { 192 + CHECK_NOT_NULL(root); 193 + CHECK_CLIENT_ID(client_id); 194 + CHECK_NOT_NULL(out_name); 195 + 196 + mnd_result_t mret = get_client_info(root, client_id); 197 + if (mret < 0) { 198 + return mret; // Prints error. 199 + } 200 + 201 + *out_name = &(root->app_state.info.application_name[0]); 202 + 203 + return MND_SUCCESS; 204 + } 205 + 206 + mnd_result_t 207 + mnd_root_get_client_state(mnd_root_t *root, uint32_t client_id, uint32_t *out_flags) 208 + { 209 + CHECK_NOT_NULL(root); 210 + CHECK_CLIENT_ID(client_id); 211 + CHECK_NOT_NULL(out_flags); 212 + 213 + mnd_result_t mret = get_client_info(root, client_id); 214 + if (mret < 0) { 215 + return mret; // Prints error. 216 + } 217 + 218 + uint32_t flags = 0; 219 + flags |= (root->app_state.primary_application) ? MND_CLIENT_PRIMARY_APP : 0u; 220 + flags |= (root->app_state.session_active) ? MND_CLIENT_SESSION_ACTIVE : 0u; 221 + flags |= (root->app_state.session_visible) ? MND_CLIENT_SESSION_VISIBLE : 0u; 222 + flags |= (root->app_state.session_focused) ? MND_CLIENT_SESSION_FOCUSED : 0u; 223 + flags |= (root->app_state.session_overlay) ? MND_CLIENT_SESSION_OVERLAY : 0u; 224 + flags |= (root->app_state.io_active) ? MND_CLIENT_IO_ACTIVE : 0u; 225 + *out_flags = flags; 226 + 227 + return MND_SUCCESS; 228 + } 229 + 230 + mnd_result_t 231 + mnd_root_set_client_primary(mnd_root_t *root, uint32_t client_id) 232 + { 233 + CHECK_NOT_NULL(root); 234 + CHECK_CLIENT_ID(client_id); 235 + 236 + xrt_result_t r = ipc_call_system_set_primary_client(&root->ipc_c, client_id); 237 + if (r != XRT_SUCCESS) { 238 + PE("Failed to set primary to client id: %u.\n", client_id); 239 + return MND_ERROR_OPERATION_FAILED; 240 + } 241 + 242 + return MND_SUCCESS; 243 + } 244 + 245 + mnd_result_t 246 + mnd_root_set_client_focused(mnd_root_t *root, uint32_t client_id) 247 + { 248 + CHECK_NOT_NULL(root); 249 + CHECK_CLIENT_ID(client_id); 250 + 251 + xrt_result_t r = ipc_call_system_set_focused_client(&root->ipc_c, client_id); 252 + if (r != XRT_SUCCESS) { 253 + PE("Failed to set focused to client id: %u.\n", client_id); 254 + return MND_ERROR_OPERATION_FAILED; 255 + } 256 + 257 + return MND_SUCCESS; 258 + } 259 + 260 + mnd_result_t 261 + mnd_root_toggle_client_io_active(mnd_root_t *root, uint32_t client_id) 262 + { 263 + CHECK_NOT_NULL(root); 264 + CHECK_CLIENT_ID(client_id); 265 + 266 + xrt_result_t r = ipc_call_system_toggle_io_client(&root->ipc_c, client_id); 267 + if (r != XRT_SUCCESS) { 268 + PE("Failed to toggle io for client id: %u.\n", client_id); 269 + return MND_ERROR_OPERATION_FAILED; 270 + } 271 + 272 + return MND_SUCCESS; 273 + } 274 + 275 + mnd_result_t 276 + mnd_root_get_device_count(mnd_root_t *root, uint32_t *out_device_count) 277 + { 278 + CHECK_NOT_NULL(root); 279 + CHECK_NOT_NULL(out_device_count); 280 + 281 + *out_device_count = root->ipc_c.ism->isdev_count; 282 + 283 + return MND_SUCCESS; 284 + } 285 + 286 + mnd_result_t 287 + mnd_root_get_device_info(mnd_root_t *root, uint32_t device_index, uint32_t *out_device_id, const char **out_dev_name) 288 + { 289 + CHECK_NOT_NULL(root); 290 + CHECK_NOT_NULL(out_device_id); 291 + CHECK_NOT_NULL(out_dev_name); 292 + 293 + if (device_index >= root->ipc_c.ism->isdev_count) { 294 + PE("Invalid device index (%u)", device_index); 295 + return MND_ERROR_INVALID_VALUE; 296 + } 297 + 298 + const struct ipc_shared_device *shared_device = &root->ipc_c.ism->isdevs[device_index]; 299 + *out_device_id = shared_device->name; 300 + *out_dev_name = shared_device->str; 301 + 302 + return MND_SUCCESS; 303 + } 304 + 305 + mnd_result_t 306 + mnd_root_get_device_from_role(mnd_root_t *root, const char *role_name, int32_t *out_device_id) 307 + { 308 + CHECK_NOT_NULL(root); 309 + CHECK_NOT_NULL(role_name); 310 + CHECK_NOT_NULL(out_device_id); 311 + 312 + #ifndef MND_PP_DEV_ID_FROM_ROLE 313 + #define MND_PP_DEV_ID_FROM_ROLE(role) \ 314 + if (strcmp(role_name, #role) == 0) { \ 315 + *out_device_id = (int32_t)root->ipc_c.ism->roles.role; \ 316 + return MND_SUCCESS; \ 317 + } 318 + #endif 319 + MND_PP_DEV_ID_FROM_ROLE(head) 320 + MND_PP_DEV_ID_FROM_ROLE(left) 321 + MND_PP_DEV_ID_FROM_ROLE(right) 322 + MND_PP_DEV_ID_FROM_ROLE(gamepad) 323 + MND_PP_DEV_ID_FROM_ROLE(eyes) 324 + #undef MND_PP_DEV_ID_FROM_ROLE 325 + if (strcmp(role_name, "hand-tracking-left") == 0) { 326 + *out_device_id = root->ipc_c.ism->roles.hand_tracking.left; 327 + return MND_SUCCESS; 328 + } 329 + if (strcmp(role_name, "hand-tracking-right") == 0) { 330 + *out_device_id = root->ipc_c.ism->roles.hand_tracking.right; 331 + return MND_SUCCESS; 332 + } 333 + 334 + PE("Invalid role name (%s)", role_name); 335 + return MND_ERROR_INVALID_VALUE; 336 + }
+247
src/xrt/targets/libmonado/monado.h
··· 1 + // Copyright 2019-2023, Collabora, Ltd. 2 + // SPDX-License-Identifier: BSL-1.0 3 + /*! 4 + * @file 5 + * @brief Interface of libmonado 6 + * @author Jakob Bornecrantz <jakob@collabora.com> 7 + * @author Ryan Pavlik <ryan.pavlik@collabora.com> 8 + */ 9 + 10 + #include <stdint.h> 11 + 12 + 13 + #ifdef __cplusplus 14 + extern "C" { 15 + #endif 16 + 17 + 18 + /* 19 + * 20 + * Enums, defines and objects. 21 + * 22 + */ 23 + 24 + //! Major version of the API. 25 + #define MND_API_VERSION_MAJOR 1 26 + //! Minor version of the API. 27 + #define MND_API_VERSION_MINOR 0 28 + //! Patch version of the API. 29 + #define MND_API_VERSION_PATCH 0 30 + 31 + /*! 32 + * Result codes for operations, negative are errors, zero or positives are 33 + * success. 34 + */ 35 + typedef enum mnd_result 36 + { 37 + MND_SUCCESS = 0, 38 + MND_ERROR_INVALID_VERSION = -1, 39 + MND_ERROR_INVALID_VALUE = -2, 40 + MND_ERROR_CONNECTING_FAILED = -3, 41 + MND_ERROR_OPERATION_FAILED = -4, 42 + } mnd_result_t; 43 + 44 + /*! 45 + * Bitflags for client application state. 46 + */ 47 + typedef enum mnd_client_flags 48 + { 49 + MND_CLIENT_PRIMARY_APP = (1u << 0u), 50 + MND_CLIENT_SESSION_ACTIVE = (1u << 1u), 51 + MND_CLIENT_SESSION_VISIBLE = (1u << 2u), 52 + MND_CLIENT_SESSION_FOCUSED = (1u << 3u), 53 + MND_CLIENT_SESSION_OVERLAY = (1u << 4u), 54 + MND_CLIENT_IO_ACTIVE = (1u << 5u), 55 + } mnd_client_flags_t; 56 + 57 + /*! 58 + * Opaque type for libmonado state 59 + */ 60 + typedef struct mnd_root mnd_root_t; 61 + 62 + 63 + /* 64 + * 65 + * Functions 66 + * 67 + */ 68 + 69 + /*! 70 + * Returns the version of the API (not Monado itself), follows the versioning 71 + * semantics of https://semver.org/ standard. In short if the major version 72 + * mismatch then the interface is incompatible. 73 + * 74 + * @param[out] out_major Major version number, must be valid pointer. 75 + * @param[out] out_minor Minor version number, must be valid pointer. 76 + * @param[out] out_patch Patch version number, must be valid pointer. 77 + * 78 + * Always succeeds, or crashes if any pointer isn't valid. 79 + */ 80 + void 81 + mnd_api_get_version(uint32_t *out_major, uint32_t *out_minor, uint32_t *out_patch); 82 + 83 + /*! 84 + * Create libmonado state and connect to service 85 + * 86 + * @param[out] out_root Address to populate with the opaque state type. 87 + * @return MND_SUCCESS on success 88 + */ 89 + mnd_result_t 90 + mnd_root_create(mnd_root_t **out_root); 91 + 92 + /*! 93 + * Destroy libmonado state, disconnecting from the service, and zeroing the 94 + * pointer. 95 + * 96 + * @param root_ptr Pointer to your libmonado state. Null-checked, will be set to null. 97 + */ 98 + void 99 + mnd_root_destroy(mnd_root_t **root_ptr); 100 + 101 + /*! 102 + * Update our local cached copy of the client list 103 + * 104 + * @param root The libmonado state. 105 + * @return MND_SUCCESS on success 106 + */ 107 + mnd_result_t 108 + mnd_root_update_client_list(mnd_root_t *root); 109 + 110 + /*! 111 + * Get the number of active clients 112 + * 113 + * This value only changes on calls to @ref mnd_root_update_client_list 114 + * 115 + * @param root The libmonado state. 116 + * @param[out] out_num Pointer to value to populate with the number of clients. 117 + * 118 + * @pre Called @ref mnd_root_update_client_list at least once 119 + * 120 + * @return MND_SUCCESS on success 121 + */ 122 + mnd_result_t 123 + mnd_root_get_number_clients(mnd_root_t *root, uint32_t *out_num); 124 + 125 + /*! 126 + * Get the id from the current client list. 127 + * 128 + * @param root The libmonado state. 129 + * @param index Index to retrieve id for. 130 + * @param[out] out_client_id Pointer to value to populate with the id at the given index. 131 + */ 132 + mnd_result_t 133 + mnd_root_get_client_id_at_index(mnd_root_t *root, uint32_t index, uint32_t *out_client_id); 134 + 135 + /*! 136 + * Get the name of the client at the given index. 137 + * 138 + * The string returned is only valid until the next call into libmonado. 139 + * 140 + * @param root The libmonado state. 141 + * @param client_id ID of client to retrieve name from. 142 + * @param[out] out_name Pointer to populate with the client name. 143 + * 144 + * @pre Called @ref mnd_root_update_client_list at least once 145 + * 146 + * @return MND_SUCCESS on success 147 + */ 148 + mnd_result_t 149 + mnd_root_get_client_name(mnd_root_t *root, uint32_t client_id, const char **out_name); 150 + 151 + /*! 152 + * Get the state flags of the client at the given index. 153 + * 154 + * This result only changes on calls to @ref mnd_root_update_client_list 155 + * 156 + * @param root The libmonado state. 157 + * @param client_id ID of client to retrieve flags from. 158 + * @param[out] out_flags Pointer to populate with the flags, a bitwise combination of @ref mnd_client_flags. 159 + * 160 + * @pre Called @ref mnd_root_update_client_list at least once 161 + * 162 + * @return MND_SUCCESS on success 163 + */ 164 + mnd_result_t 165 + mnd_root_get_client_state(mnd_root_t *root, uint32_t client_id, uint32_t *out_flags); 166 + 167 + /*! 168 + * Set the client at the given index as "primary". 169 + * 170 + * @param root The libmonado state. 171 + * @param client_id ID of the client set as primary. 172 + * 173 + * @pre Called @ref mnd_root_update_client_list at least once 174 + * 175 + * @return MND_SUCCESS on success 176 + */ 177 + mnd_result_t 178 + mnd_root_set_client_primary(mnd_root_t *root, uint32_t client_id); 179 + 180 + /*! 181 + * Set the client at the given index as "focused". 182 + * 183 + * @param root The libmonado state. 184 + * @param client_id ID of the client set as focused. 185 + * 186 + * @pre Called @ref mnd_root_update_client_list at least once 187 + * 188 + * @return MND_SUCCESS on success 189 + */ 190 + mnd_result_t 191 + mnd_root_set_client_focused(mnd_root_t *root, uint32_t client_id); 192 + 193 + /*! 194 + * Toggle io activity for the client at the given index. 195 + * 196 + * @param root The libmonado state. 197 + * @param client_id ID of the client to toggle IO for. 198 + * 199 + * @pre Called @ref mnd_root_update_client_list at least once 200 + * 201 + * @return MND_SUCCESS on success 202 + */ 203 + mnd_result_t 204 + mnd_root_toggle_client_io_active(mnd_root_t *root, uint32_t client_id); 205 + 206 + /*! 207 + * Get the number of devices 208 + * 209 + * @param root The libmonado state. 210 + * @param[out] out_device_count Pointer to value to populate with the number of devices. 211 + * 212 + * @return MND_SUCCESS on success 213 + */ 214 + mnd_result_t 215 + mnd_root_get_device_count(mnd_root_t *root, uint32_t *out_device_count); 216 + 217 + /*! 218 + * Get device info at the given index. 219 + * 220 + * @param root The libmonado state. 221 + * @param device_index Index of device to retrieve name from. 222 + * @param[out] out_device_id Pointer to value to populate with the device id at the given index. 223 + * @param[out] out_dev_name Pointer to populate with the device name. 224 + * 225 + * @return MND_SUCCESS on success 226 + */ 227 + mnd_result_t 228 + mnd_root_get_device_info(mnd_root_t *root, uint32_t device_index, uint32_t *out_device_id, const char **out_dev_name); 229 + 230 + /*! 231 + * Get the device index associated for a given role name. 232 + * 233 + * @param root The libmonado state. 234 + * @param role_name Name of the role, one-of 235 + * "head","left","right"."gamepad","eyes","hand-tracking-left","hand-tracking-right" 236 + * @param[out] out_device_id Pointer to value to populate with the device id associated with given role name, -1 if not 237 + * role is set. 238 + * 239 + * @return MND_SUCCESS on success 240 + */ 241 + mnd_result_t 242 + mnd_root_get_device_from_role(mnd_root_t *root, const char *role_name, int32_t *out_device_id); 243 + 244 + 245 + #ifdef __cplusplus 246 + } 247 + #endif
+211
src/xrt/targets/libmonado/monado.py
··· 1 + # Copyright 2020-2023, Collabora, Ltd. 2 + # SPDX-License-Identifier: BSL-1.0 3 + # Author: Jakob Bornecrantz <jakob@collabora.com> 4 + # Author: Lubosz Sarnecki <lubosz.sarnecki@collabora.com> 5 + # Author: Korcan Hussein <korcan.hussein@collabora.com> 6 + 7 + import os 8 + from cffi import FFI 9 + from pathlib import Path 10 + 11 + 12 + def preprocess_ffi(lines: list[str]) -> str: 13 + out_lines = [] 14 + skipping = False 15 + for line in lines: 16 + if line.startswith("#ifdef"): 17 + skipping = True 18 + continue 19 + elif line.startswith("#endif"): 20 + skipping = False 21 + continue 22 + elif line.startswith("#include"): 23 + continue 24 + if skipping: 25 + continue 26 + out_lines.append(line) 27 + 28 + return "\n".join(out_lines) 29 + 30 + 31 + class MonadoHeaderNotFoundError(Exception): 32 + pass 33 + 34 + 35 + def find_monado_header() -> Path: 36 + search_paths = [ 37 + # Try script directory first, in case running uninstalled 38 + Path(__file__).parent / "monado.h", 39 + # path from installed package 40 + Path("/usr/include/monado/monado.h"), 41 + # path from local install 42 + Path("/usr/local/include/monado/monado.h") 43 + ] 44 + 45 + # path from ENV 46 + if os.getenv('MONADO_HEADER_PATH'): 47 + search_paths.append(Path(os.getenv('MONADO_HEADER_PATH'))) 48 + 49 + for path in search_paths: 50 + if path.is_file(): 51 + return path 52 + 53 + # Give up 54 + raise MonadoHeaderNotFoundError("Could not find monado.h. Define the path by setting MONADO_HEADER_PATH.") 55 + 56 + 57 + def load_ffi() -> FFI: 58 + ffi = FFI() 59 + 60 + header_path = find_monado_header() 61 + with header_path.open("r") as f: 62 + header_lines = f.readlines() 63 + 64 + header = preprocess_ffi(header_lines) 65 + 66 + ffi.cdef(header) 67 + return ffi 68 + 69 + 70 + class Device: 71 + def __init__(self, ident, name): 72 + self.ident = ident 73 + self.name = name 74 + 75 + 76 + class Client: 77 + def __init__(self, ident, name, primary, focused, visible, active, overlay, io_active): 78 + self.ident = ident 79 + self.name = name 80 + self.primary = primary 81 + self.focused = focused 82 + self.visible = visible 83 + self.active = active 84 + self.overlay = overlay 85 + self.io_active = io_active 86 + 87 + 88 + class MonadoLibraryNotFoundError(Exception): 89 + pass 90 + 91 + 92 + class Monado: 93 + def __init__(self): 94 + self.ffi = load_ffi() 95 + try: 96 + self.lib = self.ffi.dlopen("libmonado.so") 97 + except OSError: 98 + raise MonadoLibraryNotFoundError("Could not load libmonado.so.") 99 + 100 + self.root_ptr = self.ffi.new("mnd_root_t **") 101 + 102 + ret = self.lib.mnd_root_create(self.root_ptr) 103 + if ret != 0: 104 + raise Exception("Could not create root") 105 + 106 + self.root = self.root_ptr[0] 107 + self.client_count = 0 108 + self.client_count_ptr = self.ffi.new("uint32_t *") 109 + self.name_ptr = self.ffi.new("char **") 110 + self.flags_ptr = self.ffi.new("uint32_t *") 111 + self.client_id_ptr = self.ffi.new("uint32_t *") 112 + self.device_id_ptr = self.ffi.new("uint32_t *") 113 + self.device_name_ptr = self.ffi.new("char **") 114 + self.device_count_ptr = self.ffi.new("uint32_t *") 115 + 116 + def update_clients(self): 117 + ret = self.lib.mnd_root_update_client_list(self.root) 118 + if ret != 0: 119 + raise Exception("Could not update clients") 120 + 121 + ret = self.lib.mnd_root_get_number_clients(self.root, self.client_count_ptr) 122 + if ret != 0: 123 + raise Exception("Could not update clients") 124 + 125 + self.client_count = self.client_count_ptr[0] 126 + 127 + def get_client_id_at_index(self, index): 128 + ret = self.lib.mnd_root_get_client_id_at_index(self.root, index, self.client_id_ptr) 129 + if ret != 0: 130 + raise Exception("Could not get client id at index") 131 + 132 + return self.client_id_ptr[0] 133 + 134 + def get_client_name(self, client_id): 135 + ret = self.lib.mnd_root_get_client_name(self.root, client_id, self.name_ptr) 136 + if ret != 0: 137 + raise Exception("Could not get client name") 138 + 139 + return self.ffi.string(self.name_ptr[0]).decode("utf-8") 140 + 141 + def get_client_flags(self, client_id): 142 + ret = self.lib.mnd_root_get_client_state(self.root, client_id, self.flags_ptr) 143 + if ret != 0: 144 + raise Exception("Could not get client state") 145 + 146 + return self.flags_ptr[0] 147 + 148 + def snapshot_client(self, index): 149 + ident = self.get_client_id_at_index(index) 150 + name = self.get_client_name(ident) 151 + flags = self.get_client_flags(ident) 152 + 153 + primary = (flags & self.lib.MND_CLIENT_PRIMARY_APP) != 0 154 + active = (flags & self.lib.MND_CLIENT_SESSION_ACTIVE) != 0 155 + visible = (flags & self.lib.MND_CLIENT_SESSION_VISIBLE) != 0 156 + focused = (flags & self.lib.MND_CLIENT_SESSION_FOCUSED) != 0 157 + overlay = (flags & self.lib.MND_CLIENT_SESSION_OVERLAY) != 0 158 + io_active = (flags & self.lib.MND_CLIENT_IO_ACTIVE) != 0 159 + 160 + return Client(ident, name, primary, focused, visible, active, overlay, io_active) 161 + 162 + def destroy(self): 163 + self.lib.mnd_root_destroy(self.root_ptr) 164 + self.root = self.root_ptr[0] 165 + 166 + def set_primary(self, client_id: int): 167 + ret = self.lib.mnd_root_set_client_primary(self.root, client_id) 168 + if ret != 0: 169 + raise Exception(f"Failed to set primary client id to {client_id}.") 170 + 171 + def set_focused(self, client_id: int): 172 + ret = self.lib.mnd_root_set_client_focused(self.root, client_id) 173 + if ret != 0: 174 + raise Exception(f"Failed to set focused client id to {client_id}.") 175 + 176 + def toggle_io(self, client_id: int): 177 + ret = self.lib.mnd_root_toggle_client_io_active(self.root, client_id) 178 + if ret != 0: 179 + raise Exception(f"Failed to toggle io for client id {client_id}.") 180 + 181 + def get_device_count(self): 182 + ret = self.lib.mnd_root_get_device_count(self.root, self.device_count_ptr) 183 + if ret != 0: 184 + raise Exception("Could not get device count") 185 + return self.device_count_ptr[0] 186 + 187 + def get_device_at_index(self, index): 188 + ret = self.lib.mnd_root_get_device_info(self.root, index, self.device_id_ptr, self.device_name_ptr) 189 + if ret != 0: 190 + raise Exception(f"Could not get device at index:{index}") 191 + dev_id = self.device_id_ptr[0] 192 + dev_name = self.ffi.string(self.device_name_ptr[0]).decode("utf-8") 193 + return Device(dev_id, dev_name) 194 + 195 + def get_devices(self): 196 + devices = [] 197 + dev_count = self.get_device_count() 198 + for i in range(dev_count): 199 + devices.append(self.get_device_at_index(i)) 200 + return devices 201 + 202 + def get_device_roles(self): 203 + role_map = dict() 204 + device_int_id_ptr = self.ffi.new("int32_t *") 205 + for role_name in ["head", "left", "right", "gamepad", "eyes", "hand-tracking-left", "hand-tracking-right"]: 206 + crole_name = role_name.encode('utf-8') 207 + ret = self.lib.mnd_root_get_device_from_role(self.root, crole_name, device_int_id_ptr) 208 + if ret != 0: 209 + raise Exception(f"Could not get device role: {role_name}") 210 + role_map[role_name] = device_int_id_ptr[0] 211 + return role_map