531 lines
12 KiB
C
531 lines
12 KiB
C
#include <windows.h>
|
|
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "config/bind-adv.h"
|
|
#include "config/resource.h"
|
|
#include "config/usages.h"
|
|
|
|
#include "geninput/hid-mgr.h"
|
|
|
|
#include "util/array.h"
|
|
#include "util/defs.h"
|
|
#include "util/mem.h"
|
|
#include "util/str.h"
|
|
|
|
struct bind_adv_state {
|
|
uintptr_t timer_id;
|
|
struct mapped_action ma;
|
|
struct array devs;
|
|
struct hid_stub *cur_hid;
|
|
struct hid_control *ctls;
|
|
size_t nctls;
|
|
bool was_valid;
|
|
};
|
|
|
|
static INT_PTR CALLBACK
|
|
bind_adv_dlg_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
|
|
static INT_PTR bind_adv_handle_init(HWND hwnd, struct bind_adv_state *state);
|
|
static void bind_adv_handle_init_devs(HWND hwnd);
|
|
static void bind_adv_handle_init_insert_dev(HWND hwnd, struct hid_stub *hid);
|
|
static INT_PTR bind_adv_handle_change_ctl(HWND hwnd);
|
|
static INT_PTR bind_adv_handle_change_dev(HWND hwnd);
|
|
static INT_PTR bind_adv_handle_change_range(HWND hwnd);
|
|
static INT_PTR bind_adv_handle_tick(HWND hwnd);
|
|
static INT_PTR bind_adv_handle_ok(HWND hwnd);
|
|
static INT_PTR bind_adv_handle_fini(HWND hwnd);
|
|
static bool bind_adv_get_dev_no(HWND hwnd, size_t *dev_no_out);
|
|
static bool bind_adv_get_ctl_no(HWND hwnd, size_t *ctl_no_out);
|
|
static bool bind_adv_get_range(HWND hwnd, int32_t *out_min, int32_t *out_max);
|
|
static bool bind_adv_get_int(HWND control, int32_t *out);
|
|
static bool bind_adv_is_valid(HWND hwnd);
|
|
static void bind_adv_validate(HWND hwnd);
|
|
static void bind_adv_select_dev(HWND hwnd, size_t dev_no);
|
|
static void bind_adv_select_ctl(HWND hwnd, size_t ctl_no);
|
|
static void bind_adv_set_range(HWND hwnd, int32_t range_min, int32_t range_max);
|
|
|
|
bool bind_adv(
|
|
HINSTANCE inst, HWND hwnd, struct mapped_action *ma, bool was_valid)
|
|
{
|
|
struct bind_adv_state state;
|
|
INT_PTR ok;
|
|
|
|
memset(&state, 0, sizeof(state));
|
|
|
|
state.ma = *ma;
|
|
state.was_valid = was_valid;
|
|
|
|
ok = DialogBoxParam(
|
|
inst,
|
|
MAKEINTRESOURCE(IDD_BIND_ADV),
|
|
hwnd,
|
|
bind_adv_dlg_proc,
|
|
(LPARAM) &state) != 0;
|
|
|
|
if (ok) {
|
|
*ma = state.ma;
|
|
}
|
|
|
|
return ok != 0;
|
|
}
|
|
|
|
static INT_PTR CALLBACK
|
|
bind_adv_dlg_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
|
|
{
|
|
switch (msg) {
|
|
case WM_INITDIALOG:
|
|
return bind_adv_handle_init(hwnd, (struct bind_adv_state *) lparam);
|
|
|
|
case WM_COMMAND:
|
|
switch (LOWORD(wparam)) {
|
|
case IDC_CONTROL:
|
|
switch (HIWORD(wparam)) {
|
|
case CBN_SELCHANGE:
|
|
return bind_adv_handle_change_ctl(hwnd);
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
case IDC_DEVICE:
|
|
switch (HIWORD(wparam)) {
|
|
case CBN_SELCHANGE:
|
|
return bind_adv_handle_change_dev(hwnd);
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
case IDC_BINDING_MIN:
|
|
case IDC_BINDING_MAX:
|
|
switch (HIWORD(wparam)) {
|
|
case EN_CHANGE:
|
|
return bind_adv_handle_change_range(hwnd);
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
case IDOK:
|
|
switch (HIWORD(wparam)) {
|
|
case BN_CLICKED:
|
|
return bind_adv_handle_ok(hwnd);
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
case IDCANCEL:
|
|
switch (HIWORD(wparam)) {
|
|
case BN_CLICKED:
|
|
EndDialog(hwnd, FALSE);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
case WM_TIMER:
|
|
return bind_adv_handle_tick(hwnd);
|
|
|
|
case WM_DESTROY:
|
|
return bind_adv_handle_fini(hwnd);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static INT_PTR bind_adv_handle_init(HWND hwnd, struct bind_adv_state *state)
|
|
{
|
|
struct hid_stub *hid;
|
|
size_t i;
|
|
|
|
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LPARAM) state);
|
|
|
|
bind_adv_handle_init_devs(hwnd);
|
|
|
|
state->timer_id = SetTimer(hwnd, 1, 17, NULL);
|
|
|
|
if (!state->was_valid) {
|
|
return TRUE;
|
|
}
|
|
|
|
for (i = 0; i < state->devs.nitems; i++) {
|
|
hid = *array_item(struct hid_stub *, &state->devs, i);
|
|
|
|
if (hid == state->ma.hid) {
|
|
bind_adv_select_dev(hwnd, (int) i);
|
|
bind_adv_select_ctl(hwnd, state->ma.control_no);
|
|
bind_adv_set_range(hwnd, state->ma.value_min, state->ma.value_max);
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void bind_adv_handle_init_devs(HWND hwnd)
|
|
{
|
|
struct hid_stub *hid;
|
|
|
|
hid_mgr_lock();
|
|
|
|
for (hid = hid_mgr_get_first_stub(); hid != NULL;
|
|
hid = hid_mgr_get_next_stub(hid)) {
|
|
bind_adv_handle_init_insert_dev(hwnd, hid);
|
|
}
|
|
|
|
hid_mgr_unlock();
|
|
}
|
|
|
|
static void bind_adv_handle_init_insert_dev(HWND hwnd, struct hid_stub *hid)
|
|
{
|
|
struct bind_adv_state *state;
|
|
wchar_t *name;
|
|
size_t nchars;
|
|
HWND devs;
|
|
|
|
state = (struct bind_adv_state *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
|
devs = GetDlgItem(hwnd, IDC_DEVICE);
|
|
|
|
if (!hid_stub_get_name(hid, NULL, &nchars)) {
|
|
goto size_fail;
|
|
}
|
|
|
|
name = xmalloc(sizeof(*name) * nchars);
|
|
|
|
if (!hid_stub_get_name(hid, name, &nchars)) {
|
|
goto name_fail;
|
|
}
|
|
|
|
*array_append(struct hid_stub *, &state->devs) = hid;
|
|
|
|
SendMessage(devs, CB_ADDSTRING, 0, (LPARAM) name);
|
|
|
|
name_fail:
|
|
free(name);
|
|
|
|
size_fail:;
|
|
}
|
|
|
|
static INT_PTR bind_adv_handle_change_ctl(HWND hwnd)
|
|
{
|
|
size_t ctl_no;
|
|
|
|
if (bind_adv_get_ctl_no(hwnd, &ctl_no)) {
|
|
bind_adv_select_ctl(hwnd, ctl_no);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static INT_PTR bind_adv_handle_change_dev(HWND hwnd)
|
|
{
|
|
size_t dev_no;
|
|
|
|
if (bind_adv_get_dev_no(hwnd, &dev_no)) {
|
|
bind_adv_select_dev(hwnd, dev_no);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static INT_PTR bind_adv_handle_change_range(HWND hwnd)
|
|
{
|
|
bind_adv_validate(hwnd);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static INT_PTR bind_adv_handle_tick(HWND hwnd)
|
|
{
|
|
struct bind_adv_state *state;
|
|
wchar_t wchars[16];
|
|
size_t ctl_no;
|
|
int32_t value;
|
|
HWND label;
|
|
|
|
state = (struct bind_adv_state *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
|
|
|
if (!bind_adv_get_ctl_no(hwnd, &ctl_no)) {
|
|
return TRUE;
|
|
}
|
|
|
|
hid_mgr_lock();
|
|
|
|
if (hid_stub_get_value(state->cur_hid, ctl_no, &value)) {
|
|
wstr_format(wchars, lengthof(wchars), L"%d", value);
|
|
|
|
label = GetDlgItem(hwnd, IDC_CURRENT);
|
|
SetWindowText(label, wchars);
|
|
}
|
|
|
|
hid_mgr_unlock();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static INT_PTR bind_adv_handle_ok(HWND hwnd)
|
|
{
|
|
struct bind_adv_state *state;
|
|
size_t dev_no;
|
|
size_t ctl_no;
|
|
|
|
state = (struct bind_adv_state *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
|
|
|
/* bind_adv_validate() ought to ensure that OK is disabled whenever the
|
|
user's input is somehow invalid. Still, it never hurts to double check.
|
|
|
|
Ignore any OK clicks that we somehow receive with invalid input. */
|
|
|
|
if (!bind_adv_get_dev_no(hwnd, &dev_no)) {
|
|
return TRUE;
|
|
}
|
|
|
|
state->ma.hid = *array_item(struct hid_stub *, &state->devs, dev_no);
|
|
|
|
if (!bind_adv_get_ctl_no(hwnd, &ctl_no)) {
|
|
return TRUE;
|
|
}
|
|
|
|
state->ma.control_no = ctl_no;
|
|
|
|
if (!bind_adv_get_range(hwnd, &state->ma.value_min, &state->ma.value_max)) {
|
|
return TRUE;
|
|
}
|
|
|
|
/* Input OK, shut down this dialog. */
|
|
|
|
EndDialog(hwnd, TRUE);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static INT_PTR bind_adv_handle_fini(HWND hwnd)
|
|
{
|
|
struct bind_adv_state *state;
|
|
|
|
state = (struct bind_adv_state *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
|
|
|
array_fini(&state->devs);
|
|
|
|
KillTimer(hwnd, state->timer_id);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static bool bind_adv_get_dev_no(HWND hwnd, size_t *dev_no_out)
|
|
{
|
|
const struct bind_adv_state *state;
|
|
size_t dev_no;
|
|
HWND devs;
|
|
|
|
state = (struct bind_adv_state *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
|
devs = GetDlgItem(hwnd, IDC_DEVICE);
|
|
|
|
dev_no = SendMessage(devs, CB_GETCURSEL, 0, 0);
|
|
|
|
if (dev_no >= state->devs.nitems) {
|
|
return false;
|
|
}
|
|
|
|
if (dev_no_out != NULL) {
|
|
*dev_no_out = dev_no;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool bind_adv_get_ctl_no(HWND hwnd, size_t *ctl_no_out)
|
|
{
|
|
const struct bind_adv_state *state;
|
|
size_t ctl_no;
|
|
HWND ctls;
|
|
|
|
state = (struct bind_adv_state *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
|
ctls = GetDlgItem(hwnd, IDC_CONTROL);
|
|
|
|
ctl_no = SendMessage(ctls, CB_GETCURSEL, 0, 0);
|
|
|
|
if (ctl_no >= state->nctls) {
|
|
return false;
|
|
}
|
|
|
|
if (ctl_no_out != NULL) {
|
|
*ctl_no_out = ctl_no;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool bind_adv_get_range(HWND hwnd, int32_t *out_min, int32_t *out_max)
|
|
{
|
|
HWND wnd_min;
|
|
HWND wnd_max;
|
|
|
|
wnd_min = GetDlgItem(hwnd, IDC_BINDING_MIN);
|
|
wnd_max = GetDlgItem(hwnd, IDC_BINDING_MAX);
|
|
|
|
return bind_adv_get_int(wnd_min, out_min) &&
|
|
bind_adv_get_int(wnd_max, out_max);
|
|
}
|
|
|
|
static bool bind_adv_get_int(HWND control, int32_t *out)
|
|
{
|
|
wchar_t text[16];
|
|
int32_t tmp;
|
|
|
|
text[0] = L'\0';
|
|
GetWindowText(control, text, lengthof(text));
|
|
|
|
if (swscanf(text, L"%d", &tmp) != 1) {
|
|
return false;
|
|
}
|
|
|
|
if (out != NULL) {
|
|
*out = tmp;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool bind_adv_is_valid(HWND hwnd)
|
|
{
|
|
if (!bind_adv_get_dev_no(hwnd, NULL)) {
|
|
return false;
|
|
}
|
|
|
|
if (!bind_adv_get_ctl_no(hwnd, NULL)) {
|
|
return false;
|
|
}
|
|
|
|
if (!bind_adv_get_range(hwnd, NULL, NULL)) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void bind_adv_validate(HWND hwnd)
|
|
{
|
|
HWND ok;
|
|
|
|
ok = GetDlgItem(hwnd, IDOK);
|
|
EnableWindow(ok, bind_adv_is_valid(hwnd) ? TRUE : FALSE);
|
|
}
|
|
|
|
static void bind_adv_select_dev(HWND hwnd, size_t dev_no)
|
|
{
|
|
struct bind_adv_state *state;
|
|
struct hid_stub *hid;
|
|
char chars[256];
|
|
wchar_t wchars[256];
|
|
size_t i;
|
|
HWND ctls;
|
|
HWND devs;
|
|
|
|
state = (struct bind_adv_state *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
|
ctls = GetDlgItem(hwnd, IDC_CONTROL);
|
|
devs = GetDlgItem(hwnd, IDC_DEVICE);
|
|
|
|
if (dev_no >= state->devs.nitems) {
|
|
return;
|
|
}
|
|
|
|
SendMessage(devs, CB_SETCURSEL, (WPARAM) dev_no, 0);
|
|
SendMessage(ctls, CB_RESETCONTENT, 0, 0);
|
|
|
|
hid = *array_item(struct hid_stub *, &state->devs, dev_no);
|
|
|
|
state->cur_hid = hid;
|
|
|
|
hid_mgr_lock();
|
|
|
|
if (!hid_stub_get_controls(state->cur_hid, NULL, &state->nctls)) {
|
|
goto size_fail;
|
|
}
|
|
|
|
free(state->ctls);
|
|
state->ctls = xmalloc(sizeof(*state->ctls) * state->nctls);
|
|
|
|
if (!hid_stub_get_controls(state->cur_hid, state->ctls, &state->nctls)) {
|
|
goto data_fail;
|
|
}
|
|
|
|
for (i = 0; i < state->nctls; i++) {
|
|
wchars[0] = L'\0';
|
|
|
|
usages_get(chars, lengthof(chars), state->ctls[i].usage);
|
|
MultiByteToWideChar(CP_UTF8, 0, chars, -1, wchars, lengthof(wchars));
|
|
|
|
SendMessage(ctls, CB_ADDSTRING, 0, (LPARAM) wchars);
|
|
}
|
|
|
|
hid_mgr_unlock();
|
|
|
|
bind_adv_validate(hwnd);
|
|
|
|
return;
|
|
|
|
data_fail:
|
|
free(state->ctls);
|
|
|
|
state->ctls = NULL;
|
|
state->nctls = 0;
|
|
|
|
size_fail:
|
|
hid_mgr_unlock();
|
|
|
|
bind_adv_validate(hwnd);
|
|
}
|
|
|
|
static void bind_adv_select_ctl(HWND hwnd, size_t ctl_no)
|
|
{
|
|
wchar_t text[128];
|
|
struct bind_adv_state *state;
|
|
const struct hid_control *ctl;
|
|
HWND ctls;
|
|
HWND limit_min;
|
|
HWND limit_max;
|
|
|
|
state = (struct bind_adv_state *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
|
ctls = GetDlgItem(hwnd, IDC_CONTROL);
|
|
limit_min = GetDlgItem(hwnd, IDC_LIMIT_MIN);
|
|
limit_max = GetDlgItem(hwnd, IDC_LIMIT_MAX);
|
|
|
|
if (ctl_no >= state->nctls) {
|
|
return;
|
|
}
|
|
|
|
SendMessage(ctls, CB_SETCURSEL, (WPARAM) ctl_no, 0);
|
|
|
|
ctl = &state->ctls[ctl_no];
|
|
|
|
wstr_format(text, lengthof(text), L"%d", ctl->value_min);
|
|
SetWindowText(limit_min, text);
|
|
|
|
wstr_format(text, lengthof(text), L"%d", ctl->value_max);
|
|
SetWindowText(limit_max, text);
|
|
|
|
bind_adv_validate(hwnd);
|
|
}
|
|
|
|
static void bind_adv_set_range(HWND hwnd, int32_t range_min, int32_t range_max)
|
|
{
|
|
wchar_t text[16];
|
|
HWND wnd_min;
|
|
HWND wnd_max;
|
|
|
|
wnd_min = GetDlgItem(hwnd, IDC_BINDING_MIN);
|
|
wnd_max = GetDlgItem(hwnd, IDC_BINDING_MAX);
|
|
|
|
wstr_format(text, lengthof(text), L"%d", range_min);
|
|
SetWindowText(wnd_min, text);
|
|
|
|
wstr_format(text, lengthof(text), L"%d", range_max);
|
|
SetWindowText(wnd_max, text);
|
|
|
|
bind_adv_validate(hwnd);
|
|
}
|