mirror of https://github.com/nirenjan/libx52.git
Add vkm library
parent
2bc59ae6f0
commit
ae57db0db9
|
|
@ -8,7 +8,12 @@ if not dep_hidapi.found()
|
|||
dep_hidapi = dependency('hidapi', required: true)
|
||||
endif
|
||||
|
||||
dep_evdev = dependency('libevdev', required: false)
|
||||
if host_machine.system() == 'linux'
|
||||
dep_evdev = dependency('libevdev', required: false)
|
||||
else
|
||||
# Create a dummy dependency
|
||||
dep_evdev = dependency('', required: false)
|
||||
endif
|
||||
|
||||
dep_systemd = dependency('systemd', required: false)
|
||||
dep_udev = dependency('udev', required: false)
|
||||
|
|
@ -110,6 +115,7 @@ includes = include_directories('.', 'libx52', 'libx52io', 'libx52util')
|
|||
subdir('libx52')
|
||||
subdir('libx52io')
|
||||
subdir('libx52util')
|
||||
subdir('vkm')
|
||||
subdir('bugreport')
|
||||
subdir('cli')
|
||||
subdir('joytest')
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
vkm_version = '0.1.0'
|
||||
|
||||
vkm_files = files(
|
||||
'vkm_common.c'
|
||||
)
|
||||
|
||||
vkm_stub_files = files(
|
||||
'vkm_stub.c'
|
||||
)
|
||||
|
||||
if host_machine.system() == 'linux'
|
||||
vkm_dep = dep_evdev
|
||||
if dep_evdev.found()
|
||||
vkm_platform_files = files(
|
||||
'vkm_linux_evdev.c'
|
||||
)
|
||||
else
|
||||
vkm_platform_files = vkm_stub_files
|
||||
endif
|
||||
else
|
||||
vkm_platform_files = vkm_stub_files
|
||||
endif
|
||||
|
||||
lib_vkm = library('vkm', vkm_files + vkm_platform_files,
|
||||
install: true,
|
||||
version: vkm_version,
|
||||
dependencies: [vkm_dep],
|
||||
include_directories: [includes])
|
||||
|
||||
install_headers('vkm.h', subdir: 'vkm')
|
||||
pkgconfig.generate(lib_vkm,
|
||||
name: 'vkm',
|
||||
description: 'Linux/Unix library to control Saitek X52/X52Pro joystick extended functionality.',
|
||||
subdirs: meson.project_name(),
|
||||
version: vkm_version)
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* VKM common functions
|
||||
*
|
||||
* Copyright (C) 2026 Nirenjan Krishnan <nirenjan@nirenjan.org>
|
||||
*
|
||||
* SPDX-LicenseIdentifier: GPL-2.0-only WITH Classpath-exception-2.0
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "vkm.h"
|
||||
|
||||
#ifndef VKM_INTERNAL_H
|
||||
#define VKM_INTERNAL_H
|
||||
|
||||
struct vkm_mouse_button_state {
|
||||
vkm_button_state pressed[VKM_MOUSE_BTN_MAX];
|
||||
};
|
||||
|
||||
vkm_button_state _vkm_get_mouse_button_state(struct vkm_mouse_button_state *state, vkm_mouse_button button);
|
||||
void _vkm_set_mouse_button_state(struct vkm_mouse_button_state *sstate, vkm_mouse_button button, vkm_button_state state);
|
||||
|
||||
#endif // !defined VKM_INTERNAL_H
|
||||
273
vkm/vkm.h
273
vkm/vkm.h
|
|
@ -21,6 +21,7 @@
|
|||
#define VKM_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
|
@ -45,6 +46,149 @@ typedef struct vkm_context vkm_context;
|
|||
*/
|
||||
typedef int32_t vkm_result;
|
||||
|
||||
/**
|
||||
* @brief Feature identifiers
|
||||
*
|
||||
* These are used to check for platform support of the relevant feature.
|
||||
*/
|
||||
typedef enum {
|
||||
/** Check if mouse is supported on this platform */
|
||||
VKM_FEAT_MOUSE = (1 << 0),
|
||||
|
||||
/** Check if US keyboard is supported on this platform */
|
||||
VKM_FEAT_KEYBOARD_US = (1 << 1),
|
||||
|
||||
/* Additional flags may be added in the future */
|
||||
} vkm_feature;
|
||||
|
||||
/**
|
||||
* @brief Error code list
|
||||
*/
|
||||
typedef enum {
|
||||
/** No error, indicates success */
|
||||
VKM_SUCCESS = 0,
|
||||
|
||||
/** Unknown error, used as a catch-all error state */
|
||||
VKM_ERROR_UNKNOWN,
|
||||
|
||||
/** Not started, must call vkm_start first */
|
||||
VKM_ERROR_NOT_READY,
|
||||
|
||||
/** Out of memory */
|
||||
VKM_ERROR_OUT_OF_MEMORY,
|
||||
|
||||
/** Invalid parameter(s) */
|
||||
VKM_ERROR_INVALID_PARAM,
|
||||
|
||||
/** Not supported */
|
||||
VKM_ERROR_NOT_SUPPORTED,
|
||||
|
||||
/** Unable to create virtual devices */
|
||||
VKM_ERROR_DEV_FAILURE,
|
||||
|
||||
/** Unable to write event */
|
||||
VKM_ERROR_EVENT,
|
||||
|
||||
/** No state change in the event, please retry */
|
||||
VKM_ERROR_NO_CHANGE,
|
||||
|
||||
/* Maximum error states, do not use in external code*/
|
||||
VKM_ERROR_MAX,
|
||||
} vkm_error_code;
|
||||
|
||||
/**
|
||||
* @brief Option list
|
||||
*/
|
||||
typedef enum {
|
||||
/**
|
||||
* @brief Set the high resolution scrolling behavior of the mouse
|
||||
*
|
||||
* This option must be passed a boolean which lets VKM know whether to
|
||||
* enable or disable high resolution scrolling.
|
||||
*
|
||||
* Defaults to true.
|
||||
*/
|
||||
VKM_OPT_HI_RES_SCROLL,
|
||||
|
||||
/**
|
||||
* @brief Enable or disable horizontal scrolling of the mouse
|
||||
*
|
||||
* This option must be passed in a boolean which lets VKM know whether to
|
||||
* enable or disable horizontal scrolling. If horizontal scrolling is
|
||||
* disabled, then any requests to \ref vkm_mouse_scroll with
|
||||
* \ref VKM_MOUSE_SCROLL_LEFT or \ref VKM_MOUSE_SCROLL_RIGHT will return
|
||||
* \ref VKM_ERROR_INVALID_PARAM.
|
||||
*
|
||||
* Defaults to false.
|
||||
*/
|
||||
VKM_OPT_HORIZONTAL_SCROLL,
|
||||
|
||||
/**
|
||||
* @brief Set the virtual device name in the system.
|
||||
*
|
||||
* This option sets the name of the virtual input device in the system.
|
||||
* If not set, the virtual device will have a name determined by the
|
||||
* timestamp at which it was initialized.
|
||||
*
|
||||
* Only applicable on Linux.
|
||||
*/
|
||||
VKM_OPT_DEVICE_NAME,
|
||||
|
||||
/* Max number of options, do not use in external code */
|
||||
VKM_OPT_MAX
|
||||
} vkm_option;
|
||||
|
||||
/**
|
||||
* @brief Button state
|
||||
*/
|
||||
typedef enum {
|
||||
/** Button is released */
|
||||
VKM_BUTTON_RELEASED,
|
||||
|
||||
/** Button is pressed */
|
||||
VKM_BUTTON_PRESSED,
|
||||
|
||||
/* Max number of button states, do not use in external code */
|
||||
VKM_BUTTON_STATE_MAX
|
||||
} vkm_button_state;
|
||||
|
||||
/**
|
||||
* @brief Mouse button identifiers
|
||||
*/
|
||||
typedef enum {
|
||||
/** Mouse left button */
|
||||
VKM_MOUSE_BTN_LEFT,
|
||||
|
||||
/** Mouse right button */
|
||||
VKM_MOUSE_BTN_RIGHT,
|
||||
|
||||
/** Mouse middle button */
|
||||
VKM_MOUSE_BTN_MIDDLE,
|
||||
|
||||
/* Max number of mouse buttons, do not use in external code */
|
||||
VKM_MOUSE_BTN_MAX
|
||||
} vkm_mouse_button;
|
||||
|
||||
/**
|
||||
* @brief Scroll directions
|
||||
*/
|
||||
typedef enum {
|
||||
/** Scroll up */
|
||||
VKM_MOUSE_SCROLL_UP,
|
||||
|
||||
/** Scroll down */
|
||||
VKM_MOUSE_SCROLL_DOWN,
|
||||
|
||||
/** Scroll left (horizontal scrolling) */
|
||||
VKM_MOUSE_SCROLL_LEFT,
|
||||
|
||||
/** Scroll right (horizontal scrolling) */
|
||||
VKM_MOUSE_SCROLL_RIGHT,
|
||||
|
||||
/* Maximum number of scroll states, do not use in external code */
|
||||
VKM_MOUSE_SCROLL_MAX
|
||||
} vkm_mouse_scroll_direction;
|
||||
|
||||
/**
|
||||
* @brief Initialize the VKM library
|
||||
*
|
||||
|
|
@ -82,6 +226,135 @@ vkm_result vkm_init(vkm_context **ctx);
|
|||
*/
|
||||
void vkm_exit(vkm_context *ctx);
|
||||
|
||||
/**
|
||||
* @brief Start any virtual keyboard/mouse devices on the platform
|
||||
*
|
||||
* This must be done before injecting any events, and after setting all
|
||||
* options through \ref vkm_set_option.
|
||||
*
|
||||
* @param[in] ctx Context pointer
|
||||
*
|
||||
* @returns
|
||||
* - \ref VKM_SUCCESS on successful start
|
||||
* - \ref VKM_ERROR_INVALID_PARAM on bad pointer
|
||||
* - \ref VKM_ERROR_UNKNOWN on other errors
|
||||
*/
|
||||
vkm_result vkm_start(vkm_context *ctx);
|
||||
|
||||
/**
|
||||
* @brief check if VKM is started and ready
|
||||
*
|
||||
* @param[in] ctx Context pointer
|
||||
*
|
||||
* @returns boolean indicating if ready or not.
|
||||
*/
|
||||
bool vkm_is_ready(vkm_context *ctx);
|
||||
|
||||
/**
|
||||
* @brief Check if VKM is supported on this platform
|
||||
*
|
||||
* On some platforms, there is no support yet for the virtual keyboard/mouse.
|
||||
* This function will return a boolean indicating if it is supported or not.
|
||||
*
|
||||
* @returns boolean indicating support.
|
||||
*/
|
||||
bool vkm_platform_supported(void);
|
||||
|
||||
/**
|
||||
* @brief Check if a particular feature is enabled on this platform
|
||||
*
|
||||
* Features may be limited on a per-platform basis.
|
||||
*
|
||||
* @param[in] feat Feature identifier
|
||||
*
|
||||
* @returns boolean indicating if feature is supported or not.
|
||||
*/
|
||||
bool vkm_feature_supported(vkm_feature feat);
|
||||
|
||||
/**
|
||||
* @brief Set an option flag for VKM.
|
||||
*
|
||||
* Option flags control the behavior of VKM. All options must be set before
|
||||
* calling \ref vkm_start.
|
||||
*
|
||||
* @param[in] ctx Context pointer
|
||||
* @param[in] option Which option to set
|
||||
* @param[in] ... Any required arguments for the specified option
|
||||
*
|
||||
* @returns
|
||||
* - \ref VKM_SUCCESS on success
|
||||
* - \ref VKM_ERROR_INVALID_PARAM if the option or arguments are invalid
|
||||
* - \ref VKM_ERROR_NOT_SUPPORTED if the option is valid but not supported on this platform
|
||||
*/
|
||||
vkm_result vkm_set_option(vkm_context *ctx, vkm_option option, ...);
|
||||
|
||||
/**
|
||||
* @brief Move the mouse by the specified amount
|
||||
*
|
||||
* The move mouse takes in a delta of x and y coordinates that tell the system
|
||||
* to move the mouse by those relative numbers.
|
||||
*
|
||||
* @param[in] ctx Context pointer
|
||||
* @param[in] dx Delta by which to move the mouse in the horizontal axis
|
||||
* @param[in] dy Delta by which to move the mouse in the vertical axis
|
||||
*
|
||||
* @returns
|
||||
* - \ref VKM_SUCCESS on successful move
|
||||
* - \ref VKM_ERROR_UNKNOWN on a generic error
|
||||
* - \ref VKM_ERROR_NOT_SUPPORTED if the mouse move is not supported on this platform
|
||||
*/
|
||||
vkm_result vkm_mouse_move(vkm_context *ctx, int dx, int dy);
|
||||
|
||||
/**
|
||||
* @brief Click the mouse button
|
||||
*
|
||||
* Send a mouse button event, this may be either a button down or button up event.
|
||||
*
|
||||
* @param[in] ctx Context pointer
|
||||
* @param[in] button Button identifier
|
||||
* @param[in] state Button state (press or release)
|
||||
*
|
||||
* @returns
|
||||
* - \ref VKM_SUCCESS on successful move
|
||||
* - \ref VKM_ERROR_UNKNOWN on a generic error
|
||||
* - \ref VKM_ERROR_NOT_SUPPORTED if the mouse button click is not supported on this platform
|
||||
*/
|
||||
vkm_result vkm_mouse_click(vkm_context *ctx, vkm_mouse_button button, vkm_button_state state);
|
||||
|
||||
/**
|
||||
* @brief Scroll the mouse wheel
|
||||
*
|
||||
* Send a single scroll event to the mouse wheel
|
||||
*
|
||||
* @param[in] ctx Context pointer
|
||||
* @param[in] dir Scroll direction
|
||||
* @param[in] state Button state (press or release)
|
||||
*
|
||||
* @returns
|
||||
* - \ref VKM_SUCCESS on successful move
|
||||
* - \ref VKM_ERROR_UNKNOWN on a generic error
|
||||
* - \ref VKM_ERROR_INVALID_PARAM if horizontal scrolling is not enabled
|
||||
* and \p dir is \ref VKM_MOUSE_SCROLL_LEFT or \ref VKM_MOUSE_SCROLL_RIGHT
|
||||
* - \ref VKM_ERROR_NOT_SUPPORTED if the mouse scrolling is not supported on this platform
|
||||
*/
|
||||
vkm_result vkm_mouse_scroll(vkm_context *ctx, vkm_mouse_scroll_direction dir);
|
||||
|
||||
/**
|
||||
* @brief Send a sync packet to the OS
|
||||
*
|
||||
* On some platforms, a sync packet is necessary for the previously injected
|
||||
* events to actually get reflected in the system. For platforms where this
|
||||
* is not needed, this is a noop.
|
||||
*
|
||||
* @param[in] ctx Context pointer
|
||||
*
|
||||
* @returns
|
||||
* - \ref VKM_SUCCESS on successful move
|
||||
* - \ref VKM_ERROR_UNKNOWN on a generic error
|
||||
* - \ref VKM_ERROR_INVALID_PARAM if parameters are invalid
|
||||
*/
|
||||
vkm_result vkm_sync(vkm_context *ctx);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* VKM common functions
|
||||
*
|
||||
* Copyright (C) 2026 Nirenjan Krishnan <nirenjan@nirenjan.org>
|
||||
*
|
||||
* SPDX-LicenseIdentifier: GPL-2.0-only WITH Classpath-exception-2.0
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "vkm-internal.h"
|
||||
|
||||
vkm_button_state _vkm_get_mouse_button_state(struct vkm_mouse_button_state *state, vkm_mouse_button button)
|
||||
{
|
||||
if (state == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return state->pressed[button];
|
||||
}
|
||||
|
||||
void _vkm_set_mouse_button_state(struct vkm_mouse_button_state *sstate, vkm_mouse_button button, vkm_button_state state)
|
||||
{
|
||||
if (sstate != NULL) {
|
||||
sstate->pressed[button] = state;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,373 @@
|
|||
/*
|
||||
* VKM Linux evdev implementation
|
||||
*
|
||||
* Copyright (C) 2026 Nirenjan Krishnan <nirenjan@nirenjan.org>
|
||||
*
|
||||
* SPDX-LicenseIdentifier: GPL-2.0-only WITH Classpath-exception-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "libevdev/libevdev.h"
|
||||
#include "libevdev/libevdev-uinput.h"
|
||||
|
||||
#include "vkm-internal.h"
|
||||
|
||||
struct vkm_context {
|
||||
struct libevdev *dev;
|
||||
struct libevdev_uinput *uidev;
|
||||
|
||||
char *name;
|
||||
bool mouse_hi_res_scroll;
|
||||
bool mouse_horizontal_scroll;
|
||||
|
||||
// Existing state of buttons
|
||||
struct vkm_mouse_button_state mouse_buttons;
|
||||
};
|
||||
|
||||
static const int mouse_button_map[VKM_MOUSE_BTN_MAX] = {
|
||||
[VKM_MOUSE_BTN_LEFT] = BTN_LEFT,
|
||||
[VKM_MOUSE_BTN_RIGHT] = BTN_RIGHT,
|
||||
[VKM_MOUSE_BTN_MIDDLE] = BTN_MIDDLE,
|
||||
};
|
||||
|
||||
static const int button_value_map[VKM_BUTTON_STATE_MAX] = {
|
||||
[VKM_BUTTON_PRESSED] = 1,
|
||||
[VKM_BUTTON_RELEASED] = 0,
|
||||
};
|
||||
|
||||
vkm_result vkm_init(vkm_context **ctx)
|
||||
{
|
||||
char name_buf[32];
|
||||
char *name;
|
||||
vkm_result rc;
|
||||
if (ctx == NULL) {
|
||||
return VKM_ERROR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
*ctx = calloc(1, sizeof(*ctx));
|
||||
if (*ctx == NULL) {
|
||||
return VKM_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
rc = VKM_SUCCESS;
|
||||
// Create a default name for the virtual device, this will be replaced when
|
||||
// the user calls vkm_set_option with the appropriate name field.
|
||||
snprintf(name_buf, sizeof(name_buf), "VKM virtual device @%08" PRIx64,
|
||||
(uint64_t)(time(NULL)));
|
||||
name = strdup(name_buf);
|
||||
if (name == NULL) {
|
||||
rc = VKM_ERROR_OUT_OF_MEMORY;
|
||||
goto error;
|
||||
}
|
||||
(*ctx)->name = name;
|
||||
|
||||
// Set defaults for the flags
|
||||
|
||||
error:
|
||||
if (name) {
|
||||
free(name);
|
||||
}
|
||||
free(*ctx);
|
||||
*ctx = NULL;
|
||||
return rc;
|
||||
}
|
||||
|
||||
void vkm_exit(vkm_context *ctx)
|
||||
{
|
||||
char *name;
|
||||
struct libevdev_uinput *uidev;
|
||||
struct libevdev *dev;
|
||||
|
||||
if (ctx == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
dev = ctx->dev;
|
||||
uidev = ctx->uidev;
|
||||
name = ctx->name;
|
||||
|
||||
memset(ctx, 0, sizeof(*ctx));
|
||||
|
||||
free(name);
|
||||
|
||||
if (uidev) {
|
||||
libevdev_uinput_destroy(uidev);
|
||||
}
|
||||
|
||||
if (dev) {
|
||||
libevdev_free(dev);
|
||||
}
|
||||
|
||||
free(ctx);
|
||||
}
|
||||
|
||||
// Enable mouse events
|
||||
static void enable_mouse_events(vkm_context *ctx)
|
||||
{
|
||||
libevdev_enable_event_type(ctx->dev, EV_REL);
|
||||
libevdev_enable_event_code(ctx->dev, EV_REL, REL_X, NULL);
|
||||
libevdev_enable_event_code(ctx->dev, EV_REL, REL_Y, NULL);
|
||||
libevdev_enable_event_code(ctx->dev, EV_REL, REL_WHEEL, NULL);
|
||||
if (ctx->mouse_hi_res_scroll) {
|
||||
libevdev_enable_event_code(ctx->dev, EV_REL, REL_WHEEL_HI_RES, NULL);
|
||||
}
|
||||
|
||||
if (ctx->mouse_horizontal_scroll) {
|
||||
libevdev_enable_event_code(ctx->dev, EV_REL, REL_HWHEEL, NULL);
|
||||
if (ctx->mouse_hi_res_scroll) {
|
||||
libevdev_enable_event_code(ctx->dev, EV_REL, REL_HWHEEL_HI_RES, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
libevdev_enable_event_type(ctx->dev, EV_KEY);
|
||||
libevdev_enable_event_code(ctx->dev, EV_KEY, BTN_LEFT, NULL);
|
||||
libevdev_enable_event_code(ctx->dev, EV_KEY, BTN_RIGHT, NULL);
|
||||
libevdev_enable_event_code(ctx->dev, EV_KEY, BTN_MIDDLE, NULL);
|
||||
}
|
||||
|
||||
vkm_result vkm_start(vkm_context *ctx)
|
||||
{
|
||||
int rc;
|
||||
if (ctx == NULL) {
|
||||
return VKM_ERROR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
if (ctx->uidev) {
|
||||
// Already initialized, no need to do anything else
|
||||
return VKM_SUCCESS;
|
||||
}
|
||||
|
||||
if (!(ctx->dev)) {
|
||||
// This will happen on the first time this function is called, but
|
||||
// will get cleared up if the uinput device is not created, so this
|
||||
// is just a safety check.
|
||||
ctx->dev = libevdev_new();
|
||||
if (ctx->dev == NULL) {
|
||||
return VKM_ERROR_DEV_FAILURE;
|
||||
}
|
||||
|
||||
libevdev_set_name(ctx->dev, ctx->name);
|
||||
enable_mouse_events(ctx);
|
||||
}
|
||||
|
||||
rc = libevdev_uinput_create_from_device(ctx->dev, LIBEVDEV_UINPUT_OPEN_MANAGED,
|
||||
&(ctx->uidev));
|
||||
if (rc != 0) {
|
||||
goto error;
|
||||
}
|
||||
return VKM_SUCCESS;
|
||||
|
||||
error:
|
||||
libevdev_free(ctx->dev);
|
||||
ctx->dev = NULL;
|
||||
return VKM_ERROR_DEV_FAILURE;
|
||||
}
|
||||
|
||||
bool vkm_is_ready(vkm_context *ctx) {
|
||||
if (ctx == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (ctx->dev != NULL && ctx->uidev != NULL);
|
||||
}
|
||||
|
||||
bool vkm_platform_supported(void)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool vkm_feature_supported(vkm_feature feat)
|
||||
{
|
||||
switch (feat) {
|
||||
case VKM_FEAT_MOUSE:
|
||||
return true;
|
||||
|
||||
case VKM_FEAT_KEYBOARD_US:
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
vkm_result vkm_set_option(vkm_context *ctx, vkm_option option, ...)
|
||||
{
|
||||
va_list ap;
|
||||
bool flag;
|
||||
char *name;
|
||||
char *name2;
|
||||
vkm_result rc;
|
||||
|
||||
if (ctx == NULL) {
|
||||
return VKM_ERROR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
va_start(ap, option);
|
||||
rc = VKM_SUCCESS;
|
||||
switch (option) {
|
||||
case VKM_OPT_HI_RES_SCROLL:
|
||||
// read as int, as bool is promoted to int when passed as a va_arg
|
||||
flag = va_arg(ap, int);
|
||||
ctx->mouse_hi_res_scroll = (bool)flag;
|
||||
break;
|
||||
|
||||
case VKM_OPT_HORIZONTAL_SCROLL:
|
||||
// read as int, as bool is promoted to int when passed as a va_arg
|
||||
flag = va_arg(ap, int);
|
||||
ctx->mouse_horizontal_scroll = (bool)flag;
|
||||
break;
|
||||
|
||||
case VKM_OPT_DEVICE_NAME:
|
||||
name = va_arg(ap, char *);
|
||||
name2 = strdup(name);
|
||||
if (name2 == NULL) {
|
||||
rc = VKM_ERROR_OUT_OF_MEMORY;
|
||||
} else {
|
||||
free(ctx->name);
|
||||
ctx->name = name2;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
rc = VKM_ERROR_INVALID_PARAM;
|
||||
break;
|
||||
}
|
||||
|
||||
va_end(ap);
|
||||
return rc;
|
||||
}
|
||||
|
||||
vkm_result vkm_mouse_move(vkm_context *ctx, int dx, int dy)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (ctx == NULL) {
|
||||
return VKM_ERROR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
if (!vkm_is_ready(ctx)) {
|
||||
return VKM_ERROR_NOT_READY;
|
||||
}
|
||||
|
||||
if (!dx && !dy) {
|
||||
return VKM_ERROR_NO_CHANGE;
|
||||
}
|
||||
|
||||
if (dx) {
|
||||
rc = libevdev_uinput_write_event(ctx->uidev, EV_REL, REL_X, dx);
|
||||
if (rc != 0) {
|
||||
return VKM_ERROR_EVENT;
|
||||
}
|
||||
}
|
||||
|
||||
if (dy) {
|
||||
rc = libevdev_uinput_write_event(ctx->uidev, EV_REL, REL_X, dx);
|
||||
if (rc != 0) {
|
||||
return VKM_ERROR_EVENT;
|
||||
}
|
||||
}
|
||||
|
||||
return VKM_SUCCESS;
|
||||
}
|
||||
|
||||
vkm_result vkm_mouse_click(vkm_context *ctx, vkm_mouse_button button, vkm_button_state state)
|
||||
{
|
||||
int rc;
|
||||
vkm_button_state existing;
|
||||
|
||||
if (ctx == NULL) {
|
||||
return VKM_ERROR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
if (button >= VKM_MOUSE_BTN_MAX) {
|
||||
return VKM_ERROR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
if (state >= VKM_BUTTON_STATE_MAX) {
|
||||
return VKM_ERROR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
if (!vkm_is_ready(ctx)) {
|
||||
return VKM_ERROR_NOT_READY;
|
||||
}
|
||||
|
||||
existing = _vkm_get_mouse_button_state(&(ctx->mouse_buttons), button);
|
||||
if (existing == state) {
|
||||
return VKM_ERROR_NO_CHANGE;
|
||||
}
|
||||
|
||||
rc = libevdev_uinput_write_event(ctx->uidev, EV_KEY,
|
||||
mouse_button_map[button], button_value_map[state]);
|
||||
if (rc != 0) {
|
||||
return VKM_ERROR_EVENT;
|
||||
}
|
||||
|
||||
return VKM_SUCCESS;
|
||||
}
|
||||
|
||||
vkm_result vkm_mouse_scroll(vkm_context *ctx, vkm_mouse_scroll_direction dir)
|
||||
{
|
||||
int axis;
|
||||
int val;
|
||||
int rc;
|
||||
|
||||
if (ctx == NULL) {
|
||||
return VKM_ERROR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
if (dir >= VKM_MOUSE_SCROLL_MAX) {
|
||||
return VKM_ERROR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
if (!vkm_is_ready(ctx)) {
|
||||
return VKM_ERROR_NOT_READY;
|
||||
}
|
||||
|
||||
if (dir == VKM_MOUSE_SCROLL_LEFT || dir == VKM_MOUSE_SCROLL_RIGHT) {
|
||||
if (!ctx->mouse_horizontal_scroll) {
|
||||
return VKM_ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
axis = REL_HWHEEL;
|
||||
val = (dir == VKM_MOUSE_SCROLL_LEFT) ? -1 : +1;
|
||||
} else {
|
||||
axis = REL_WHEEL;
|
||||
val = (dir == VKM_MOUSE_SCROLL_UP) ? +1 : -1;
|
||||
}
|
||||
|
||||
if (val == 0) {
|
||||
return VKM_ERROR_NO_CHANGE;
|
||||
}
|
||||
|
||||
rc = libevdev_uinput_write_event(ctx->uidev, EV_REL, axis, val);
|
||||
if (rc != 0) {
|
||||
return VKM_ERROR_EVENT;
|
||||
}
|
||||
|
||||
return VKM_SUCCESS;
|
||||
}
|
||||
|
||||
vkm_result vkm_sync(vkm_context *ctx)
|
||||
{
|
||||
int rc;
|
||||
if (ctx == NULL) {
|
||||
return VKM_ERROR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
if (!vkm_is_ready(ctx)) {
|
||||
return VKM_ERROR_NOT_READY;
|
||||
}
|
||||
|
||||
rc = libevdev_uinput_write_event(ctx->uidev, EV_SYN, SYN_REPORT, 0);
|
||||
if (rc != 0) {
|
||||
return VKM_ERROR_EVENT;
|
||||
}
|
||||
|
||||
return VKM_SUCCESS;
|
||||
}
|
||||
|
|
@ -0,0 +1,129 @@
|
|||
/*
|
||||
* VKM stub implementation
|
||||
*
|
||||
* Copyright (C) 2026 Nirenjan Krishnan <nirenjan@nirenjan.org>
|
||||
*
|
||||
* SPDX-LicenseIdentifier: GPL-2.0-only WITH Classpath-exception-2.0
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "vkm.h"
|
||||
|
||||
// Dummy struct
|
||||
struct vkm_context {
|
||||
};
|
||||
|
||||
#define DUMMY_CTX (vkm_context *)0xDEADBEEF
|
||||
|
||||
vkm_result vkm_init(vkm_context **ctx)
|
||||
{
|
||||
if (ctx != NULL) {
|
||||
*ctx = DUMMY_CTX;
|
||||
return VKM_SUCCESS;
|
||||
}
|
||||
|
||||
return VKM_ERROR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
void vkm_exit(vkm_context *ctx)
|
||||
{
|
||||
(void)ctx;
|
||||
}
|
||||
|
||||
vkm_result vkm_start(vkm_context *ctx)
|
||||
{
|
||||
if (ctx != DUMMY_CTX) {
|
||||
return VKM_ERROR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
return VKM_SUCCESS;
|
||||
}
|
||||
|
||||
bool vkm_platform_supported(void)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool vkm_feature_supported(vkm_feature feat)
|
||||
{
|
||||
(void)feat;
|
||||
return false;
|
||||
}
|
||||
|
||||
vkm_result vkm_set_option(vkm_context *ctx, vkm_option option, ...)
|
||||
{
|
||||
va_list ap;
|
||||
bool flag;
|
||||
char *name;
|
||||
vkm_result rc;
|
||||
|
||||
if (ctx != DUMMY_CTX) {
|
||||
return VKM_ERROR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
va_start(ap, option);
|
||||
rc = VKM_SUCCESS;
|
||||
switch (option) {
|
||||
case VKM_OPT_HI_RES_SCROLL:
|
||||
case VKM_OPT_HORIZONTAL_SCROLL:
|
||||
// read as int, as bool is promoted to int when passed as a va_arg
|
||||
flag = va_arg(ap, int);
|
||||
(void)flag;
|
||||
break;
|
||||
|
||||
case VKM_OPT_DEVICE_NAME:
|
||||
name = va_arg(ap, char *);
|
||||
(void)name;
|
||||
break;
|
||||
|
||||
default:
|
||||
rc = VKM_ERROR_INVALID_PARAM;
|
||||
break;
|
||||
}
|
||||
|
||||
va_end(ap);
|
||||
return rc;
|
||||
}
|
||||
|
||||
vkm_result vkm_mouse_move(vkm_context *ctx, int dx, int dy)
|
||||
{
|
||||
(void)dx;
|
||||
(void)dy;
|
||||
if (ctx != DUMMY_CTX) {
|
||||
return VKM_ERROR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
return VKM_SUCCESS;
|
||||
}
|
||||
|
||||
vkm_result vkm_mouse_click(vkm_context *ctx, vkm_mouse_button button, vkm_button_state state)
|
||||
{
|
||||
if (ctx != DUMMY_CTX) {
|
||||
return VKM_ERROR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
if (button >= VKM_MOUSE_BTN_MAX) {
|
||||
return VKM_ERROR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
if (state >= VKM_BUTTON_STATE_MAX) {
|
||||
return VKM_ERROR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
return VKM_SUCCESS;
|
||||
}
|
||||
|
||||
vkm_result vkm_mouse_scroll(vkm_context *ctx, vkm_mouse_scroll_direction dir)
|
||||
{
|
||||
if (ctx != DUMMY_CTX) {
|
||||
return VKM_ERROR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
if (dir >= VKM_MOUSE_SCROLL_MAX) {
|
||||
return VKM_ERROR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
return VKM_SUCCESS;
|
||||
}
|
||||
Loading…
Reference in New Issue