mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-11-27 14:42:07 +00:00
33e48025dd
And create rua_game to coordinate other game builtins. Menus are broken for key handling, but have been since the input rewrite anyway. rua_input adds the ability to create buttons and axes (but not destroy them). More work needs to be done to flesh things out, though.
369 lines
12 KiB
C
369 lines
12 KiB
C
/*
|
|
binding.h
|
|
|
|
Input Mapping Table management
|
|
|
|
Copyright (C) 2001 Zephaniah E. Hull <warp@babylon.d2dc.net>
|
|
Copyright (C) 2021 Bill Currie <bill@taniwha.org>
|
|
|
|
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
|
|
|
|
*/
|
|
|
|
#ifndef __QF_input_binding_h
|
|
#define __QF_input_binding_h
|
|
|
|
#ifndef __QFCC__
|
|
#include "QF/listener.h"
|
|
#include "QF/mathlib.h"
|
|
#endif
|
|
|
|
/*** Recipe for converting an axis to a floating point value.
|
|
|
|
Absolute axes are converted to the 0..1 range for unbalanced axes, and
|
|
the -1..1 range for balanced axes, and then scaled.
|
|
|
|
Relative axes are simply converted to floating point and scaled as they
|
|
have no fixed limits.
|
|
|
|
Relative axes should have \a minzone and \a maxzone set to 0, or weird
|
|
things will happen.
|
|
|
|
\a min and \a max normally come from the system. \a min and \a max being
|
|
equal indicates the axis is relative.
|
|
|
|
\a deadzone applies only to balanced axes, thus it doubles as a flag
|
|
for balanced (>= 0) or unbalanced (< 0). However, relative axes are always
|
|
balanced and so \a deadzone < 0 is the same as 0 for relative axes.
|
|
|
|
\a curve is applied after the input has been converted to a float, and the
|
|
0..1 or -1..1 ranges for absolute axes.
|
|
|
|
\a scale is applied after \a curve for absolute axes, but before \a curve
|
|
for relative axes.
|
|
*/
|
|
typedef struct in_recipe_s {
|
|
int min; ///< Axis minimum value (from system)
|
|
int max; ///< Axis maximum value (from system)
|
|
int minzone; ///< Size of deadzone near axis minimum
|
|
int maxzone; ///< Size of deadzone near axis maximum
|
|
int deadzone; ///< Size of deadzone near axis center (balanced)
|
|
float curve; ///< Power factor for absolute axes
|
|
float scale; ///< Final scale factor
|
|
} in_recipe_t;
|
|
|
|
typedef enum {
|
|
ina_set, ///< write the axis value to the destination
|
|
ina_accumulate, ///< add the axis value to the destination
|
|
} in_axis_mode;
|
|
|
|
/*** Logical axis.
|
|
|
|
Logical axes are the inputs defined by the game on which axis inputs
|
|
(usually "physical" axes) can act. Depending on the mode, the physical
|
|
axis value is either written as-is, or added to the existing value. It is
|
|
the responsibility of the code using the axis to clear the value for
|
|
accumulated inputs.
|
|
*/
|
|
typedef struct in_axis_s {
|
|
float value; ///< converted value of the axis
|
|
in_axis_mode mode; ///< method used for updating the destination
|
|
float abs_input; ///< input from an absolute axis (eg, joystick)
|
|
float rel_input; ///< input from a relative axis (eg, mouse)
|
|
#ifndef __QFCC__
|
|
struct axis_listener_set_s *listeners;
|
|
const char *name;
|
|
const char *description;
|
|
#endif
|
|
} in_axis_t;
|
|
|
|
#ifndef __QFCC__
|
|
typedef struct axis_listener_set_s LISTENER_SET_TYPE (in_axis_t)
|
|
axis_listener_set_t;
|
|
|
|
/*** Function type for axis listeners.
|
|
*/
|
|
typedef void (*axis_listener_t) (void *data, const in_axis_t *axis);
|
|
#endif
|
|
|
|
/*** Current state of the logical button.
|
|
|
|
Captures the current state and any transitions during the last frame.
|
|
Not all combinations are valid (inb_edge_up|inb_down and inb_edge_down
|
|
(no inb_down) are not valid states), but inb_edge_up|inb_edge_down )with
|
|
or without inb_down) is valid as it represents a double transition during
|
|
the frame.
|
|
*/
|
|
typedef enum {
|
|
inb_down = 1<<0, ///< button is held
|
|
inb_edge_down = 1<<1, ///< button pressed this frame
|
|
inb_edge_up = 1<<2, ///< button released this frame
|
|
} in_button_state;
|
|
|
|
/*** Logical button.
|
|
|
|
Logical buttons are the inputs defined by the game on which button inputs
|
|
(usually "physical" buttons) can act. Up to two button inputs can be
|
|
bound to a logical button. The logical button acts as an or gate where
|
|
either input will put the logical button in the pressed state, and both
|
|
inputs must be inactive for the logical button to be released.
|
|
*/
|
|
typedef struct in_button_s {
|
|
int down[2]; ///< button ids holding this button down
|
|
int state; ///< in_button_state
|
|
#ifndef __QFCC__
|
|
struct button_listener_set_s *listeners;
|
|
const char *name;
|
|
const char *description;
|
|
#endif
|
|
} in_button_t;
|
|
|
|
#ifndef __QFCC__
|
|
typedef struct button_listener_set_s LISTENER_SET_TYPE (in_button_t)
|
|
button_listener_set_t;
|
|
|
|
/*** Function type for button listeners.
|
|
*/
|
|
typedef void (*button_listener_t) (void *data, const in_button_t *button);
|
|
|
|
typedef struct in_axisbinding_s {
|
|
in_recipe_t *recipe;
|
|
in_axis_t *axis;
|
|
} in_axisbinding_t;
|
|
|
|
typedef enum {
|
|
inb_button,
|
|
inb_command,
|
|
} in_button_type;
|
|
|
|
typedef struct in_buttonbinding_s {
|
|
in_button_type type;
|
|
union {
|
|
in_button_t *button;
|
|
char *command;
|
|
};
|
|
} in_buttonbinding_t;
|
|
|
|
/*** Represent the button's activity in the last frame as a float.
|
|
|
|
The detected activity is:
|
|
steady off (up)
|
|
steady on (down)
|
|
off to on (up to down) transition
|
|
on to off )down to up) transition
|
|
pulse on (off-on-off or up-down-up)
|
|
pulse off (on-off-on or down-up-down)
|
|
Any additional transitions are treated as a pulse appropriate for the
|
|
final state of the button.
|
|
|
|
\param button Pointer to the button being tested.
|
|
\return Float value between 0 (off/up) and 1 (on/down)
|
|
\note The edge transitions are cleared, so for each frame, this
|
|
is a one-shot function (ie, it is NOT idempotent).
|
|
*/
|
|
GNU89INLINE inline float IN_ButtonState (in_button_t *button);
|
|
|
|
/*** Test whether a button has been pressed in the last frame.
|
|
|
|
Both steady-state on, and brief clicks are detected.
|
|
|
|
\param button Pointer to the button being tested.
|
|
\return True if the button is currently held or was pulsed on
|
|
in the last frame.
|
|
\note The edge transitions are cleared, so for each frame, this
|
|
is a one-shot function (ie, it is NOT idempotent).
|
|
*/
|
|
GNU89INLINE inline int IN_ButtonPressed (in_button_t *button);
|
|
|
|
/*** Test whether a button was released in the last frame.
|
|
|
|
Valid only if the button is still released. A pulsed off does not
|
|
count as being released as the button is still held.
|
|
|
|
\param button Pointer to the button being tested.
|
|
\return True if the button is currently released and the release
|
|
was in the last frame.
|
|
\note The edge transitions are cleared, so for each frame, this
|
|
is a one-shot function (ie, it is NOT idempotent).
|
|
*/
|
|
GNU89INLINE inline int IN_ButtonReleased (in_button_t *button);
|
|
|
|
/*** Update the axis value based on its mode and clear its relative input.
|
|
|
|
The absolute and relative inputs are separate because absolute inputs
|
|
usually get written when the input actually changes (and thus must not
|
|
be cleared each frame), while relative inputs indicate a per-frame delta
|
|
and thus must be cleared each frame.
|
|
|
|
\param axis Pointer to the axis being updated.
|
|
\return The resulting output value of the axis.
|
|
\note The relative input (\a rel_input) is zeroed.
|
|
*/
|
|
GNU89INLINE inline float IN_UpdateAxis (in_axis_t *axis);
|
|
|
|
/*** Update and clamp the axis value (see IN_UpdateAxis())
|
|
|
|
Like IN_UpdateAxis(), but clamps the final output to the specified range.
|
|
This is most useful for \a ina_accumulate axes, but can be used to ensure
|
|
\a ina_set axes never exceed a given range.
|
|
|
|
The absolute and relative inputs are separate because absolute inputs
|
|
usually get written when the input actually changes (and thus must not
|
|
be cleared each frame), while relative inputs indicate a per-frame delta
|
|
and thus must be cleared each frame.
|
|
|
|
\param axis Pointer to the axis being updated.
|
|
\param minval The minimum value to which the axis output will be clamped.
|
|
\param minval The minimum value to which the axis output will be clamped.
|
|
\return The resulting output value of the axis.
|
|
\note The relative input (\a rel_input) is zeroed. The absolute
|
|
input is not affected by the clamping, only the output
|
|
\a value.
|
|
*/
|
|
GNU89INLINE inline float IN_ClampAxis (in_axis_t *axis,
|
|
float minval, float maxval);
|
|
|
|
#ifndef IMPLEMENT_INPUT_Funcs
|
|
GNU89INLINE inline
|
|
#else
|
|
VISIBLE
|
|
#endif
|
|
float
|
|
IN_ButtonState (in_button_t *button)
|
|
{
|
|
static const float state_values[8] = {
|
|
// held down for the entire frame
|
|
[inb_down] = 1,
|
|
// released this frame
|
|
[inb_edge_up] = 0, // instant falloff
|
|
// pressed this frame
|
|
[inb_edge_down|inb_down] = 0.5,
|
|
// pressed and released this frame
|
|
[inb_edge_down|inb_edge_up] = 0.25,
|
|
// released and pressed this frame
|
|
[inb_edge_down|inb_edge_up|inb_down] = 0.75,
|
|
};
|
|
int state = button->state;
|
|
button->state &= inb_down; // clear edges, preserve pressed
|
|
return state_values[state & (inb_down|inb_edge_down|inb_edge_up)];
|
|
}
|
|
|
|
#ifndef IMPLEMENT_INPUT_Funcs
|
|
GNU89INLINE inline
|
|
#else
|
|
VISIBLE
|
|
#endif
|
|
int
|
|
IN_ButtonPressed (in_button_t *button)
|
|
{
|
|
int state = button->state;
|
|
button->state &= inb_down; // clear edges, preserve pressed
|
|
// catch even press and release that occurs between frames
|
|
return (state & (inb_down | inb_edge_down)) != 0;
|
|
}
|
|
|
|
#ifndef IMPLEMENT_INPUT_Funcs
|
|
GNU89INLINE inline
|
|
#else
|
|
VISIBLE
|
|
#endif
|
|
int
|
|
IN_ButtonReleased (in_button_t *button)
|
|
{
|
|
int state = button->state;
|
|
button->state &= inb_down; // clear edges, preserve pressed
|
|
// catch only full release (a pulsed on does count as a release)
|
|
return (state & (inb_down | inb_edge_up)) == inb_edge_up;
|
|
}
|
|
|
|
#ifndef IMPLEMENT_INPUT_Funcs
|
|
GNU89INLINE inline
|
|
#else
|
|
VISIBLE
|
|
#endif
|
|
float
|
|
IN_UpdateAxis (in_axis_t *axis)
|
|
{
|
|
float prev_value = axis->value;
|
|
switch (axis->mode) {
|
|
case ina_set:
|
|
axis->value = axis->abs_input + axis->rel_input;
|
|
break;
|
|
case ina_accumulate:
|
|
axis->value += axis->abs_input + axis->rel_input;
|
|
break;
|
|
}
|
|
axis->rel_input = 0;
|
|
if (axis->value != prev_value && axis->listeners) {
|
|
LISTENER_INVOKE (axis->listeners, axis);
|
|
}
|
|
return axis->value;
|
|
}
|
|
|
|
#ifndef IMPLEMENT_INPUT_Funcs
|
|
GNU89INLINE inline
|
|
#else
|
|
VISIBLE
|
|
#endif
|
|
float
|
|
IN_ClampAxis (in_axis_t *axis, float minval, float maxval)
|
|
{
|
|
float prev_value = axis->value;
|
|
switch (axis->mode) {
|
|
case ina_set:
|
|
axis->value = axis->abs_input + axis->rel_input;
|
|
break;
|
|
case ina_accumulate:
|
|
axis->value += axis->abs_input + axis->rel_input;
|
|
break;
|
|
}
|
|
axis->rel_input = 0;
|
|
axis->value = bound (minval, axis->value, maxval);
|
|
if (axis->value != prev_value && axis->listeners) {
|
|
LISTENER_INVOKE (axis->listeners, axis);
|
|
}
|
|
return axis->value;
|
|
}
|
|
|
|
void IN_ButtonAction (in_button_t *buttin, int id, int pressed);
|
|
|
|
int IN_RegisterButton (in_button_t *button);
|
|
int IN_RegisterAxis (in_axis_t *axis);
|
|
in_button_t *IN_FindButton (const char *name);
|
|
in_axis_t *IN_FindAxis (const char *name);
|
|
void IN_ButtonAddListener (in_button_t *button, button_listener_t listener,
|
|
void *data);
|
|
void IN_ButtonRemoveListener (in_button_t *button, button_listener_t listener,
|
|
void *data);
|
|
void IN_AxisAddListener (in_axis_t *axis, axis_listener_t listener,
|
|
void *data);
|
|
void IN_AxisRemoveListener (in_axis_t *axis, axis_listener_t listener,
|
|
void *data);
|
|
|
|
struct IE_event_s;
|
|
int IN_Binding_HandleEvent (const struct IE_event_s *ie_event);
|
|
void IN_Binding_Activate (void);
|
|
void IN_Binding_Init (void);
|
|
struct plitem_s;
|
|
void IN_Binding_SaveConfig (struct plitem_s *config);
|
|
void IN_Binding_LoadConfig (struct plitem_s *config);
|
|
|
|
#endif
|
|
|
|
#endif//__QF_input_binding_h
|