/* in_button.c Logical button support Copyright (C) 1996-1997 Id Software, Inc. Copyright (C) 2021 Bill Currie Author: Bill Currie 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 #endif #ifdef HAVE_STRINGS_H # include #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) { free (b); } 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 __attribute__((constructor)) in_button_init (void) { button_tab = Hash_NewTable (127, button_get_key, button_free, 0, 0); }