mirror of
https://gitlab.freedesktop.org/monado/monado.git
synced 2024-12-29 11:06:18 +00:00
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>
This commit is contained in:
parent
be0df71004
commit
040ad33188
|
@ -41,3 +41,8 @@ if(XRT_FEATURE_SERVICE
|
|||
)
|
||||
add_subdirectory(sdl_test)
|
||||
endif()
|
||||
|
||||
# Monado management library
|
||||
if(XRT_FEATURE_SERVICE AND XRT_HAVE_LINUX)
|
||||
add_subdirectory(libmonado)
|
||||
endif()
|
||||
|
|
16
src/xrt/targets/libmonado/CMakeLists.txt
Normal file
16
src/xrt/targets/libmonado/CMakeLists.txt
Normal file
|
@ -0,0 +1,16 @@
|
|||
# Copyright 2019-2023, Collabora, Ltd.
|
||||
# SPDX-License-Identifier: BSL-1.0
|
||||
|
||||
add_library(monado SHARED monado.c libmonado.def)
|
||||
set(LIBMONADO_HEADER_DIR ${CMAKE_INSTALL_INCLUDEDIR}/monado)
|
||||
target_link_libraries(monado PRIVATE aux_util ipc_client)
|
||||
target_include_directories(
|
||||
monado INTERFACE $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
|
||||
$<INSTALL_INTERFACE:${LIBMONADO_HEADER_DIR}>
|
||||
)
|
||||
|
||||
install(TARGETS monado RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
install(FILES monado.h DESTINATION ${LIBMONADO_HEADER_DIR})
|
||||
|
||||
add_executable(libmonado-example example.c)
|
||||
target_link_libraries(libmonado-example PRIVATE monado)
|
200
src/xrt/targets/libmonado/example.c
Normal file
200
src/xrt/targets/libmonado/example.c
Normal file
|
@ -0,0 +1,200 @@
|
|||
// Copyright 2020-2023, Collabora, Ltd.
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
/*!
|
||||
* @file
|
||||
* @brief Small cli application to demonstrate use of libmonado.
|
||||
* @author Ryan Pavlik <ryan.pavlik@collabora.com>
|
||||
* @author Pete Black <pblack@collabora.com>
|
||||
*/
|
||||
|
||||
#include "monado.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <limits.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <getopt.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
|
||||
#define P(...) fprintf(stdout, __VA_ARGS__)
|
||||
#define PE(...) fprintf(stderr, __VA_ARGS__)
|
||||
#define CHECK_ID_EXIT(ID) \
|
||||
do { \
|
||||
if (ID < 0 || ID > INT_MAX) { \
|
||||
PE("Invalid client index %i.\n", s_val); \
|
||||
exit(1); \
|
||||
} \
|
||||
} while (false)
|
||||
|
||||
typedef enum op_mode
|
||||
{
|
||||
MODE_GET,
|
||||
MODE_SET_PRIMARY,
|
||||
MODE_SET_FOCUSED,
|
||||
MODE_TOGGLE_IO,
|
||||
} op_mode_t;
|
||||
|
||||
int
|
||||
get_mode(mnd_root_t *root)
|
||||
{
|
||||
mnd_result_t mret = mnd_root_update_client_list(root);
|
||||
if (mret != MND_SUCCESS) {
|
||||
PE("Failed to get client list.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
uint32_t num_clients = 0;
|
||||
mret = mnd_root_get_number_clients(root, &num_clients);
|
||||
if (mret != MND_SUCCESS) {
|
||||
PE("Failed to get client count.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
P("Clients: (%d)\n", num_clients);
|
||||
for (uint32_t i = 0; i < num_clients; i++) {
|
||||
uint32_t client_id = 0;
|
||||
uint32_t flags = 0;
|
||||
const char *name = NULL;
|
||||
|
||||
mret = mnd_root_get_client_id_at_index(root, i, &client_id);
|
||||
if (mret != MND_SUCCESS) {
|
||||
PE("Failed to get client id for index %u", i);
|
||||
continue;
|
||||
}
|
||||
|
||||
mret = mnd_root_get_client_state(root, client_id, &flags);
|
||||
if (mret != MND_SUCCESS) {
|
||||
PE("Failed to get client state for client id: %u (index: %u)", client_id, i);
|
||||
continue;
|
||||
}
|
||||
|
||||
mret = mnd_root_get_client_name(root, client_id, &name);
|
||||
if (mret != MND_SUCCESS) {
|
||||
PE("Failed to get client name for client id: %u (index: %u)", client_id, i);
|
||||
continue;
|
||||
}
|
||||
|
||||
P("\tid: % 8d"
|
||||
"\tact: %d"
|
||||
"\tdisp: %d"
|
||||
"\tfoc: %d"
|
||||
"\tio: %d"
|
||||
"\tovly: %d"
|
||||
"\t%s\n",
|
||||
client_id, //
|
||||
(flags & MND_CLIENT_SESSION_ACTIVE) != 0 ? 1 : 0, //
|
||||
(flags & MND_CLIENT_SESSION_VISIBLE) != 0 ? 1 : 0, //
|
||||
(flags & MND_CLIENT_SESSION_FOCUSED) != 0 ? 1 : 0, //
|
||||
(flags & MND_CLIENT_IO_ACTIVE) != 0 ? 1 : 0, //
|
||||
(flags & MND_CLIENT_SESSION_OVERLAY) != 0 ? 1 : 0, //
|
||||
name);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
set_primary(mnd_root_t *root, int client_index)
|
||||
{
|
||||
mnd_result_t mret = mnd_root_set_client_primary(root, client_index);
|
||||
if (mret != MND_SUCCESS) {
|
||||
PE("Failed to set active client to index %d.\n", client_index);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
set_focused(mnd_root_t *root, int client_index)
|
||||
{
|
||||
mnd_result_t mret = mnd_root_set_client_focused(root, client_index);
|
||||
if (mret != MND_SUCCESS) {
|
||||
PE("Failed to set focused client to index %d.\n", client_index);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
toggle_io(mnd_root_t *root, int client_index)
|
||||
{
|
||||
mnd_result_t mret = mnd_root_toggle_client_io_active(root, client_index);
|
||||
if (mret != MND_SUCCESS) {
|
||||
PE("Failed to toggle io for client index %d.\n", client_index);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
op_mode_t op_mode = MODE_GET;
|
||||
|
||||
// parse arguments
|
||||
int c;
|
||||
int s_val = 0;
|
||||
|
||||
opterr = 0;
|
||||
while ((c = getopt(argc, argv, "p:f:i:")) != -1) {
|
||||
switch (c) {
|
||||
case 'p':
|
||||
s_val = atoi(optarg);
|
||||
CHECK_ID_EXIT(s_val);
|
||||
op_mode = MODE_SET_PRIMARY;
|
||||
break;
|
||||
case 'f':
|
||||
s_val = atoi(optarg);
|
||||
CHECK_ID_EXIT(s_val);
|
||||
op_mode = MODE_SET_FOCUSED;
|
||||
break;
|
||||
case 'i':
|
||||
s_val = atoi(optarg);
|
||||
CHECK_ID_EXIT(s_val);
|
||||
op_mode = MODE_TOGGLE_IO;
|
||||
break;
|
||||
case '?':
|
||||
if (optopt == 's') {
|
||||
PE("Option -s requires a client index to set.\n");
|
||||
} else if (isprint(optopt)) {
|
||||
PE("Option `-%c' unknown. Usage:\n", optopt);
|
||||
PE(" -f <index>: Set focused client\n");
|
||||
PE(" -p <index>: Set primary client\n");
|
||||
PE(" -i <index>: Toggle whether client receives input\n");
|
||||
} else {
|
||||
PE("Option `\\x%x' unknown.\n", optopt);
|
||||
}
|
||||
exit(1);
|
||||
default: exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
mnd_root_t *root = NULL;
|
||||
mnd_result_t mret;
|
||||
|
||||
mret = mnd_root_create(&root);
|
||||
if (mret != MND_SUCCESS) {
|
||||
PE("Failed to connect.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
mret = mnd_root_update_client_list(root);
|
||||
if (mret != MND_SUCCESS) {
|
||||
PE("Failed to update client list.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
switch (op_mode) {
|
||||
case MODE_GET: exit(get_mode(root)); break;
|
||||
case MODE_SET_PRIMARY: exit(set_primary(root, s_val)); break;
|
||||
case MODE_SET_FOCUSED: exit(set_focused(root, s_val)); break;
|
||||
case MODE_TOGGLE_IO: exit(toggle_io(root, s_val)); break;
|
||||
default: P("Unrecognised operation mode.\n"); exit(1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
189
src/xrt/targets/libmonado/example.lua
Executable file
189
src/xrt/targets/libmonado/example.lua
Executable file
|
@ -0,0 +1,189 @@
|
|||
#!/usr/bin/env luajit
|
||||
-- Copyright 2020-2023, Collabora, Ltd.
|
||||
-- SPDX-License-Identifier: BSL-1.0
|
||||
-- Author: Lubosz Sarnecki <lubosz.sarnecki@collabora.com>
|
||||
|
||||
local ffi = require("ffi")
|
||||
|
||||
ffi.cdef[[
|
||||
typedef enum mnd_result
|
||||
{
|
||||
MND_SUCCESS = 0,
|
||||
MND_ERROR_INVALID_VERSION = -1,
|
||||
MND_ERROR_INVALID_VALUE = -2,
|
||||
MND_ERROR_CONNECTING_FAILED = -3,
|
||||
MND_ERROR_OPERATION_FAILED = -4,
|
||||
} mnd_result_t;
|
||||
|
||||
typedef enum mnd_client_flags
|
||||
{
|
||||
MND_CLIENT_PRIMARY_APP = (1u << 0u),
|
||||
MND_CLIENT_SESSION_ACTIVE = (1u << 1u),
|
||||
MND_CLIENT_SESSION_VISIBLE = (1u << 2u),
|
||||
MND_CLIENT_SESSION_FOCUSED = (1u << 3u),
|
||||
MND_CLIENT_SESSION_OVERLAY = (1u << 4u),
|
||||
MND_CLIENT_IO_ACTIVE = (1u << 5u),
|
||||
} mnd_client_flags_t;
|
||||
|
||||
typedef struct mnd_root mnd_root_t;
|
||||
|
||||
void
|
||||
mnd_api_get_version(uint32_t *out_major, uint32_t *out_minor, uint32_t *out_patch);
|
||||
|
||||
mnd_result_t
|
||||
mnd_root_create(mnd_root_t **out_root);
|
||||
|
||||
void
|
||||
mnd_root_destroy(mnd_root_t **root_ptr);
|
||||
|
||||
mnd_result_t
|
||||
mnd_root_update_client_list(mnd_root_t *root);
|
||||
|
||||
mnd_result_t
|
||||
mnd_root_get_number_clients(mnd_root_t *root, uint32_t *out_num);
|
||||
|
||||
mnd_result_t
|
||||
mnd_root_get_client_id_at_index(mnd_root_t *root, uint32_t index, uint32_t *out_client_id);
|
||||
|
||||
mnd_result_t
|
||||
mnd_root_get_client_name(mnd_root_t *root, uint32_t client_id, const char **out_name);
|
||||
|
||||
mnd_result_t
|
||||
mnd_root_get_client_state(mnd_root_t *root, uint32_t client_id, uint32_t *out_flags);
|
||||
|
||||
mnd_result_t
|
||||
mnd_root_set_client_primary(mnd_root_t *root, uint32_t client_id);
|
||||
|
||||
mnd_result_t
|
||||
mnd_root_set_client_focused(mnd_root_t *root, uint32_t client_id);
|
||||
|
||||
mnd_result_t
|
||||
mnd_root_toggle_client_io_active(mnd_root_t *root, uint32_t client_id);
|
||||
]]
|
||||
|
||||
local status, lib = pcall(ffi.load, "libmonado.so")
|
||||
if not status then
|
||||
print("Could not find an installed libmonado.so in your path.")
|
||||
print("Add the Monado build directory to your LD_LIBRARY_PATH:")
|
||||
print(string.format("LD_LIBRARY_PATH=/home/user/monado/build/src/xrt/targets/libmonado/ %s", arg[0]))
|
||||
os.exit(1)
|
||||
end
|
||||
|
||||
-- Parse arguments
|
||||
local args = {...}
|
||||
for i=1, #args, 1 do
|
||||
if args[i] == "-f" or args[i] == "--focused" then
|
||||
args_focused = tonumber(args[i+1])
|
||||
elseif args[i] == "-p" or args[i] == "--primary" then
|
||||
args_primary = tonumber(args[i+1])
|
||||
elseif args[i] == "-i" or args[i] == "--input" then
|
||||
args_input = tonumber(args[i+1])
|
||||
end
|
||||
end
|
||||
|
||||
-- Using ** doesn't work here, it hits an assertion in moando due being NULL
|
||||
local root_ptr = ffi.new("mnd_root_t*[1]")
|
||||
|
||||
function create_root()
|
||||
local ret = lib.mnd_root_create(root_ptr)
|
||||
if ret ~= 0 then
|
||||
error("Could not create root")
|
||||
end
|
||||
return root_ptr[0]
|
||||
end
|
||||
|
||||
local root = create_root()
|
||||
|
||||
function update_clients()
|
||||
local ret = lib.mnd_root_update_client_list(root)
|
||||
if ret ~= 0 then
|
||||
error("Could not update clients")
|
||||
end
|
||||
end
|
||||
|
||||
function get_client_count()
|
||||
client_count_ptr = ffi.new("uint32_t[1]")
|
||||
ret = lib.mnd_root_get_number_clients(root, client_count_ptr)
|
||||
if ret ~= 0 then
|
||||
error("Could not get number of clients")
|
||||
end
|
||||
return client_count_ptr[0]
|
||||
end
|
||||
|
||||
function get_client_id_at_index(index)
|
||||
client_id_ptr = ffi.new("uint32_t[1]")
|
||||
local ret = lib.mnd_root_get_client_id_at_index(root, index, client_id_ptr)
|
||||
if ret ~= 0 then
|
||||
error("Could not get client id at index.")
|
||||
end
|
||||
return client_id_ptr[0]
|
||||
end
|
||||
|
||||
function get_client_name(client_id)
|
||||
name_ptr = ffi.new("const char*[1]")
|
||||
local ret = lib.mnd_root_get_client_name(root, client_id, name_ptr)
|
||||
if ret ~= 0 then
|
||||
error("Could not get client name.")
|
||||
end
|
||||
return name_ptr[0]
|
||||
end
|
||||
|
||||
function get_client_flags(client_id)
|
||||
flags_ptr = ffi.new("uint32_t[1]")
|
||||
local ret = lib.mnd_root_get_client_state(root, client_id, flags_ptr)
|
||||
if ret ~= 0 then
|
||||
error("Could not get client flags.")
|
||||
end
|
||||
return flags_ptr[0]
|
||||
end
|
||||
|
||||
function set_primary(client_id)
|
||||
local ret = lib.mnd_root_set_client_primary(root, client_id)
|
||||
if ret ~= 0 then
|
||||
error("Failed to set primary client to client id", client_id)
|
||||
end
|
||||
end
|
||||
|
||||
function set_focused(client_id)
|
||||
ret = lib.mnd_root_set_client_focused(root, client_id)
|
||||
if ret ~= 0 then
|
||||
error("Failed to set focused client to client id", client_id)
|
||||
end
|
||||
end
|
||||
|
||||
function toggle_io(client_id)
|
||||
ret = lib.mnd_root_toggle_client_io_active(root, client_id)
|
||||
if ret ~= 0 then
|
||||
error("Failed to toggle io for client client id", client_id)
|
||||
end
|
||||
end
|
||||
|
||||
if args_primary then
|
||||
set_primary(args_primary)
|
||||
end
|
||||
if args_focused then
|
||||
set_focused(args_focused)
|
||||
end
|
||||
if args_input then
|
||||
toggle_io(args_input)
|
||||
end
|
||||
|
||||
update_clients(root)
|
||||
print("Client count:", get_client_count())
|
||||
|
||||
for i = 0, get_client_count() - 1 do
|
||||
local client_id = get_client_id_at_index(i)
|
||||
local name = get_client_name(client_id)
|
||||
local flags = get_client_flags(client_id)
|
||||
local primary = bit.band(flags, lib.MND_CLIENT_PRIMARY_APP) ~= 0
|
||||
local active = bit.band(flags, lib.MND_CLIENT_SESSION_ACTIVE) ~= 0
|
||||
local visible = bit.band(flags, lib.MND_CLIENT_SESSION_VISIBLE) ~= 0
|
||||
local focused = bit.band(flags, lib.MND_CLIENT_SESSION_FOCUSED) ~= 0
|
||||
local overlay = bit.band(flags, lib.MND_CLIENT_SESSION_OVERLAY) ~= 0
|
||||
local io_active = bit.band(flags, lib.MND_CLIENT_IO_ACTIVE) ~= 0
|
||||
print(string.format("id: %d primary: %5s active: %5s visible: %5s focused: %5s io: %5s overlay: %5s name: %s",
|
||||
client_id, tostring(primary), tostring(active), tostring(visible), tostring(focused),
|
||||
tostring(io_active), tostring(overlay), ffi.string(name)))
|
||||
end
|
||||
|
||||
lib.mnd_root_destroy(root_ptr)
|
65
src/xrt/targets/libmonado/example.py
Executable file
65
src/xrt/targets/libmonado/example.py
Executable file
|
@ -0,0 +1,65 @@
|
|||
#!/bin/env python3
|
||||
# Copyright 2020-2023, Collabora, Ltd.
|
||||
# SPDX-License-Identifier: BSL-1.0
|
||||
# Author: Jakob Bornecrantz <jakob@collabora.com>
|
||||
# Author: Lubosz Sarnecki <lubosz.sarnecki@collabora.com>
|
||||
# Author: Korcan Hussein <korcan.hussein@collabora.com>
|
||||
|
||||
import argparse
|
||||
from monado import Monado, MonadoLibraryNotFoundError, MonadoHeaderNotFoundError
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description='libmonado Python example.')
|
||||
parser.add_argument("-f", "--focused", type=int, metavar='CLIENT_ID',
|
||||
help="Set focused client")
|
||||
parser.add_argument("-p", "--primary", type=int, metavar='CLIENT_ID',
|
||||
help="Set primary client")
|
||||
parser.add_argument("-i", "--input", type=int, metavar='CLIENT_ID',
|
||||
help="Toggle whether client receives input")
|
||||
args = parser.parse_args()
|
||||
|
||||
try:
|
||||
m = Monado()
|
||||
except MonadoLibraryNotFoundError:
|
||||
print("Could not find an installed libmonado.so in your path.")
|
||||
print("Add the Monado build directory to your LD_LIBRARY_PATH:")
|
||||
print(f"LD_LIBRARY_PATH=/home/user/monado/build/src/xrt/targets/libmonado/ ./{parser.prog}")
|
||||
return
|
||||
except MonadoHeaderNotFoundError:
|
||||
print("Could not find an installed monado.h.")
|
||||
print("Try setting the MONADO_HEADER_PATH manually:")
|
||||
print(f"MONADO_HEADER_PATH=/home/user/monado/src/xrt/targets/libmonado/monado.h ./{parser.prog}")
|
||||
return
|
||||
|
||||
if args.focused:
|
||||
m.set_focused(args.focused)
|
||||
if args.primary:
|
||||
m.set_primary(args.primary)
|
||||
if args.input:
|
||||
m.toggle_io(args.input)
|
||||
|
||||
m.update_clients()
|
||||
|
||||
print(f"Clients: {m.client_count}")
|
||||
for x in range(m.client_count):
|
||||
c = m.snapshot_client(x)
|
||||
print(f"\tid: {c.ident:4d}, primary: {c.primary:d}, active: {c.active:d}, "
|
||||
f"visible: {c.visible:d}, focused: {c.focused:d}, io: {c.io_active:d}, "
|
||||
f"overlay: {c.overlay:d}, name: {c.name}")
|
||||
|
||||
devices = m.get_devices()
|
||||
print(f"Devices: {len(devices)}")
|
||||
for dev in devices:
|
||||
print(f"\tid: {dev.ident:4d}, name: {dev.name}")
|
||||
|
||||
roles_map = m.get_device_roles()
|
||||
print(f"Roles: {len(roles_map)}")
|
||||
for role_name, dev_id in roles_map.items():
|
||||
print(f"\trole: {role_name},\tdevice-index: {dev_id:4d}")
|
||||
|
||||
m.destroy()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
15
src/xrt/targets/libmonado/libmonado.def
Normal file
15
src/xrt/targets/libmonado/libmonado.def
Normal file
|
@ -0,0 +1,15 @@
|
|||
; Copyright 2019-2023, Collabora, Ltd.
|
||||
; SPDX-License-Identifier: BSL-1.0
|
||||
EXPORTS
|
||||
mnd_root_create
|
||||
mnd_root_destroy
|
||||
mnd_root_update_client_list
|
||||
mnd_root_get_number_clients
|
||||
mnd_root_get_client_name
|
||||
mnd_root_get_client_state
|
||||
mnd_root_set_client_primary
|
||||
mnd_root_set_client_focused
|
||||
mnd_root_toggle_client_io_active
|
||||
mnd_root_get_device_count
|
||||
mnd_root_get_device_info
|
||||
mnd_root_get_device_from_role
|
336
src/xrt/targets/libmonado/monado.c
Normal file
336
src/xrt/targets/libmonado/monado.c
Normal file
|
@ -0,0 +1,336 @@
|
|||
// Copyright 2019-2023, Collabora, Ltd.
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
/*!
|
||||
* @file
|
||||
* @brief Implementation of libmonado
|
||||
* @author Jakob Bornecrantz <jakob@collabora.com>
|
||||
* @author Pete Black <pblack@collabora.com>
|
||||
* @author Ryan Pavlik <ryan.pavlik@collabora.com>
|
||||
*/
|
||||
|
||||
#include "monado.h"
|
||||
|
||||
#include "xrt/xrt_results.h"
|
||||
|
||||
#include "util/u_misc.h"
|
||||
#include "util/u_file.h"
|
||||
#include "util/u_logging.h"
|
||||
|
||||
#include "shared/ipc_protocol.h"
|
||||
|
||||
#include "client/ipc_client_connection.h"
|
||||
#include "client/ipc_client.h"
|
||||
#include "ipc_client_generated.h"
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdint.h>
|
||||
#include <assert.h>
|
||||
|
||||
|
||||
struct mnd_root
|
||||
{
|
||||
struct ipc_connection ipc_c;
|
||||
|
||||
//! List of clients.
|
||||
struct ipc_client_list clients;
|
||||
|
||||
/// State of most recent app asked about
|
||||
struct ipc_app_state app_state;
|
||||
};
|
||||
|
||||
#define P(...) fprintf(stdout, __VA_ARGS__)
|
||||
#define PE(...) fprintf(stderr, __VA_ARGS__)
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
* Helper functions.
|
||||
*
|
||||
*/
|
||||
|
||||
#define CHECK_NOT_NULL(ARG) \
|
||||
do { \
|
||||
if (ARG == NULL) { \
|
||||
PE("Argument '" #ARG "' can not be null!"); \
|
||||
return MND_ERROR_INVALID_VALUE; \
|
||||
} \
|
||||
} while (false)
|
||||
|
||||
#define CHECK_CLIENT_ID(ID) \
|
||||
do { \
|
||||
if (ID == 0 && ID > INT_MAX) { \
|
||||
PE("Invalid client id (%u)", ID); \
|
||||
return MND_ERROR_INVALID_VALUE; \
|
||||
} \
|
||||
} while (false)
|
||||
|
||||
#define CHECK_CLIENT_INDEX(INDEX) \
|
||||
do { \
|
||||
if (INDEX >= root->clients.id_count) { \
|
||||
PE("Invalid client index, too large (%u)", INDEX); \
|
||||
return MND_ERROR_INVALID_VALUE; \
|
||||
} \
|
||||
} while (false)
|
||||
|
||||
static int
|
||||
get_client_info(mnd_root_t *root, uint32_t client_id)
|
||||
{
|
||||
assert(root != NULL);
|
||||
|
||||
xrt_result_t r = ipc_call_system_get_client_info(&root->ipc_c, client_id, &root->app_state);
|
||||
if (r != XRT_SUCCESS) {
|
||||
PE("Failed to get client info for client id: %u.\n", client_id);
|
||||
return MND_ERROR_INVALID_VALUE;
|
||||
}
|
||||
|
||||
return MND_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
* API API.
|
||||
*
|
||||
*/
|
||||
|
||||
void
|
||||
mnd_api_get_version(uint32_t *out_major, uint32_t *out_minor, uint32_t *out_patch)
|
||||
{
|
||||
*out_major = MND_API_VERSION_MAJOR;
|
||||
*out_minor = MND_API_VERSION_MINOR;
|
||||
*out_patch = MND_API_VERSION_PATCH;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
* Root API
|
||||
*
|
||||
*/
|
||||
|
||||
mnd_result_t
|
||||
mnd_root_create(mnd_root_t **out_root)
|
||||
{
|
||||
CHECK_NOT_NULL(out_root);
|
||||
|
||||
mnd_root_t *r = U_TYPED_CALLOC(mnd_root_t);
|
||||
|
||||
struct xrt_instance_info info = {0};
|
||||
snprintf(info.application_name, sizeof(info.application_name), "%s", "libmonado");
|
||||
|
||||
xrt_result_t xret = ipc_client_connection_init(&r->ipc_c, U_LOGGING_INFO, &info);
|
||||
if (xret != XRT_SUCCESS) {
|
||||
PE("Connection init error '%i'!\n", xret);
|
||||
free(r);
|
||||
return MND_ERROR_CONNECTING_FAILED;
|
||||
}
|
||||
|
||||
*out_root = r;
|
||||
|
||||
return MND_SUCCESS;
|
||||
}
|
||||
|
||||
void
|
||||
mnd_root_destroy(mnd_root_t **root_ptr)
|
||||
{
|
||||
if (root_ptr == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
mnd_root_t *r = *root_ptr;
|
||||
if (r == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
ipc_client_connection_fini(&r->ipc_c);
|
||||
free(r);
|
||||
|
||||
*root_ptr = NULL;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
mnd_result_t
|
||||
mnd_root_update_client_list(mnd_root_t *root)
|
||||
{
|
||||
CHECK_NOT_NULL(root);
|
||||
|
||||
xrt_result_t r = ipc_call_system_get_clients(&root->ipc_c, &root->clients);
|
||||
if (r != XRT_SUCCESS) {
|
||||
PE("Failed to get client list.\n");
|
||||
return MND_ERROR_OPERATION_FAILED;
|
||||
}
|
||||
|
||||
return MND_SUCCESS;
|
||||
}
|
||||
|
||||
mnd_result_t
|
||||
mnd_root_get_number_clients(mnd_root_t *root, uint32_t *out_num)
|
||||
{
|
||||
CHECK_NOT_NULL(root);
|
||||
CHECK_NOT_NULL(out_num);
|
||||
|
||||
*out_num = root->clients.id_count;
|
||||
|
||||
return MND_SUCCESS;
|
||||
}
|
||||
|
||||
mnd_result_t
|
||||
mnd_root_get_client_id_at_index(mnd_root_t *root, uint32_t index, uint32_t *out_client_id)
|
||||
{
|
||||
CHECK_NOT_NULL(root);
|
||||
CHECK_CLIENT_INDEX(index);
|
||||
|
||||
*out_client_id = root->clients.ids[index];
|
||||
|
||||
return MND_SUCCESS;
|
||||
}
|
||||
|
||||
mnd_result_t
|
||||
mnd_root_get_client_name(mnd_root_t *root, uint32_t client_id, const char **out_name)
|
||||
{
|
||||
CHECK_NOT_NULL(root);
|
||||
CHECK_CLIENT_ID(client_id);
|
||||
CHECK_NOT_NULL(out_name);
|
||||
|
||||
mnd_result_t mret = get_client_info(root, client_id);
|
||||
if (mret < 0) {
|
||||
return mret; // Prints error.
|
||||
}
|
||||
|
||||
*out_name = &(root->app_state.info.application_name[0]);
|
||||
|
||||
return MND_SUCCESS;
|
||||
}
|
||||
|
||||
mnd_result_t
|
||||
mnd_root_get_client_state(mnd_root_t *root, uint32_t client_id, uint32_t *out_flags)
|
||||
{
|
||||
CHECK_NOT_NULL(root);
|
||||
CHECK_CLIENT_ID(client_id);
|
||||
CHECK_NOT_NULL(out_flags);
|
||||
|
||||
mnd_result_t mret = get_client_info(root, client_id);
|
||||
if (mret < 0) {
|
||||
return mret; // Prints error.
|
||||
}
|
||||
|
||||
uint32_t flags = 0;
|
||||
flags |= (root->app_state.primary_application) ? MND_CLIENT_PRIMARY_APP : 0u;
|
||||
flags |= (root->app_state.session_active) ? MND_CLIENT_SESSION_ACTIVE : 0u;
|
||||
flags |= (root->app_state.session_visible) ? MND_CLIENT_SESSION_VISIBLE : 0u;
|
||||
flags |= (root->app_state.session_focused) ? MND_CLIENT_SESSION_FOCUSED : 0u;
|
||||
flags |= (root->app_state.session_overlay) ? MND_CLIENT_SESSION_OVERLAY : 0u;
|
||||
flags |= (root->app_state.io_active) ? MND_CLIENT_IO_ACTIVE : 0u;
|
||||
*out_flags = flags;
|
||||
|
||||
return MND_SUCCESS;
|
||||
}
|
||||
|
||||
mnd_result_t
|
||||
mnd_root_set_client_primary(mnd_root_t *root, uint32_t client_id)
|
||||
{
|
||||
CHECK_NOT_NULL(root);
|
||||
CHECK_CLIENT_ID(client_id);
|
||||
|
||||
xrt_result_t r = ipc_call_system_set_primary_client(&root->ipc_c, client_id);
|
||||
if (r != XRT_SUCCESS) {
|
||||
PE("Failed to set primary to client id: %u.\n", client_id);
|
||||
return MND_ERROR_OPERATION_FAILED;
|
||||
}
|
||||
|
||||
return MND_SUCCESS;
|
||||
}
|
||||
|
||||
mnd_result_t
|
||||
mnd_root_set_client_focused(mnd_root_t *root, uint32_t client_id)
|
||||
{
|
||||
CHECK_NOT_NULL(root);
|
||||
CHECK_CLIENT_ID(client_id);
|
||||
|
||||
xrt_result_t r = ipc_call_system_set_focused_client(&root->ipc_c, client_id);
|
||||
if (r != XRT_SUCCESS) {
|
||||
PE("Failed to set focused to client id: %u.\n", client_id);
|
||||
return MND_ERROR_OPERATION_FAILED;
|
||||
}
|
||||
|
||||
return MND_SUCCESS;
|
||||
}
|
||||
|
||||
mnd_result_t
|
||||
mnd_root_toggle_client_io_active(mnd_root_t *root, uint32_t client_id)
|
||||
{
|
||||
CHECK_NOT_NULL(root);
|
||||
CHECK_CLIENT_ID(client_id);
|
||||
|
||||
xrt_result_t r = ipc_call_system_toggle_io_client(&root->ipc_c, client_id);
|
||||
if (r != XRT_SUCCESS) {
|
||||
PE("Failed to toggle io for client id: %u.\n", client_id);
|
||||
return MND_ERROR_OPERATION_FAILED;
|
||||
}
|
||||
|
||||
return MND_SUCCESS;
|
||||
}
|
||||
|
||||
mnd_result_t
|
||||
mnd_root_get_device_count(mnd_root_t *root, uint32_t *out_device_count)
|
||||
{
|
||||
CHECK_NOT_NULL(root);
|
||||
CHECK_NOT_NULL(out_device_count);
|
||||
|
||||
*out_device_count = root->ipc_c.ism->isdev_count;
|
||||
|
||||
return MND_SUCCESS;
|
||||
}
|
||||
|
||||
mnd_result_t
|
||||
mnd_root_get_device_info(mnd_root_t *root, uint32_t device_index, uint32_t *out_device_id, const char **out_dev_name)
|
||||
{
|
||||
CHECK_NOT_NULL(root);
|
||||
CHECK_NOT_NULL(out_device_id);
|
||||
CHECK_NOT_NULL(out_dev_name);
|
||||
|
||||
if (device_index >= root->ipc_c.ism->isdev_count) {
|
||||
PE("Invalid device index (%u)", device_index);
|
||||
return MND_ERROR_INVALID_VALUE;
|
||||
}
|
||||
|
||||
const struct ipc_shared_device *shared_device = &root->ipc_c.ism->isdevs[device_index];
|
||||
*out_device_id = shared_device->name;
|
||||
*out_dev_name = shared_device->str;
|
||||
|
||||
return MND_SUCCESS;
|
||||
}
|
||||
|
||||
mnd_result_t
|
||||
mnd_root_get_device_from_role(mnd_root_t *root, const char *role_name, int32_t *out_device_id)
|
||||
{
|
||||
CHECK_NOT_NULL(root);
|
||||
CHECK_NOT_NULL(role_name);
|
||||
CHECK_NOT_NULL(out_device_id);
|
||||
|
||||
#ifndef MND_PP_DEV_ID_FROM_ROLE
|
||||
#define MND_PP_DEV_ID_FROM_ROLE(role) \
|
||||
if (strcmp(role_name, #role) == 0) { \
|
||||
*out_device_id = (int32_t)root->ipc_c.ism->roles.role; \
|
||||
return MND_SUCCESS; \
|
||||
}
|
||||
#endif
|
||||
MND_PP_DEV_ID_FROM_ROLE(head)
|
||||
MND_PP_DEV_ID_FROM_ROLE(left)
|
||||
MND_PP_DEV_ID_FROM_ROLE(right)
|
||||
MND_PP_DEV_ID_FROM_ROLE(gamepad)
|
||||
MND_PP_DEV_ID_FROM_ROLE(eyes)
|
||||
#undef MND_PP_DEV_ID_FROM_ROLE
|
||||
if (strcmp(role_name, "hand-tracking-left") == 0) {
|
||||
*out_device_id = root->ipc_c.ism->roles.hand_tracking.left;
|
||||
return MND_SUCCESS;
|
||||
}
|
||||
if (strcmp(role_name, "hand-tracking-right") == 0) {
|
||||
*out_device_id = root->ipc_c.ism->roles.hand_tracking.right;
|
||||
return MND_SUCCESS;
|
||||
}
|
||||
|
||||
PE("Invalid role name (%s)", role_name);
|
||||
return MND_ERROR_INVALID_VALUE;
|
||||
}
|
247
src/xrt/targets/libmonado/monado.h
Normal file
247
src/xrt/targets/libmonado/monado.h
Normal file
|
@ -0,0 +1,247 @@
|
|||
// Copyright 2019-2023, Collabora, Ltd.
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
/*!
|
||||
* @file
|
||||
* @brief Interface of libmonado
|
||||
* @author Jakob Bornecrantz <jakob@collabora.com>
|
||||
* @author Ryan Pavlik <ryan.pavlik@collabora.com>
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
* Enums, defines and objects.
|
||||
*
|
||||
*/
|
||||
|
||||
//! Major version of the API.
|
||||
#define MND_API_VERSION_MAJOR 1
|
||||
//! Minor version of the API.
|
||||
#define MND_API_VERSION_MINOR 0
|
||||
//! Patch version of the API.
|
||||
#define MND_API_VERSION_PATCH 0
|
||||
|
||||
/*!
|
||||
* Result codes for operations, negative are errors, zero or positives are
|
||||
* success.
|
||||
*/
|
||||
typedef enum mnd_result
|
||||
{
|
||||
MND_SUCCESS = 0,
|
||||
MND_ERROR_INVALID_VERSION = -1,
|
||||
MND_ERROR_INVALID_VALUE = -2,
|
||||
MND_ERROR_CONNECTING_FAILED = -3,
|
||||
MND_ERROR_OPERATION_FAILED = -4,
|
||||
} mnd_result_t;
|
||||
|
||||
/*!
|
||||
* Bitflags for client application state.
|
||||
*/
|
||||
typedef enum mnd_client_flags
|
||||
{
|
||||
MND_CLIENT_PRIMARY_APP = (1u << 0u),
|
||||
MND_CLIENT_SESSION_ACTIVE = (1u << 1u),
|
||||
MND_CLIENT_SESSION_VISIBLE = (1u << 2u),
|
||||
MND_CLIENT_SESSION_FOCUSED = (1u << 3u),
|
||||
MND_CLIENT_SESSION_OVERLAY = (1u << 4u),
|
||||
MND_CLIENT_IO_ACTIVE = (1u << 5u),
|
||||
} mnd_client_flags_t;
|
||||
|
||||
/*!
|
||||
* Opaque type for libmonado state
|
||||
*/
|
||||
typedef struct mnd_root mnd_root_t;
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
* Functions
|
||||
*
|
||||
*/
|
||||
|
||||
/*!
|
||||
* Returns the version of the API (not Monado itself), follows the versioning
|
||||
* semantics of https://semver.org/ standard. In short if the major version
|
||||
* mismatch then the interface is incompatible.
|
||||
*
|
||||
* @param[out] out_major Major version number, must be valid pointer.
|
||||
* @param[out] out_minor Minor version number, must be valid pointer.
|
||||
* @param[out] out_patch Patch version number, must be valid pointer.
|
||||
*
|
||||
* Always succeeds, or crashes if any pointer isn't valid.
|
||||
*/
|
||||
void
|
||||
mnd_api_get_version(uint32_t *out_major, uint32_t *out_minor, uint32_t *out_patch);
|
||||
|
||||
/*!
|
||||
* Create libmonado state and connect to service
|
||||
*
|
||||
* @param[out] out_root Address to populate with the opaque state type.
|
||||
* @return MND_SUCCESS on success
|
||||
*/
|
||||
mnd_result_t
|
||||
mnd_root_create(mnd_root_t **out_root);
|
||||
|
||||
/*!
|
||||
* Destroy libmonado state, disconnecting from the service, and zeroing the
|
||||
* pointer.
|
||||
*
|
||||
* @param root_ptr Pointer to your libmonado state. Null-checked, will be set to null.
|
||||
*/
|
||||
void
|
||||
mnd_root_destroy(mnd_root_t **root_ptr);
|
||||
|
||||
/*!
|
||||
* Update our local cached copy of the client list
|
||||
*
|
||||
* @param root The libmonado state.
|
||||
* @return MND_SUCCESS on success
|
||||
*/
|
||||
mnd_result_t
|
||||
mnd_root_update_client_list(mnd_root_t *root);
|
||||
|
||||
/*!
|
||||
* Get the number of active clients
|
||||
*
|
||||
* This value only changes on calls to @ref mnd_root_update_client_list
|
||||
*
|
||||
* @param root The libmonado state.
|
||||
* @param[out] out_num Pointer to value to populate with the number of clients.
|
||||
*
|
||||
* @pre Called @ref mnd_root_update_client_list at least once
|
||||
*
|
||||
* @return MND_SUCCESS on success
|
||||
*/
|
||||
mnd_result_t
|
||||
mnd_root_get_number_clients(mnd_root_t *root, uint32_t *out_num);
|
||||
|
||||
/*!
|
||||
* Get the id from the current client list.
|
||||
*
|
||||
* @param root The libmonado state.
|
||||
* @param index Index to retrieve id for.
|
||||
* @param[out] out_client_id Pointer to value to populate with the id at the given index.
|
||||
*/
|
||||
mnd_result_t
|
||||
mnd_root_get_client_id_at_index(mnd_root_t *root, uint32_t index, uint32_t *out_client_id);
|
||||
|
||||
/*!
|
||||
* Get the name of the client at the given index.
|
||||
*
|
||||
* The string returned is only valid until the next call into libmonado.
|
||||
*
|
||||
* @param root The libmonado state.
|
||||
* @param client_id ID of client to retrieve name from.
|
||||
* @param[out] out_name Pointer to populate with the client name.
|
||||
*
|
||||
* @pre Called @ref mnd_root_update_client_list at least once
|
||||
*
|
||||
* @return MND_SUCCESS on success
|
||||
*/
|
||||
mnd_result_t
|
||||
mnd_root_get_client_name(mnd_root_t *root, uint32_t client_id, const char **out_name);
|
||||
|
||||
/*!
|
||||
* Get the state flags of the client at the given index.
|
||||
*
|
||||
* This result only changes on calls to @ref mnd_root_update_client_list
|
||||
*
|
||||
* @param root The libmonado state.
|
||||
* @param client_id ID of client to retrieve flags from.
|
||||
* @param[out] out_flags Pointer to populate with the flags, a bitwise combination of @ref mnd_client_flags.
|
||||
*
|
||||
* @pre Called @ref mnd_root_update_client_list at least once
|
||||
*
|
||||
* @return MND_SUCCESS on success
|
||||
*/
|
||||
mnd_result_t
|
||||
mnd_root_get_client_state(mnd_root_t *root, uint32_t client_id, uint32_t *out_flags);
|
||||
|
||||
/*!
|
||||
* Set the client at the given index as "primary".
|
||||
*
|
||||
* @param root The libmonado state.
|
||||
* @param client_id ID of the client set as primary.
|
||||
*
|
||||
* @pre Called @ref mnd_root_update_client_list at least once
|
||||
*
|
||||
* @return MND_SUCCESS on success
|
||||
*/
|
||||
mnd_result_t
|
||||
mnd_root_set_client_primary(mnd_root_t *root, uint32_t client_id);
|
||||
|
||||
/*!
|
||||
* Set the client at the given index as "focused".
|
||||
*
|
||||
* @param root The libmonado state.
|
||||
* @param client_id ID of the client set as focused.
|
||||
*
|
||||
* @pre Called @ref mnd_root_update_client_list at least once
|
||||
*
|
||||
* @return MND_SUCCESS on success
|
||||
*/
|
||||
mnd_result_t
|
||||
mnd_root_set_client_focused(mnd_root_t *root, uint32_t client_id);
|
||||
|
||||
/*!
|
||||
* Toggle io activity for the client at the given index.
|
||||
*
|
||||
* @param root The libmonado state.
|
||||
* @param client_id ID of the client to toggle IO for.
|
||||
*
|
||||
* @pre Called @ref mnd_root_update_client_list at least once
|
||||
*
|
||||
* @return MND_SUCCESS on success
|
||||
*/
|
||||
mnd_result_t
|
||||
mnd_root_toggle_client_io_active(mnd_root_t *root, uint32_t client_id);
|
||||
|
||||
/*!
|
||||
* Get the number of devices
|
||||
*
|
||||
* @param root The libmonado state.
|
||||
* @param[out] out_device_count Pointer to value to populate with the number of devices.
|
||||
*
|
||||
* @return MND_SUCCESS on success
|
||||
*/
|
||||
mnd_result_t
|
||||
mnd_root_get_device_count(mnd_root_t *root, uint32_t *out_device_count);
|
||||
|
||||
/*!
|
||||
* Get device info at the given index.
|
||||
*
|
||||
* @param root The libmonado state.
|
||||
* @param device_index Index of device to retrieve name from.
|
||||
* @param[out] out_device_id Pointer to value to populate with the device id at the given index.
|
||||
* @param[out] out_dev_name Pointer to populate with the device name.
|
||||
*
|
||||
* @return MND_SUCCESS on success
|
||||
*/
|
||||
mnd_result_t
|
||||
mnd_root_get_device_info(mnd_root_t *root, uint32_t device_index, uint32_t *out_device_id, const char **out_dev_name);
|
||||
|
||||
/*!
|
||||
* Get the device index associated for a given role name.
|
||||
*
|
||||
* @param root The libmonado state.
|
||||
* @param role_name Name of the role, one-of
|
||||
* "head","left","right"."gamepad","eyes","hand-tracking-left","hand-tracking-right"
|
||||
* @param[out] out_device_id Pointer to value to populate with the device id associated with given role name, -1 if not
|
||||
* role is set.
|
||||
*
|
||||
* @return MND_SUCCESS on success
|
||||
*/
|
||||
mnd_result_t
|
||||
mnd_root_get_device_from_role(mnd_root_t *root, const char *role_name, int32_t *out_device_id);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
211
src/xrt/targets/libmonado/monado.py
Normal file
211
src/xrt/targets/libmonado/monado.py
Normal file
|
@ -0,0 +1,211 @@
|
|||
# Copyright 2020-2023, Collabora, Ltd.
|
||||
# SPDX-License-Identifier: BSL-1.0
|
||||
# Author: Jakob Bornecrantz <jakob@collabora.com>
|
||||
# Author: Lubosz Sarnecki <lubosz.sarnecki@collabora.com>
|
||||
# Author: Korcan Hussein <korcan.hussein@collabora.com>
|
||||
|
||||
import os
|
||||
from cffi import FFI
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def preprocess_ffi(lines: list[str]) -> str:
|
||||
out_lines = []
|
||||
skipping = False
|
||||
for line in lines:
|
||||
if line.startswith("#ifdef"):
|
||||
skipping = True
|
||||
continue
|
||||
elif line.startswith("#endif"):
|
||||
skipping = False
|
||||
continue
|
||||
elif line.startswith("#include"):
|
||||
continue
|
||||
if skipping:
|
||||
continue
|
||||
out_lines.append(line)
|
||||
|
||||
return "\n".join(out_lines)
|
||||
|
||||
|
||||
class MonadoHeaderNotFoundError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def find_monado_header() -> Path:
|
||||
search_paths = [
|
||||
# Try script directory first, in case running uninstalled
|
||||
Path(__file__).parent / "monado.h",
|
||||
# path from installed package
|
||||
Path("/usr/include/monado/monado.h"),
|
||||
# path from local install
|
||||
Path("/usr/local/include/monado/monado.h")
|
||||
]
|
||||
|
||||
# path from ENV
|
||||
if os.getenv('MONADO_HEADER_PATH'):
|
||||
search_paths.append(Path(os.getenv('MONADO_HEADER_PATH')))
|
||||
|
||||
for path in search_paths:
|
||||
if path.is_file():
|
||||
return path
|
||||
|
||||
# Give up
|
||||
raise MonadoHeaderNotFoundError("Could not find monado.h. Define the path by setting MONADO_HEADER_PATH.")
|
||||
|
||||
|
||||
def load_ffi() -> FFI:
|
||||
ffi = FFI()
|
||||
|
||||
header_path = find_monado_header()
|
||||
with header_path.open("r") as f:
|
||||
header_lines = f.readlines()
|
||||
|
||||
header = preprocess_ffi(header_lines)
|
||||
|
||||
ffi.cdef(header)
|
||||
return ffi
|
||||
|
||||
|
||||
class Device:
|
||||
def __init__(self, ident, name):
|
||||
self.ident = ident
|
||||
self.name = name
|
||||
|
||||
|
||||
class Client:
|
||||
def __init__(self, ident, name, primary, focused, visible, active, overlay, io_active):
|
||||
self.ident = ident
|
||||
self.name = name
|
||||
self.primary = primary
|
||||
self.focused = focused
|
||||
self.visible = visible
|
||||
self.active = active
|
||||
self.overlay = overlay
|
||||
self.io_active = io_active
|
||||
|
||||
|
||||
class MonadoLibraryNotFoundError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class Monado:
|
||||
def __init__(self):
|
||||
self.ffi = load_ffi()
|
||||
try:
|
||||
self.lib = self.ffi.dlopen("libmonado.so")
|
||||
except OSError:
|
||||
raise MonadoLibraryNotFoundError("Could not load libmonado.so.")
|
||||
|
||||
self.root_ptr = self.ffi.new("mnd_root_t **")
|
||||
|
||||
ret = self.lib.mnd_root_create(self.root_ptr)
|
||||
if ret != 0:
|
||||
raise Exception("Could not create root")
|
||||
|
||||
self.root = self.root_ptr[0]
|
||||
self.client_count = 0
|
||||
self.client_count_ptr = self.ffi.new("uint32_t *")
|
||||
self.name_ptr = self.ffi.new("char **")
|
||||
self.flags_ptr = self.ffi.new("uint32_t *")
|
||||
self.client_id_ptr = self.ffi.new("uint32_t *")
|
||||
self.device_id_ptr = self.ffi.new("uint32_t *")
|
||||
self.device_name_ptr = self.ffi.new("char **")
|
||||
self.device_count_ptr = self.ffi.new("uint32_t *")
|
||||
|
||||
def update_clients(self):
|
||||
ret = self.lib.mnd_root_update_client_list(self.root)
|
||||
if ret != 0:
|
||||
raise Exception("Could not update clients")
|
||||
|
||||
ret = self.lib.mnd_root_get_number_clients(self.root, self.client_count_ptr)
|
||||
if ret != 0:
|
||||
raise Exception("Could not update clients")
|
||||
|
||||
self.client_count = self.client_count_ptr[0]
|
||||
|
||||
def get_client_id_at_index(self, index):
|
||||
ret = self.lib.mnd_root_get_client_id_at_index(self.root, index, self.client_id_ptr)
|
||||
if ret != 0:
|
||||
raise Exception("Could not get client id at index")
|
||||
|
||||
return self.client_id_ptr[0]
|
||||
|
||||
def get_client_name(self, client_id):
|
||||
ret = self.lib.mnd_root_get_client_name(self.root, client_id, self.name_ptr)
|
||||
if ret != 0:
|
||||
raise Exception("Could not get client name")
|
||||
|
||||
return self.ffi.string(self.name_ptr[0]).decode("utf-8")
|
||||
|
||||
def get_client_flags(self, client_id):
|
||||
ret = self.lib.mnd_root_get_client_state(self.root, client_id, self.flags_ptr)
|
||||
if ret != 0:
|
||||
raise Exception("Could not get client state")
|
||||
|
||||
return self.flags_ptr[0]
|
||||
|
||||
def snapshot_client(self, index):
|
||||
ident = self.get_client_id_at_index(index)
|
||||
name = self.get_client_name(ident)
|
||||
flags = self.get_client_flags(ident)
|
||||
|
||||
primary = (flags & self.lib.MND_CLIENT_PRIMARY_APP) != 0
|
||||
active = (flags & self.lib.MND_CLIENT_SESSION_ACTIVE) != 0
|
||||
visible = (flags & self.lib.MND_CLIENT_SESSION_VISIBLE) != 0
|
||||
focused = (flags & self.lib.MND_CLIENT_SESSION_FOCUSED) != 0
|
||||
overlay = (flags & self.lib.MND_CLIENT_SESSION_OVERLAY) != 0
|
||||
io_active = (flags & self.lib.MND_CLIENT_IO_ACTIVE) != 0
|
||||
|
||||
return Client(ident, name, primary, focused, visible, active, overlay, io_active)
|
||||
|
||||
def destroy(self):
|
||||
self.lib.mnd_root_destroy(self.root_ptr)
|
||||
self.root = self.root_ptr[0]
|
||||
|
||||
def set_primary(self, client_id: int):
|
||||
ret = self.lib.mnd_root_set_client_primary(self.root, client_id)
|
||||
if ret != 0:
|
||||
raise Exception(f"Failed to set primary client id to {client_id}.")
|
||||
|
||||
def set_focused(self, client_id: int):
|
||||
ret = self.lib.mnd_root_set_client_focused(self.root, client_id)
|
||||
if ret != 0:
|
||||
raise Exception(f"Failed to set focused client id to {client_id}.")
|
||||
|
||||
def toggle_io(self, client_id: int):
|
||||
ret = self.lib.mnd_root_toggle_client_io_active(self.root, client_id)
|
||||
if ret != 0:
|
||||
raise Exception(f"Failed to toggle io for client id {client_id}.")
|
||||
|
||||
def get_device_count(self):
|
||||
ret = self.lib.mnd_root_get_device_count(self.root, self.device_count_ptr)
|
||||
if ret != 0:
|
||||
raise Exception("Could not get device count")
|
||||
return self.device_count_ptr[0]
|
||||
|
||||
def get_device_at_index(self, index):
|
||||
ret = self.lib.mnd_root_get_device_info(self.root, index, self.device_id_ptr, self.device_name_ptr)
|
||||
if ret != 0:
|
||||
raise Exception(f"Could not get device at index:{index}")
|
||||
dev_id = self.device_id_ptr[0]
|
||||
dev_name = self.ffi.string(self.device_name_ptr[0]).decode("utf-8")
|
||||
return Device(dev_id, dev_name)
|
||||
|
||||
def get_devices(self):
|
||||
devices = []
|
||||
dev_count = self.get_device_count()
|
||||
for i in range(dev_count):
|
||||
devices.append(self.get_device_at_index(i))
|
||||
return devices
|
||||
|
||||
def get_device_roles(self):
|
||||
role_map = dict()
|
||||
device_int_id_ptr = self.ffi.new("int32_t *")
|
||||
for role_name in ["head", "left", "right", "gamepad", "eyes", "hand-tracking-left", "hand-tracking-right"]:
|
||||
crole_name = role_name.encode('utf-8')
|
||||
ret = self.lib.mnd_root_get_device_from_role(self.root, crole_name, device_int_id_ptr)
|
||||
if ret != 0:
|
||||
raise Exception(f"Could not get device role: {role_name}")
|
||||
role_map[role_name] = device_int_id_ptr[0]
|
||||
return role_map
|
Loading…
Reference in a new issue