quakeforge/libs/input/in_button.c
Bill Currie 89f8dfce09 [input] Clear button inputs when IN_ClearStates called
This fixes the annoying persistence of inputs when respawning and
changing levels. Axis input clearing is hooked up but does nothing as of
yet. Active device input clearing has always been hooked up, but also
does nothing in the evdev and x11 drivers.
2022-05-19 16:47:47 +09:00

254 lines
5.4 KiB
C

/*
in_button.c
Logical button support
Copyright (C) 1996-1997 Id Software, Inc.
Copyright (C) 2021 Bill Currie <bill@taniwha.org>
Author: Bill Currie <bill@taniwha.org>
Date: 2021/09/29
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#ifdef HAVE_STRING_H
# include <string.h>
#endif
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif
#include "QF/cmd.h"
#include "QF/hash.h"
#include "QF/input.h"
#include "QF/sys.h"
typedef struct regbutton_s {
in_button_t *button;
char *press_cmd;
char *release_cmd;
} regbutton_t;
static hashtab_t *button_tab;
static const char *
button_get_key (const void *b, void *data)
{
__auto_type regbutton = (const regbutton_t *) b;
return regbutton->button->name;
}
static void
button_free (void *b, void *data)
{
regbutton_t *rb = b;
if (rb->button->listeners) {
DARRAY_CLEAR (rb->button->listeners);
free (rb->button->listeners);
}
free (rb);
}
static void
button_press (in_button_t *button, int id)
{
if (id == button->down[0] || id == button->down[1]) {
// repeating key
return;
}
if (!button->down[0]) {
button->down[0] = id;
} else if (!button->down[1]) {
button->down[1] = id;
} else {
Sys_Printf ("Three keys down for a button!\n");
return;
}
if (button->state & inb_down) {
// still down
return;
}
button->state |= inb_down | inb_edge_down;
if (button->listeners) {
LISTENER_INVOKE (button->listeners, button);
}
}
static void
button_release (in_button_t *button, int id)
{
if (id == -1) {
// typed manually at the console, assume for unsticking, so clear all
button->down[0] = button->down[1] = 0;
button->state = inb_edge_up;
return;
}
if (button->down[0] == id) {
button->down[0] = 0;
} else if (button->down[1] == id) {
button->down[1] = 0;
} else {
// key up without coresponding down (menu pass through)
return;
}
if (button->down[0] || button->down[1]) {
// some other key is still holding it down
return;
}
if (!(button->state & inb_down)) {
// still up (this should not happen)
return;
}
button->state &= ~inb_down; // now up
button->state |= inb_edge_up;
if (button->listeners) {
LISTENER_INVOKE (button->listeners, button);
}
}
void
IN_ButtonAction (in_button_t *button, int id, int pressed)
{
if (pressed) {
button_press (button, id);
} else {
button_release (button, id);
}
}
static void
button_press_cmd (void *_b)
{
in_button_t *button = _b;
const char *idstr = Cmd_Argv (1);
// assume typed manually at the console for continuous down
int id = -1;
if (idstr[0]) {
id = atoi (idstr);
}
button_press (button, id);
}
static void
button_release_cmd (void *_b)
{
in_button_t *button = _b;
const char *idstr = Cmd_Argv (1);
// assume typed manually at the console, probably for unsticking
int id = -1;
if (idstr[0]) {
id = atoi (idstr);
}
button_release (button, id);
}
VISIBLE int
IN_RegisterButton (in_button_t *button)
{
const char *name = button->name;
if (Hash_Find (button_tab, name)) {
return 0;
}
size_t size = strlen (name) + 2;
regbutton_t *regbutton = malloc (sizeof (regbutton_t) + 2 * size);
regbutton->button = button;
regbutton->press_cmd = (char *) (regbutton + 1);
regbutton->release_cmd = regbutton->press_cmd + size;
*regbutton->press_cmd = '+';
*regbutton->release_cmd = '-';
strcpy (regbutton->press_cmd + 1, name);
strcpy (regbutton->release_cmd + 1, name);
Cmd_AddDataCommand (regbutton->press_cmd, button_press_cmd, button,
"Set the button's state to on/pressed.");
Cmd_AddDataCommand (regbutton->release_cmd, button_release_cmd, button,
"Set the button's state to off/released.");
Hash_Add (button_tab, regbutton);
return 1;
}
in_button_t *
IN_FindButton (const char *name)
{
regbutton_t *regbutton = Hash_Find (button_tab, name);
if (regbutton) {
return regbutton->button;
}
return 0;
}
void
IN_ButtonAddListener (in_button_t *button, button_listener_t listener,
void *data)
{
if (!button->listeners) {
button->listeners = malloc (sizeof (*button->listeners));
LISTENER_SET_INIT (button->listeners, 8);
}
LISTENER_ADD (button->listeners, listener, data);
}
void
IN_ButtonRemoveListener (in_button_t *button, button_listener_t listener,
void *data)
{
if (button->listeners) {
LISTENER_REMOVE (button->listeners, listener, data);
}
}
static void
button_clear_state (void *_rb, void *data)
{
regbutton_t *rb = _rb;
button_release_cmd (rb->button);
}
void
IN_ButtonClearStates (void)
{
Hash_ForEach (button_tab, button_clear_state, 0);
}
static void
in_button_shutdown (void *data)
{
Hash_DelTable (button_tab);
}
static void __attribute__((constructor))
in_button_init (void)
{
button_tab = Hash_NewTable (127, button_get_key, button_free, 0, 0);
Sys_RegisterShutdown (in_button_shutdown, 0);
}