Bemanitools v5.26 release
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
dlls += geninput
|
||||
|
||||
ldflags_geninput := \
|
||||
-lhid \
|
||||
-lsetupapi \
|
||||
|
||||
libs_geninput := \
|
||||
util \
|
||||
|
||||
src_geninput := \
|
||||
dev-list.c \
|
||||
guid.c \
|
||||
hid.c \
|
||||
hid-generic.c \
|
||||
hid-generic-strings.c \
|
||||
hid-meta-in.c \
|
||||
hid-meta-out.c \
|
||||
hid-mgr.c \
|
||||
hid-report-in.c \
|
||||
hid-report-out.c \
|
||||
hotplug.c \
|
||||
input.c \
|
||||
io-thread.c \
|
||||
kbd.c \
|
||||
kbd-data.c \
|
||||
mapper.c \
|
||||
mapper-s11n.c \
|
||||
mouse.c \
|
||||
msg-thread.c \
|
||||
pacdrive.c \
|
||||
ri.c \
|
||||
|
||||
@@ -0,0 +1,173 @@
|
||||
#include <windows.h>
|
||||
#include <setupapi.h>
|
||||
#include <hidsdi.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <wchar.h>
|
||||
|
||||
#include "geninput/dev-list.h"
|
||||
|
||||
#include "util/log.h"
|
||||
#include "util/mem.h"
|
||||
|
||||
void dev_list_init(struct dev_list *devs, const GUID *class_guid)
|
||||
{
|
||||
devs->class_guid = (GUID *) class_guid;
|
||||
devs->infolist = SetupDiGetClassDevs(devs->class_guid, NULL, NULL,
|
||||
DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
|
||||
|
||||
if (devs->infolist == NULL) {
|
||||
log_fatal("SetupDiGetClassDevs failed: %08x",
|
||||
(unsigned int) GetLastError());
|
||||
}
|
||||
|
||||
devs->dev_no = 0;
|
||||
devs->iface_no = 0;
|
||||
|
||||
devs->detail = NULL;
|
||||
devs->name = NULL;
|
||||
}
|
||||
|
||||
bool dev_list_next(struct dev_list *devs)
|
||||
{
|
||||
if (devs->detail != NULL) {
|
||||
free(devs->detail);
|
||||
|
||||
devs->detail = NULL;
|
||||
}
|
||||
|
||||
if (devs->name != NULL) {
|
||||
free(devs->name);
|
||||
|
||||
devs->name = NULL;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
devs->dev.cbSize = sizeof(devs->dev);
|
||||
|
||||
if (!SetupDiEnumDeviceInfo(devs->infolist, devs->dev_no, &devs->dev)) {
|
||||
if (GetLastError() == ERROR_NO_MORE_ITEMS) {
|
||||
return false;
|
||||
} else {
|
||||
log_fatal("SetupDiEnumDeviceInfo failed: %08x",
|
||||
(unsigned int) GetLastError());
|
||||
}
|
||||
}
|
||||
|
||||
devs->iface.cbSize = sizeof(devs->iface);
|
||||
|
||||
if (!SetupDiEnumDeviceInterfaces(devs->infolist, &devs->dev,
|
||||
devs->class_guid, devs->iface_no, &devs->iface)) {
|
||||
if (GetLastError() == ERROR_NO_MORE_ITEMS) {
|
||||
devs->dev_no++;
|
||||
devs->iface_no = 0;
|
||||
|
||||
continue;
|
||||
} else {
|
||||
log_fatal("SetupDiEnumDeviceInterfaces failed: %08x",
|
||||
(unsigned int) GetLastError());
|
||||
}
|
||||
}
|
||||
|
||||
devs->iface_no++;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
const char *dev_list_get_dev_node(struct dev_list *devs)
|
||||
{
|
||||
DWORD detail_size;
|
||||
|
||||
if (devs->detail != NULL) {
|
||||
return devs->detail->DevicePath;
|
||||
}
|
||||
|
||||
if (SetupDiGetDeviceInterfaceDetail(devs->infolist, &devs->iface, NULL, 0,
|
||||
&detail_size, NULL)
|
||||
|| GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
|
||||
log_fatal("SetupDiGetDeviceInterfaceDetail sizing failed: %08x",
|
||||
(unsigned int) GetLastError());
|
||||
}
|
||||
|
||||
devs->detail = xmalloc(detail_size);
|
||||
devs->detail->cbSize = sizeof(*devs->detail);
|
||||
|
||||
if (!SetupDiGetDeviceInterfaceDetail(devs->infolist, &devs->iface,
|
||||
devs->detail, detail_size, NULL, NULL)) {
|
||||
log_fatal("SetupDiGetDeviceInterfaceDetail content failed: %08x",
|
||||
(unsigned int) GetLastError());
|
||||
}
|
||||
|
||||
return devs->detail->DevicePath;
|
||||
}
|
||||
|
||||
const wchar_t *dev_list_get_dev_name(struct dev_list *devs)
|
||||
{
|
||||
DWORD name_size;
|
||||
const char* dev_path;
|
||||
// largest possible product string according to WinAPI docs
|
||||
wchar_t product_string[126];
|
||||
DWORD product_size;
|
||||
bool product_success;
|
||||
|
||||
if (devs->name != NULL) {
|
||||
return devs->name;
|
||||
}
|
||||
|
||||
if (SetupDiGetDeviceRegistryPropertyW(devs->infolist, &devs->dev,
|
||||
SPDRP_DEVICEDESC, NULL, NULL, 0, &name_size)
|
||||
|| GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
|
||||
log_fatal("SetupDiGetDeviceRegistryPropertyW(SPDRP_DEVICEDESC) sizing "
|
||||
"failed: %08x", (unsigned int) GetLastError());
|
||||
}
|
||||
|
||||
/* This function returns arbitrary data, not just strings. Therefore, we
|
||||
are actually working in units of bytes and not wchar_t's here for once */
|
||||
|
||||
devs->name = xmalloc(name_size);
|
||||
|
||||
if (!SetupDiGetDeviceRegistryPropertyW(devs->infolist, &devs->dev,
|
||||
SPDRP_DEVICEDESC, NULL, (void *) devs->name, name_size, 0)) {
|
||||
log_fatal("SetupDiGetDeviceRegistryPropertyW(SPDRP_DEVICEDESC) content "
|
||||
"failed: %08x", (unsigned int) GetLastError());
|
||||
}
|
||||
|
||||
/* Also, try and get the product string - this is usually far more human
|
||||
readable than the registry value, which is often "HID Compliant Thing".
|
||||
We then append this in parentheses */
|
||||
dev_path = dev_list_get_dev_node(devs);
|
||||
HANDLE hid_hnd = CreateFile(dev_path, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
|
||||
if(hid_hnd) {
|
||||
product_success = HidD_GetProductString(hid_hnd, product_string, sizeof(product_string));
|
||||
CloseHandle(hid_hnd);
|
||||
if(product_success && wcslen(product_string) > 0 && wcscmp(product_string, devs->name)) {
|
||||
// len + null + 2*parentheses + space separator
|
||||
product_size = (wcslen(product_string)+4) * sizeof(wchar_t);
|
||||
devs->name = xrealloc(devs->name, name_size + product_size);
|
||||
wcscat(devs->name, L" (");
|
||||
wcscat(devs->name, product_string);
|
||||
wcscat(devs->name, L")");
|
||||
}
|
||||
}
|
||||
|
||||
return devs->name;
|
||||
}
|
||||
|
||||
void dev_list_fini(struct dev_list *devs)
|
||||
{
|
||||
if (devs->detail != NULL) {
|
||||
free(devs->detail);
|
||||
}
|
||||
|
||||
if (devs->name != NULL) {
|
||||
free(devs->name);
|
||||
}
|
||||
|
||||
if (!SetupDiDestroyDeviceInfoList(devs->infolist)) {
|
||||
log_fatal("SetupDiDestroyDeviceList failed: %08x",
|
||||
(unsigned int) GetLastError());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
#ifndef GENINPUT_DEV_LIST_H
|
||||
#define GENINPUT_DEV_LIST_H
|
||||
|
||||
#include <windows.h>
|
||||
#include <setupapi.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <wchar.h>
|
||||
|
||||
struct dev_list {
|
||||
GUID *class_guid;
|
||||
HDEVINFO infolist;
|
||||
SP_DEVINFO_DATA dev;
|
||||
SP_DEVICE_INTERFACE_DATA iface;
|
||||
SP_DEVICE_INTERFACE_DETAIL_DATA *detail;
|
||||
wchar_t *name;
|
||||
unsigned int dev_no;
|
||||
unsigned int iface_no;
|
||||
};
|
||||
|
||||
void dev_list_init(struct dev_list *devs, const GUID *class_guid);
|
||||
bool dev_list_next(struct dev_list *devs);
|
||||
const char *dev_list_get_dev_node(struct dev_list *devs);
|
||||
const wchar_t *dev_list_get_dev_name(struct dev_list *devs);
|
||||
void dev_list_fini(struct dev_list *devs);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,50 @@
|
||||
LIBRARY geninput
|
||||
|
||||
EXPORTS
|
||||
action_iter_get_mapping
|
||||
action_iter_get_action
|
||||
action_iter_get_page
|
||||
action_iter_is_valid
|
||||
action_iter_next
|
||||
action_iter_free
|
||||
hid_mgr_get_first_stub
|
||||
hid_mgr_get_named_stub
|
||||
hid_mgr_get_next_stub
|
||||
hid_mgr_lock
|
||||
hid_mgr_unlock
|
||||
hid_stub_get_name
|
||||
hid_stub_get_device_usage
|
||||
hid_stub_get_controls
|
||||
hid_stub_get_dev_node
|
||||
hid_stub_get_lights
|
||||
hid_stub_get_value
|
||||
hid_stub_is_attached
|
||||
hid_stub_set_light
|
||||
input_fini
|
||||
input_init
|
||||
input_set_loggers
|
||||
light_iter_get_mapping
|
||||
light_iter_get_game_light
|
||||
light_iter_is_valid
|
||||
light_iter_next
|
||||
light_iter_free
|
||||
mapper_config_load
|
||||
mapper_config_save
|
||||
mapper_clear_action_map
|
||||
mapper_clear_light_map
|
||||
mapper_get_action_map
|
||||
mapper_get_analog_map
|
||||
mapper_get_analog_sensitivity
|
||||
mapper_get_npages
|
||||
mapper_is_analog_absolute
|
||||
mapper_iterate_lights
|
||||
mapper_iterate_actions
|
||||
mapper_set_action_map
|
||||
mapper_set_analog_map
|
||||
mapper_set_analog_sensitivity
|
||||
mapper_set_light_map
|
||||
mapper_set_nanalogs
|
||||
mapper_set_nlights
|
||||
mapper_read_analog
|
||||
mapper_update
|
||||
mapper_write_light
|
||||
@@ -0,0 +1,4 @@
|
||||
#include <windows.h>
|
||||
#include <initguid.h>
|
||||
|
||||
#include "geninput/hid.h"
|
||||
@@ -0,0 +1,49 @@
|
||||
#include <windows.h>
|
||||
#include <hidsdi.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "geninput/hid-generic-strings.h"
|
||||
|
||||
#include "util/defs.h"
|
||||
#include "util/str.h"
|
||||
|
||||
void hid_generic_strings_init(struct hid_generic_strings *strings, HANDLE fd)
|
||||
{
|
||||
wchar_t wstr[128];
|
||||
|
||||
wstr_cpy(wstr, lengthof(wstr), L"???");
|
||||
HidD_GetProductString(fd, wstr, sizeof(wstr));
|
||||
strings->wstr_prod = wstr_dup(wstr);
|
||||
|
||||
if (!wstr_narrow(wstr, &strings->str_prod)) {
|
||||
strings->str_prod = str_dup("???");
|
||||
}
|
||||
|
||||
wstr_cpy(wstr, lengthof(wstr), L"???");
|
||||
HidD_GetManufacturerString(fd, wstr, sizeof(wstr));
|
||||
strings->wstr_manf = wstr_dup(wstr);
|
||||
|
||||
if (!wstr_narrow(wstr, &strings->str_manf)) {
|
||||
strings->str_manf = str_dup("???");
|
||||
}
|
||||
}
|
||||
|
||||
const char *hid_generic_strings_get_manf(struct hid_generic_strings *strings)
|
||||
{
|
||||
return strings->str_manf;
|
||||
}
|
||||
|
||||
const char *hid_generic_strings_get_prod(struct hid_generic_strings *strings)
|
||||
{
|
||||
return strings->str_prod;
|
||||
}
|
||||
|
||||
void hid_generic_strings_fini(struct hid_generic_strings *strings)
|
||||
{
|
||||
free(strings->str_manf);
|
||||
free(strings->wstr_manf);
|
||||
free(strings->str_prod);
|
||||
free(strings->wstr_prod);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
#ifndef GENINPUT_HID_GENERIC_STRINGS_H
|
||||
#define GENINPUT_HID_GENERIC_STRINGS_H
|
||||
|
||||
struct hid_generic_strings {
|
||||
wchar_t *wstr_prod;
|
||||
wchar_t *wstr_manf;
|
||||
char *str_prod;
|
||||
char *str_manf;
|
||||
};
|
||||
|
||||
void hid_generic_strings_init(struct hid_generic_strings *strings, HANDLE fd);
|
||||
const char *hid_generic_strings_get_manf(struct hid_generic_strings *strings);
|
||||
const char *hid_generic_strings_get_prod(struct hid_generic_strings *strings);
|
||||
void hid_generic_strings_fini(struct hid_generic_strings *strings);
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,408 @@
|
||||
#define LOG_MODULE "hid-generic"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <wchar.h>
|
||||
|
||||
#include <windows.h>
|
||||
#include <hidsdi.h>
|
||||
|
||||
#include "geninput/hid.h"
|
||||
#include "geninput/hid-generic.h"
|
||||
#include "geninput/hid-generic-strings.h"
|
||||
#include "geninput/hid-meta-in.h"
|
||||
#include "geninput/hid-meta-out.h"
|
||||
|
||||
#include "util/defs.h"
|
||||
#include "util/log.h"
|
||||
#include "util/mem.h"
|
||||
#include "util/str.h"
|
||||
|
||||
struct hid_generic {
|
||||
struct hid_fd super;
|
||||
|
||||
char *dev_node;
|
||||
|
||||
HANDLE fd;
|
||||
PHIDP_PREPARSED_DATA ppd;
|
||||
|
||||
OVERLAPPED in_ovl;
|
||||
OVERLAPPED out_ovl;
|
||||
|
||||
struct hid_generic_strings strings;
|
||||
struct hid_meta_in meta_in;
|
||||
struct hid_meta_out meta_out;
|
||||
|
||||
uint8_t *in_buf;
|
||||
size_t in_nbytes;
|
||||
|
||||
uint8_t *out_buf;
|
||||
size_t out_nbytes;
|
||||
bool out_started;
|
||||
bool out_faulty;
|
||||
};
|
||||
|
||||
static bool hid_generic_get_device_usage(const struct hid *hid,
|
||||
uint32_t *usage);
|
||||
static bool hid_generic_get_name(const struct hid *hid, wchar_t *chars,
|
||||
size_t *nchars);
|
||||
static bool hid_generic_get_controls(const struct hid *hid,
|
||||
struct hid_control *controls, size_t *ncontrols);
|
||||
static bool hid_generic_get_lights(const struct hid *hid,
|
||||
struct hid_light *lights, size_t *nlights);
|
||||
static bool hid_generic_get_value(struct hid *hid, size_t control_no,
|
||||
int32_t *out_value);
|
||||
static bool hid_generic_set_light(struct hid *hid, size_t light_no,
|
||||
uint32_t intensity);
|
||||
static bool hid_generic_handle_event(struct hid_fd *super, OVERLAPPED *ovl,
|
||||
size_t nbytes);
|
||||
static void hid_generic_close(struct hid *hid);
|
||||
|
||||
static struct hid_fd_vtbl hid_generic_vtbl = {{
|
||||
/* .close = */ hid_generic_close,
|
||||
/* .get_device_usage = */ hid_generic_get_device_usage,
|
||||
/* .get_name = */ hid_generic_get_name,
|
||||
/* .get_controls = */ hid_generic_get_controls,
|
||||
/* .get_lights = */ hid_generic_get_lights,
|
||||
/* .get_value = */ hid_generic_get_value,
|
||||
/* .set_light = */ hid_generic_set_light },
|
||||
/* .handle_event = */ hid_generic_handle_event
|
||||
};
|
||||
|
||||
bool hid_generic_open(struct hid_fd **out, const char *dev_node, HANDLE iocp,
|
||||
uintptr_t iocp_ctx)
|
||||
{
|
||||
struct hid_generic *hg;
|
||||
uint32_t tlc_usage;
|
||||
|
||||
hg = xmalloc(sizeof(*hg));
|
||||
|
||||
hg->fd = CreateFile(dev_node, GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
|
||||
OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
|
||||
|
||||
if (hg->fd == INVALID_HANDLE_VALUE) {
|
||||
/* Probably a keyboard that's locked by the OS */
|
||||
goto open_fail;
|
||||
}
|
||||
|
||||
if (!HidD_GetPreparsedData(hg->fd, &hg->ppd)) {
|
||||
log_warning("HidD_GetPreparsedData failed on dev node %s", dev_node);
|
||||
|
||||
goto ppd_fail;
|
||||
}
|
||||
|
||||
if (!hid_meta_in_init(&hg->meta_in, hg->ppd)) {
|
||||
goto meta_in_fail;
|
||||
}
|
||||
|
||||
tlc_usage = hid_meta_in_get_tlc_usage(&hg->meta_in);
|
||||
|
||||
if ((tlc_usage >> 24) == 0xFF) {
|
||||
log_misc("Skipping vendor-specific HID on dev node %s", dev_node);
|
||||
|
||||
goto vsp_fail;
|
||||
}
|
||||
|
||||
if (!hid_meta_out_init(&hg->meta_out, hg->ppd, hg->fd)) {
|
||||
goto meta_out_fail;
|
||||
}
|
||||
|
||||
hid_generic_strings_init(&hg->strings, hg->fd);
|
||||
|
||||
/* Dump diagnostics */
|
||||
|
||||
log_misc("Initializing generic HID on dev node %s:", dev_node);
|
||||
log_misc("... Product: %s", hg->strings.str_prod);
|
||||
log_misc("... Manufacturer: %s", hg->strings.str_manf);
|
||||
log_misc("... Device usage: %08x",
|
||||
hid_meta_in_get_tlc_usage(&hg->meta_in));
|
||||
log_misc("... Number of controls: %u",
|
||||
(unsigned int) hid_meta_in_get_ncontrols(&hg->meta_in));
|
||||
log_misc("... Number of \"lights\": %u",
|
||||
(unsigned int) hid_meta_out_get_nlights(&hg->meta_out));
|
||||
|
||||
/* Start up input double-buffer chain by reading a report.
|
||||
We bind to the IO completion port here. */
|
||||
|
||||
CreateIoCompletionPort(hg->fd, iocp, iocp_ctx, 0);
|
||||
|
||||
hg->in_nbytes = hid_meta_in_get_buffer_size(&hg->meta_in);
|
||||
hg->in_buf = xmalloc(hg->in_nbytes);
|
||||
|
||||
memset(&hg->in_ovl, 0, sizeof(hg->in_ovl));
|
||||
|
||||
if (!ReadFile(hg->fd, hg->in_buf, hg->in_nbytes, NULL,
|
||||
&hg->in_ovl)) {
|
||||
if (GetLastError() != ERROR_IO_PENDING) {
|
||||
log_warning("Initial ReadFile failed: %08x: %s",
|
||||
(unsigned int) GetLastError(), dev_node);
|
||||
|
||||
goto read_fail;
|
||||
}
|
||||
}
|
||||
|
||||
/* Allocate OUT report buffers, but don't start sending the reports until
|
||||
the user tries to set a light. Some HIDs (e.g. Razer devices) freak out
|
||||
if you send them HID reports that they aren't expecting.
|
||||
|
||||
These OUT reports are probably used for configuration settings or,
|
||||
worse yet, firmware updates. Configuration changes are what feature
|
||||
reports are for. As for firmware updates over HID... no. Just, no. */
|
||||
|
||||
hg->out_nbytes = hid_meta_out_get_buffer_size(&hg->meta_out);
|
||||
hg->out_buf = xmalloc(hg->out_nbytes);
|
||||
hg->out_started = false;
|
||||
|
||||
memset(&hg->out_ovl, 0, sizeof(hg->out_ovl));
|
||||
|
||||
/* (finish up and return success) */
|
||||
|
||||
hg->dev_node = str_dup(dev_node);
|
||||
hg->super.vptr = &hid_generic_vtbl;
|
||||
|
||||
*out = &hg->super;
|
||||
|
||||
return true;
|
||||
|
||||
read_fail:
|
||||
free(hg->in_buf);
|
||||
|
||||
hid_generic_strings_fini(&hg->strings);
|
||||
hid_meta_out_fini(&hg->meta_out);
|
||||
|
||||
meta_out_fail:
|
||||
vsp_fail:
|
||||
hid_meta_in_fini(&hg->meta_in);
|
||||
|
||||
meta_in_fail:
|
||||
HidD_FreePreparsedData(hg->ppd);
|
||||
|
||||
ppd_fail:
|
||||
CloseHandle(hg->fd);
|
||||
|
||||
open_fail:
|
||||
free(hg);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool hid_generic_get_device_usage(const struct hid *hid,
|
||||
uint32_t *usage)
|
||||
{
|
||||
const struct hid_generic *hg = containerof(hid, struct hid_generic, super);
|
||||
|
||||
*usage = hid_meta_in_get_tlc_usage(&hg->meta_in);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool hid_generic_get_name(const struct hid *hid, wchar_t *chars,
|
||||
size_t *nchars)
|
||||
{
|
||||
const struct hid_generic *hg = containerof(hid, struct hid_generic, super);
|
||||
size_t len;
|
||||
|
||||
log_assert(nchars != NULL);
|
||||
|
||||
len = wcslen(hg->strings.wstr_prod) + 1;
|
||||
|
||||
if (chars == NULL) {
|
||||
*nchars = len;
|
||||
|
||||
return true;
|
||||
} else if (*nchars >= len) {
|
||||
memcpy(chars, hg->strings.wstr_prod, len * sizeof(wchar_t));
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool hid_generic_get_controls(const struct hid *hid,
|
||||
struct hid_control *controls, size_t *ncontrols)
|
||||
{
|
||||
const struct hid_generic *hg = containerof(hid, struct hid_generic, super);
|
||||
|
||||
const struct hid_control *meta_controls;
|
||||
size_t meta_ncontrols;
|
||||
|
||||
if (ncontrols == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
meta_controls = hid_meta_in_get_controls(&hg->meta_in);
|
||||
meta_ncontrols = hid_meta_in_get_ncontrols(&hg->meta_in);
|
||||
|
||||
if (controls == NULL) {
|
||||
*ncontrols = meta_ncontrols;
|
||||
|
||||
return true;
|
||||
} else if (*ncontrols >= meta_ncontrols) {
|
||||
*ncontrols = meta_ncontrols;
|
||||
memcpy(controls, meta_controls, sizeof(*controls) * meta_ncontrols);
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool hid_generic_get_lights(const struct hid *hid,
|
||||
struct hid_light *lights, size_t *nlights)
|
||||
{
|
||||
const struct hid_generic *hg = containerof(hid, const struct hid_generic,
|
||||
super);
|
||||
|
||||
const struct hid_light *meta_lights;
|
||||
size_t meta_nlights;
|
||||
|
||||
log_assert(nlights != NULL);
|
||||
|
||||
meta_lights = hid_meta_out_get_lights(&hg->meta_out);
|
||||
meta_nlights = hid_meta_out_get_nlights(&hg->meta_out);
|
||||
|
||||
if (lights == NULL) {
|
||||
*nlights = meta_nlights;
|
||||
|
||||
return true;
|
||||
} else if (*nlights >= meta_nlights) {
|
||||
*nlights = meta_nlights;
|
||||
memcpy(lights, meta_lights, sizeof(*lights) * meta_nlights);
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool hid_generic_get_value(struct hid *hid, size_t control_no,
|
||||
int32_t *out_value)
|
||||
{
|
||||
struct hid_generic *hg = containerof(hid, struct hid_generic, super);
|
||||
|
||||
return hid_meta_in_get_value(&hg->meta_in, control_no, out_value);
|
||||
}
|
||||
|
||||
static bool hid_generic_set_light(struct hid *hid, size_t light_no,
|
||||
uint32_t intensity)
|
||||
{
|
||||
struct hid_generic *hg = containerof(hid, struct hid_generic, super);
|
||||
|
||||
size_t nlights;
|
||||
|
||||
nlights = hid_meta_out_get_nlights(&hg->meta_out);
|
||||
|
||||
if (light_no >= nlights) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Lazily start the lighting double-buffer chain (see hid_generic_init) */
|
||||
|
||||
if (!hg->out_started && !hg->out_faulty) {
|
||||
log_misc("Starting light output to %s", hg->dev_node);
|
||||
hid_meta_out_get_next_report(&hg->meta_out, hg->out_buf);
|
||||
|
||||
if (!WriteFile(hg->fd, hg->out_buf, hg->out_nbytes, NULL,
|
||||
&hg->out_ovl)) {
|
||||
if (GetLastError() != ERROR_IO_PENDING) {
|
||||
log_warning("Initial WriteFile failed: %08x: %s",
|
||||
(unsigned int) GetLastError(), hg->dev_node);
|
||||
hg->out_faulty = true;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
hg->out_started = true;
|
||||
}
|
||||
|
||||
return hid_meta_out_set_light(&hg->meta_out, light_no, intensity);
|
||||
}
|
||||
|
||||
static bool hid_generic_handle_event(struct hid_fd *hid_fd, OVERLAPPED *ovl,
|
||||
size_t nbytes)
|
||||
{
|
||||
struct hid_generic *hg = containerof(hid_fd, struct hid_generic, super);
|
||||
|
||||
if (ovl == &hg->out_ovl) {
|
||||
/* OUT transmit ok, prepare and transmit another */
|
||||
|
||||
hid_meta_out_get_next_report(&hg->meta_out, hg->out_buf);
|
||||
memset(&hg->out_ovl, 0, sizeof(hg->out_ovl));
|
||||
|
||||
if (!WriteFile(hg->fd, hg->out_buf, hg->out_nbytes, NULL,
|
||||
&hg->out_ovl) && GetLastError() != ERROR_IO_PENDING) {
|
||||
log_warning("Write error %08x from \"%s\" device on dev node %s",
|
||||
(unsigned int) GetLastError(), hg->strings.str_prod,
|
||||
hg->dev_node);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} else if (ovl == &hg->in_ovl) {
|
||||
/* Received an IN report, dispatch it */
|
||||
|
||||
/* The dispatcher will swap buffers with us, i.e. it will take
|
||||
ownership of whatever hg->current_in points to, and replace it
|
||||
with a pointer to a fresh buffer we can use for subsequent I/O */
|
||||
|
||||
if (!hid_meta_in_dispatch(&hg->meta_in, &hg->in_buf, nbytes)) {
|
||||
log_warning("Failed to process input report from \"%s\" device"
|
||||
" on dev node %s", hg->strings.str_prod, hg->dev_node);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hg->in_buf == NULL) {
|
||||
/* BEEP BOOP FIRMWARE PROGRAMMED BY SHITLORD DETECTED */
|
||||
/* (ok, a less obnoxious explanation: The initial report
|
||||
solicitation failed, so the report handler did not have a
|
||||
valid buffer until we gave it one. This means that we have
|
||||
to allocate one for it to work with manually). */
|
||||
|
||||
hg->in_buf = xmalloc(hid_meta_in_get_buffer_size(&hg->meta_in));
|
||||
}
|
||||
|
||||
/* Event dispatched successfully. Kick off the next async read. */
|
||||
|
||||
memset(&hg->in_ovl, 0, sizeof(hg->in_ovl));
|
||||
|
||||
if (!ReadFile(hg->fd, hg->in_buf, hg->in_nbytes, NULL,
|
||||
&hg->in_ovl) && GetLastError() != ERROR_IO_PENDING) {
|
||||
log_warning("Read error %08x from \"%s\" device on dev node %s",
|
||||
(unsigned int) GetLastError(), hg->strings.str_prod,
|
||||
hg->dev_node);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void hid_generic_close(struct hid *hid)
|
||||
{
|
||||
struct hid_generic *hg = containerof(hid, struct hid_generic, super);
|
||||
|
||||
/* Must close the FD first. I/O might be in flight and NT could potentially
|
||||
complete an I/O while we're freeing our buffers and completely trash our
|
||||
process heap by doing so. */
|
||||
|
||||
CloseHandle(hg->fd);
|
||||
|
||||
free(hg->dev_node);
|
||||
free(hg->out_buf);
|
||||
free(hg->in_buf);
|
||||
hid_meta_out_fini(&hg->meta_out);
|
||||
hid_meta_in_fini(&hg->meta_in);
|
||||
hid_generic_strings_fini(&hg->strings);
|
||||
|
||||
HidD_FreePreparsedData(hg->ppd);
|
||||
|
||||
free(hid);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
#ifndef GENINPUT_HID_GENERIC_H
|
||||
#define GENINPUT_HID_GENERIC_H
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "geninput/hid.h"
|
||||
|
||||
bool hid_generic_open(struct hid_fd **hid, const char *dev_node,
|
||||
HANDLE iocp, uintptr_t iocp_ctx);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,410 @@
|
||||
#define LOG_MODULE "hid-generic"
|
||||
|
||||
#include <windows.h>
|
||||
#include <hidsdi.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "geninput/hid-meta-in.h"
|
||||
#include "geninput/hid-report-in.h"
|
||||
|
||||
#include "util/log.h"
|
||||
#include "util/mem.h"
|
||||
|
||||
static bool hid_meta_in_init_caps(struct hid_meta_in *meta,
|
||||
PHIDP_PREPARSED_DATA ppd);
|
||||
static void hid_meta_in_init_arrays(struct hid_meta_in *meta);
|
||||
static void hid_meta_in_init_buttons(struct hid_meta_in *meta);
|
||||
static void hid_meta_in_init_values(struct hid_meta_in *meta);
|
||||
static struct hid_report_in *hid_meta_in_lookup_report(
|
||||
struct hid_meta_in *meta, uint8_t report_id);
|
||||
static void hid_meta_in_fini_arrays(struct hid_meta_in *meta);
|
||||
static void hid_meta_in_fini_caps(struct hid_meta_in *meta);
|
||||
|
||||
bool hid_meta_in_init(struct hid_meta_in *meta, PHIDP_PREPARSED_DATA ppd)
|
||||
{
|
||||
if (!hid_meta_in_init_caps(meta, ppd)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
hid_meta_in_init_arrays(meta);
|
||||
hid_meta_in_init_buttons(meta);
|
||||
hid_meta_in_init_values(meta);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool hid_meta_in_init_caps(struct hid_meta_in *meta,
|
||||
PHIDP_PREPARSED_DATA ppd)
|
||||
{
|
||||
uint16_t len;
|
||||
NTSTATUS status;
|
||||
|
||||
meta->ppd = ppd;
|
||||
|
||||
status = HidP_GetCaps(meta->ppd, &meta->caps_tlc);
|
||||
|
||||
if (status != HIDP_STATUS_SUCCESS) {
|
||||
log_warning("Error getting top-level collection caps");
|
||||
|
||||
goto caps_tlc_fail;
|
||||
}
|
||||
|
||||
len = meta->caps_tlc.NumberInputButtonCaps;
|
||||
meta->caps_btn = xmalloc(sizeof(*meta->caps_btn) * len);
|
||||
|
||||
if (len > 0) {
|
||||
status = HidP_GetButtonCaps(HidP_Input, meta->caps_btn, &len,meta->ppd);
|
||||
|
||||
if (status != HIDP_STATUS_SUCCESS) {
|
||||
log_warning("Error getting button caps");
|
||||
|
||||
goto caps_btn_fail;
|
||||
}
|
||||
}
|
||||
|
||||
len = meta->caps_tlc.NumberInputValueCaps;
|
||||
meta->caps_val = xmalloc(sizeof(*meta->caps_val) * len);
|
||||
|
||||
if (len > 0) {
|
||||
status = HidP_GetValueCaps(HidP_Input, meta->caps_val, &len, meta->ppd);
|
||||
|
||||
if (status != HIDP_STATUS_SUCCESS) {
|
||||
log_warning("Error getting value caps");
|
||||
|
||||
goto caps_val_fail;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
caps_val_fail:
|
||||
free(meta->caps_val);
|
||||
|
||||
caps_btn_fail:
|
||||
free(meta->caps_btn);
|
||||
|
||||
caps_tlc_fail:
|
||||
return false;
|
||||
}
|
||||
|
||||
static void hid_meta_in_init_arrays(struct hid_meta_in *meta)
|
||||
{
|
||||
const HIDP_BUTTON_CAPS *bc;
|
||||
const HIDP_VALUE_CAPS *vc;
|
||||
uint16_t *report_btns;
|
||||
bool *report_presence;
|
||||
uint16_t count;
|
||||
unsigned int i;
|
||||
unsigned int j;
|
||||
void *report_buf;
|
||||
|
||||
/* Init counters */
|
||||
|
||||
meta->ncontrols = 0;
|
||||
meta->nbuttons = 0;
|
||||
meta->nreports = 0;
|
||||
|
||||
meta->reports_muxed = false;
|
||||
|
||||
/* We have extremely limited stack headroom when called from IIDX for some
|
||||
reason, gotta do this on the heap... */
|
||||
|
||||
report_btns = xcalloc(sizeof(*report_btns) * 0x100);
|
||||
report_presence = xcalloc(sizeof(*report_presence) * 0x100);
|
||||
|
||||
/* Count up buttons, globally and per-report */
|
||||
|
||||
for (i = 0 ; i < meta->caps_tlc.NumberInputButtonCaps ; i++) {
|
||||
bc = &meta->caps_btn[i];
|
||||
|
||||
if (bc->IsRange) {
|
||||
count = bc->Range.UsageMax - bc->Range.UsageMin + 1;
|
||||
} else {
|
||||
count = 1;
|
||||
}
|
||||
|
||||
if (bc->ReportID != 0) {
|
||||
meta->reports_muxed = true;
|
||||
}
|
||||
|
||||
report_btns[bc->ReportID] += count;
|
||||
|
||||
meta->ncontrols += count;
|
||||
meta->nbuttons += count;
|
||||
}
|
||||
|
||||
/* Count up values, note all reports that have values */
|
||||
|
||||
for (i = 0 ; i < meta->caps_tlc.NumberInputValueCaps ; i++) {
|
||||
vc = &meta->caps_val[i];
|
||||
|
||||
if (vc->IsRange) {
|
||||
count = vc->Range.UsageMax - vc->Range.UsageMin + 1;
|
||||
} else {
|
||||
count = 1;
|
||||
}
|
||||
|
||||
if (vc->ReportID != 0) {
|
||||
meta->reports_muxed = true;
|
||||
}
|
||||
|
||||
report_presence[vc->ReportID] = true;
|
||||
|
||||
meta->ncontrols += count;
|
||||
}
|
||||
|
||||
/* Count up total number of reports and initialize them */
|
||||
|
||||
for (i = 0 ; i < 0x100 ; i++) {
|
||||
if (report_btns[i] || report_presence[i]) {
|
||||
meta->nreports++;
|
||||
}
|
||||
}
|
||||
|
||||
j = 0;
|
||||
meta->reports = xmalloc(sizeof(*meta->reports) * meta->nreports);
|
||||
|
||||
for (i = 0 ; i < 0x100 ; i++) {
|
||||
if (report_btns[i] || report_presence[i]) {
|
||||
report_buf = xmalloc(meta->caps_tlc.InputReportByteLength);
|
||||
|
||||
hid_report_in_init(&meta->reports[j++], (uint8_t) i,
|
||||
report_btns[i], report_buf,
|
||||
meta->caps_tlc.InputReportByteLength, meta->ppd);
|
||||
}
|
||||
}
|
||||
|
||||
/* Init control arrays */
|
||||
|
||||
meta->controls = xmalloc(sizeof(*meta->controls) * meta->ncontrols);
|
||||
meta->priv_controls = xmalloc(sizeof(*meta->priv_controls)
|
||||
* meta->ncontrols);
|
||||
|
||||
/* Clean up our scratch space */
|
||||
|
||||
free(report_presence);
|
||||
free(report_btns);
|
||||
}
|
||||
|
||||
static void hid_meta_in_init_buttons(struct hid_meta_in *meta)
|
||||
{
|
||||
const HIDP_BUTTON_CAPS *bc;
|
||||
struct hid_control_in *priv;
|
||||
struct hid_control *ctl;
|
||||
unsigned int pos;
|
||||
unsigned int i;
|
||||
int j;
|
||||
|
||||
pos = 0;
|
||||
|
||||
for (i = 0 ; i < meta->caps_tlc.NumberInputButtonCaps ; i++) {
|
||||
bc = &meta->caps_btn[i];
|
||||
|
||||
if (bc->IsRange) {
|
||||
for (j = 0 ; j <= bc->Range.UsageMax - bc->Range.UsageMin ; j++) {
|
||||
priv = &meta->priv_controls[pos];
|
||||
|
||||
priv->report = hid_meta_in_lookup_report(meta, bc->ReportID);
|
||||
priv->collection_id = bc->LinkCollection;
|
||||
|
||||
ctl = &meta->controls[pos];
|
||||
|
||||
ctl->usage = (bc->UsagePage << 16) | (bc->Range.UsageMin + j);
|
||||
ctl->value_min = 0;
|
||||
ctl->value_max = 1;
|
||||
ctl->flags = 0;
|
||||
|
||||
pos++;
|
||||
}
|
||||
} else {
|
||||
priv = &meta->priv_controls[pos];
|
||||
|
||||
priv->report = hid_meta_in_lookup_report(meta, bc->ReportID);
|
||||
priv->collection_id = bc->LinkCollection;
|
||||
|
||||
ctl = &meta->controls[pos];
|
||||
|
||||
ctl->usage = (bc->UsagePage << 16) | bc->NotRange.Usage;
|
||||
ctl->value_min = 0;
|
||||
ctl->value_max = 1;
|
||||
ctl->flags = 0;
|
||||
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void hid_meta_in_init_values(struct hid_meta_in *meta)
|
||||
{
|
||||
const HIDP_VALUE_CAPS *vc;
|
||||
struct hid_control_in *priv;
|
||||
struct hid_control *ctl;
|
||||
unsigned int pos;
|
||||
unsigned int i;
|
||||
int j;
|
||||
|
||||
pos = meta->nbuttons;
|
||||
|
||||
for (i = 0 ; i < meta->caps_tlc.NumberInputValueCaps ; i++) {
|
||||
vc = &meta->caps_val[i];
|
||||
|
||||
if (vc->IsRange) {
|
||||
for (j = 0 ; j <= vc->Range.UsageMax - vc->Range.UsageMin ; j++) {
|
||||
priv = &meta->priv_controls[pos];
|
||||
|
||||
priv->report = hid_meta_in_lookup_report(meta, vc->ReportID);
|
||||
priv->collection_id = vc->LinkCollection;
|
||||
|
||||
ctl = &meta->controls[pos];
|
||||
|
||||
ctl->usage = (vc->UsagePage << 16) | (vc->Range.UsageMin + i);
|
||||
ctl->value_min = vc->LogicalMin;
|
||||
ctl->value_max = vc->LogicalMax;
|
||||
ctl->flags = 0;
|
||||
|
||||
if (vc->HasNull) {
|
||||
ctl->flags |= HID_FLAG_NULLABLE;
|
||||
}
|
||||
|
||||
if (!vc->IsAbsolute) {
|
||||
ctl->flags |= HID_FLAG_RELATIVE;
|
||||
}
|
||||
|
||||
pos++;
|
||||
}
|
||||
} else {
|
||||
priv = &meta->priv_controls[pos];
|
||||
|
||||
priv->report = hid_meta_in_lookup_report(meta, vc->ReportID);
|
||||
priv->collection_id = vc->LinkCollection;
|
||||
|
||||
ctl = &meta->controls[pos];
|
||||
|
||||
ctl->usage = (vc->UsagePage << 16) | vc->NotRange.Usage;
|
||||
ctl->value_min = vc->LogicalMin;
|
||||
ctl->value_max = vc->LogicalMax;
|
||||
ctl->flags = 0;
|
||||
|
||||
if (vc->HasNull) {
|
||||
ctl->flags |= HID_FLAG_NULLABLE;
|
||||
}
|
||||
|
||||
if (!vc->IsAbsolute) {
|
||||
ctl->flags |= HID_FLAG_RELATIVE;
|
||||
}
|
||||
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static struct hid_report_in *hid_meta_in_lookup_report(
|
||||
struct hid_meta_in *meta, uint8_t report_id)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0 ; i < meta->nreports ; i++) {
|
||||
if (meta->reports[i].id == report_id) {
|
||||
return &meta->reports[i];
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool hid_meta_in_dispatch(struct hid_meta_in *meta, uint8_t **bytes,
|
||||
size_t nbytes)
|
||||
{
|
||||
struct hid_report_in *r;
|
||||
uint8_t report_id;
|
||||
|
||||
if (meta->reports_muxed) {
|
||||
report_id = **bytes;
|
||||
} else {
|
||||
report_id = 0;
|
||||
}
|
||||
|
||||
r = hid_meta_in_lookup_report(meta, report_id);
|
||||
|
||||
if (r == NULL) {
|
||||
log_warning("Got spurious report with ID %02x", report_id);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return hid_report_in_exchange(r, bytes, nbytes);
|
||||
}
|
||||
|
||||
size_t hid_meta_in_get_buffer_size(const struct hid_meta_in *meta)
|
||||
{
|
||||
return meta->caps_tlc.InputReportByteLength;
|
||||
}
|
||||
|
||||
const struct hid_control *hid_meta_in_get_controls(
|
||||
const struct hid_meta_in *meta)
|
||||
{
|
||||
return meta->controls;
|
||||
}
|
||||
|
||||
size_t hid_meta_in_get_ncontrols(const struct hid_meta_in *meta)
|
||||
{
|
||||
return meta->ncontrols;
|
||||
}
|
||||
|
||||
uint32_t hid_meta_in_get_tlc_usage(const struct hid_meta_in *meta)
|
||||
{
|
||||
return (meta->caps_tlc.UsagePage << 16) | meta->caps_tlc.Usage;
|
||||
}
|
||||
|
||||
bool hid_meta_in_get_value(struct hid_meta_in *meta,
|
||||
size_t control_no, int32_t *value)
|
||||
{
|
||||
struct hid_control_in *priv;
|
||||
uint32_t usage;
|
||||
|
||||
if (control_no >= meta->ncontrols) {
|
||||
return false;
|
||||
}
|
||||
|
||||
priv = &meta->priv_controls[control_no];
|
||||
usage = meta->controls[control_no].usage;
|
||||
|
||||
if (control_no < meta->nbuttons) {
|
||||
return hid_report_in_get_bit(priv->report, meta->ppd,
|
||||
priv->collection_id, usage, value);
|
||||
} else {
|
||||
return hid_report_in_get_value(priv->report, meta->ppd,
|
||||
priv->collection_id, usage, value);
|
||||
}
|
||||
}
|
||||
|
||||
void hid_meta_in_fini(struct hid_meta_in *meta)
|
||||
{
|
||||
hid_meta_in_fini_arrays(meta);
|
||||
hid_meta_in_fini_caps(meta);
|
||||
}
|
||||
|
||||
static void hid_meta_in_fini_arrays(struct hid_meta_in *meta)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
free(meta->priv_controls);
|
||||
free(meta->controls);
|
||||
|
||||
for (i = 0 ; i < meta->nreports ; i++) {
|
||||
hid_report_in_fini(&meta->reports[i]);
|
||||
}
|
||||
|
||||
free(meta->reports);
|
||||
}
|
||||
|
||||
static void hid_meta_in_fini_caps(struct hid_meta_in *meta)
|
||||
{
|
||||
free(meta->caps_val);
|
||||
free(meta->caps_btn);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
#ifndef GENINPUT_HID_META_IN_H
|
||||
#define GENINPUT_HID_META_IN_H
|
||||
|
||||
#include <windows.h>
|
||||
#include <hidsdi.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "geninput/hid.h"
|
||||
#include "geninput/hid-report-in.h"
|
||||
|
||||
struct hid_control_in {
|
||||
struct hid_report_in *report;
|
||||
uint16_t collection_id;
|
||||
};
|
||||
|
||||
struct hid_meta_in {
|
||||
PHIDP_PREPARSED_DATA ppd;
|
||||
HIDP_CAPS caps_tlc;
|
||||
HIDP_BUTTON_CAPS *caps_btn;
|
||||
HIDP_VALUE_CAPS *caps_val;
|
||||
|
||||
struct hid_control *controls;
|
||||
size_t ncontrols;
|
||||
size_t nbuttons;
|
||||
|
||||
bool reports_muxed;
|
||||
struct hid_control_in *priv_controls;
|
||||
struct hid_report_in *reports;
|
||||
uint8_t nreports;
|
||||
};
|
||||
|
||||
bool hid_meta_in_init(struct hid_meta_in *meta, PHIDP_PREPARSED_DATA ppd);
|
||||
bool hid_meta_in_dispatch(struct hid_meta_in *meta, uint8_t **bytes,
|
||||
size_t nbytes);
|
||||
size_t hid_meta_in_get_buffer_size(const struct hid_meta_in *meta);
|
||||
const struct hid_control *hid_meta_in_get_controls(
|
||||
const struct hid_meta_in *meta);
|
||||
size_t hid_meta_in_get_ncontrols(const struct hid_meta_in *meta);
|
||||
uint32_t hid_meta_in_get_tlc_usage(const struct hid_meta_in *meta);
|
||||
bool hid_meta_in_get_value(struct hid_meta_in *meta,
|
||||
size_t control_no, int32_t *value);
|
||||
void hid_meta_in_fini(struct hid_meta_in *meta);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,402 @@
|
||||
#define LOG_MODULE "hid-generic"
|
||||
|
||||
#include <windows.h>
|
||||
#include <hidsdi.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "geninput/hid-meta-out.h"
|
||||
#include "geninput/hid-report-out.h"
|
||||
|
||||
#include "util/log.h"
|
||||
#include "util/mem.h"
|
||||
|
||||
static bool hid_meta_out_init_caps(struct hid_meta_out *meta,
|
||||
PHIDP_PREPARSED_DATA ppd);
|
||||
static bool hid_meta_out_init_arrays(struct hid_meta_out *meta);
|
||||
static void hid_meta_out_init_lights(struct hid_meta_out *meta, HANDLE fd);
|
||||
static struct hid_report_out *hid_meta_out_lookup_report(
|
||||
struct hid_meta_out *meta, uint8_t report_id);
|
||||
static void hid_meta_out_fini_arrays(struct hid_meta_out *meta);
|
||||
static void hid_meta_out_fini_caps(struct hid_meta_out *meta);
|
||||
|
||||
bool hid_meta_out_init(struct hid_meta_out *meta, PHIDP_PREPARSED_DATA ppd, HANDLE fd)
|
||||
{
|
||||
if (!hid_meta_out_init_caps(meta, ppd)) {
|
||||
goto caps_fail;
|
||||
}
|
||||
|
||||
if (!hid_meta_out_init_arrays(meta)) {
|
||||
goto arrays_fail;
|
||||
}
|
||||
|
||||
hid_meta_out_init_lights(meta, fd);
|
||||
|
||||
meta->next_report = 0;
|
||||
|
||||
return true;
|
||||
|
||||
arrays_fail:
|
||||
hid_meta_out_fini_caps(meta);
|
||||
|
||||
caps_fail:
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool hid_meta_out_init_caps(struct hid_meta_out *meta,
|
||||
PHIDP_PREPARSED_DATA ppd)
|
||||
{
|
||||
uint16_t len;
|
||||
NTSTATUS status;
|
||||
|
||||
meta->ppd = ppd;
|
||||
|
||||
status = HidP_GetCaps(meta->ppd, &meta->caps_tlc);
|
||||
|
||||
if (status != HIDP_STATUS_SUCCESS) {
|
||||
log_warning("Error getting top-level collection caps");
|
||||
|
||||
goto caps_tlc_fail;
|
||||
}
|
||||
|
||||
len = meta->caps_tlc.NumberOutputButtonCaps;
|
||||
meta->caps_btn = xmalloc(sizeof(*meta->caps_btn) * len);
|
||||
|
||||
if (len > 0) {
|
||||
status = HidP_GetButtonCaps(HidP_Output, meta->caps_btn, &len,
|
||||
meta->ppd);
|
||||
|
||||
if (status != HIDP_STATUS_SUCCESS) {
|
||||
log_warning("Error getting button caps");
|
||||
|
||||
goto caps_btn_fail;
|
||||
}
|
||||
}
|
||||
|
||||
len = meta->caps_tlc.NumberOutputValueCaps;
|
||||
meta->caps_val = xmalloc(sizeof(*meta->caps_val) * len);
|
||||
|
||||
if (len > 0) {
|
||||
status = HidP_GetValueCaps(HidP_Output, meta->caps_val, &len,
|
||||
meta->ppd);
|
||||
|
||||
if (status != HIDP_STATUS_SUCCESS) {
|
||||
log_warning("Error getting value caps");
|
||||
|
||||
goto caps_val_fail;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
caps_val_fail:
|
||||
free(meta->caps_val);
|
||||
|
||||
caps_btn_fail:
|
||||
free(meta->caps_btn);
|
||||
|
||||
caps_tlc_fail:
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool hid_meta_out_init_arrays(struct hid_meta_out *meta)
|
||||
{
|
||||
const HIDP_BUTTON_CAPS *bc;
|
||||
const HIDP_VALUE_CAPS *vc;
|
||||
bool *report_presence;
|
||||
unsigned int nreports;
|
||||
unsigned int i;
|
||||
uint8_t *bytes;
|
||||
size_t nbytes;
|
||||
size_t count;
|
||||
|
||||
/* N.B. "buttons" here are just one-bit values. You have to use a
|
||||
completely different API to manipulate these values even though the HID
|
||||
spec and protocol doesn't really make a distinction between them. */
|
||||
|
||||
meta->nlights = 0;
|
||||
meta->nbuttons = 0;
|
||||
|
||||
/* Use heap-based working area */
|
||||
|
||||
report_presence = xcalloc(sizeof(*report_presence) * 0x100);
|
||||
|
||||
/* Count up buttons, globally and per-report */
|
||||
|
||||
for (i = 0 ; i < meta->caps_tlc.NumberOutputButtonCaps ; i++) {
|
||||
bc = &meta->caps_btn[i];
|
||||
|
||||
if (bc->IsRange) {
|
||||
count = bc->Range.UsageMax - bc->Range.UsageMin + 1;
|
||||
} else {
|
||||
count = 1;
|
||||
}
|
||||
|
||||
meta->nlights += count;
|
||||
meta->nbuttons += count;
|
||||
|
||||
report_presence[bc->ReportID] = true;
|
||||
}
|
||||
|
||||
for (i = 0 ; i < meta->caps_tlc.NumberOutputValueCaps ; i++) {
|
||||
vc = &meta->caps_val[i];
|
||||
|
||||
if (vc->IsRange) {
|
||||
meta->nlights += vc->Range.UsageMax - vc->Range.UsageMin + 1;
|
||||
} else {
|
||||
meta->nlights += 1;
|
||||
}
|
||||
|
||||
report_presence[vc->ReportID] = true;
|
||||
}
|
||||
|
||||
/* Count up total number of reports and initialize them */
|
||||
|
||||
nreports = 0; /* Allocated */
|
||||
meta->nreports = 0; /* Initialized so far (see unwind below) */
|
||||
|
||||
for (i = 0 ; i < 0x100 ; i++) {
|
||||
if (report_presence[i]) {
|
||||
nreports++;
|
||||
}
|
||||
}
|
||||
|
||||
meta->reports = xmalloc(sizeof(*meta->reports) * nreports);
|
||||
|
||||
for (i = 0 ; i < 0x100 ; i++) {
|
||||
if (report_presence[i]) {
|
||||
nbytes = meta->caps_tlc.OutputReportByteLength;
|
||||
bytes = xmalloc(meta->caps_tlc.OutputReportByteLength);
|
||||
|
||||
if (!hid_report_out_init(&meta->reports[meta->nreports],
|
||||
meta->ppd, (uint8_t) i, nbytes)) {
|
||||
goto r_init_fail;
|
||||
}
|
||||
|
||||
meta->nreports++;
|
||||
}
|
||||
}
|
||||
|
||||
meta->lights = xcalloc(sizeof(*meta->lights) * meta->nlights);
|
||||
meta->priv_lights = xmalloc(sizeof(*meta->priv_lights) * meta->nlights);
|
||||
|
||||
/* Clean up our scratch space */
|
||||
|
||||
free(report_presence);
|
||||
|
||||
return true;
|
||||
|
||||
r_init_fail:
|
||||
free(bytes);
|
||||
|
||||
for (i = meta->nreports ; i > 0 ; i--) {
|
||||
hid_report_out_fini(&meta->reports[i - 1]);
|
||||
}
|
||||
|
||||
free(report_presence);
|
||||
free(meta->reports);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void hid_meta_out_init_lights(struct hid_meta_out *meta, HANDLE fd)
|
||||
{
|
||||
const HIDP_BUTTON_CAPS *bc;
|
||||
const HIDP_VALUE_CAPS *vc;
|
||||
struct hid_out_light *priv;
|
||||
struct hid_light *light;
|
||||
unsigned int pos;
|
||||
unsigned int i;
|
||||
int j;
|
||||
|
||||
pos = 0;
|
||||
|
||||
for (i = 0 ; i < meta->caps_tlc.NumberOutputButtonCaps ; i++) {
|
||||
bc = &meta->caps_btn[i];
|
||||
|
||||
if (bc->IsRange) {
|
||||
for (j = 0 ; j <= bc->Range.UsageMax - bc->Range.UsageMin ; j++) {
|
||||
priv = &meta->priv_lights[pos];
|
||||
|
||||
priv->report = hid_meta_out_lookup_report(meta, bc->ReportID);
|
||||
priv->collection_id = bc->LinkCollection;
|
||||
priv->size = 1;
|
||||
|
||||
light = &meta->lights[pos];
|
||||
|
||||
light->usage = (bc->UsagePage << 16) | (bc->Range.UsageMin + j);
|
||||
light->value_min = 0;
|
||||
light->value_max = 1;
|
||||
|
||||
// Devices may specify their own strings for lights
|
||||
if(bc->IsStringRange && bc->Range.StringMin != 0) {
|
||||
HidD_GetIndexedString(fd, bc->Range.StringMin + j, light->name, sizeof(light->name));
|
||||
} else if(!bc->IsStringRange && bc->NotRange.StringIndex != 0) {
|
||||
HidD_GetIndexedString(fd, bc->NotRange.StringIndex, light->name, sizeof(light->name));
|
||||
}
|
||||
|
||||
pos++;
|
||||
}
|
||||
} else {
|
||||
priv = &meta->priv_lights[pos];
|
||||
|
||||
priv->report = hid_meta_out_lookup_report(meta, bc->ReportID);
|
||||
priv->collection_id = bc->LinkCollection;
|
||||
priv->size = 1;
|
||||
|
||||
light = &meta->lights[pos];
|
||||
|
||||
light->usage = (bc->UsagePage << 16) | bc->NotRange.Usage;
|
||||
light->value_min = 0;
|
||||
light->value_max = 1;
|
||||
|
||||
if(!bc->IsStringRange && bc->NotRange.StringIndex != 0) {
|
||||
HidD_GetIndexedString(fd, bc->NotRange.StringIndex, light->name, sizeof(light->name));
|
||||
}
|
||||
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0 ; i < meta->caps_tlc.NumberOutputValueCaps ; i++) {
|
||||
vc = &meta->caps_val[i];
|
||||
|
||||
if (vc->IsRange) {
|
||||
for (j = 0 ; j <= vc->Range.UsageMax - vc->Range.UsageMin ; j++) {
|
||||
priv = &meta->priv_lights[pos];
|
||||
|
||||
priv->report = hid_meta_out_lookup_report(meta, vc->ReportID);
|
||||
priv->collection_id = vc->LinkCollection;
|
||||
priv->size = vc->BitSize;
|
||||
|
||||
light = &meta->lights[pos];
|
||||
|
||||
light->usage = (vc->UsagePage << 16) | (vc->Range.UsageMin + j);
|
||||
light->value_min = vc->LogicalMin;
|
||||
light->value_max = vc->LogicalMax;
|
||||
|
||||
// Devices may specify their own strings for lights
|
||||
if(vc->IsStringRange && vc->Range.StringMin != 0) {
|
||||
HidD_GetIndexedString(fd, vc->Range.StringMin + j, light->name, sizeof(light->name));
|
||||
} else if(!vc->IsStringRange && vc->NotRange.StringIndex != 0) {
|
||||
HidD_GetIndexedString(fd, vc->NotRange.StringIndex, light->name, sizeof(light->name));
|
||||
}
|
||||
|
||||
pos++;
|
||||
}
|
||||
} else {
|
||||
priv = &meta->priv_lights[pos];
|
||||
|
||||
priv->report = hid_meta_out_lookup_report(meta, vc->ReportID);
|
||||
priv->collection_id = vc->LinkCollection;
|
||||
priv->size = vc->BitSize;
|
||||
|
||||
light = &meta->lights[pos];
|
||||
|
||||
light->usage = (vc->UsagePage << 16) | vc->NotRange.Usage;
|
||||
light->value_min = vc->LogicalMin;
|
||||
light->value_max = vc->LogicalMax;
|
||||
|
||||
if(!vc->IsStringRange && vc->NotRange.StringIndex != 0) {
|
||||
HidD_GetIndexedString(fd, vc->NotRange.StringIndex, light->name, sizeof(light->name));
|
||||
}
|
||||
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static struct hid_report_out *hid_meta_out_lookup_report(
|
||||
struct hid_meta_out *meta, uint8_t report_id)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0 ; i < meta->nreports ; i++) {
|
||||
if (meta->reports[i].id == report_id) {
|
||||
return &meta->reports[i];
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t hid_meta_out_get_buffer_size(const struct hid_meta_out *meta)
|
||||
{
|
||||
return meta->caps_tlc.OutputReportByteLength;
|
||||
}
|
||||
|
||||
const struct hid_light *hid_meta_out_get_lights(
|
||||
const struct hid_meta_out *meta)
|
||||
{
|
||||
return meta->lights;
|
||||
}
|
||||
|
||||
size_t hid_meta_out_get_nlights(const struct hid_meta_out *meta)
|
||||
{
|
||||
return meta->nlights;
|
||||
}
|
||||
|
||||
bool hid_meta_out_set_light(struct hid_meta_out *meta,
|
||||
size_t light_no, uint32_t intensity)
|
||||
{
|
||||
struct hid_out_light *priv;
|
||||
uint32_t usage;
|
||||
|
||||
if (light_no >= meta->nlights) {
|
||||
return false;
|
||||
}
|
||||
|
||||
priv = &meta->priv_lights[light_no];
|
||||
usage = meta->lights[light_no].usage;
|
||||
|
||||
if (light_no < meta->nbuttons) {
|
||||
return hid_report_out_set_bit(priv->report, meta->ppd,
|
||||
priv->collection_id, usage, intensity != 0);
|
||||
} else {
|
||||
return hid_report_out_set_value(priv->report, meta->ppd,
|
||||
priv->collection_id, usage, intensity);
|
||||
}
|
||||
}
|
||||
|
||||
void hid_meta_out_get_next_report(struct hid_meta_out *meta, uint8_t *bytes)
|
||||
{
|
||||
struct hid_report_out *r;
|
||||
|
||||
log_assert(meta->nreports > 0);
|
||||
|
||||
r = &meta->reports[meta->next_report];
|
||||
meta->next_report = (meta->next_report + 1) % meta->nreports;
|
||||
|
||||
hid_report_out_get_bytes(r, bytes);
|
||||
}
|
||||
|
||||
void hid_meta_out_fini(struct hid_meta_out *meta)
|
||||
{
|
||||
hid_meta_out_fini_arrays(meta);
|
||||
hid_meta_out_fini_caps(meta);
|
||||
}
|
||||
|
||||
static void hid_meta_out_fini_arrays(struct hid_meta_out *meta)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
free(meta->priv_lights);
|
||||
free(meta->lights);
|
||||
|
||||
for (i = 0 ; i < meta->nreports ; i++) {
|
||||
hid_report_out_fini(&meta->reports[i]);
|
||||
}
|
||||
|
||||
free(meta->reports);
|
||||
}
|
||||
|
||||
static void hid_meta_out_fini_caps(struct hid_meta_out *meta)
|
||||
{
|
||||
free(meta->caps_btn);
|
||||
free(meta->caps_val);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
#ifndef GENINPUT_HID_META_OUT_H
|
||||
#define GENINPUT_HID_META_OUT_H
|
||||
|
||||
#include <windows.h>
|
||||
#include <hidsdi.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "geninput/hid.h"
|
||||
#include "geninput/hid-report-out.h"
|
||||
|
||||
struct hid_out_light {
|
||||
struct hid_report_out *report;
|
||||
uint16_t collection_id;
|
||||
uint16_t size;
|
||||
};
|
||||
|
||||
struct hid_meta_out {
|
||||
PHIDP_PREPARSED_DATA ppd;
|
||||
HIDP_CAPS caps_tlc;
|
||||
HIDP_BUTTON_CAPS *caps_btn;
|
||||
HIDP_VALUE_CAPS *caps_val;
|
||||
|
||||
struct hid_light *lights;
|
||||
size_t nlights;
|
||||
size_t nbuttons;
|
||||
|
||||
struct hid_out_light *priv_lights;
|
||||
struct hid_report_out *reports;
|
||||
uint8_t nreports;
|
||||
uint8_t next_report;
|
||||
};
|
||||
|
||||
bool hid_meta_out_init(struct hid_meta_out *meta, PHIDP_PREPARSED_DATA ppd, HANDLE fd);
|
||||
void hid_meta_out_get_next_report(struct hid_meta_out *meta, uint8_t *bytes);
|
||||
size_t hid_meta_out_get_buffer_size(const struct hid_meta_out *meta);
|
||||
const struct hid_light *hid_meta_out_get_lights(
|
||||
const struct hid_meta_out *meta);
|
||||
size_t hid_meta_out_get_nlights(const struct hid_meta_out *meta);
|
||||
bool hid_meta_out_set_light(struct hid_meta_out *meta,
|
||||
size_t light_no, uint32_t intensity);
|
||||
void hid_meta_out_fini(struct hid_meta_out *meta);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,200 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "geninput/hid-mgr.h"
|
||||
|
||||
#include "util/log.h"
|
||||
#include "util/mem.h"
|
||||
#include "util/str.h"
|
||||
|
||||
struct hid_stub {
|
||||
struct hid_stub *next;
|
||||
char *dev_node;
|
||||
struct hid *hid;
|
||||
};
|
||||
|
||||
static CRITICAL_SECTION hid_mgr_cs;
|
||||
static struct hid_stub *hid_stubs;
|
||||
|
||||
static bool hid_stub_check(struct hid_stub *stub, bool ok);
|
||||
|
||||
void hid_mgr_init(void)
|
||||
{
|
||||
InitializeCriticalSection(&hid_mgr_cs);
|
||||
hid_stubs = NULL;
|
||||
}
|
||||
|
||||
struct hid_stub *hid_mgr_get_first_stub(void)
|
||||
{
|
||||
return hid_stubs;
|
||||
}
|
||||
|
||||
struct hid_stub *hid_mgr_get_next_stub(struct hid_stub *stub)
|
||||
{
|
||||
return stub->next;
|
||||
}
|
||||
|
||||
struct hid_stub *hid_mgr_get_named_stub(const char *dev_node)
|
||||
{
|
||||
struct hid_stub *pos;
|
||||
|
||||
for (pos = hid_stubs ; pos != NULL ; pos = pos->next) {
|
||||
if (_stricmp(pos->dev_node, dev_node) == 0) {
|
||||
return pos;
|
||||
}
|
||||
}
|
||||
|
||||
pos = xmalloc(sizeof(*pos));
|
||||
|
||||
pos->next = hid_stubs;
|
||||
pos->dev_node = str_dup(dev_node);
|
||||
pos->hid = NULL;
|
||||
|
||||
hid_stubs = pos;
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
void hid_mgr_lock(void)
|
||||
{
|
||||
EnterCriticalSection(&hid_mgr_cs);
|
||||
}
|
||||
|
||||
void hid_mgr_unlock(void)
|
||||
{
|
||||
LeaveCriticalSection(&hid_mgr_cs);
|
||||
}
|
||||
|
||||
void hid_mgr_fini(void)
|
||||
{
|
||||
struct hid_stub *next;
|
||||
struct hid_stub *pos;
|
||||
|
||||
for (pos = hid_stubs ; pos != NULL ; pos = next) {
|
||||
next = pos->next;
|
||||
|
||||
if (pos->hid != NULL) {
|
||||
hid_close(pos->hid);
|
||||
}
|
||||
|
||||
free(pos->dev_node);
|
||||
free(pos);
|
||||
}
|
||||
|
||||
DeleteCriticalSection(&hid_mgr_cs);
|
||||
}
|
||||
|
||||
static bool hid_stub_check(struct hid_stub *stub, bool ok)
|
||||
{
|
||||
if (!ok) {
|
||||
log_warning("Detaching erroring HID at %s", stub->dev_node);
|
||||
hid_stub_detach(stub);
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
void hid_stub_attach(struct hid_stub *stub, struct hid *hid)
|
||||
{
|
||||
if (stub->hid != NULL) {
|
||||
hid_close(stub->hid);
|
||||
}
|
||||
|
||||
stub->hid = hid;
|
||||
}
|
||||
|
||||
void hid_stub_detach(struct hid_stub *stub)
|
||||
{
|
||||
if (stub->hid != NULL) {
|
||||
hid_close(stub->hid);
|
||||
}
|
||||
|
||||
stub->hid = NULL;
|
||||
}
|
||||
|
||||
bool hid_stub_is_attached(struct hid_stub *stub)
|
||||
{
|
||||
return stub->hid != NULL;
|
||||
}
|
||||
|
||||
bool hid_stub_get_device_usage(struct hid_stub *stub, uint32_t *usage)
|
||||
{
|
||||
if (stub->hid == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return hid_stub_check(stub,
|
||||
hid_get_device_usage(stub->hid, usage));
|
||||
}
|
||||
|
||||
bool hid_stub_get_controls(struct hid_stub *stub, struct hid_control *controls,
|
||||
size_t *ncontrols)
|
||||
{
|
||||
if (stub->hid == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return hid_stub_check(stub,
|
||||
hid_get_controls(stub->hid, controls, ncontrols));
|
||||
}
|
||||
|
||||
bool hid_stub_get_lights(struct hid_stub *stub, struct hid_light *lights,
|
||||
size_t *nlights)
|
||||
{
|
||||
if (stub->hid == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return hid_stub_check(stub,
|
||||
hid_get_lights(stub->hid, lights, nlights));
|
||||
}
|
||||
|
||||
const char *hid_stub_get_dev_node(struct hid_stub *stub)
|
||||
{
|
||||
return stub->dev_node;
|
||||
}
|
||||
|
||||
bool hid_stub_get_name(struct hid_stub *stub, wchar_t *chars, size_t *nchars)
|
||||
{
|
||||
if (stub->hid == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return hid_stub_check(stub,
|
||||
hid_get_name(stub->hid, chars, nchars));
|
||||
}
|
||||
|
||||
bool hid_stub_get_value(struct hid_stub *stub, size_t control_no,
|
||||
int32_t *out_value)
|
||||
{
|
||||
if (stub->hid == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return hid_stub_check(stub,
|
||||
hid_get_value(stub->hid, control_no, out_value));
|
||||
}
|
||||
|
||||
bool hid_stub_set_light(struct hid_stub *stub, size_t light_no,
|
||||
uint32_t intensity)
|
||||
{
|
||||
if (stub->hid == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return hid_stub_check(stub,
|
||||
hid_set_light(stub->hid, light_no, intensity));
|
||||
}
|
||||
|
||||
bool hid_stub_handle_completion(struct hid_stub *stub, OVERLAPPED *ovl,
|
||||
size_t nbytes)
|
||||
{
|
||||
if (stub->hid == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return hid_stub_check(stub,
|
||||
hid_fd_handle_completion((struct hid_fd *) stub->hid, ovl, nbytes));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
#ifndef GENINPUT_HID_MGR_H
|
||||
#define GENINPUT_HID_MGR_H
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <wchar.h>
|
||||
|
||||
#include "geninput/hid.h"
|
||||
|
||||
void hid_mgr_init(void);
|
||||
void hid_mgr_lock(void);
|
||||
void hid_mgr_unlock(void);
|
||||
struct hid_stub *hid_mgr_get_first_stub(void);
|
||||
struct hid_stub *hid_mgr_get_next_stub(struct hid_stub *stub);
|
||||
struct hid_stub *hid_mgr_get_named_stub(const char *dev_node);
|
||||
void hid_mgr_fini(void);
|
||||
|
||||
const char *hid_stub_get_dev_node(struct hid_stub *stub);
|
||||
void hid_stub_attach(struct hid_stub *stub, struct hid *hid);
|
||||
void hid_stub_detach(struct hid_stub *stub);
|
||||
bool hid_stub_get_name(struct hid_stub *stub, wchar_t *name, size_t *nchars);
|
||||
bool hid_stub_is_attached(struct hid_stub *stub);
|
||||
bool hid_stub_get_device_usage(struct hid_stub *stub, uint32_t *usage);
|
||||
bool hid_stub_get_controls(struct hid_stub *stub, struct hid_control *controls,
|
||||
size_t *ncontrols);
|
||||
bool hid_stub_get_lights(struct hid_stub *stub, struct hid_light *lights,
|
||||
size_t *nlights);
|
||||
bool hid_stub_get_value(struct hid_stub *stub, size_t control_no,
|
||||
int32_t *out_value);
|
||||
bool hid_stub_set_light(struct hid_stub *stub, size_t light_no,
|
||||
uint32_t intensity);
|
||||
bool hid_stub_handle_completion(struct hid_stub *stub, OVERLAPPED *ovl,
|
||||
size_t nbytes);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,110 @@
|
||||
#define LOG_MODULE "hid-generic"
|
||||
|
||||
#include <windows.h>
|
||||
#include <hidsdi.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "geninput/hid-report-in.h"
|
||||
|
||||
#include "util/log.h"
|
||||
#include "util/mem.h"
|
||||
|
||||
void hid_report_in_init(struct hid_report_in *r, uint8_t report_id,
|
||||
uint32_t nbuttons, void *bytes, size_t nbytes,
|
||||
PHIDP_PREPARSED_DATA ppd)
|
||||
{
|
||||
r->id = report_id;
|
||||
r->nbuttons = nbuttons;
|
||||
r->buttons = xmalloc(sizeof(*r->buttons) * r->nbuttons);
|
||||
r->bytes = bytes;
|
||||
r->nbytes = nbytes;
|
||||
|
||||
if (HidP_InitializeReportForID(HidP_Input, report_id, ppd, bytes, nbytes)
|
||||
!= HIDP_STATUS_SUCCESS) {
|
||||
log_warning("Error initializing IN report %02X", report_id);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t hid_report_in_get_id(struct hid_report_in *r)
|
||||
{
|
||||
return r->id;
|
||||
}
|
||||
|
||||
bool hid_report_in_exchange(struct hid_report_in *r, uint8_t **bytes,
|
||||
uint32_t nbytes)
|
||||
{
|
||||
uint8_t *last_bytes;
|
||||
|
||||
last_bytes = r->bytes;
|
||||
|
||||
r->bytes = *bytes;
|
||||
r->nbytes = nbytes;
|
||||
|
||||
*bytes = last_bytes;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool hid_report_in_get_bit(struct hid_report_in *r, PHIDP_PREPARSED_DATA ppd,
|
||||
uint16_t collection_id, uint32_t usage, int32_t *value)
|
||||
{
|
||||
unsigned long i;
|
||||
unsigned long nbuttons;
|
||||
NTSTATUS result;
|
||||
|
||||
nbuttons = r->nbuttons;
|
||||
result = HidP_GetUsagesEx(HidP_Input, collection_id,
|
||||
(USAGE_AND_PAGE *) r->buttons, &nbuttons, ppd, (PCHAR) r->bytes,
|
||||
(unsigned long) r->nbytes);
|
||||
|
||||
if (result != HIDP_STATUS_SUCCESS) {
|
||||
log_warning("HidP_GetUsagesEx failed for report %02X: %08x",
|
||||
r->id, (unsigned int) result);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
*value = 0;
|
||||
|
||||
for (i = 0 ; i < nbuttons ; i++) {
|
||||
if (r->buttons[i] == usage) {
|
||||
*value = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool hid_report_in_get_value(struct hid_report_in *r, PHIDP_PREPARSED_DATA ppd,
|
||||
uint16_t collection_id, uint32_t usage, int32_t *value)
|
||||
{
|
||||
NTSTATUS result;
|
||||
|
||||
if (r->bytes == NULL) {
|
||||
log_warning("Report %02X not yet valid", r->id);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
result = HidP_GetUsageValue(HidP_Input, usage >> 16, 0, (uint16_t) usage,
|
||||
(unsigned long *) value, ppd, (PCHAR) r->bytes, r->nbytes);
|
||||
|
||||
if (result != HIDP_STATUS_SUCCESS) {
|
||||
log_warning("HidP_GetUsageValue(%08x) failed for report %02X: %08x",
|
||||
usage, r->id, (unsigned int) result);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void hid_report_in_fini(struct hid_report_in *r)
|
||||
{
|
||||
free(r->buttons);
|
||||
free(r->bytes);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
#ifndef GENINPUT_HID_REPORT_IN_H
|
||||
#define GENINPUT_HID_REPORT_IN_H
|
||||
|
||||
#include <windows.h>
|
||||
#include <hidpi.h>
|
||||
#include <hidsdi.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
struct hid_report_in {
|
||||
uint8_t id;
|
||||
uint32_t nbuttons;
|
||||
uint32_t *buttons;
|
||||
uint8_t *bytes;
|
||||
uint32_t nbytes;
|
||||
};
|
||||
|
||||
void hid_report_in_init(struct hid_report_in *r, uint8_t report_id,
|
||||
uint32_t nbuttons, void *bytes, size_t nbytes,
|
||||
PHIDP_PREPARSED_DATA ppd);
|
||||
uint8_t hid_report_in_get_id(struct hid_report_in *r);
|
||||
bool hid_report_in_exchange(struct hid_report_in *r, uint8_t **bytes,
|
||||
uint32_t nbytes);
|
||||
bool hid_report_in_get_bit(struct hid_report_in *r, PHIDP_PREPARSED_DATA ppd,
|
||||
uint16_t collection_id, uint32_t usage, int32_t *value);
|
||||
bool hid_report_in_get_value(struct hid_report_in *r, PHIDP_PREPARSED_DATA ppd,
|
||||
uint16_t collection_id, uint32_t usage, int32_t *value);
|
||||
void hid_report_in_fini(struct hid_report_in *r);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,112 @@
|
||||
#define LOG_MODULE "hid-generic"
|
||||
|
||||
#include <windows.h>
|
||||
#include <hidsdi.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "geninput/hid-report-out.h"
|
||||
|
||||
#include "util/log.h"
|
||||
#include "util/mem.h"
|
||||
|
||||
bool hid_report_out_init(struct hid_report_out *r, PHIDP_PREPARSED_DATA ppd,
|
||||
uint8_t id, size_t nbytes)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
r->id = id;
|
||||
r->nbytes = nbytes;
|
||||
r->bytes = xmalloc(nbytes);
|
||||
|
||||
status = HidP_InitializeReportForID(HidP_Output, r->id, ppd,
|
||||
(PCHAR) r->bytes, r->nbytes);
|
||||
|
||||
if (status != HIDP_STATUS_SUCCESS) {
|
||||
log_warning("Report %02X: HidP_InitializeReportForID failed: %08x",
|
||||
id, (unsigned int) status);
|
||||
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
fail:
|
||||
free(r->bytes);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void hid_report_out_get_bytes(const struct hid_report_out *r, uint8_t *bytes)
|
||||
{
|
||||
memcpy(bytes, r->bytes, r->nbytes);
|
||||
}
|
||||
|
||||
bool hid_report_out_set_bit(struct hid_report_out *r,
|
||||
PHIDP_PREPARSED_DATA ppd, uint16_t collection_id, uint32_t usage,
|
||||
bool value)
|
||||
{
|
||||
NTSTATUS status;
|
||||
unsigned long count;
|
||||
uint16_t usage_hi;
|
||||
uint16_t usage_lo;
|
||||
|
||||
usage_hi = (uint16_t) (usage >> 16);
|
||||
usage_lo = (uint16_t) (usage >> 0);
|
||||
count = 1;
|
||||
|
||||
if (value) {
|
||||
status = HidP_SetUsages(HidP_Output, usage_hi, collection_id,
|
||||
&usage_lo, &count, ppd, (PCHAR) r->bytes, r->nbytes);
|
||||
|
||||
if (status != HIDP_STATUS_SUCCESS) {
|
||||
log_warning("HidP_SetUsages failed: %08x", (unsigned int) status);
|
||||
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
status = HidP_UnsetUsages(HidP_Output, usage_hi, collection_id,
|
||||
&usage_lo, &count, ppd, (PCHAR) r->bytes, r->nbytes);
|
||||
|
||||
if (status != HIDP_STATUS_SUCCESS
|
||||
&& status != HIDP_STATUS_BUTTON_NOT_PRESSED /* jfc */) {
|
||||
log_warning("HidP_UnsetUsages failed: %08x", (unsigned int) status);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool hid_report_out_set_value(struct hid_report_out *r,
|
||||
PHIDP_PREPARSED_DATA ppd, uint16_t collection_id, uint32_t usage,
|
||||
uint32_t value)
|
||||
{
|
||||
NTSTATUS status;
|
||||
uint16_t usage_hi;
|
||||
uint16_t usage_lo;
|
||||
|
||||
usage_hi = (uint16_t) (usage >> 16);
|
||||
usage_lo = (uint16_t) (usage >> 0);
|
||||
|
||||
status = HidP_SetUsageValue(HidP_Output, usage_hi, collection_id,
|
||||
usage_lo, value, ppd, (PCHAR) r->bytes, r->nbytes);
|
||||
|
||||
if (status != HIDP_STATUS_SUCCESS) {
|
||||
log_warning("HidP_SetUsages failed: %08x", (unsigned int) status);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void hid_report_out_fini(struct hid_report_out *r)
|
||||
{
|
||||
free(r->bytes);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
#ifndef GENINPUT_HID_REPORT_OUT_H
|
||||
#define GENINPUT_HID_REPORT_OUT_H
|
||||
|
||||
#include <windows.h>
|
||||
#include <hidsdi.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
struct hid_report_out {
|
||||
uint8_t id;
|
||||
uint8_t *bytes;
|
||||
size_t nbytes;
|
||||
};
|
||||
|
||||
bool hid_report_out_init(struct hid_report_out *r, PHIDP_PREPARSED_DATA ppd,
|
||||
uint8_t id, size_t nbytes);
|
||||
void hid_report_out_get_bytes(const struct hid_report_out *r, uint8_t *bytes);
|
||||
bool hid_report_out_set_bit(struct hid_report_out *r,
|
||||
PHIDP_PREPARSED_DATA ppd, uint16_t collection_id, uint32_t usage,
|
||||
bool value);
|
||||
bool hid_report_out_set_value(struct hid_report_out *r,
|
||||
PHIDP_PREPARSED_DATA ppd, uint16_t collection_id, uint32_t usage,
|
||||
uint32_t value);
|
||||
void hid_report_out_fini(struct hid_report_out *r);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,77 @@
|
||||
#include <string.h>
|
||||
#include <wchar.h>
|
||||
|
||||
#include "geninput/dev-list.h"
|
||||
#include "geninput/hid.h"
|
||||
|
||||
#include "util/log.h"
|
||||
#include "util/str.h"
|
||||
|
||||
wchar_t *hid_ri_init_name(const GUID *class_guid, const char *dev_node)
|
||||
{
|
||||
struct dev_list devs;
|
||||
wchar_t *name;
|
||||
|
||||
dev_list_init(&devs, class_guid);
|
||||
|
||||
name = NULL;
|
||||
|
||||
while (dev_list_next(&devs)) {
|
||||
if (_stricmp(dev_list_get_dev_node(&devs), dev_node) == 0) {
|
||||
name = wstr_dup(dev_list_get_dev_name(&devs));
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (name == NULL) {
|
||||
/* Shouldn't ever happen, but... */
|
||||
name = wstr_dup(L"???");
|
||||
}
|
||||
|
||||
dev_list_fini(&devs);
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
bool hid_ri_get_name(wchar_t *chars, size_t *nchars, const wchar_t *src_chars)
|
||||
{
|
||||
size_t len;
|
||||
|
||||
log_assert(nchars != NULL);
|
||||
|
||||
len = src_chars != NULL ? wcslen(src_chars) + 1 : 0;
|
||||
|
||||
if (chars == NULL) {
|
||||
*nchars = len;
|
||||
|
||||
return true;
|
||||
} else if (*nchars >= len) {
|
||||
memcpy(chars, src_chars, len * sizeof(wchar_t));
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool hid_ri_get_controls(struct hid_control *controls, size_t *ncontrols,
|
||||
const struct hid_control *src_controls, size_t src_ncontrols)
|
||||
{
|
||||
log_assert(ncontrols != NULL);
|
||||
|
||||
if (controls == NULL) {
|
||||
*ncontrols = src_ncontrols;
|
||||
|
||||
return true;
|
||||
} else if (*ncontrols >= src_ncontrols) {
|
||||
*ncontrols = src_ncontrols;
|
||||
|
||||
memcpy(controls, src_controls, src_ncontrols * sizeof(*src_controls));
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
#ifndef GENINPUT_HID_H
|
||||
#define GENINPUT_HID_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <wchar.h>
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#define HID_FLAG_NULLABLE 0x01
|
||||
#define HID_FLAG_RELATIVE 0x02
|
||||
|
||||
DEFINE_GUID(hid_guid, 0x4D1E55B2L, 0xF16F, 0x11CF, 0x88, 0xCB, 0x00, \
|
||||
0x11, 0x11, 0x00, 0x00, 0x30);
|
||||
|
||||
/* Alright, I'd take the vtable stuff from C++, because this is just gross.
|
||||
But you can keep everything else from that trainwreck of a language. */
|
||||
|
||||
struct hid;
|
||||
|
||||
struct hid_control {
|
||||
uint32_t usage;
|
||||
int32_t value_min;
|
||||
int32_t value_max;
|
||||
uint32_t flags;
|
||||
};
|
||||
|
||||
struct hid_light {
|
||||
uint32_t usage;
|
||||
|
||||
/* I'm going to go ahead and assume nobody will declare a negative light
|
||||
intensity on their lighting device.
|
||||
|
||||
I will probably regret this decision. */
|
||||
|
||||
uint32_t value_min;
|
||||
uint32_t value_max;
|
||||
|
||||
wchar_t name[126];
|
||||
};
|
||||
|
||||
struct hid_vtbl {
|
||||
void (*close)(struct hid *hid);
|
||||
bool (*get_device_usage)(const struct hid *hid, uint32_t *usage);
|
||||
bool (*get_name)(const struct hid *hid, wchar_t *chars, size_t *nchars);
|
||||
bool (*get_controls)(const struct hid *hid, struct hid_control *controls,
|
||||
size_t *ncontrols);
|
||||
bool (*get_lights)(const struct hid *hid, struct hid_light *lights,
|
||||
size_t *nlights);
|
||||
bool (*get_value)(struct hid *hid, size_t control_no, int32_t *out_value);
|
||||
bool (*set_light)(struct hid *hid, size_t light_no, uint32_t intensity);
|
||||
};
|
||||
|
||||
struct hid {
|
||||
const struct hid_vtbl *vptr;
|
||||
};
|
||||
|
||||
#define hid_close(hid) \
|
||||
(hid)->vptr->close(hid)
|
||||
|
||||
#define hid_get_device_usage(hid, usage) \
|
||||
(hid)->vptr->get_device_usage(hid, usage)
|
||||
|
||||
#define hid_get_name(hid, chars, nchars) \
|
||||
(hid)->vptr->get_name(hid, chars, nchars)
|
||||
|
||||
#define hid_get_controls(hid, controls, ncontrols) \
|
||||
(hid)->vptr->get_controls(hid, controls, ncontrols)
|
||||
|
||||
#define hid_get_lights(hid, lights, nlights) \
|
||||
(hid)->vptr->get_lights(hid, lights, nlights)
|
||||
|
||||
#define hid_get_value(hid, control_no, out_value) \
|
||||
(hid)->vptr->get_value(hid, control_no, out_value)
|
||||
|
||||
#define hid_set_light(hid, light_no, illuminated) \
|
||||
(hid)->vptr->set_light(hid, light_no, illuminated)
|
||||
|
||||
struct hid_ri;
|
||||
|
||||
struct hid_ri_vtbl {
|
||||
struct hid_vtbl super;
|
||||
void (*handle_event)(struct hid_ri *hid, const RAWINPUT *ri);
|
||||
};
|
||||
|
||||
struct hid_ri {
|
||||
const struct hid_ri_vtbl *vptr;
|
||||
};
|
||||
|
||||
wchar_t *hid_ri_init_name(const GUID *class_guid, const char *dev_node);
|
||||
bool hid_ri_get_name(wchar_t *chars, size_t *nchars,
|
||||
const wchar_t *src_chars);
|
||||
bool hid_ri_get_controls(struct hid_control *controls, size_t *ncontrols,
|
||||
const struct hid_control *src_controls, size_t src_ncontrols);
|
||||
|
||||
#define hid_ri_handle_event(hid_ri, ri) \
|
||||
(hid_ri)->vptr->handle_event(hid_ri, ri)
|
||||
|
||||
struct hid_fd;
|
||||
|
||||
struct hid_fd_vtbl {
|
||||
struct hid_vtbl super;
|
||||
bool (*handle_completion)(struct hid_fd *hid, OVERLAPPED *ovl,
|
||||
size_t nbytes);
|
||||
};
|
||||
|
||||
struct hid_fd {
|
||||
const struct hid_fd_vtbl *vptr;
|
||||
};
|
||||
|
||||
#define hid_fd_handle_completion(hid_fd, ovl, nbytes) \
|
||||
(hid_fd)->vptr->handle_completion(hid_fd, ovl, nbytes)
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,67 @@
|
||||
#include <windows.h>
|
||||
#include <dbt.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "geninput/hid.h"
|
||||
#include "geninput/hotplug.h"
|
||||
#include "geninput/io-thread.h"
|
||||
#include "geninput/ri.h"
|
||||
|
||||
#include "util/log.h"
|
||||
|
||||
static HDEVNOTIFY hotplug_handle;
|
||||
|
||||
void hotplug_init(HWND wnd)
|
||||
{
|
||||
DEV_BROADCAST_DEVICEINTERFACE filter;
|
||||
|
||||
memset(&filter, 0, sizeof(filter));
|
||||
filter.dbcc_size = sizeof(filter);
|
||||
filter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
|
||||
filter.dbcc_classguid = hid_guid;
|
||||
|
||||
hotplug_handle = RegisterDeviceNotification(wnd, &filter,
|
||||
DEVICE_NOTIFY_WINDOW_HANDLE);
|
||||
|
||||
if (hotplug_handle == NULL) {
|
||||
log_fatal("Failed to register for HID hotplug notifications: %08x",
|
||||
(unsigned int) GetLastError());
|
||||
}
|
||||
}
|
||||
|
||||
void hotplug_handle_msg(WPARAM wparam, const DEV_BROADCAST_HDR *hdr)
|
||||
{
|
||||
const DEV_BROADCAST_DEVICEINTERFACE *dev;
|
||||
|
||||
if (wparam != DBT_DEVICEARRIVAL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (hdr->dbch_devicetype != DBT_DEVTYP_DEVICEINTERFACE) {
|
||||
return;
|
||||
}
|
||||
|
||||
dev = (const DEV_BROADCAST_DEVICEINTERFACE *) hdr;
|
||||
|
||||
if (memcmp(&dev->dbcc_classguid, &hid_guid, sizeof(hid_guid))) {
|
||||
return;
|
||||
}
|
||||
|
||||
log_misc("HID hotplug detected: %s", dev->dbcc_name);
|
||||
io_thread_add_device(dev->dbcc_name);
|
||||
ri_scan_devices();
|
||||
}
|
||||
|
||||
void hotplug_fini(void)
|
||||
{
|
||||
BOOL ok;
|
||||
|
||||
ok = UnregisterDeviceNotification(hotplug_handle);
|
||||
|
||||
if (!ok) {
|
||||
log_warning("Failed to unregister HID hotplug notifications: %08x",
|
||||
(unsigned int) GetLastError());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
#ifndef GENINPUT_HOTPLUG_H
|
||||
#define GENINPUT_HOTPLUG_H
|
||||
|
||||
#include <windows.h>
|
||||
#include <dbt.h>
|
||||
|
||||
void hotplug_init(HWND wnd);
|
||||
void hotplug_handle_msg(WPARAM wparam, const DEV_BROADCAST_HDR *hdr);
|
||||
void hotplug_fini(void);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,31 @@
|
||||
#ifndef GENINPUT_INPUT_CONFIG_H
|
||||
#define GENINPUT_INPUT_CONFIG_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "bemanitools/input.h"
|
||||
|
||||
#include "geninput/mapper.h"
|
||||
|
||||
void mapper_config_save(const char *game_type);
|
||||
void mapper_clear_action_map(uint8_t action, uint8_t page);
|
||||
void mapper_clear_light_map(const struct mapped_light *ml);
|
||||
bool mapper_get_action_map(uint8_t action, uint8_t page,
|
||||
struct mapped_action *ma);
|
||||
bool mapper_get_analog_map(uint8_t analog, struct mapped_analog *ma);
|
||||
int32_t mapper_get_analog_sensitivity(uint8_t analog);
|
||||
uint8_t mapper_get_nanalogs(void);
|
||||
uint8_t mapper_get_npages(void);
|
||||
bool mapper_is_analog_absolute(uint8_t analog);
|
||||
action_iter_t mapper_iterate_actions(void);
|
||||
light_iter_t mapper_iterate_lights(void);
|
||||
void mapper_set_action_map(uint8_t action, uint8_t page, uint8_t bit,
|
||||
const struct mapped_action *ma);
|
||||
bool mapper_set_analog_map(uint8_t analog, const struct mapped_analog *ma);
|
||||
bool mapper_set_analog_sensitivity(uint8_t analog, int32_t sensitivity);
|
||||
void mapper_set_nanalogs(uint8_t nanalogs);
|
||||
void mapper_set_nlights(uint8_t nlights);
|
||||
void mapper_set_light_map(const struct mapped_light *ml, uint8_t game_light);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,223 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "bemanitools/glue.h"
|
||||
#include "bemanitools/input.h"
|
||||
|
||||
#include "geninput/hid.h"
|
||||
#include "geninput/hid-mgr.h"
|
||||
#include "geninput/io-thread.h"
|
||||
#include "geninput/mapper.h"
|
||||
#include "geninput/mapper-s11n.h"
|
||||
|
||||
#include "util/fs.h"
|
||||
#include "util/log.h"
|
||||
#include "util/msg-thread.h"
|
||||
#include "util/str.h"
|
||||
#include "util/thread.h"
|
||||
|
||||
static HINSTANCE input_hinst;
|
||||
static volatile long input_init_count;
|
||||
|
||||
static FILE *mapper_config_open(const char *game_type, const char *mode)
|
||||
{
|
||||
char path[MAX_PATH];
|
||||
|
||||
str_format(path, sizeof(path), "%s_v5_00a07.bin", game_type);
|
||||
|
||||
return fopen_appdata("DJHACKERS", path, mode);
|
||||
}
|
||||
|
||||
void input_set_loggers(log_formatter_t misc, log_formatter_t info,
|
||||
log_formatter_t warning, log_formatter_t fatal)
|
||||
{
|
||||
log_to_external(misc, info, warning, fatal);
|
||||
}
|
||||
|
||||
void input_init(thread_create_t create, thread_join_t join,
|
||||
thread_destroy_t destroy)
|
||||
{
|
||||
if (InterlockedIncrement(&input_init_count) != 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
log_info("Generic input subsystem is starting up");
|
||||
|
||||
hid_mgr_init();
|
||||
|
||||
mapper_inst = mapper_impl_create();
|
||||
|
||||
thread_api_init(create, join, destroy);
|
||||
msg_thread_init(input_hinst);
|
||||
io_thread_init();
|
||||
}
|
||||
|
||||
void input_fini(void)
|
||||
{
|
||||
if (InterlockedDecrement(&input_init_count) != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
msg_thread_fini();
|
||||
io_thread_fini();
|
||||
|
||||
mapper_impl_destroy(mapper_inst);
|
||||
hid_mgr_fini();
|
||||
|
||||
log_info("Generic input subsystem has shut down");
|
||||
}
|
||||
|
||||
bool mapper_config_load(const char *game_type)
|
||||
{
|
||||
struct mapper *m;
|
||||
FILE *f;
|
||||
|
||||
f = mapper_config_open(game_type, "rb");
|
||||
|
||||
if (f == NULL) {
|
||||
goto open_fail;
|
||||
}
|
||||
|
||||
m = mapper_impl_config_load(f);
|
||||
|
||||
if (m == NULL) {
|
||||
goto read_fail;
|
||||
}
|
||||
|
||||
mapper_impl_destroy(mapper_inst);
|
||||
mapper_inst = m;
|
||||
|
||||
fclose(f);
|
||||
|
||||
return true;
|
||||
|
||||
read_fail:
|
||||
fclose(f);
|
||||
|
||||
open_fail:
|
||||
return false;
|
||||
}
|
||||
|
||||
void mapper_config_save(const char *game_type)
|
||||
{
|
||||
FILE *f;
|
||||
|
||||
f = mapper_config_open(game_type, "wb");
|
||||
|
||||
if (f == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
mapper_impl_config_save(mapper_inst, f);
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
void mapper_clear_action_map(uint8_t action, uint8_t page)
|
||||
{
|
||||
mapper_impl_clear_action_map(mapper_inst, action, page);
|
||||
}
|
||||
|
||||
void mapper_clear_light_map(const struct mapped_light *ml)
|
||||
{
|
||||
mapper_impl_clear_light_map(mapper_inst, ml);
|
||||
}
|
||||
|
||||
bool mapper_get_action_map(uint8_t action, uint8_t page,
|
||||
struct mapped_action *ma)
|
||||
{
|
||||
return mapper_impl_get_action_map(mapper_inst, action, page, ma);
|
||||
}
|
||||
|
||||
bool mapper_get_analog_map(uint8_t analog, struct mapped_analog *ma)
|
||||
{
|
||||
return mapper_impl_get_analog_map(mapper_inst, analog, ma);
|
||||
}
|
||||
|
||||
int32_t mapper_get_analog_sensitivity(uint8_t analog)
|
||||
{
|
||||
return mapper_impl_get_analog_sensitivity(mapper_inst, analog);
|
||||
}
|
||||
|
||||
uint8_t mapper_get_nanalogs(void)
|
||||
{
|
||||
return mapper_impl_get_nanalogs(mapper_inst);
|
||||
}
|
||||
|
||||
uint8_t mapper_get_npages(void)
|
||||
{
|
||||
return mapper_impl_get_npages(mapper_inst);
|
||||
}
|
||||
|
||||
bool mapper_is_analog_absolute(uint8_t analog)
|
||||
{
|
||||
return mapper_impl_is_analog_absolute(mapper_inst, analog);
|
||||
}
|
||||
|
||||
action_iter_t mapper_iterate_actions(void)
|
||||
{
|
||||
return mapper_impl_iterate_actions(mapper_inst);
|
||||
}
|
||||
|
||||
light_iter_t mapper_iterate_lights(void)
|
||||
{
|
||||
return mapper_impl_iterate_lights(mapper_inst);
|
||||
}
|
||||
|
||||
void mapper_set_action_map(uint8_t action, uint8_t page, uint8_t bit,
|
||||
const struct mapped_action *ma)
|
||||
{
|
||||
mapper_impl_set_action_map(mapper_inst, action, page, bit, ma);
|
||||
}
|
||||
|
||||
bool mapper_set_analog_map(uint8_t analog, const struct mapped_analog *ma)
|
||||
{
|
||||
return mapper_impl_set_analog_map(mapper_inst, analog, ma);
|
||||
}
|
||||
|
||||
bool mapper_set_analog_sensitivity(uint8_t analog, int32_t sensitivity)
|
||||
{
|
||||
return mapper_impl_set_analog_sensitivity(mapper_inst, analog, sensitivity);
|
||||
}
|
||||
|
||||
void mapper_set_light_map(const struct mapped_light *ml, uint8_t game_light)
|
||||
{
|
||||
mapper_impl_set_light_map(mapper_inst, ml, game_light);
|
||||
}
|
||||
|
||||
void mapper_set_nanalogs(uint8_t nanalogs)
|
||||
{
|
||||
mapper_impl_set_nanalogs(mapper_inst, nanalogs);
|
||||
}
|
||||
|
||||
void mapper_set_nlights(uint8_t nlights)
|
||||
{
|
||||
mapper_impl_set_nlights(mapper_inst, nlights);
|
||||
}
|
||||
|
||||
uint8_t mapper_read_analog(uint8_t analog)
|
||||
{
|
||||
return mapper_impl_read_analog(mapper_inst, analog);
|
||||
}
|
||||
|
||||
uint64_t mapper_update(void)
|
||||
{
|
||||
return mapper_impl_update(mapper_inst);
|
||||
}
|
||||
|
||||
void mapper_write_light(uint8_t light, uint8_t intensity)
|
||||
{
|
||||
mapper_impl_write_light(mapper_inst, light, intensity);
|
||||
}
|
||||
|
||||
BOOL WINAPI DllMain(HINSTANCE hinst, DWORD reason, void *ctx)
|
||||
{
|
||||
if (reason == DLL_PROCESS_ATTACH) {
|
||||
input_hinst = hinst;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,204 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "geninput/dev-list.h"
|
||||
#include "geninput/hid.h"
|
||||
#include "geninput/hid-generic.h"
|
||||
#include "geninput/hid-mgr.h"
|
||||
#include "geninput/io-thread.h"
|
||||
#include "geninput/pacdrive.h"
|
||||
|
||||
#include "util/defs.h"
|
||||
#include "util/log.h"
|
||||
#include "util/mem.h"
|
||||
#include "util/thread.h"
|
||||
|
||||
enum io_thread_cmd {
|
||||
IO_THREAD_CMD_STOP,
|
||||
IO_THREAD_CMD_ADD_DEVICE,
|
||||
};
|
||||
|
||||
struct io_thread_msg {
|
||||
enum io_thread_cmd cmd;
|
||||
const char *dev_node;
|
||||
HANDLE barrier;
|
||||
};
|
||||
|
||||
static HANDLE io_thread_cp;
|
||||
static int io_thread_id;
|
||||
|
||||
static void io_thread_proc_add_device(const char *dev_node);
|
||||
static void io_thread_proc_init(void);
|
||||
static void io_thread_proc_main_loop(void);
|
||||
static void io_thread_proc_fini(void);
|
||||
static int io_thread_proc(void *param);
|
||||
|
||||
static void io_thread_proc_add_device(const char *dev_node)
|
||||
{
|
||||
struct hid_stub *stub;
|
||||
struct hid_fd *hid_fd;
|
||||
uintptr_t ctx;
|
||||
|
||||
hid_mgr_lock();
|
||||
|
||||
stub = hid_mgr_get_named_stub(dev_node);
|
||||
|
||||
if (!hid_stub_is_attached(stub)) {
|
||||
ctx = (uintptr_t) stub;
|
||||
|
||||
if (pac_open(&hid_fd, dev_node, io_thread_cp, ctx)
|
||||
|| hid_generic_open(&hid_fd, dev_node, io_thread_cp, ctx)) {
|
||||
hid_stub_attach(stub, (struct hid *) hid_fd);
|
||||
}
|
||||
}
|
||||
|
||||
hid_mgr_unlock();
|
||||
}
|
||||
|
||||
static void io_thread_proc_init(void)
|
||||
{
|
||||
const char *dev_node;
|
||||
struct dev_list devs;
|
||||
|
||||
io_thread_cp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
|
||||
|
||||
dev_list_init(&devs, &hid_guid);
|
||||
|
||||
while (dev_list_next(&devs)) {
|
||||
dev_node = dev_list_get_dev_node(&devs);
|
||||
io_thread_proc_add_device(dev_node);
|
||||
}
|
||||
|
||||
dev_list_fini(&devs);
|
||||
|
||||
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
|
||||
|
||||
log_info("USB I/O thread ready, thread id = %d",
|
||||
(unsigned int) GetCurrentThreadId());
|
||||
}
|
||||
|
||||
static void io_thread_proc_main_loop(void)
|
||||
{
|
||||
uintptr_t ctx;
|
||||
OVERLAPPED *p_ovl;
|
||||
DWORD nbytes;
|
||||
struct hid_stub *stub;
|
||||
struct io_thread_msg *msg;
|
||||
BOOL ok;
|
||||
|
||||
for (;;) {
|
||||
ok = GetQueuedCompletionStatus(io_thread_cp, &nbytes,
|
||||
(ULONG_PTR *) &ctx, &p_ovl, 0);
|
||||
|
||||
if (p_ovl != NULL) {
|
||||
/* An I/O completed (depending on the value of `ok' either
|
||||
successfuly or not), either way dispatch the completion event */
|
||||
|
||||
stub = (struct hid_stub *) ctx;
|
||||
|
||||
hid_mgr_lock();
|
||||
|
||||
if (!ok) {
|
||||
log_warning("Async IO returned failure: %08x",
|
||||
(unsigned int) GetLastError());
|
||||
hid_stub_detach(stub);
|
||||
} else {
|
||||
ok = hid_stub_handle_completion(stub, p_ovl, nbytes);
|
||||
}
|
||||
|
||||
hid_mgr_unlock();
|
||||
|
||||
} else if (ok) {
|
||||
/* No OVERLAPPED*, so this is a command message queued from a
|
||||
different thread. */
|
||||
|
||||
msg = (struct io_thread_msg *) ctx;
|
||||
|
||||
if (msg->cmd == IO_THREAD_CMD_STOP) {
|
||||
break;
|
||||
} else if (msg->cmd == IO_THREAD_CMD_ADD_DEVICE) {
|
||||
io_thread_proc_add_device(msg->dev_node);
|
||||
SetEvent(msg->barrier);
|
||||
}
|
||||
|
||||
} else if (GetLastError() == WAIT_TIMEOUT) {
|
||||
/* Failure, but a benign one: timeout due to no event.
|
||||
I don't exactly know why, but we need to back off here for a bit
|
||||
or we end up soaking the CPU (in kernel mode...). It's probably
|
||||
a bemanitools bug instead of an NT deficiency but I'll have to
|
||||
track down the root cause later. */
|
||||
|
||||
Sleep(1);
|
||||
} else {
|
||||
/* Otherwise idk what's going on */
|
||||
|
||||
log_warning("Spurious wakeup in I/O mux: %08x",
|
||||
(unsigned int) GetLastError());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void io_thread_proc_fini(void)
|
||||
{
|
||||
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL);
|
||||
CloseHandle(io_thread_cp);
|
||||
|
||||
log_misc("USB I/O thread shutting down");
|
||||
}
|
||||
|
||||
static int io_thread_proc(void *param)
|
||||
{
|
||||
HANDLE barrier;
|
||||
|
||||
barrier = param;
|
||||
|
||||
io_thread_proc_init();
|
||||
SetEvent(barrier);
|
||||
|
||||
io_thread_proc_main_loop();
|
||||
|
||||
io_thread_proc_fini();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void io_thread_init(void)
|
||||
{
|
||||
HANDLE barrier;
|
||||
|
||||
barrier = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
|
||||
io_thread_id = thread_create(io_thread_proc, barrier,
|
||||
16384, 0);
|
||||
|
||||
WaitForSingleObject(barrier, INFINITE);
|
||||
CloseHandle(barrier);
|
||||
}
|
||||
|
||||
void io_thread_add_device(const char *dev_node)
|
||||
{
|
||||
struct io_thread_msg msg;
|
||||
|
||||
msg.cmd = IO_THREAD_CMD_ADD_DEVICE;
|
||||
msg.dev_node = dev_node;
|
||||
msg.barrier = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
|
||||
PostQueuedCompletionStatus(io_thread_cp, 0, (uintptr_t) &msg, NULL);
|
||||
|
||||
WaitForSingleObject(msg.barrier, INFINITE);
|
||||
CloseHandle(msg.barrier);
|
||||
}
|
||||
|
||||
void io_thread_fini(void)
|
||||
{
|
||||
struct io_thread_msg msg;
|
||||
|
||||
msg.cmd = IO_THREAD_CMD_STOP;
|
||||
|
||||
PostQueuedCompletionStatus(io_thread_cp, 0, (uintptr_t) &msg, NULL);
|
||||
|
||||
thread_join(io_thread_id, NULL);
|
||||
thread_destroy(io_thread_id);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
#ifndef GENINPUT_IO_THREAD_H
|
||||
#define GENINPUT_IO_THREAD_H
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
void io_thread_init(void);
|
||||
void io_thread_add_device(const char *dev_node);
|
||||
void io_thread_fini(void);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,55 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
const size_t kbd_basic_nusages = 243;
|
||||
const size_t kbd_ext_nusages = 100;
|
||||
|
||||
const uint8_t kbd_basic_usages[] = {
|
||||
0x00, 0x29, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23,
|
||||
0x24, 0x25, 0x26, 0x27, 0x2d, 0x2e, 0x2a, 0x2b,
|
||||
0x14, 0x1a, 0x08, 0x15, 0x17, 0x1c, 0x18, 0x0c,
|
||||
0x12, 0x13, 0x2f, 0x30, 0x28, 0xe0, 0x04, 0x16,
|
||||
0x07, 0x09, 0x0a, 0x0b, 0x0d, 0x0e, 0x0f, 0x33,
|
||||
0x34, 0x35, 0xe1, 0x32, 0x1d, 0x1b, 0x06, 0x19,
|
||||
0x05, 0x11, 0x10, 0x36, 0x37, 0x38, 0xe5, 0x55,
|
||||
0xe2, 0x2c, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e,
|
||||
0x3f, 0x40, 0x41, 0x42, 0x43, 0x53, 0x47, 0x5f,
|
||||
0x60, 0x61, 0x56, 0x5c, 0x5d, 0x5e, 0x57, 0x59,
|
||||
0x5a, 0x5b, 0x62, 0x63, 0x00, 0x00, 0x64, 0x44,
|
||||
0x45, 0x67, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x68, 0x69, 0x6a, 0x6b,
|
||||
0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x00,
|
||||
0x88, 0x00, 0x00, 0x87, 0x00, 0x00, 0x94, 0x93,
|
||||
0x92, 0x8a, 0x00, 0x8b, 0x00, 0x89, 0x85, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x91, 0x90,
|
||||
};
|
||||
|
||||
const uint8_t kbd_ext_usages[] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x58, 0xe4, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x00, 0x46,
|
||||
0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a,
|
||||
0x52, 0x4b, 0x00, 0x50, 0x00, 0x4f, 0x00, 0x4d,
|
||||
0x51, 0x4e, 0x49, 0x4c, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0xe3, 0xe7, 0x65, 0xe9, 0xea,
|
||||
0x00, 0x00, 0x00, 0xeb,
|
||||
};
|
||||
@@ -0,0 +1,13 @@
|
||||
#ifndef GENINPUT_KBD_DATA_H
|
||||
#define GENINPUT_KBD_DATA_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
extern const uint8_t kbd_basic_usages[];
|
||||
extern const size_t kbd_basic_nusages;
|
||||
|
||||
extern const size_t kbd_ext_nusages;
|
||||
extern const uint8_t kbd_ext_usages[];
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,184 @@
|
||||
#define LOG_MODULE "kbd"
|
||||
|
||||
#include <windows.h>
|
||||
#include <hidsdi.h>
|
||||
#include <initguid.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "geninput/hid.h"
|
||||
#include "geninput/kbd.h"
|
||||
#include "geninput/kbd-data.h"
|
||||
|
||||
#include "util/defs.h"
|
||||
#include "util/log.h"
|
||||
#include "util/mem.h"
|
||||
#include "util/str.h"
|
||||
|
||||
DEFINE_GUID(kbd_guid,
|
||||
0x884B96C3,
|
||||
0x56EF,
|
||||
0x11D1,
|
||||
0xBC, 0x8C, 0x00, 0xA0, 0xC9, 0x14, 0x05, 0xDD);
|
||||
|
||||
static struct hid_control kbd_controls[512];
|
||||
static size_t kbd_ncontrols;
|
||||
|
||||
static void kbd_static_init(void);
|
||||
static bool kbd_get_device_usage(const struct hid *hid, uint32_t *usage);
|
||||
static bool kbd_get_name(const struct hid *hid, wchar_t *chars,
|
||||
size_t *nchars);
|
||||
static bool kbd_get_controls(const struct hid *hid,
|
||||
struct hid_control *controls, size_t *ncontrols);
|
||||
static bool kbd_get_lights(const struct hid *hid,
|
||||
struct hid_light *lights, size_t *nlights);
|
||||
static bool kbd_get_value(struct hid *hid, size_t control_no,
|
||||
int32_t *out_value);
|
||||
static bool kbd_set_light(struct hid *hid, size_t light_no,
|
||||
uint32_t intensity);
|
||||
static void kbd_handle_event(struct hid_ri *hid_ri, const RAWINPUT *ri);
|
||||
static void kbd_free(struct hid *hid);
|
||||
|
||||
static const struct hid_ri_vtbl kbd_vtbl = {{
|
||||
/* .free = */ kbd_free,
|
||||
/* .get_device_usage = */ kbd_get_device_usage,
|
||||
/* .get_name = */ kbd_get_name,
|
||||
/* .get_controls = */ kbd_get_controls,
|
||||
/* .get_lights = */ kbd_get_lights,
|
||||
/* .get_value = */ kbd_get_value,
|
||||
/* .set_light = */ kbd_set_light },
|
||||
/* .handle_event = */ kbd_handle_event
|
||||
};
|
||||
|
||||
struct kbd {
|
||||
struct hid_ri super;
|
||||
wchar_t *name;
|
||||
uint32_t state[512];
|
||||
};
|
||||
|
||||
static void kbd_static_init(void)
|
||||
{
|
||||
uint8_t code;
|
||||
size_t i;
|
||||
|
||||
if (kbd_ncontrols == 0) {
|
||||
kbd_ncontrols = kbd_basic_nusages + kbd_ext_nusages;
|
||||
|
||||
for (i = 0 ; i < kbd_ncontrols ; i++) {
|
||||
if (i < kbd_basic_nusages) {
|
||||
code = kbd_basic_usages[i];
|
||||
} else {
|
||||
code = kbd_ext_usages[i - kbd_basic_nusages];
|
||||
}
|
||||
|
||||
kbd_controls[i].usage = 0x00070000 | code;
|
||||
kbd_controls[i].value_min = 0;
|
||||
kbd_controls[i].value_max = 1;
|
||||
kbd_controls[i].flags = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void kbd_create(struct hid_ri **hid_ri, const char *dev_node)
|
||||
{
|
||||
struct kbd *kbd;
|
||||
char *tmp;
|
||||
|
||||
kbd_static_init();
|
||||
|
||||
kbd = xcalloc(sizeof(*kbd));
|
||||
kbd->super.vptr = &kbd_vtbl;
|
||||
kbd->name = hid_ri_init_name(&kbd_guid, dev_node);
|
||||
|
||||
*hid_ri = &kbd->super;
|
||||
|
||||
/* Output some debugging info */
|
||||
if (!wstr_narrow(kbd->name, &tmp)) {
|
||||
tmp = str_dup("???");
|
||||
}
|
||||
|
||||
/* Dump diagnostics */
|
||||
log_misc("Opened keyboard on dev node %s", dev_node);
|
||||
log_misc("... Product: %s", tmp);
|
||||
|
||||
free(tmp);
|
||||
}
|
||||
|
||||
static bool kbd_get_device_usage(const struct hid *hid, uint32_t *usage)
|
||||
{
|
||||
*usage = KBD_DEVICE_USAGE_KEYBOARD;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool kbd_get_name(const struct hid *hid, wchar_t *chars,
|
||||
size_t *nchars)
|
||||
{
|
||||
struct kbd *kbd = containerof(hid, struct kbd, super);
|
||||
|
||||
return hid_ri_get_name(chars, nchars, kbd->name);
|
||||
}
|
||||
|
||||
static bool kbd_get_controls(const struct hid *hid,
|
||||
struct hid_control *controls, size_t *ncontrols)
|
||||
{
|
||||
return hid_ri_get_controls(controls, ncontrols, kbd_controls,
|
||||
kbd_ncontrols);
|
||||
}
|
||||
|
||||
static bool kbd_get_lights(const struct hid *hid,
|
||||
struct hid_light *lights, size_t *nlights)
|
||||
{
|
||||
log_assert(nlights != NULL);
|
||||
|
||||
*nlights = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool kbd_get_value(struct hid *hid, size_t control_no,
|
||||
int32_t *out_value)
|
||||
{
|
||||
struct kbd *kbd = containerof(hid, struct kbd, super);
|
||||
|
||||
log_assert(control_no < kbd_ncontrols);
|
||||
|
||||
*out_value = kbd->state[control_no];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool kbd_set_light(struct hid *hid, size_t light_no,
|
||||
uint32_t intensity)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static void kbd_handle_event(struct hid_ri *hid_ri, const RAWINPUT *ri)
|
||||
{
|
||||
struct kbd *kbd = containerof(hid_ri, struct kbd, super);
|
||||
uint32_t value;
|
||||
|
||||
value = (ri->data.keyboard.Flags & RI_KEY_BREAK) == 0;
|
||||
|
||||
if (ri->data.keyboard.Flags & RI_KEY_E0) {
|
||||
if (ri->data.keyboard.MakeCode < kbd_ext_nusages) {
|
||||
kbd->state[ri->data.keyboard.MakeCode + kbd_basic_nusages] = value;
|
||||
}
|
||||
} else if (!(ri->data.keyboard.Flags & RI_KEY_E1)) {
|
||||
if (ri->data.keyboard.MakeCode < kbd_basic_nusages) {
|
||||
kbd->state[ri->data.keyboard.MakeCode] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void kbd_free(struct hid *hid)
|
||||
{
|
||||
struct kbd *kbd = containerof(hid, struct kbd, super);
|
||||
|
||||
free(kbd->name);
|
||||
free(kbd);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
#ifndef GENINPUT_KBD_H
|
||||
#define GENINPUT_KBD_H
|
||||
|
||||
#include "geninput/hid.h"
|
||||
|
||||
#define KBD_DEVICE_USAGE_KEYBOARD 0x00010006
|
||||
#define KBD_DEVICE_USAGE_KEYPAD 0x00010007
|
||||
|
||||
void kbd_create(struct hid_ri **hid_ri, const char *dev_node);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,275 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "geninput/mapper.h"
|
||||
#include "geninput/mapper-s11n.h"
|
||||
|
||||
#include "util/fs.h"
|
||||
#include "util/mem.h"
|
||||
|
||||
static bool mapper_impl_config_load_actions(struct mapper *m, FILE *f);
|
||||
static bool mapper_impl_config_load_analogs(struct mapper *m, FILE *f);
|
||||
static bool mapper_impl_config_load_lights(struct mapper *m, FILE *f);
|
||||
static void mapper_impl_config_save_actions(struct mapper *m, FILE *f);
|
||||
static void mapper_impl_config_save_analogs(struct mapper *m, FILE *f);
|
||||
static void mapper_impl_config_save_lights(struct mapper *m, FILE *f);
|
||||
|
||||
struct mapper *mapper_impl_config_load(FILE *f)
|
||||
{
|
||||
struct mapper *m;
|
||||
|
||||
hid_mgr_lock(); /* Need to convert from HID objects to /dev nodes */
|
||||
|
||||
m = mapper_impl_create();
|
||||
|
||||
if ( !mapper_impl_config_load_actions(m, f) ||
|
||||
!mapper_impl_config_load_analogs(m, f) ||
|
||||
!mapper_impl_config_load_lights(m, f)) {
|
||||
mapper_impl_destroy(m);
|
||||
m = NULL;
|
||||
}
|
||||
|
||||
hid_mgr_unlock();
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
static bool mapper_impl_config_load_actions(struct mapper *m, FILE *f)
|
||||
{
|
||||
char *dev_node;
|
||||
struct mapped_action ma;
|
||||
uint8_t action;
|
||||
uint8_t page;
|
||||
uint8_t bit;
|
||||
uint32_t count;
|
||||
uint32_t i;
|
||||
|
||||
if (!read_u32(f, &count)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (i = 0 ; i < count ; i++) {
|
||||
memset(&ma, 0, sizeof(ma));
|
||||
|
||||
if (!read_str(f, &dev_node)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ma.hid = hid_mgr_get_named_stub(dev_node);
|
||||
free(dev_node);
|
||||
|
||||
if ( !read_u32(f, &ma.control_no) ||
|
||||
!read_u32(f, &ma.value_min) ||
|
||||
!read_u32(f, &ma.value_max) ||
|
||||
!read_u8(f, &action) ||
|
||||
!read_u8(f, &page) ||
|
||||
!read_u8(f, &bit)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mapper_impl_set_action_map(m, action, page, bit, &ma);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool mapper_impl_config_load_analogs(struct mapper *m, FILE *f)
|
||||
{
|
||||
char *dev_node;
|
||||
struct mapped_analog ma;
|
||||
int32_t sensitivity;
|
||||
uint8_t nanalogs;
|
||||
uint8_t i;
|
||||
|
||||
if (!read_u8(f, &nanalogs)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mapper_impl_set_nanalogs(m, nanalogs);
|
||||
|
||||
for (i = 0 ; i < nanalogs ; i++) {
|
||||
memset(&ma, 0, sizeof(ma));
|
||||
|
||||
if (!read_str(f, &dev_node)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (dev_node[0] != '\0') {
|
||||
ma.hid = hid_mgr_get_named_stub(dev_node);
|
||||
|
||||
if (!read_u32(f, &ma.control_no)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!read_u32(f, &sensitivity)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mapper_impl_set_analog_map(m, i, &ma);
|
||||
mapper_impl_set_analog_sensitivity(m, i, sensitivity);
|
||||
}
|
||||
|
||||
free(dev_node);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool mapper_impl_config_load_lights(struct mapper *m, FILE *f)
|
||||
{
|
||||
char *dev_node;
|
||||
struct mapped_light ml;
|
||||
uint8_t game_light;
|
||||
uint8_t nlights;
|
||||
uint32_t count;
|
||||
uint32_t i;
|
||||
|
||||
if (!read_u8(f, &nlights)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mapper_impl_set_nlights(m, nlights);
|
||||
|
||||
if (!read_u32(f, &count)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (i = 0 ; i < count ; i++) {
|
||||
memset(&ml, 0, sizeof(ml));
|
||||
|
||||
if (!read_str(f, &dev_node)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ml.hid = hid_mgr_get_named_stub(dev_node);
|
||||
free(dev_node);
|
||||
|
||||
if (!read_u32(f, &ml.light_no)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!read_u8(f, &game_light)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mapper_impl_set_light_map(m, &ml, game_light);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void mapper_impl_config_save(struct mapper *m, FILE *f)
|
||||
{
|
||||
hid_mgr_lock();
|
||||
|
||||
mapper_impl_config_save_actions(m, f);
|
||||
mapper_impl_config_save_analogs(m, f);
|
||||
mapper_impl_config_save_lights(m, f);
|
||||
|
||||
hid_mgr_unlock();
|
||||
}
|
||||
|
||||
static void mapper_impl_config_save_actions(struct mapper *m, FILE *f)
|
||||
{
|
||||
action_iter_t pos;
|
||||
struct mapped_action ma;
|
||||
uint32_t count;
|
||||
uint8_t action;
|
||||
uint8_t page;
|
||||
uint8_t bit;
|
||||
|
||||
/* Count up bindings */
|
||||
|
||||
count = 0;
|
||||
|
||||
for (pos = mapper_impl_iterate_actions(m)
|
||||
; action_iter_is_valid(pos)
|
||||
; action_iter_next(pos)) {
|
||||
count++;
|
||||
}
|
||||
|
||||
action_iter_free(pos);
|
||||
write_u32(f, &count);
|
||||
|
||||
/* Write out bindings */
|
||||
|
||||
for (pos = mapper_impl_iterate_actions(m)
|
||||
; action_iter_is_valid(pos)
|
||||
; action_iter_next(pos)) {
|
||||
action_iter_get_mapping(pos, &ma);
|
||||
action = action_iter_get_action(pos);
|
||||
page = action_iter_get_page(pos);
|
||||
bit = action_iter_get_bit(pos);
|
||||
|
||||
write_str(f, hid_stub_get_dev_node(ma.hid));
|
||||
write_u32(f, &ma.control_no);
|
||||
write_u32(f, &ma.value_min);
|
||||
write_u32(f, &ma.value_max);
|
||||
write_u8(f, &action);
|
||||
write_u8(f, &page);
|
||||
write_u8(f, &bit);
|
||||
}
|
||||
|
||||
action_iter_free(pos);
|
||||
}
|
||||
|
||||
static void mapper_impl_config_save_analogs(struct mapper *m, FILE *f)
|
||||
{
|
||||
struct mapped_analog ma;
|
||||
int32_t sensitivity;
|
||||
uint8_t nanalogs;
|
||||
uint8_t i;
|
||||
|
||||
nanalogs = mapper_impl_get_nanalogs(m);
|
||||
write_u8(f, &nanalogs);
|
||||
|
||||
for (i = 0 ; i < nanalogs ; i++) {
|
||||
mapper_impl_get_analog_map(m, i, &ma);
|
||||
|
||||
if (ma.hid == NULL) {
|
||||
write_str(f, "");
|
||||
} else {
|
||||
sensitivity = mapper_impl_get_analog_sensitivity(m, i);
|
||||
|
||||
write_str(f, hid_stub_get_dev_node(ma.hid));
|
||||
write_u32(f, &ma.control_no);
|
||||
write_u32(f, &sensitivity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void mapper_impl_config_save_lights(struct mapper *m, FILE *f)
|
||||
{
|
||||
light_iter_t pos;
|
||||
struct mapped_light ml;
|
||||
uint8_t game_light;
|
||||
uint8_t nlights;
|
||||
uint32_t count;
|
||||
|
||||
nlights = mapper_impl_get_nlights(m);
|
||||
write_u8(f, &nlights);
|
||||
|
||||
count = 0;
|
||||
|
||||
for (pos = mapper_impl_iterate_lights(m)
|
||||
; light_iter_is_valid(pos)
|
||||
; light_iter_next(pos)) {
|
||||
count++;
|
||||
}
|
||||
|
||||
write_u32(f, &count);
|
||||
light_iter_free(pos);
|
||||
|
||||
for (pos = mapper_impl_iterate_lights(m)
|
||||
; light_iter_is_valid(pos)
|
||||
; light_iter_next(pos)) {
|
||||
light_iter_get_mapping(pos, &ml);
|
||||
game_light = light_iter_get_game_light(pos);
|
||||
|
||||
write_str(f, hid_stub_get_dev_node(ml.hid));
|
||||
write_u32(f, &ml.light_no);
|
||||
write_u8(f, &game_light);
|
||||
}
|
||||
|
||||
light_iter_free(pos);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
#ifndef GENINPUT_MAPPER_S11N_H
|
||||
#define GENINPUT_MAPPER_S11N_H
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "geninput/mapper.h"
|
||||
|
||||
struct mapper *mapper_impl_config_load(FILE *f);
|
||||
void mapper_impl_config_save(struct mapper *m, FILE *f);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,626 @@
|
||||
#include <math.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "geninput/hid-mgr.h"
|
||||
#include "geninput/mapper.h"
|
||||
|
||||
#include "util/array.h"
|
||||
#include "util/log.h"
|
||||
#include "util/mem.h"
|
||||
|
||||
struct action_iter {
|
||||
struct mapper *mapper;
|
||||
size_t i;
|
||||
};
|
||||
|
||||
struct action_mapping {
|
||||
struct mapped_action src;
|
||||
uint8_t action;
|
||||
uint8_t page;
|
||||
uint8_t bit;
|
||||
};
|
||||
|
||||
struct analog_mapping {
|
||||
struct mapped_analog src;
|
||||
int32_t sensitivity;
|
||||
bool bound;
|
||||
bool valid;
|
||||
bool absolute;
|
||||
double affine_scale;
|
||||
double affine_bias;
|
||||
uint8_t pos;
|
||||
};
|
||||
|
||||
struct light_iter {
|
||||
struct mapper *mapper;
|
||||
size_t i;
|
||||
};
|
||||
|
||||
struct light_mapping {
|
||||
struct mapped_light dest;
|
||||
uint8_t game_light;
|
||||
bool bound;
|
||||
bool valid;
|
||||
double affine_scale;
|
||||
int32_t affine_bias;
|
||||
};
|
||||
|
||||
struct mapper {
|
||||
struct array actions;
|
||||
struct analog_mapping *analogs;
|
||||
uint8_t nanalogs;
|
||||
struct array light_maps;
|
||||
uint8_t *lights;
|
||||
uint8_t nlights;
|
||||
};
|
||||
|
||||
static uint64_t action_mapping_update(struct action_mapping *am);
|
||||
static void analog_mapping_bind(struct analog_mapping *am);
|
||||
static void analog_mapping_update(struct analog_mapping *am);
|
||||
static void light_mapping_bind(struct light_mapping *lm);
|
||||
static void light_mapping_send(struct light_mapping *lm,
|
||||
const struct mapper *m);
|
||||
|
||||
struct mapper *mapper_inst;
|
||||
|
||||
void action_iter_get_mapping(action_iter_t iter, struct mapped_action *ma)
|
||||
{
|
||||
struct action_mapping *am;
|
||||
|
||||
log_assert(action_iter_is_valid(iter));
|
||||
|
||||
am = array_item(struct action_mapping, &iter->mapper->actions, iter->i);
|
||||
*ma = am->src;
|
||||
}
|
||||
|
||||
uint8_t action_iter_get_action(action_iter_t iter)
|
||||
{
|
||||
struct action_mapping *am;
|
||||
|
||||
log_assert(action_iter_is_valid(iter));
|
||||
|
||||
am = array_item(struct action_mapping, &iter->mapper->actions, iter->i);
|
||||
|
||||
return am->action;
|
||||
}
|
||||
|
||||
uint8_t action_iter_get_page(action_iter_t iter)
|
||||
{
|
||||
struct action_mapping *am;
|
||||
|
||||
log_assert(action_iter_is_valid(iter));
|
||||
|
||||
am = array_item(struct action_mapping, &iter->mapper->actions, iter->i);
|
||||
|
||||
return am->page;
|
||||
}
|
||||
|
||||
uint8_t action_iter_get_bit(action_iter_t iter)
|
||||
{
|
||||
struct action_mapping *am;
|
||||
|
||||
log_assert(action_iter_is_valid(iter));
|
||||
|
||||
am = array_item(struct action_mapping, &iter->mapper->actions, iter->i);
|
||||
|
||||
return am->bit;
|
||||
}
|
||||
|
||||
bool action_iter_is_valid(action_iter_t iter)
|
||||
{
|
||||
return iter->i < iter->mapper->actions.nitems;
|
||||
}
|
||||
|
||||
void action_iter_next(action_iter_t iter)
|
||||
{
|
||||
iter->i++;
|
||||
}
|
||||
|
||||
void action_iter_free(action_iter_t iter)
|
||||
{
|
||||
free(iter);
|
||||
}
|
||||
|
||||
static uint64_t action_mapping_update(struct action_mapping *am)
|
||||
{
|
||||
int32_t value;
|
||||
|
||||
if (am->src.hid == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!hid_stub_get_value(am->src.hid, am->src.control_no, &value)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (value < am->src.value_min || value > am->src.value_max) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1ULL << am->bit;
|
||||
}
|
||||
|
||||
static void analog_mapping_bind(struct analog_mapping *am)
|
||||
{
|
||||
const struct hid_control *ctl;
|
||||
struct hid_control *controls;
|
||||
size_t ncontrols;
|
||||
|
||||
am->bound = true;
|
||||
am->valid = false;
|
||||
|
||||
if (am->src.hid == NULL) {
|
||||
goto unbound_fail;
|
||||
}
|
||||
|
||||
if (!hid_stub_get_controls(am->src.hid, NULL, &ncontrols)) {
|
||||
goto size_fail;
|
||||
}
|
||||
|
||||
if (am->src.control_no >= ncontrols) {
|
||||
goto bounds_fail;
|
||||
}
|
||||
|
||||
controls = xmalloc(sizeof(*controls) * ncontrols);
|
||||
|
||||
if (!hid_stub_get_controls(am->src.hid, controls, &ncontrols)) {
|
||||
goto read_fail;
|
||||
}
|
||||
|
||||
ctl = &controls[am->src.control_no];
|
||||
|
||||
am->affine_bias = ctl->value_min;
|
||||
am->affine_scale = ctl->value_max - ctl->value_min;
|
||||
am->absolute = !(ctl->flags & HID_FLAG_RELATIVE);
|
||||
am->valid = true;
|
||||
|
||||
read_fail:
|
||||
free(controls);
|
||||
|
||||
bounds_fail:
|
||||
size_fail:
|
||||
unbound_fail:
|
||||
return;
|
||||
}
|
||||
|
||||
static void analog_mapping_update(struct analog_mapping *am)
|
||||
{
|
||||
double tmp;
|
||||
int32_t value;
|
||||
|
||||
if (!am->bound) {
|
||||
analog_mapping_bind(am);
|
||||
}
|
||||
|
||||
if (!am->valid) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!hid_stub_get_value(am->src.hid, am->src.control_no, &value)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (am->absolute) {
|
||||
tmp = (value - am->affine_bias) / am->affine_scale;
|
||||
am->pos = (uint8_t) ((tmp + 0.5) * 256.0);
|
||||
} else {
|
||||
am->pos += (int8_t) (value * exp(am->sensitivity / 256.0));
|
||||
}
|
||||
}
|
||||
|
||||
void light_iter_get_mapping(light_iter_t iter, struct mapped_light *ml)
|
||||
{
|
||||
struct light_mapping *lm;
|
||||
|
||||
log_assert(light_iter_is_valid(iter));
|
||||
|
||||
lm = array_item(struct light_mapping, &iter->mapper->light_maps, iter->i);
|
||||
*ml = lm->dest;
|
||||
}
|
||||
|
||||
uint8_t light_iter_get_game_light(light_iter_t iter)
|
||||
{
|
||||
struct light_mapping *lm;
|
||||
|
||||
log_assert(light_iter_is_valid(iter));
|
||||
|
||||
lm = array_item(struct light_mapping, &iter->mapper->light_maps, iter->i);
|
||||
|
||||
return lm->game_light;
|
||||
}
|
||||
|
||||
bool light_iter_is_valid(light_iter_t iter)
|
||||
{
|
||||
return iter->i < iter->mapper->light_maps.nitems;
|
||||
}
|
||||
|
||||
void light_iter_next(light_iter_t iter)
|
||||
{
|
||||
iter->i++;
|
||||
}
|
||||
|
||||
void light_iter_free(light_iter_t iter)
|
||||
{
|
||||
free(iter);
|
||||
}
|
||||
|
||||
static void light_mapping_bind(struct light_mapping *lm)
|
||||
{
|
||||
const struct hid_light *light;
|
||||
struct hid_light *light_defs;
|
||||
size_t nlights;
|
||||
|
||||
lm->bound = true;
|
||||
lm->valid = false;
|
||||
|
||||
if (lm->dest.hid == NULL) {
|
||||
goto unbound_fail;
|
||||
}
|
||||
|
||||
if (!hid_stub_get_lights(lm->dest.hid, NULL, &nlights)) {
|
||||
goto size_fail;
|
||||
}
|
||||
|
||||
if (lm->dest.light_no >= nlights) {
|
||||
goto bounds_fail;
|
||||
}
|
||||
|
||||
light_defs = xmalloc(sizeof(*light_defs) * nlights);
|
||||
|
||||
if (!hid_stub_get_lights(lm->dest.hid, light_defs, &nlights)) {
|
||||
goto read_fail;
|
||||
}
|
||||
|
||||
light = &light_defs[lm->dest.light_no];
|
||||
|
||||
lm->affine_bias = light->value_min;
|
||||
lm->affine_scale = light->value_max - light->value_min;
|
||||
lm->valid = true;
|
||||
|
||||
read_fail:
|
||||
free(light_defs);
|
||||
|
||||
bounds_fail:
|
||||
size_fail:
|
||||
unbound_fail:
|
||||
return;
|
||||
}
|
||||
|
||||
static void light_mapping_send(struct light_mapping *lm,
|
||||
const struct mapper *m)
|
||||
{
|
||||
double tmp;
|
||||
uint32_t value;
|
||||
uint8_t intensity;
|
||||
|
||||
if (!lm->bound) {
|
||||
light_mapping_bind(lm);
|
||||
}
|
||||
|
||||
if (!lm->valid) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (lm->game_light >= m->nlights) {
|
||||
return;
|
||||
}
|
||||
|
||||
intensity = m->lights[lm->game_light];
|
||||
tmp = (intensity / 256.0) * lm->affine_scale;
|
||||
value = (int32_t) (tmp + 0.5) + lm->affine_bias;
|
||||
|
||||
hid_stub_set_light(lm->dest.hid, lm->dest.light_no, value);
|
||||
}
|
||||
|
||||
struct mapper *mapper_impl_create(void)
|
||||
{
|
||||
struct mapper *m;
|
||||
|
||||
m = xmalloc(sizeof(*m));
|
||||
|
||||
array_init(&m->actions);
|
||||
|
||||
m->analogs = NULL;
|
||||
m->nanalogs = 0;
|
||||
|
||||
array_init(&m->light_maps);
|
||||
|
||||
m->lights = NULL;
|
||||
m->nlights = 0;
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
void mapper_impl_clear_action_map(struct mapper *m, uint8_t action,
|
||||
uint8_t page)
|
||||
{
|
||||
struct action_mapping *am;
|
||||
size_t i;
|
||||
|
||||
for (i = 0 ; i < m->actions.nitems ; i++) {
|
||||
am = array_item(struct action_mapping, &m->actions, i);
|
||||
|
||||
if (am->action == action && am->page == page) {
|
||||
array_remove(struct action_mapping, &m->actions, i);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void mapper_impl_clear_light_map(struct mapper *m,
|
||||
const struct mapped_light *ml)
|
||||
{
|
||||
struct light_mapping *lm;
|
||||
size_t i;
|
||||
|
||||
for (i = 0 ; i < m->light_maps.nitems ; ) {
|
||||
lm = array_item(struct light_mapping, &m->light_maps, i);
|
||||
|
||||
if (memcmp(&lm->dest, ml, sizeof(*ml)) == 0) {
|
||||
array_remove(struct light_mapping, &m->light_maps, i);
|
||||
} else {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool mapper_impl_get_action_map(struct mapper *m, uint8_t action, uint8_t page,
|
||||
struct mapped_action *ma)
|
||||
{
|
||||
const struct action_mapping *am;
|
||||
size_t i;
|
||||
|
||||
for (i = 0 ; i < m->actions.nitems ; i++) {
|
||||
am = array_item(struct action_mapping, &m->actions, i);
|
||||
|
||||
if (am->action == action && am->page == page) {
|
||||
*ma = am->src;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool mapper_impl_get_analog_map(struct mapper *m, uint8_t analog,
|
||||
struct mapped_analog *ma)
|
||||
{
|
||||
memset(ma, 0, sizeof(*ma));
|
||||
|
||||
if (analog >= m->nanalogs) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*ma = m->analogs[analog].src;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int32_t mapper_impl_get_analog_sensitivity(struct mapper *m, uint8_t analog)
|
||||
{
|
||||
if (analog >= m->nanalogs) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return m->analogs[analog].sensitivity;
|
||||
}
|
||||
|
||||
uint8_t mapper_impl_get_nanalogs(struct mapper *m)
|
||||
{
|
||||
return m->nanalogs;
|
||||
}
|
||||
|
||||
uint8_t mapper_impl_get_nlights(struct mapper *m)
|
||||
{
|
||||
return m->nlights;
|
||||
}
|
||||
|
||||
uint8_t mapper_impl_get_npages(struct mapper *m)
|
||||
{
|
||||
const struct action_mapping *am;
|
||||
size_t i;
|
||||
int max_page;
|
||||
|
||||
max_page = -1;
|
||||
|
||||
for (i = 0 ; i < m->actions.nitems ; i++) {
|
||||
am = array_item(struct action_mapping, &m->actions, i);
|
||||
|
||||
if (max_page < am->page) {
|
||||
max_page = am->page;
|
||||
}
|
||||
}
|
||||
|
||||
return (uint8_t) (max_page + 1);
|
||||
}
|
||||
|
||||
action_iter_t mapper_impl_iterate_actions(struct mapper *m)
|
||||
{
|
||||
struct action_iter *iter;
|
||||
|
||||
iter = xmalloc(sizeof(*iter));
|
||||
iter->mapper = m;
|
||||
iter->i = 0;
|
||||
|
||||
return iter;
|
||||
}
|
||||
|
||||
light_iter_t mapper_impl_iterate_lights(struct mapper *m)
|
||||
{
|
||||
struct light_iter *iter;
|
||||
|
||||
iter = xmalloc(sizeof(*iter));
|
||||
iter->mapper = m;
|
||||
iter->i = 0;
|
||||
|
||||
return iter;
|
||||
}
|
||||
|
||||
bool mapper_impl_is_analog_absolute(struct mapper *m, uint8_t analog)
|
||||
{
|
||||
struct analog_mapping *am;
|
||||
|
||||
if (analog >= m->nanalogs) {
|
||||
return false;
|
||||
}
|
||||
|
||||
am = &m->analogs[analog];
|
||||
|
||||
if (!am->bound) {
|
||||
analog_mapping_bind(am);
|
||||
}
|
||||
|
||||
return m->analogs[analog].absolute;
|
||||
}
|
||||
|
||||
void mapper_impl_set_action_map(struct mapper *m, uint8_t action, uint8_t page,
|
||||
uint8_t bit, const struct mapped_action *ma)
|
||||
{
|
||||
struct action_mapping *am;
|
||||
size_t i;
|
||||
|
||||
for (i = 0 ; i < m->actions.nitems ; i++) {
|
||||
am = array_item(struct action_mapping, &m->actions, i);
|
||||
|
||||
if (am->action == action && am->page == page) {
|
||||
am->src = *ma;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
am = array_append(struct action_mapping, &m->actions);
|
||||
|
||||
am->src = *ma;
|
||||
am->action = action;
|
||||
am->page = page;
|
||||
am->bit = bit;
|
||||
}
|
||||
|
||||
bool mapper_impl_set_analog_map(struct mapper *m, uint8_t analog,
|
||||
const struct mapped_analog *ma)
|
||||
{
|
||||
struct analog_mapping *am;
|
||||
|
||||
if (analog >= m->nanalogs) {
|
||||
return false;
|
||||
}
|
||||
|
||||
am = &m->analogs[analog];
|
||||
|
||||
am->src = *ma;
|
||||
am->bound = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool mapper_impl_set_analog_sensitivity(struct mapper *m, uint8_t analog,
|
||||
int32_t sensitivity)
|
||||
{
|
||||
if (analog >= m->nanalogs) {
|
||||
return false;
|
||||
}
|
||||
|
||||
m->analogs[analog].sensitivity = sensitivity;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void mapper_impl_set_light_map(struct mapper *m, const struct mapped_light *ml,
|
||||
uint8_t game_light)
|
||||
{
|
||||
struct light_mapping *lm;
|
||||
size_t i;
|
||||
|
||||
for (i = 0 ; i < m->light_maps.nitems ; i++) {
|
||||
lm = array_item(struct light_mapping, &m->light_maps, i);
|
||||
|
||||
if (memcmp(&lm->dest, ml, sizeof(*ml)) == 0) {
|
||||
lm->game_light = game_light;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
lm = array_append(struct light_mapping, &m->light_maps);
|
||||
|
||||
memset(lm, 0, sizeof(*lm));
|
||||
|
||||
lm->dest = *ml;
|
||||
lm->game_light = game_light;
|
||||
}
|
||||
|
||||
void mapper_impl_set_nanalogs(struct mapper *m, uint8_t nanalogs)
|
||||
{
|
||||
free(m->analogs);
|
||||
m->analogs = xcalloc(sizeof(*m->analogs) * nanalogs);
|
||||
m->nanalogs = nanalogs;
|
||||
}
|
||||
|
||||
void mapper_impl_set_nlights(struct mapper *m, uint8_t nlights)
|
||||
{
|
||||
free(m->lights);
|
||||
m->lights = xcalloc(sizeof(*m->lights) * nlights);
|
||||
m->nlights = nlights;
|
||||
}
|
||||
|
||||
uint8_t mapper_impl_read_analog(struct mapper *m, uint8_t analog)
|
||||
{
|
||||
if (analog >= m->nanalogs) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return m->analogs[analog].pos;
|
||||
}
|
||||
|
||||
uint64_t mapper_impl_update(struct mapper *m)
|
||||
{
|
||||
struct action_mapping *am;
|
||||
struct light_mapping *lm;
|
||||
size_t i;
|
||||
uint64_t result;
|
||||
|
||||
hid_mgr_lock();
|
||||
|
||||
for (i = 0 ; i < m->light_maps.nitems ; i++) {
|
||||
lm = array_item(struct light_mapping, &m->light_maps, i);
|
||||
light_mapping_send(lm, m);
|
||||
}
|
||||
|
||||
for (i = 0 ; i < m->nanalogs ; i++) {
|
||||
analog_mapping_update(&m->analogs[i]);
|
||||
}
|
||||
|
||||
result = 0;
|
||||
|
||||
for (i = 0 ; i < m->actions.nitems ; i++) {
|
||||
am = array_item(struct action_mapping, &m->actions, i);
|
||||
result |= action_mapping_update(am);
|
||||
}
|
||||
|
||||
hid_mgr_unlock();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void mapper_impl_write_light(struct mapper *m, uint8_t light,
|
||||
uint8_t intensity)
|
||||
{
|
||||
if (light >= m->nlights) {
|
||||
return;
|
||||
}
|
||||
|
||||
m->lights[light] = intensity;
|
||||
}
|
||||
|
||||
void mapper_impl_destroy(struct mapper *m)
|
||||
{
|
||||
free(m->lights);
|
||||
array_fini(&m->light_maps);
|
||||
free(m->analogs);
|
||||
array_fini(&m->actions);
|
||||
free(m);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
#ifndef GENINPUT_MAPPER_H
|
||||
#define GENINPUT_MAPPER_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "geninput/hid-mgr.h"
|
||||
|
||||
#include "util/defs.h"
|
||||
|
||||
typedef struct action_iter *action_iter_t;
|
||||
typedef struct light_iter *light_iter_t;
|
||||
|
||||
struct mapper;
|
||||
|
||||
struct mapped_action {
|
||||
struct hid_stub *hid;
|
||||
uint32_t control_no;
|
||||
int32_t value_min;
|
||||
int32_t value_max;
|
||||
};
|
||||
|
||||
struct mapped_analog {
|
||||
struct hid_stub *hid;
|
||||
size_t control_no;
|
||||
};
|
||||
|
||||
struct mapped_light {
|
||||
struct hid_stub *hid;
|
||||
size_t light_no;
|
||||
};
|
||||
|
||||
extern struct mapper *mapper_inst;
|
||||
|
||||
void action_iter_get_mapping(action_iter_t iter, struct mapped_action *ma);
|
||||
uint8_t action_iter_get_action(action_iter_t iter);
|
||||
uint8_t action_iter_get_page(action_iter_t iter);
|
||||
uint8_t action_iter_get_bit(action_iter_t iter);
|
||||
bool action_iter_is_valid(action_iter_t iter);
|
||||
void action_iter_next(action_iter_t iter);
|
||||
void action_iter_free(action_iter_t iter);
|
||||
|
||||
void light_iter_get_mapping(light_iter_t iter, struct mapped_light *ml);
|
||||
uint8_t light_iter_get_game_light(light_iter_t iter);
|
||||
bool light_iter_is_valid(light_iter_t iter);
|
||||
void light_iter_next(light_iter_t iter);
|
||||
void light_iter_free(light_iter_t iter);
|
||||
|
||||
struct mapper *mapper_impl_create(void);
|
||||
void mapper_impl_clear_action_map(struct mapper *m, uint8_t action,
|
||||
uint8_t page);
|
||||
void mapper_impl_clear_light_map(struct mapper *m,
|
||||
const struct mapped_light *ml);
|
||||
bool mapper_impl_get_analog_map(struct mapper *m, uint8_t analog,
|
||||
struct mapped_analog *ma);
|
||||
bool mapper_impl_get_action_map(struct mapper *m, uint8_t action, uint8_t page,
|
||||
struct mapped_action *ma);
|
||||
int32_t mapper_impl_get_analog_sensitivity(struct mapper *m, uint8_t analog);
|
||||
uint8_t mapper_impl_get_nanalogs(struct mapper *m);
|
||||
uint8_t mapper_impl_get_nlights(struct mapper *m);
|
||||
uint8_t mapper_impl_get_npages(struct mapper *m);
|
||||
bool mapper_impl_is_analog_absolute(struct mapper *m, uint8_t analog);
|
||||
action_iter_t mapper_impl_iterate_actions(struct mapper *m);
|
||||
light_iter_t mapper_impl_iterate_lights(struct mapper *m);
|
||||
void mapper_impl_set_action_map(struct mapper *m, uint8_t action, uint8_t page,
|
||||
uint8_t bit, const struct mapped_action *ma);
|
||||
bool mapper_impl_set_analog_map(struct mapper *m, uint8_t analog,
|
||||
const struct mapped_analog *ma);
|
||||
bool mapper_impl_set_analog_sensitivity(struct mapper *m, uint8_t analog,
|
||||
int32_t sensitivity);
|
||||
void mapper_impl_set_light_map(struct mapper *m, const struct mapped_light *ml,
|
||||
uint8_t game_light);
|
||||
void mapper_impl_set_nanalogs(struct mapper *m, uint8_t nanalogs);
|
||||
void mapper_impl_set_nlights(struct mapper *m, uint8_t nlights);
|
||||
uint8_t mapper_impl_read_analog(struct mapper *m, uint8_t analog);
|
||||
uint64_t mapper_impl_update(struct mapper *m);
|
||||
void mapper_impl_write_light(struct mapper *m, uint8_t light,
|
||||
uint8_t intensity);
|
||||
void mapper_impl_destroy(struct mapper *m);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,257 @@
|
||||
#define LOG_MODULE "mouse"
|
||||
|
||||
#include <windows.h>
|
||||
#include <initguid.h>
|
||||
|
||||
#include <malloc.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <wchar.h>
|
||||
|
||||
#include "geninput/hid.h"
|
||||
#include "geninput/mouse.h"
|
||||
|
||||
#include "util/defs.h"
|
||||
#include "util/log.h"
|
||||
#include "util/mem.h"
|
||||
#include "util/str.h"
|
||||
|
||||
enum mouse_ctl_no {
|
||||
MOUSE_CTL_BTN1,
|
||||
MOUSE_CTL_BTN2,
|
||||
MOUSE_CTL_BTN3,
|
||||
MOUSE_CTL_BTN4,
|
||||
MOUSE_CTL_BTN5,
|
||||
MOUSE_CTL_X,
|
||||
MOUSE_CTL_Y,
|
||||
MOUSE_CTL_WHEEL,
|
||||
|
||||
MOUSE_CTL_COUNT
|
||||
};
|
||||
|
||||
struct mouse {
|
||||
struct hid_ri super;
|
||||
wchar_t *name;
|
||||
long last_x;
|
||||
long last_y;
|
||||
long values[MOUSE_CTL_COUNT];
|
||||
};
|
||||
|
||||
static const struct hid_control mouse_controls[MOUSE_CTL_COUNT] = {
|
||||
/* Left, right, middle, AUX1, AUX2 buttons */
|
||||
{ 0x00090001, 0x00000000, 0x00000001, 0 },
|
||||
{ 0x00090002, 0x00000000, 0x00000001, 0 },
|
||||
{ 0x00090003, 0x00000000, 0x00000001, 0 },
|
||||
{ 0x00090004, 0x00000000, 0x00000001, 0 },
|
||||
{ 0x00090005, 0x00000000, 0x00000001, 0 },
|
||||
|
||||
/* X axis */
|
||||
{ 0x00010030, 0xC0000000, 0x3FFFFFFF, HID_FLAG_RELATIVE },
|
||||
|
||||
/* Y axis */
|
||||
{ 0x00010031, 0xC0000000, 0x3FFFFFFF, HID_FLAG_RELATIVE },
|
||||
|
||||
/* Mouse wheel */
|
||||
{ 0x00010038, 0xFFFF8000, 0x00007FFF, HID_FLAG_RELATIVE },
|
||||
};
|
||||
|
||||
static const uint16_t mouse_down_flg[] = {
|
||||
RI_MOUSE_BUTTON_1_DOWN,
|
||||
RI_MOUSE_BUTTON_2_DOWN,
|
||||
RI_MOUSE_BUTTON_3_DOWN,
|
||||
RI_MOUSE_BUTTON_4_DOWN,
|
||||
RI_MOUSE_BUTTON_5_DOWN,
|
||||
};
|
||||
|
||||
static const uint16_t mouse_up_flg[] = {
|
||||
RI_MOUSE_BUTTON_1_UP,
|
||||
RI_MOUSE_BUTTON_2_UP,
|
||||
RI_MOUSE_BUTTON_3_UP,
|
||||
RI_MOUSE_BUTTON_4_UP,
|
||||
RI_MOUSE_BUTTON_5_UP,
|
||||
};
|
||||
|
||||
DEFINE_GUID(mouse_guid, 0x378DE44C, 0x56EF, 0x11D1, 0xBC, 0x8C, \
|
||||
0x00, 0xA0, 0xC9, 0x14, 0x05, 0xDD);
|
||||
|
||||
static bool mouse_get_device_usage(const struct hid *hid, uint32_t *usage);
|
||||
static bool mouse_get_name(const struct hid *hid, wchar_t *chars,
|
||||
size_t *nchars);
|
||||
static bool mouse_get_controls(const struct hid *hid,
|
||||
struct hid_control *controls, size_t *ncontrols);
|
||||
static bool mouse_get_lights(const struct hid *hid, struct hid_light *lights,
|
||||
size_t *nlights);
|
||||
static bool mouse_get_value(struct hid *hid, size_t control_no,
|
||||
int32_t *out_value);
|
||||
static bool mouse_set_light(struct hid *hid, size_t light_no,
|
||||
uint32_t intensity);
|
||||
static void mouse_handle_event(struct hid_ri *hid_ri, const RAWINPUT *ri);
|
||||
static void mouse_close(struct hid *hid);
|
||||
|
||||
static const struct hid_ri_vtbl mouse_vtbl = {{
|
||||
/* .close = */ mouse_close,
|
||||
/* .get_device_usage = */ mouse_get_device_usage,
|
||||
/* .get_name = */ mouse_get_name,
|
||||
/* .get_controls = */ mouse_get_controls,
|
||||
/* .get_lights = */ mouse_get_lights,
|
||||
/* .get_value = */ mouse_get_value,
|
||||
/* .set_light = */ mouse_set_light },
|
||||
/* .handle_event = */ mouse_handle_event
|
||||
};
|
||||
|
||||
void mouse_create(struct hid_ri **hid_ri, const char *dev_node)
|
||||
{
|
||||
struct mouse *m;
|
||||
char *tmp;
|
||||
|
||||
m = xcalloc(sizeof(*m));
|
||||
m->super.vptr = &mouse_vtbl;
|
||||
m->name = hid_ri_init_name(&mouse_guid, dev_node);
|
||||
|
||||
*hid_ri = &m->super;
|
||||
|
||||
if (!wstr_narrow(m->name, &tmp)) {
|
||||
tmp = str_dup("???");
|
||||
}
|
||||
|
||||
log_misc("Opened mouse on dev node %s", dev_node);
|
||||
log_misc("... Product: %s", tmp);
|
||||
|
||||
free(tmp);
|
||||
}
|
||||
|
||||
static bool mouse_get_device_usage(const struct hid *hid, uint32_t *usage)
|
||||
{
|
||||
*usage = MOUSE_DEVICE_USAGE;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool mouse_get_name(const struct hid *hid, wchar_t *chars,
|
||||
size_t *nchars)
|
||||
{
|
||||
struct mouse *m = containerof(hid, struct mouse, super);
|
||||
|
||||
return hid_ri_get_name(chars, nchars, m->name);
|
||||
}
|
||||
|
||||
static bool mouse_get_controls(const struct hid *hid,
|
||||
struct hid_control *controls, size_t *ncontrols)
|
||||
{
|
||||
return hid_ri_get_controls(controls, ncontrols, mouse_controls,
|
||||
MOUSE_CTL_COUNT);
|
||||
}
|
||||
|
||||
static bool mouse_get_lights(const struct hid *hid,
|
||||
struct hid_light *lights, size_t *nlights)
|
||||
{
|
||||
log_assert(nlights != NULL);
|
||||
|
||||
if (lights == NULL) {
|
||||
*nlights = 0;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool mouse_get_value(struct hid *hid, size_t control_no,
|
||||
int32_t *out_value)
|
||||
{
|
||||
struct mouse *m = containerof(hid, struct mouse, super);
|
||||
long tmp;
|
||||
|
||||
log_assert(control_no < MOUSE_CTL_COUNT);
|
||||
|
||||
switch (control_no) {
|
||||
case MOUSE_CTL_BTN1:
|
||||
case MOUSE_CTL_BTN2:
|
||||
case MOUSE_CTL_BTN3:
|
||||
case MOUSE_CTL_BTN4:
|
||||
case MOUSE_CTL_BTN5:
|
||||
*out_value = m->values[control_no];
|
||||
|
||||
return true;
|
||||
|
||||
case MOUSE_CTL_X:
|
||||
case MOUSE_CTL_Y:
|
||||
case MOUSE_CTL_WHEEL:
|
||||
do {
|
||||
tmp = m->values[control_no];
|
||||
} while (InterlockedCompareExchange(
|
||||
&m->values[control_no], 0, tmp) != tmp);
|
||||
|
||||
*out_value = tmp;
|
||||
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool mouse_set_light(struct hid *hid, size_t light_no,
|
||||
uint32_t intensity)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static void mouse_handle_event(struct hid_ri *hid_ri, const RAWINPUT *ri)
|
||||
{
|
||||
struct mouse *m = containerof(hid_ri, struct mouse, super);
|
||||
long tmp;
|
||||
long dwheel;
|
||||
long dx;
|
||||
long dy;
|
||||
int i;
|
||||
|
||||
if (ri->data.mouse.usFlags & MOUSE_MOVE_ABSOLUTE) {
|
||||
dx = ri->data.mouse.lLastX - m->last_x;
|
||||
dy = ri->data.mouse.lLastY - m->last_y;
|
||||
|
||||
m->last_x = ri->data.mouse.lLastX;
|
||||
m->last_y = ri->data.mouse.lLastY;
|
||||
} else {
|
||||
dx = ri->data.mouse.lLastX;
|
||||
dy = ri->data.mouse.lLastY;
|
||||
|
||||
m->last_x += dx;
|
||||
m->last_y += dy;
|
||||
}
|
||||
|
||||
do {
|
||||
tmp = m->values[MOUSE_CTL_X];
|
||||
} while (InterlockedCompareExchange(
|
||||
&m->values[MOUSE_CTL_X], tmp + dx, tmp) != tmp);
|
||||
|
||||
do {
|
||||
tmp = m->values[MOUSE_CTL_Y];
|
||||
} while (InterlockedCompareExchange(
|
||||
&m->values[MOUSE_CTL_Y], tmp + dy, tmp) != tmp);
|
||||
|
||||
if (ri->data.mouse.usButtonFlags & RI_MOUSE_WHEEL) {
|
||||
dwheel = ri->data.mouse.usButtonData;
|
||||
|
||||
do {
|
||||
tmp = m->values[MOUSE_CTL_WHEEL];
|
||||
} while (InterlockedCompareExchange(
|
||||
&m->values[MOUSE_CTL_WHEEL], tmp + dwheel, tmp) != tmp);
|
||||
}
|
||||
|
||||
for (i = 0 ; i < 5 ; i++) {
|
||||
if (ri->data.mouse.usButtonFlags & mouse_down_flg[i]) {
|
||||
m->values[i] = 1;
|
||||
} else if (ri->data.mouse.usButtonFlags & mouse_up_flg[i]) {
|
||||
m->values[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void mouse_close(struct hid *hid)
|
||||
{
|
||||
struct mouse *m = containerof(hid, struct mouse, super);
|
||||
|
||||
free(m->name);
|
||||
free(m);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
#ifndef GENINPUT_MOUSE_H
|
||||
#define GENINPUT_MOUSE_H
|
||||
|
||||
#include "geninput/hid.h"
|
||||
|
||||
#define MOUSE_DEVICE_USAGE 0x00010002
|
||||
|
||||
void mouse_create(struct hid_ri **hid_ri, const char *dev_node);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,51 @@
|
||||
#include <windows.h>
|
||||
#include <dbt.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "geninput/hotplug.h"
|
||||
#include "geninput/ri.h"
|
||||
|
||||
#include "util/log.h"
|
||||
#include "util/msg-thread.h"
|
||||
|
||||
void msg_window_setup(HWND hwnd)
|
||||
{
|
||||
ri_init(hwnd);
|
||||
hotplug_init(hwnd);
|
||||
|
||||
log_info("Message pump thread ready, thread id = %d",
|
||||
(unsigned int) GetCurrentThreadId());
|
||||
|
||||
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
|
||||
}
|
||||
|
||||
LRESULT WINAPI msg_window_proc(HWND hwnd, UINT msg, WPARAM wparam,
|
||||
LPARAM lparam)
|
||||
{
|
||||
switch (msg) {
|
||||
case WM_INPUT:
|
||||
ri_handle_msg((HRAWINPUT) lparam);
|
||||
|
||||
return 0;
|
||||
|
||||
case WM_DEVICECHANGE:
|
||||
hotplug_handle_msg(wparam, (DEV_BROADCAST_HDR *) lparam);
|
||||
|
||||
return TRUE;
|
||||
|
||||
default:
|
||||
return DefWindowProc(hwnd, msg, wparam, lparam);
|
||||
}
|
||||
}
|
||||
|
||||
void msg_window_teardown(HWND hwnd)
|
||||
{
|
||||
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL);
|
||||
|
||||
log_info("Message pump thread shutting down");
|
||||
|
||||
hotplug_fini();
|
||||
ri_fini();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,235 @@
|
||||
#define LOG_MODULE "pacdrive"
|
||||
|
||||
#include <windows.h>
|
||||
#include <hidsdi.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "geninput/hid.h"
|
||||
#include "geninput/io-thread.h"
|
||||
#include "geninput/pacdrive.h"
|
||||
|
||||
#include "util/defs.h"
|
||||
#include "util/log.h"
|
||||
#include "util/mem.h"
|
||||
|
||||
/* The PacDrive appears to have a malformed descriptor for its OUT report.
|
||||
Since PacDrives are fairly prevalent, we have to have a special-case HID
|
||||
driver for them that uses a hard-coded report structure. */
|
||||
|
||||
struct pac_report {
|
||||
uint8_t report_id;
|
||||
uint8_t static_00;
|
||||
uint8_t static_dd;
|
||||
uint8_t leds_hi;
|
||||
uint8_t leds_lo;
|
||||
};
|
||||
|
||||
struct pac {
|
||||
struct hid_fd super;
|
||||
HANDLE fd;
|
||||
OVERLAPPED ovl;
|
||||
struct pac_report io_buf;
|
||||
uint16_t leds;
|
||||
};
|
||||
|
||||
static struct hid_light pac_lights[16];
|
||||
static const wchar_t pac_name[] = L"PacDrive Shim";
|
||||
|
||||
static bool pac_get_device_usage(const struct hid *hid, uint32_t *usage);
|
||||
static bool pac_get_name(const struct hid *hid, wchar_t *chars,
|
||||
size_t *nchars);
|
||||
static bool pac_get_controls(const struct hid *hid,
|
||||
struct hid_control *controls, size_t *ncontrols);
|
||||
static bool pac_get_lights(const struct hid *hid, struct hid_light *lights,
|
||||
size_t *nlights);
|
||||
static bool pac_get_value(struct hid *hid, size_t control_no, int32_t *value);
|
||||
static bool pac_set_light(struct hid *hid, size_t light_no,
|
||||
uint32_t intensity);
|
||||
static bool pac_handle_event(struct hid_fd *hid_fd, OVERLAPPED *ovl,
|
||||
size_t nbytes);
|
||||
static void pac_close(struct hid *hid);
|
||||
|
||||
static const struct hid_fd_vtbl pac_vtbl = {{
|
||||
/* .close = */ pac_close,
|
||||
/* .get_device_usage = */ pac_get_device_usage,
|
||||
/* .get_name = */ pac_get_name,
|
||||
/* .get_controls = */ pac_get_controls,
|
||||
/* .get_lights = */ pac_get_lights,
|
||||
/* .get_value = */ pac_get_value,
|
||||
/* .set_light = */ pac_set_light },
|
||||
/* .handle_event = */ pac_handle_event
|
||||
};
|
||||
|
||||
bool pac_open(struct hid_fd **hid_out, const char *dev_node, HANDLE iocp,
|
||||
uintptr_t iocp_ctx)
|
||||
{
|
||||
HIDD_ATTRIBUTES attrs;
|
||||
HANDLE fd;
|
||||
struct pac *pac;
|
||||
int i;
|
||||
|
||||
fd = CreateFile(dev_node, GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
|
||||
FILE_FLAG_OVERLAPPED, NULL);
|
||||
|
||||
if (fd == INVALID_HANDLE_VALUE) {
|
||||
goto open_fail;
|
||||
}
|
||||
|
||||
if (!HidD_GetAttributes(fd, &attrs)) {
|
||||
goto attrs_fail;
|
||||
}
|
||||
|
||||
if (attrs.VendorID != 0xD209 || (attrs.ProductID & 0xFFF8) != 0x1500) {
|
||||
goto id_fail;
|
||||
}
|
||||
|
||||
pac = xcalloc(sizeof(*pac));
|
||||
|
||||
pac->super.vptr = &pac_vtbl;
|
||||
pac->fd = fd;
|
||||
|
||||
CreateIoCompletionPort(fd, iocp, iocp_ctx, 0);
|
||||
|
||||
if (!WriteFile(fd, &pac->io_buf, sizeof(pac->io_buf), NULL, &pac->ovl)
|
||||
&& GetLastError() != ERROR_IO_PENDING) {
|
||||
log_warning("%s: Initial WriteFile failed: %08x",
|
||||
dev_node, (unsigned int) GetLastError());
|
||||
|
||||
goto write_fail;
|
||||
}
|
||||
|
||||
for (i = 0 ; i < lengthof(pac_lights) ; i++) {
|
||||
pac_lights[i].usage = 0x0008004B; /* LED, Generic Indicator */
|
||||
pac_lights[i].value_min = 0;
|
||||
pac_lights[i].value_max = 1;
|
||||
}
|
||||
|
||||
log_misc("Opened PacDrive on %s", dev_node);
|
||||
|
||||
*hid_out = &pac->super;
|
||||
|
||||
return true;
|
||||
|
||||
write_fail:
|
||||
free(pac);
|
||||
|
||||
id_fail:
|
||||
attrs_fail:
|
||||
CloseHandle(fd);
|
||||
|
||||
open_fail:
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool pac_get_device_usage(const struct hid *hid, uint32_t *usage)
|
||||
{
|
||||
*usage = 0x00010000;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool pac_get_name(const struct hid *hid, wchar_t *chars,
|
||||
size_t *nchars)
|
||||
{
|
||||
log_assert(nchars != NULL);
|
||||
|
||||
if (chars != NULL) {
|
||||
if (*nchars < lengthof(pac_name)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(chars, pac_name, sizeof(pac_name));
|
||||
}
|
||||
|
||||
*nchars = lengthof(pac_name);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool pac_get_controls(const struct hid *hid,
|
||||
struct hid_control *controls, size_t *ncontrols)
|
||||
{
|
||||
log_assert(ncontrols != NULL);
|
||||
|
||||
*ncontrols = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool pac_get_lights(const struct hid *hid, struct hid_light *lights,
|
||||
size_t *nlights)
|
||||
{
|
||||
log_assert(nlights != NULL);
|
||||
|
||||
if (lights != NULL) {
|
||||
if (*nlights < lengthof(pac_lights)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(lights, pac_lights, sizeof(pac_lights));
|
||||
}
|
||||
|
||||
*nlights = lengthof(pac_lights);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool pac_get_value(struct hid *hid, size_t control_no, int32_t *value)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool pac_set_light(struct hid *hid, size_t light_no,
|
||||
uint32_t intensity)
|
||||
{
|
||||
struct pac *pac = containerof(hid, struct pac, super);
|
||||
|
||||
if (light_no >= lengthof(pac_lights)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (intensity != 0) {
|
||||
pac->leds |= (1 << light_no);
|
||||
} else {
|
||||
pac->leds &= ~(1 << light_no);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool pac_handle_event(struct hid_fd *hid_fd, OVERLAPPED *ovl,
|
||||
size_t nbytes)
|
||||
{
|
||||
struct pac *pac = containerof(hid_fd, struct pac, super);
|
||||
|
||||
/* OUT report successfully sent, send another */
|
||||
|
||||
pac->io_buf.leds_hi = HIBYTE(pac->leds);
|
||||
pac->io_buf.leds_lo = LOBYTE(pac->leds);
|
||||
|
||||
memset(&pac->ovl, 0, sizeof(pac->ovl));
|
||||
|
||||
if (!WriteFile(pac->fd, &pac->io_buf, sizeof(pac->io_buf), NULL,
|
||||
&pac->ovl) && GetLastError() != ERROR_IO_PENDING) {
|
||||
log_warning("WriteFile failed: %08x", (unsigned int) GetLastError());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void pac_close(struct hid *hid)
|
||||
{
|
||||
struct pac *pac = containerof(hid, struct pac, super);
|
||||
|
||||
CloseHandle(pac->fd);
|
||||
|
||||
free(pac);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
#ifndef GENINPUT_PACDRIVE_H
|
||||
#define GENINPUT_PACDRIVE_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "geninput/io-thread.h"
|
||||
|
||||
bool pac_open(struct hid_fd **hid_out, const char *dev_node, HANDLE iocp,
|
||||
uintptr_t iocp_ctx);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,187 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "geninput/hid.h"
|
||||
#include "geninput/hid-mgr.h"
|
||||
#include "geninput/kbd.h"
|
||||
#include "geninput/mouse.h"
|
||||
#include "geninput/ri.h"
|
||||
|
||||
#include "util/defs.h"
|
||||
#include "util/log.h"
|
||||
#include "util/mem.h"
|
||||
|
||||
struct ri_handle {
|
||||
HANDLE fake_fd;
|
||||
struct hid_ri *hid_ri;
|
||||
};
|
||||
|
||||
static struct ri_handle *ri_handles;
|
||||
static unsigned int ri_ndevs;
|
||||
|
||||
void ri_init(HWND hwnd)
|
||||
{
|
||||
RAWINPUTDEVICE filter[3];
|
||||
|
||||
memset(filter, 0, sizeof(filter));
|
||||
|
||||
filter[0].usUsagePage = KBD_DEVICE_USAGE_KEYBOARD >> 16;
|
||||
filter[0].usUsage = (uint16_t) KBD_DEVICE_USAGE_KEYBOARD;
|
||||
filter[0].dwFlags = RIDEV_INPUTSINK;
|
||||
filter[0].hwndTarget = hwnd;
|
||||
|
||||
filter[1].usUsagePage = KBD_DEVICE_USAGE_KEYPAD >> 16;
|
||||
filter[1].usUsage = (uint16_t) KBD_DEVICE_USAGE_KEYPAD;
|
||||
filter[1].dwFlags = RIDEV_INPUTSINK;
|
||||
filter[1].hwndTarget = hwnd;
|
||||
|
||||
filter[2].usUsagePage = MOUSE_DEVICE_USAGE >> 16;
|
||||
filter[2].usUsage = (uint16_t) MOUSE_DEVICE_USAGE;
|
||||
filter[2].dwFlags = RIDEV_INPUTSINK;
|
||||
filter[2].hwndTarget = hwnd;
|
||||
|
||||
if (!RegisterRawInputDevices(filter, lengthof(filter), sizeof(filter[0]))) {
|
||||
log_fatal("RegisterRawInputDevices failed: %x",
|
||||
(unsigned int) GetLastError());
|
||||
}
|
||||
|
||||
ri_scan_devices();
|
||||
}
|
||||
|
||||
void ri_scan_devices(void)
|
||||
{
|
||||
char *dev_node;
|
||||
struct hid_stub *stub;
|
||||
RAWINPUTDEVICELIST *ridl;
|
||||
unsigned int ridl_size;
|
||||
unsigned int size;
|
||||
unsigned int i;
|
||||
|
||||
hid_mgr_lock();
|
||||
|
||||
GetRawInputDeviceList(NULL, &ri_ndevs, sizeof(*ridl));
|
||||
|
||||
/* Param 2 of GetRawInputDeviceList changes meaning depending on whether or
|
||||
not param 1 is NULL */
|
||||
|
||||
ridl_size = sizeof(*ridl) * ri_ndevs;
|
||||
ridl = xmalloc(ridl_size);
|
||||
|
||||
if (GetRawInputDeviceList(ridl, &ridl_size, sizeof(*ridl)) == -1) {
|
||||
log_fatal("GetRawInputDeviceList data failed");
|
||||
}
|
||||
|
||||
ri_handles = xrealloc(ri_handles, sizeof(*ri_handles) * ri_ndevs);
|
||||
|
||||
for (i = 0 ; i < ri_ndevs ; i++) {
|
||||
ri_handles[i].fake_fd = NULL;
|
||||
ri_handles[i].hid_ri = NULL;
|
||||
|
||||
size = 0;
|
||||
GetRawInputDeviceInfo(ridl[i].hDevice, RIDI_DEVICENAME, NULL, &size);
|
||||
|
||||
dev_node = xmalloc(size);
|
||||
|
||||
if (GetRawInputDeviceInfo(ridl[i].hDevice, RIDI_DEVICENAME, dev_node,
|
||||
&size) == -1) {
|
||||
log_warning("GetRawInputDeviceInfo(RIDI_DEVICENAME) data failed");
|
||||
|
||||
goto skip;
|
||||
}
|
||||
|
||||
if (size > 1 && dev_node[0] == '\\' && dev_node[1] == '?') {
|
||||
/* WinXP (and only WinXP, not Vista or up) return device nodes
|
||||
starting with \??\ here instead of \\?\ like every other part
|
||||
of Windows does. I'm assuming this is a bug. */
|
||||
dev_node[1] = '\\';
|
||||
}
|
||||
|
||||
stub = hid_mgr_get_named_stub(dev_node);
|
||||
|
||||
/* NOTE: hid_mgr owns the ridev, not us. Raw Input devices do not
|
||||
get destroyed until geninput shuts down, they merely cease to
|
||||
update if the corresponding physical device is unplugged. */
|
||||
|
||||
if (ridl[i].dwType == RIM_TYPEKEYBOARD) {
|
||||
ri_handles[i].fake_fd = ridl[i].hDevice;
|
||||
kbd_create(&ri_handles[i].hid_ri, dev_node);
|
||||
|
||||
hid_stub_attach(stub, (struct hid *) ri_handles[i].hid_ri);
|
||||
} else if (ridl[i].dwType == RIM_TYPEMOUSE) {
|
||||
ri_handles[i].fake_fd = ridl[i].hDevice;
|
||||
mouse_create(&ri_handles[i].hid_ri, dev_node);
|
||||
|
||||
hid_stub_attach(stub, (struct hid *) ri_handles[i].hid_ri);
|
||||
}
|
||||
|
||||
skip:
|
||||
free(dev_node);
|
||||
}
|
||||
|
||||
free(ridl);
|
||||
|
||||
hid_mgr_unlock();
|
||||
}
|
||||
|
||||
void ri_handle_msg(HRAWINPUT msg)
|
||||
{
|
||||
unsigned int i;
|
||||
unsigned int nbytes;
|
||||
RAWINPUT ri;
|
||||
|
||||
nbytes = sizeof(ri);
|
||||
|
||||
if (GetRawInputData(msg, RID_INPUT, &ri, &nbytes, sizeof(ri.header))
|
||||
== (UINT) -1) {
|
||||
log_warning("GetRawInputData failed: %x",
|
||||
(unsigned int) GetLastError());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (ri.header.hDevice == NULL) {
|
||||
/* WTF?? I've seen this happen while remote-controlling someone's
|
||||
desktop using TeamViewer, possibly due to an API hook injected by TV
|
||||
doing something strange. However, if it's actually me misusing the
|
||||
API somehow then I'd like to trap this case so that it doesn't blow
|
||||
up my RawInput dispatcher. */
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0 ; i < ri_ndevs ; i++) {
|
||||
if (ri_handles[i].fake_fd == ri.header.hDevice) {
|
||||
hid_ri_handle_event(ri_handles[i].hid_ri, &ri);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ri_fini(void)
|
||||
{
|
||||
RAWINPUTDEVICE filter[3];
|
||||
|
||||
free(ri_handles);
|
||||
ri_handles = NULL;
|
||||
ri_ndevs = 0;
|
||||
|
||||
filter[0].usUsagePage = KBD_DEVICE_USAGE_KEYBOARD >> 16;
|
||||
filter[0].usUsage = (uint16_t) KBD_DEVICE_USAGE_KEYBOARD;
|
||||
filter[0].dwFlags = RIDEV_REMOVE;
|
||||
filter[0].hwndTarget = NULL;
|
||||
|
||||
filter[1].usUsagePage = KBD_DEVICE_USAGE_KEYPAD >> 16;
|
||||
filter[1].usUsage = (uint16_t) KBD_DEVICE_USAGE_KEYPAD;
|
||||
filter[1].dwFlags = RIDEV_REMOVE;
|
||||
filter[1].hwndTarget = NULL;
|
||||
|
||||
filter[2].usUsagePage = MOUSE_DEVICE_USAGE >> 16;
|
||||
filter[2].usUsage = (uint16_t) MOUSE_DEVICE_USAGE;
|
||||
filter[2].dwFlags = RIDEV_REMOVE;
|
||||
filter[2].hwndTarget = NULL;
|
||||
|
||||
if (!RegisterRawInputDevices(filter, lengthof(filter), sizeof(filter[0]))) {
|
||||
log_warning("RegisterRawInputDevices(RIDEV_REMOVE) failed: %x",
|
||||
(unsigned int) GetLastError());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
#ifndef GENINPUT_RI_H
|
||||
#define GENINPUT_RI_H
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
void ri_init(HWND hwnd);
|
||||
void ri_scan_devices(void);
|
||||
void ri_handle_msg(HRAWINPUT msg);
|
||||
void ri_fini(void);
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user