Files
bemanitools/src/main/config/buttons.c
icex2 b6e588116d Various import fixes caused by clang-format
Added format protection block to avoid re-ordering of header files
according to formatting rules.
2019-10-22 21:04:45 +02:00

433 lines
11 KiB
C

// clang-format off
// Don't format because the order is important here
#include <windows.h>
#include <commctrl.h>
#include <wchar.h>
// clang-format on
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "config/bind-adv.h"
#include "config/bind.h"
#include "config/resource.h"
#include "config/schema.h"
#include "config/usages.h"
#include "geninput/input-config.h"
#include "geninput/mapper.h"
#include "util/defs.h"
#include "util/log.h"
#include "util/mem.h"
#include "util/winres.h"
static INT_PTR CALLBACK
buttons_dlg_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
static bool buttons_get_control_name(
const struct mapped_action *ma, wchar_t *str, size_t nchars);
static INT_PTR buttons_handle_init(HWND hwnd, const PROPSHEETPAGE *psp);
static INT_PTR buttons_handle_binding_adv(HWND hwnd);
static INT_PTR buttons_handle_binding_clear(HWND hwnd);
static INT_PTR buttons_handle_binding_edit(HWND hwnd, BOOL bind_many);
static INT_PTR buttons_handle_switch_page(HWND hwnd, NMUPDOWN *n);
static INT_PTR buttons_handle_fini(HWND hwnd);
static void buttons_update(HWND hwnd);
static void buttons_update_bindings(HWND hwnd);
static void buttons_update_pager(HWND hwnd);
struct buttons_tab {
const struct schema *schema;
uint8_t page_no;
uint8_t npages;
};
HPROPSHEETPAGE
buttons_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_BUTTONS);
psp.pfnDlgProc = buttons_dlg_proc;
psp.lParam = (LPARAM) schema;
return CreatePropertySheetPage(&psp);
}
static INT_PTR CALLBACK
buttons_dlg_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
NMHDR *n;
switch (msg) {
case WM_INITDIALOG:
return buttons_handle_init(hwnd, (const PROPSHEETPAGE *) lparam);
case WM_NOTIFY:
n = (NMHDR *) lparam;
switch (n->idFrom) {
case IDC_PAGE:
switch (n->code) {
case UDN_DELTAPOS:
return buttons_handle_switch_page(
hwnd, (NMUPDOWN *) n);
default:
return FALSE;
}
case IDC_BINDINGS:
switch (n->code) {
case NM_DBLCLK:
return buttons_handle_binding_edit(hwnd, FALSE);
default:
return FALSE;
}
default:
return FALSE;
}
case WM_COMMAND:
switch (LOWORD(wparam)) {
case IDC_BINDING_ADV:
return buttons_handle_binding_adv(hwnd);
case IDC_BINDING_CLEAR:
return buttons_handle_binding_clear(hwnd);
case IDC_BINDING_EDIT:
return buttons_handle_binding_edit(hwnd, FALSE);
case IDC_BINDING_EDIT_MANY:
return buttons_handle_binding_edit(hwnd, TRUE);
default:
return FALSE;
}
case WM_DESTROY:
return buttons_handle_fini(hwnd);
default:
return FALSE;
}
}
static INT_PTR buttons_handle_init(HWND hwnd, const PROPSHEETPAGE *psp)
{
struct buttons_tab *self;
HWND bindings_ctl;
HINSTANCE inst;
LVCOLUMN col;
LVITEM item;
wchar_t str[128];
size_t i;
self = xmalloc(sizeof(*self));
self->schema = (const struct schema *) psp->lParam;
self->page_no = 0;
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR) self);
inst = (HINSTANCE) GetWindowLongPtr(hwnd, GWLP_HINSTANCE);
bindings_ctl = GetDlgItem(hwnd, IDC_BINDINGS);
ListView_SetExtendedListViewStyle(
bindings_ctl, LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES);
LoadString(inst, IDS_COL_ACTION, str, lengthof(str));
col.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT;
col.fmt = LVCFMT_LEFT;
col.cx = 120;
col.pszText = str;
ListView_InsertColumn(bindings_ctl, 0, &col);
LoadString(inst, IDS_COL_BUTTON, str, lengthof(str));
col.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT;
col.fmt = LVCFMT_LEFT;
col.cx = 120;
col.pszText = str;
ListView_InsertColumn(bindings_ctl, 1, &col);
for (i = 0; i < self->schema->nactions; i++) {
memset(&item, 0, sizeof(item));
LoadString(
inst, self->schema->actions[i].name_rsrc, str, lengthof(str));
item.mask = LVIF_TEXT;
item.iItem = (int) i;
item.iSubItem = 0;
item.pszText = str;
ListView_InsertItem(bindings_ctl, &item);
}
buttons_update(hwnd);
return TRUE;
}
static void buttons_update(HWND hwnd)
{
buttons_update_pager(hwnd);
buttons_update_bindings(hwnd);
/* Flickery, but seems to be the only way to avoid label garbling when
the page label changes. Need to do better. */
InvalidateRect(hwnd, NULL, TRUE);
}
static void buttons_update_pager(HWND hwnd)
{
struct buttons_tab *self;
HINSTANCE inst;
wchar_t str[128];
inst = (HINSTANCE) GetWindowLongPtr(hwnd, GWLP_HINSTANCE);
self = (struct buttons_tab *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
self->npages = mapper_get_npages() + 1;
rswprintf(
str, lengthof(str), inst, IDS_PAGE, self->page_no + 1, self->npages);
SetWindowText(GetDlgItem(hwnd, IDC_PAGE_TEXT), str);
}
static void buttons_update_bindings(HWND hwnd)
{
action_iter_t pos;
struct mapped_action ma;
struct buttons_tab *self;
HINSTANCE inst;
HWND bindings_ctl;
LVITEM item;
wchar_t str[128];
size_t i;
self = (struct buttons_tab *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
inst = (HINSTANCE) GetWindowLongPtr(hwnd, GWLP_HINSTANCE);
bindings_ctl = GetDlgItem(hwnd, IDC_BINDINGS);
/* Clear binding cell text */
for (i = 0; i < self->schema->nactions; i++) {
memset(&item, 0, sizeof(item));
item.mask = LVIF_TEXT;
item.iItem = (int) i;
item.iSubItem = 1;
item.pszText = L"";
ListView_SetItem(bindings_ctl, &item);
}
/* Repopulate bindings */
hid_mgr_lock();
for (pos = mapper_iterate_actions(); action_iter_is_valid(pos);
action_iter_next(pos)) {
if (action_iter_get_page(pos) != self->page_no) {
continue;
}
action_iter_get_mapping(pos, &ma);
if (!buttons_get_control_name(&ma, str, lengthof(str))) {
/* Can't read device metadata, device is probably unplugged */
LoadString(inst, IDS_NOT_PRESENT, str, lengthof(str));
}
memset(&item, 0, sizeof(item));
item.mask = LVIF_TEXT;
item.iItem = (int) action_iter_get_action(pos);
item.iSubItem = 1;
item.pszText = str;
ListView_SetItem(bindings_ctl, &item);
}
action_iter_free(pos);
hid_mgr_unlock();
}
static bool buttons_get_control_name(
const struct mapped_action *ma, wchar_t *str, size_t nchars)
{
char chars[128];
struct hid_control *controls;
size_t ncontrols;
if (!hid_stub_get_controls(ma->hid, NULL, &ncontrols)) {
goto count_fail;
}
controls = xmalloc(ncontrols * sizeof(*controls));
if (!hid_stub_get_controls(ma->hid, controls, &ncontrols)) {
goto data_fail;
}
if (ma->control_no >= ncontrols) {
goto bounds_fail;
}
usages_get(chars, lengthof(chars), controls[ma->control_no].usage);
MultiByteToWideChar(CP_UTF8, 0, chars, lengthof(chars), str, nchars);
free(controls);
return true;
bounds_fail:
data_fail:
free(controls);
count_fail:
return false;
}
static INT_PTR buttons_handle_binding_adv(HWND hwnd)
{
HINSTANCE inst;
struct mapped_action ma;
struct buttons_tab *self;
int action_no;
uint8_t bit;
bool is_valid;
inst = (HINSTANCE) GetWindowLongPtr(hwnd, GWLP_HINSTANCE);
self = (struct buttons_tab *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
action_no = ListView_GetSelectionMark(GetDlgItem(hwnd, IDC_BINDINGS));
if (action_no < 0 || action_no >= (int) self->schema->nactions) {
return TRUE;
}
bit = self->schema->actions[action_no].bit;
is_valid = mapper_get_action_map((uint8_t) action_no, self->page_no, &ma);
if (bind_adv(inst, hwnd, &ma, is_valid)) {
mapper_set_action_map((uint8_t) action_no, self->page_no, bit, &ma);
buttons_update(hwnd);
}
return TRUE;
}
static INT_PTR buttons_handle_binding_clear(HWND hwnd)
{
struct buttons_tab *self;
int action_no;
self = (struct buttons_tab *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
action_no = ListView_GetSelectionMark(GetDlgItem(hwnd, IDC_BINDINGS));
if (action_no < 0 || action_no >= (int) self->schema->nactions) {
return TRUE;
}
mapper_clear_action_map((uint8_t) action_no, self->page_no);
buttons_update(hwnd);
return TRUE;
}
static INT_PTR buttons_handle_binding_edit(HWND hwnd, BOOL bind_many)
{
HINSTANCE inst;
HWND listview;
struct buttons_tab *self;
struct mapped_action ma;
int action_start;
int action_no;
uint8_t bit;
inst = (HINSTANCE) GetWindowLongPtr(hwnd, GWLP_HINSTANCE);
self = (struct buttons_tab *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
listview = GetDlgItem(hwnd, IDC_BINDINGS);
action_start = ListView_GetSelectionMark(listview);
if (action_start < 0 || action_start >= (int) self->schema->nactions) {
/* wtf... */
return TRUE;
}
for (action_no = action_start; action_no < self->schema->nactions;
action_no++) {
// Make sure the next listview item is visible
ListView_SetItemState(
listview, action_no, LVIS_FOCUSED | LVIS_SELECTED, 0x000F);
ListView_EnsureVisible(listview, action_no, FALSE);
bit = self->schema->actions[action_no].bit;
if (bind_control(inst, hwnd, &self->schema->actions[action_no], &ma)) {
mapper_set_action_map((uint8_t) action_no, self->page_no, bit, &ma);
buttons_update(hwnd);
log_misc(
"Bind act %d -> dev %p ctl %u range [%d, %d]",
self->schema->actions[action_no].bit,
ma.hid,
(unsigned int) ma.control_no,
ma.value_min,
ma.value_max);
} else {
break;
}
if (!bind_many) {
break;
}
}
return TRUE;
}
static INT_PTR buttons_handle_switch_page(HWND hwnd, NMUPDOWN *n)
{
struct buttons_tab *self;
self = (struct buttons_tab *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
/* The buttons on a horizontal spinner are backwards -.- */
if (n->iDelta > 0 && self->page_no > 0) {
self->page_no--;
buttons_update(hwnd);
} else if (n->iDelta < 0 && self->page_no < self->npages - 1) {
self->page_no++;
buttons_update(hwnd);
}
return TRUE;
}
static INT_PTR buttons_handle_fini(HWND hwnd)
{
struct buttons_tab *self;
self = (struct buttons_tab *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
free(self);
return TRUE;
}