Boils down to: - Include headers - Reduce boiler plate with helpers - Swap out explicit usages with core API layer and ensure the right API is configured beforehand
588 lines
14 KiB
C
588 lines
14 KiB
C
// clang-format off
|
|
// Don't format because the order is important here
|
|
#include <windows.h>
|
|
#include <commctrl.h>
|
|
// clang-format on
|
|
|
|
#include <stdbool.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "config/resource.h"
|
|
#include "config/schema.h"
|
|
#include "config/usages.h"
|
|
|
|
#include "core/log.h"
|
|
|
|
#include "geninput/hid-mgr.h"
|
|
#include "geninput/input-config.h"
|
|
#include "geninput/mapper.h"
|
|
|
|
#include "util/array.h"
|
|
#include "util/defs.h"
|
|
#include "util/mem.h"
|
|
#include "util/str.h"
|
|
|
|
#define SENSITIVITY_SCALE 4
|
|
|
|
struct analogs_ui {
|
|
struct array children;
|
|
};
|
|
|
|
struct analog_ui {
|
|
const struct analog_def *def;
|
|
struct array hids;
|
|
struct array control_nos;
|
|
struct hid_stub *selected_hid;
|
|
uint8_t pos;
|
|
};
|
|
|
|
static INT_PTR CALLBACK
|
|
analogs_ui_dlg_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
|
|
static INT_PTR analogs_ui_handle_init(HWND hwnd, const PROPSHEETPAGE *psp);
|
|
static INT_PTR analogs_ui_handle_activate(HWND hwnd);
|
|
static INT_PTR analogs_ui_handle_passivate(HWND hwnd);
|
|
static INT_PTR analogs_ui_handle_tick(HWND hwnd);
|
|
static INT_PTR analogs_ui_handle_fini(HWND hwnd);
|
|
|
|
static INT_PTR CALLBACK
|
|
analog_ui_dlg_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
|
|
static INT_PTR analog_ui_handle_init(HWND hwnd, struct analog_def *def);
|
|
static void analog_ui_handle_init_label(HWND hwnd);
|
|
static void analog_ui_handle_init_dev(HWND hwnd);
|
|
static void analog_ui_handle_init_sensitivity(HWND hwnd);
|
|
static void analog_ui_handle_init_invert(HWND hwnd);
|
|
static bool analog_ui_match_device(struct hid_stub *hid);
|
|
static void analog_ui_populate_controls(HWND hwnd);
|
|
static INT_PTR analog_ui_handle_device_change(HWND hwnd);
|
|
static INT_PTR analog_ui_handle_control_change(HWND hwnd);
|
|
static INT_PTR analog_ui_handle_sensitivity_change(HWND hwnd);
|
|
static INT_PTR analog_ui_handle_invert_change(HWND hwnd);
|
|
static INT_PTR analog_ui_handle_tick(HWND hwnd);
|
|
static INT_PTR analog_ui_handle_fini(HWND hwnd);
|
|
|
|
HPROPSHEETPAGE
|
|
analogs_ui_tab_create(HINSTANCE inst, const struct schema *schema)
|
|
{
|
|
PROPSHEETPAGE psp;
|
|
|
|
memset(&psp, 0, sizeof(psp));
|
|
psp.dwSize = sizeof(psp);
|
|
psp.dwFlags = PSP_DEFAULT;
|
|
psp.hInstance = inst;
|
|
psp.pszTemplate = MAKEINTRESOURCE(IDD_TAB_ANALOGS);
|
|
psp.pfnDlgProc = analogs_ui_dlg_proc;
|
|
psp.lParam = (LPARAM) schema;
|
|
|
|
return CreatePropertySheetPage(&psp);
|
|
}
|
|
|
|
static INT_PTR CALLBACK
|
|
analogs_ui_dlg_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
|
|
{
|
|
const NMHDR *n;
|
|
|
|
switch (msg) {
|
|
case WM_INITDIALOG:
|
|
return analogs_ui_handle_init(hwnd, (PROPSHEETPAGE *) lparam);
|
|
|
|
case WM_NOTIFY:
|
|
n = (NMHDR *) lparam;
|
|
|
|
switch (n->code) {
|
|
case PSN_SETACTIVE:
|
|
return analogs_ui_handle_activate(hwnd);
|
|
|
|
case PSN_KILLACTIVE:
|
|
return analogs_ui_handle_passivate(hwnd);
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
case WM_TIMER:
|
|
return analogs_ui_handle_tick(hwnd);
|
|
|
|
case WM_DESTROY:
|
|
return analogs_ui_handle_fini(hwnd);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static INT_PTR analogs_ui_handle_init(HWND hwnd, const PROPSHEETPAGE *psp)
|
|
{
|
|
struct analogs_ui *ui;
|
|
const struct schema *schema;
|
|
long ypos;
|
|
size_t i;
|
|
HINSTANCE inst;
|
|
HWND child;
|
|
RECT r;
|
|
|
|
ui = xmalloc(sizeof(*ui));
|
|
array_init(&ui->children);
|
|
|
|
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LPARAM) ui);
|
|
|
|
inst = (HINSTANCE) GetWindowLongPtr(hwnd, GWLP_HINSTANCE);
|
|
schema = (struct schema *) psp->lParam;
|
|
|
|
ypos = 0;
|
|
|
|
for (i = 0; i < schema->nanalogs; i++) {
|
|
child = CreateDialogParam(
|
|
inst,
|
|
MAKEINTRESOURCE(IDD_ANALOG),
|
|
hwnd,
|
|
analog_ui_dlg_proc,
|
|
(LPARAM) &schema->analogs[i]);
|
|
|
|
GetWindowRect(child, &r);
|
|
SetWindowPos(
|
|
child, HWND_BOTTOM, 0, ypos, 0, 0, SWP_NOSIZE | SWP_SHOWWINDOW);
|
|
|
|
ypos += r.bottom - r.top;
|
|
|
|
*array_append(HWND, &ui->children) = child;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static INT_PTR analogs_ui_handle_activate(HWND hwnd)
|
|
{
|
|
SetTimer(hwnd, 1, 17, NULL);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static INT_PTR analogs_ui_handle_passivate(HWND hwnd)
|
|
{
|
|
KillTimer(hwnd, 1);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static INT_PTR analogs_ui_handle_tick(HWND hwnd)
|
|
{
|
|
HWND child;
|
|
struct analogs_ui *ui;
|
|
size_t i;
|
|
|
|
mapper_update();
|
|
|
|
ui = (struct analogs_ui *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
|
|
|
for (i = 0; i < ui->children.nitems; i++) {
|
|
child = *array_item(HWND, &ui->children, i);
|
|
SendMessage(child, WM_USER, 0, 0);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static INT_PTR analogs_ui_handle_fini(HWND hwnd)
|
|
{
|
|
struct analogs_ui *ui;
|
|
|
|
ui = (struct analogs_ui *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
|
|
|
array_fini(&ui->children);
|
|
free(ui);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static INT_PTR CALLBACK
|
|
analog_ui_dlg_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
|
|
{
|
|
switch (msg) {
|
|
case WM_INITDIALOG:
|
|
return analog_ui_handle_init(hwnd, (struct analog_def *) lparam);
|
|
|
|
case WM_USER:
|
|
return analog_ui_handle_tick(hwnd);
|
|
|
|
case WM_COMMAND:
|
|
switch (HIWORD(wparam)) {
|
|
case CBN_SELCHANGE:
|
|
switch (LOWORD(wparam)) {
|
|
case IDC_DEVICE:
|
|
return analog_ui_handle_device_change(hwnd);
|
|
|
|
case IDC_CONTROL:
|
|
return analog_ui_handle_control_change(hwnd);
|
|
|
|
default:
|
|
return FALSE;
|
|
}
|
|
|
|
case BN_CLICKED:
|
|
switch (LOWORD(wparam)) {
|
|
case IDC_ANALOG_INVERT:
|
|
return analog_ui_handle_invert_change(hwnd);
|
|
|
|
default:
|
|
return FALSE;
|
|
}
|
|
|
|
default:
|
|
return FALSE;
|
|
}
|
|
|
|
case WM_HSCROLL:
|
|
if (GetDlgItem(hwnd, IDC_SENSITIVITY) == (HWND) lparam) {
|
|
return analog_ui_handle_sensitivity_change(hwnd);
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
|
|
case WM_DESTROY:
|
|
return analog_ui_handle_fini(hwnd);
|
|
|
|
default:
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
static INT_PTR analog_ui_handle_init(HWND hwnd, struct analog_def *def)
|
|
{
|
|
struct analog_ui *ui;
|
|
|
|
ui = xmalloc(sizeof(*ui));
|
|
ui->def = def;
|
|
array_init(&ui->hids);
|
|
array_init(&ui->control_nos);
|
|
ui->selected_hid = NULL;
|
|
ui->pos = 0;
|
|
|
|
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LPARAM) ui);
|
|
|
|
analog_ui_handle_init_label(hwnd);
|
|
analog_ui_handle_init_dev(hwnd);
|
|
analog_ui_handle_init_sensitivity(hwnd);
|
|
analog_ui_handle_init_invert(hwnd);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void analog_ui_handle_init_label(HWND hwnd)
|
|
{
|
|
struct analog_ui *ui;
|
|
wchar_t label[128];
|
|
HINSTANCE inst;
|
|
HWND box;
|
|
|
|
ui = (struct analog_ui *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
|
inst = (HINSTANCE) GetWindowLongPtr(hwnd, GWLP_HINSTANCE);
|
|
box = GetDlgItem(hwnd, IDC_GROUP);
|
|
|
|
LoadString(inst, ui->def->label_rsrc, label, lengthof(label));
|
|
SendMessage(box, WM_SETTEXT, 0, (LPARAM) label);
|
|
}
|
|
|
|
static void analog_ui_handle_init_dev(HWND hwnd)
|
|
{
|
|
struct analog_ui *ui;
|
|
struct mapped_analog ma;
|
|
wchar_t *dev_name;
|
|
struct hid_stub *hid;
|
|
size_t nchars;
|
|
LRESULT index;
|
|
HWND dev_list;
|
|
|
|
ui = (struct analog_ui *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
|
dev_list = GetDlgItem(hwnd, IDC_DEVICE);
|
|
|
|
SendMessage(dev_list, CB_ADDSTRING, 0, (LPARAM) L"");
|
|
|
|
hid_mgr_lock();
|
|
|
|
ma.hid = NULL;
|
|
mapper_get_analog_map(ui->def->tag, &ma);
|
|
|
|
for (hid = hid_mgr_get_first_stub(); hid != NULL;
|
|
hid = hid_mgr_get_next_stub(hid)) {
|
|
if (!analog_ui_match_device(hid)) {
|
|
continue;
|
|
}
|
|
|
|
if (!hid_stub_get_name(hid, NULL, &nchars)) {
|
|
continue;
|
|
}
|
|
|
|
dev_name = xmalloc(nchars * sizeof(*dev_name));
|
|
|
|
if (!hid_stub_get_name(hid, dev_name, &nchars)) {
|
|
free(dev_name);
|
|
|
|
continue;
|
|
}
|
|
|
|
index = SendMessage(dev_list, CB_ADDSTRING, 0, (LPARAM) dev_name);
|
|
|
|
free(dev_name);
|
|
|
|
*array_append(struct hid_stub *, &ui->hids) = hid;
|
|
|
|
if (ma.hid == hid) {
|
|
SendMessage(dev_list, CB_SETCURSEL, index, 0);
|
|
|
|
ui->selected_hid = hid;
|
|
analog_ui_populate_controls(hwnd);
|
|
}
|
|
}
|
|
|
|
hid_mgr_unlock();
|
|
}
|
|
|
|
static void analog_ui_handle_init_sensitivity(HWND hwnd)
|
|
{
|
|
struct analog_ui *ui;
|
|
int pos;
|
|
HWND slider;
|
|
|
|
ui = (struct analog_ui *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
|
slider = GetDlgItem(hwnd, IDC_SENSITIVITY);
|
|
pos = 256 * SENSITIVITY_SCALE + mapper_get_analog_sensitivity(ui->def->tag);
|
|
|
|
SendMessage(slider, TBM_SETTICFREQ, 256, 0);
|
|
SendMessage(
|
|
slider,
|
|
TBM_SETRANGE,
|
|
FALSE,
|
|
MAKELPARAM(0, SENSITIVITY_SCALE * 256 * 2));
|
|
SendMessage(slider, TBM_SETPOS, TRUE, (LPARAM) pos);
|
|
|
|
EnableWindow(slider, !mapper_is_analog_absolute(ui->def->tag));
|
|
}
|
|
|
|
static void analog_ui_handle_init_invert(HWND hwnd)
|
|
{
|
|
struct analog_ui *ui;
|
|
|
|
ui = (struct analog_ui *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
|
|
|
if (mapper_get_analog_invert(ui->def->tag)) {
|
|
SendMessage(GetDlgItem(hwnd, IDC_ANALOG_INVERT), BM_SETCHECK, BST_CHECKED, 0);
|
|
}
|
|
}
|
|
|
|
static bool analog_ui_match_device(struct hid_stub *hid)
|
|
{
|
|
struct hid_control *controls;
|
|
size_t ncontrols;
|
|
size_t i;
|
|
|
|
if (!hid_stub_get_controls(hid, NULL, &ncontrols)) {
|
|
goto size_fail;
|
|
}
|
|
|
|
controls = xmalloc(ncontrols * sizeof(*controls));
|
|
|
|
if (!hid_stub_get_controls(hid, controls, &ncontrols)) {
|
|
goto content_fail;
|
|
}
|
|
|
|
for (i = 0; i < ncontrols; i++) {
|
|
if (controls[i].value_max - controls[i].value_min > 1) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
free(controls);
|
|
|
|
return i < ncontrols;
|
|
|
|
content_fail:
|
|
free(controls);
|
|
|
|
size_fail:
|
|
return false;
|
|
}
|
|
|
|
static void analog_ui_populate_controls(HWND hwnd)
|
|
{
|
|
char usage_desc[512];
|
|
wchar_t *tmp;
|
|
struct analog_ui *ui;
|
|
struct mapped_analog ma;
|
|
struct hid_control *controls;
|
|
size_t ncontrols;
|
|
size_t i;
|
|
long nitems;
|
|
LRESULT index;
|
|
HWND controls_ctl;
|
|
|
|
ui = (struct analog_ui *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
|
controls_ctl = GetDlgItem(hwnd, IDC_CONTROL);
|
|
nitems = (long) SendMessage(controls_ctl, CB_GETCOUNT, 0, 0);
|
|
|
|
InvalidateRect(controls_ctl, NULL, TRUE);
|
|
|
|
for (index = nitems; index >= 0; index--) {
|
|
SendMessage(controls_ctl, CB_DELETESTRING, index, 0);
|
|
}
|
|
|
|
array_fini(&ui->control_nos);
|
|
array_init(&ui->control_nos);
|
|
|
|
if (ui->selected_hid == NULL) {
|
|
return;
|
|
}
|
|
|
|
if (!hid_stub_get_controls(ui->selected_hid, NULL, &ncontrols)) {
|
|
goto size_fail;
|
|
}
|
|
|
|
controls = xmalloc(sizeof(*controls) * ncontrols);
|
|
|
|
if (!hid_stub_get_controls(ui->selected_hid, controls, &ncontrols)) {
|
|
goto content_fail;
|
|
}
|
|
|
|
SendMessage(controls_ctl, CB_ADDSTRING, 0, (LPARAM) L"");
|
|
|
|
mapper_get_analog_map(ui->def->tag, &ma);
|
|
|
|
for (i = 0; i < ncontrols; i++) {
|
|
if (controls[i].value_max - controls[i].value_min <= 1) {
|
|
continue;
|
|
}
|
|
|
|
usages_get(usage_desc, lengthof(usage_desc), controls[i].usage);
|
|
tmp = str_widen(usage_desc);
|
|
|
|
index = SendMessage(controls_ctl, CB_ADDSTRING, 0, (LPARAM) tmp);
|
|
free(tmp);
|
|
|
|
if (i == ma.control_no) {
|
|
SendMessage(controls_ctl, CB_SETCURSEL, index, 0);
|
|
}
|
|
|
|
*array_append(size_t, &ui->control_nos) = i;
|
|
}
|
|
|
|
free(controls);
|
|
|
|
return;
|
|
|
|
content_fail:
|
|
free(controls);
|
|
|
|
size_fail:
|
|
return;
|
|
}
|
|
|
|
static INT_PTR analog_ui_handle_device_change(HWND hwnd)
|
|
{
|
|
struct mapped_analog ma;
|
|
struct analog_ui *ui;
|
|
LRESULT index;
|
|
|
|
ui = (struct analog_ui *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
|
index = SendMessage(GetDlgItem(hwnd, IDC_DEVICE), CB_GETCURSEL, 0, 0);
|
|
|
|
log_assert((size_t) index <= ui->hids.nitems);
|
|
|
|
if (index == 0) {
|
|
ui->selected_hid = NULL;
|
|
} else {
|
|
ui->selected_hid = *array_item(struct hid_stub *, &ui->hids, index - 1);
|
|
}
|
|
|
|
ma.hid = NULL;
|
|
mapper_set_analog_map(ui->def->tag, &ma);
|
|
|
|
hid_mgr_lock();
|
|
analog_ui_populate_controls(hwnd);
|
|
hid_mgr_unlock();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static INT_PTR analog_ui_handle_control_change(HWND hwnd)
|
|
{
|
|
struct mapped_analog ma;
|
|
struct analog_ui *ui;
|
|
LRESULT index;
|
|
HWND slider;
|
|
|
|
ui = (struct analog_ui *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
|
index = SendMessage(GetDlgItem(hwnd, IDC_CONTROL), CB_GETCURSEL, 0, 0);
|
|
|
|
log_assert((size_t) index <= ui->control_nos.nitems);
|
|
|
|
if (index == 0) {
|
|
ma.hid = NULL;
|
|
} else {
|
|
ma.hid = ui->selected_hid;
|
|
ma.control_no = *array_item(size_t, &ui->control_nos, index - 1);
|
|
}
|
|
|
|
mapper_set_analog_map(ui->def->tag, &ma);
|
|
|
|
slider = GetDlgItem(hwnd, IDC_SENSITIVITY);
|
|
|
|
EnableWindow(slider, !mapper_is_analog_absolute(ui->def->tag));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static INT_PTR analog_ui_handle_sensitivity_change(HWND hwnd)
|
|
{
|
|
struct analog_ui *ui;
|
|
int pos;
|
|
HWND slider;
|
|
|
|
ui = (struct analog_ui *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
|
slider = GetDlgItem(hwnd, IDC_SENSITIVITY);
|
|
pos = (int) SendMessage(slider, TBM_GETPOS, 0, 0);
|
|
|
|
mapper_set_analog_sensitivity(ui->def->tag, pos - 256 * SENSITIVITY_SCALE);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static INT_PTR analog_ui_handle_invert_change(HWND hwnd)
|
|
{
|
|
struct analog_ui *ui;
|
|
HWND btn;
|
|
bool invert;
|
|
|
|
ui = (struct analog_ui *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
|
btn = GetDlgItem(hwnd, IDC_ANALOG_INVERT);
|
|
invert = SendMessage(btn, BM_GETCHECK, 0, 0) == BST_CHECKED;
|
|
|
|
mapper_set_analog_invert(ui->def->tag, invert);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static INT_PTR analog_ui_handle_tick(HWND hwnd)
|
|
{
|
|
struct analog_ui *ui;
|
|
HWND pos_ctl;
|
|
|
|
ui = (struct analog_ui *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
|
pos_ctl = GetDlgItem(hwnd, IDC_POSITION);
|
|
|
|
ui->pos = mapper_read_analog(ui->def->tag);
|
|
|
|
SendMessage(pos_ctl, WM_USER, 0, (LPARAM) ui->pos);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static INT_PTR analog_ui_handle_fini(HWND hwnd)
|
|
{
|
|
struct analog_ui *ui;
|
|
|
|
ui = (struct analog_ui *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
|
|
|
array_fini(&ui->hids);
|
|
array_fini(&ui->control_nos);
|
|
free(ui);
|
|
|
|
return TRUE;
|
|
}
|