mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-11-26 14:20:59 +00:00
973ae0ad54
This is meant for a "permanent" tear-down before freeing the memory holding the VM state or at program shutdown. As a consequence, builtin sub-systems registering resources are now required to pass a "destroy" function pointer that will be called just before the memory holding those resources is freed by the VM resource manager (ie, the manager owns the resource memory block, but each subsystem is responsible for cleaning up any resources held within that block). This even enhances thread-safety in rua_obj (there are some problems with cmd, cvar, and gib).
531 lines
13 KiB
C
531 lines
13 KiB
C
/*
|
|
bi_input.c
|
|
|
|
CSQC file builtins
|
|
|
|
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
|
|
|
|
*/
|
|
#ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#endif
|
|
|
|
#include <stdlib.h>
|
|
#ifdef HAVE_STRING_H
|
|
# include <string.h>
|
|
#endif
|
|
#ifdef HAVE_STRINGS_H
|
|
# include <strings.h>
|
|
#endif
|
|
|
|
#include "QF/cmem.h"
|
|
#include "QF/hash.h"
|
|
#include "QF/input.h"
|
|
#include "QF/progs.h"
|
|
|
|
#include "rua_internal.h"
|
|
|
|
typedef struct rua_in_cookie_s {
|
|
size_t users;
|
|
progs_t *pr;
|
|
pr_func_t func;
|
|
pr_ptr_t data;
|
|
} rua_in_cookie_t;
|
|
|
|
typedef struct input_resources_s {
|
|
hashctx_t *hashctx;
|
|
hashtab_t *cookies;
|
|
memsuper_t *cookie_super;
|
|
} input_resources_t;
|
|
|
|
static void
|
|
bi_IN_FindDeviceId (progs_t *pr, void *_res)
|
|
{
|
|
const char *id = P_GSTRING (pr, 0);
|
|
|
|
R_INT (pr) = IN_FindDeviceId (id);
|
|
}
|
|
|
|
static void
|
|
bi_IN_GetDeviceName (progs_t *pr, void *_res)
|
|
{
|
|
int devid = P_INT (pr, 0);
|
|
|
|
RETURN_STRING (pr, IN_GetDeviceName (devid));
|
|
}
|
|
|
|
static void
|
|
bi_IN_GetDeviceId (progs_t *pr, void *_res)
|
|
{
|
|
int devid = P_INT (pr, 0);
|
|
|
|
RETURN_STRING (pr, IN_GetDeviceId (devid));
|
|
}
|
|
|
|
static void
|
|
bi_IN_AxisInfo (progs_t *pr, void *_res)
|
|
{
|
|
}
|
|
|
|
static void
|
|
bi_IN_ButtonInfo (progs_t *pr, void *_res)
|
|
{
|
|
}
|
|
|
|
static void
|
|
bi_IN_GetAxisName (progs_t *pr, void *_res)
|
|
{
|
|
int devid = P_INT (pr, 0);
|
|
int axis = P_INT (pr, 1);
|
|
|
|
RETURN_STRING (pr, IN_GetAxisName (devid, axis));
|
|
}
|
|
|
|
static void
|
|
bi_IN_GetButtonName (progs_t *pr, void *_res)
|
|
{
|
|
int devid = P_INT (pr, 0);
|
|
int button = P_INT (pr, 1);
|
|
|
|
RETURN_STRING (pr, IN_GetButtonName (devid, button));
|
|
}
|
|
|
|
static void
|
|
bi_IN_GetAxisNumber (progs_t *pr, void *_res)
|
|
{
|
|
int devid = P_INT (pr, 0);
|
|
const char *axis_name = P_GSTRING (pr, 1);
|
|
|
|
R_INT (pr) = IN_GetAxisNumber (devid, axis_name);
|
|
}
|
|
|
|
static void
|
|
bi_IN_GetButtonNumber (progs_t *pr, void *_res)
|
|
{
|
|
int devid = P_INT (pr, 0);
|
|
const char *button_name = P_GSTRING (pr, 1);
|
|
|
|
R_INT (pr) = IN_GetButtonNumber (devid, button_name);
|
|
}
|
|
|
|
static void
|
|
bi_IN_ProcessEvents (progs_t *pr, void *_res)
|
|
{
|
|
IN_ProcessEvents ();
|
|
}
|
|
|
|
static void
|
|
bi_IN_ClearStates (progs_t *pr, void *_res)
|
|
{
|
|
IN_ClearStates ();
|
|
}
|
|
|
|
static void
|
|
bi_IN_CreateButton (progs_t *pr, void *_res)
|
|
{
|
|
const char *name = P_GSTRING (pr, 0);
|
|
const char *desc = P_GSTRING (pr, 1);
|
|
in_button_t *button = PR_Zone_Malloc (pr, sizeof (in_button_t));
|
|
button->name = name;
|
|
button->description = desc;
|
|
IN_RegisterButton (button);
|
|
RETURN_POINTER (pr, button);
|
|
}
|
|
|
|
static void
|
|
bi_IN_CreateAxis (progs_t *pr, void *_res)
|
|
{
|
|
const char *name = P_GSTRING (pr, 0);
|
|
const char *desc = P_GSTRING (pr, 1);
|
|
in_axis_t *axis = PR_Zone_Malloc (pr, sizeof (in_axis_t));
|
|
axis->name = name;
|
|
axis->description = desc;
|
|
IN_RegisterAxis (axis);
|
|
RETURN_POINTER (pr, axis);
|
|
}
|
|
|
|
static void
|
|
bi_IN_GetAxisInfo (progs_t *pr, void *_res)
|
|
{
|
|
int devid = P_INT (pr, 0);
|
|
int axis = P_INT (pr, 1);
|
|
|
|
R_INT (pr) = 0;
|
|
|
|
in_axisinfo_t info;
|
|
if (IN_GetAxisInfo (devid, axis, &info)) {
|
|
P_STRUCT (pr, in_axisinfo_t, 2) = info;
|
|
R_INT (pr) = 1;
|
|
}
|
|
}
|
|
|
|
static void
|
|
bi_IN_GetButtonInfo (progs_t *pr, void *_res)
|
|
{
|
|
int devid = P_INT (pr, 0);
|
|
int button = P_INT (pr, 1);
|
|
|
|
R_INT (pr) = 0;
|
|
|
|
in_buttoninfo_t info;
|
|
if (IN_GetButtonInfo (devid, button, &info)) {
|
|
P_STRUCT (pr, in_buttoninfo_t, 2) = info;
|
|
R_INT (pr) = 1;
|
|
}
|
|
}
|
|
|
|
static rua_in_cookie_t *
|
|
make_cookie (progs_t *pr, input_resources_t *res, pr_func_t func, pr_ptr_t data)
|
|
{
|
|
rua_in_cookie_t search = {
|
|
.func = func,
|
|
.data = data,
|
|
};
|
|
rua_in_cookie_t *cookie = Hash_FindElement (res->cookies, &search);
|
|
if (!cookie) {
|
|
cookie = cmemalloc (res->cookie_super, sizeof (rua_in_cookie_t));
|
|
*cookie = search;
|
|
cookie->pr = pr;
|
|
Hash_AddElement (res->cookies, cookie);
|
|
}
|
|
cookie->users++;
|
|
return cookie;
|
|
}
|
|
|
|
static rua_in_cookie_t *
|
|
find_cookie (progs_t *pr, input_resources_t *res, pr_func_t func, pr_ptr_t data)
|
|
{
|
|
rua_in_cookie_t search = {
|
|
.func = func,
|
|
.data = data,
|
|
};
|
|
return Hash_FindElement (res->cookies, &search);
|
|
}
|
|
|
|
static void
|
|
release_cookie (progs_t *pr, input_resources_t *res, rua_in_cookie_t *cookie)
|
|
{
|
|
if (!--cookie->users) {
|
|
Hash_DelElement (res->cookies, cookie);
|
|
Hash_Free (res->cookies, cookie);
|
|
}
|
|
}
|
|
|
|
static void
|
|
rua_add_axis_listener (progs_t *pr, input_resources_t *res,
|
|
axis_listener_t listener)
|
|
{
|
|
in_axis_t *axis = &P_STRUCT (pr, in_axis_t, 0);
|
|
pr_func_t func = P_FUNCTION (pr, 1);
|
|
pr_func_t data = P_POINTER (pr, 2);
|
|
rua_in_cookie_t *cookie = make_cookie (pr, res, func, data);
|
|
IN_AxisAddListener (axis, listener, cookie);
|
|
}
|
|
|
|
static void
|
|
rua_remove_axis_listener (progs_t *pr, input_resources_t *res,
|
|
axis_listener_t listener)
|
|
{
|
|
in_axis_t *axis = &P_STRUCT (pr, in_axis_t, 0);
|
|
pr_func_t func = P_FUNCTION (pr, 1);
|
|
pr_func_t data = P_POINTER (pr, 2);
|
|
rua_in_cookie_t *cookie = find_cookie (pr, res, func, data);
|
|
if (cookie) {
|
|
IN_AxisRemoveListener (axis, listener, cookie);
|
|
release_cookie (pr, res, cookie);
|
|
}
|
|
}
|
|
|
|
static void
|
|
rua_add_button_listener (progs_t *pr, input_resources_t *res,
|
|
button_listener_t listener)
|
|
{
|
|
in_button_t *button = &P_STRUCT (pr, in_button_t, 0);
|
|
pr_func_t func = P_FUNCTION (pr, 1);
|
|
pr_func_t data = P_POINTER (pr, 2);
|
|
rua_in_cookie_t *cookie = make_cookie (pr, res, func, data);
|
|
IN_ButtonAddListener (button, listener, cookie);
|
|
}
|
|
|
|
static void
|
|
rua_remove_button_listener (progs_t *pr, input_resources_t *res,
|
|
button_listener_t listener)
|
|
{
|
|
in_button_t *button = &P_STRUCT (pr, in_button_t, 0);
|
|
pr_func_t func = P_FUNCTION (pr, 1);
|
|
pr_func_t data = P_POINTER (pr, 2);
|
|
rua_in_cookie_t *cookie = find_cookie (pr, res, func, data);
|
|
if (cookie) {
|
|
IN_ButtonRemoveListener (button, listener, cookie);
|
|
release_cookie (pr, res, cookie);
|
|
}
|
|
}
|
|
|
|
static void
|
|
rua_listener_func (rua_in_cookie_t *cookie, const void *input)
|
|
{
|
|
progs_t *pr = cookie->pr;
|
|
PR_PushFrame (pr);
|
|
PR_RESET_PARAMS (pr);
|
|
P_POINTER (pr, 0) = cookie->data;
|
|
P_POINTER (pr, 1) = PR_SetPointer (pr, input);//FIXME check input
|
|
pr->pr_argc = 2;
|
|
PR_ExecuteProgram (pr, cookie->func);
|
|
PR_PopFrame (pr);
|
|
}
|
|
|
|
static void
|
|
rua_listener_method (rua_in_cookie_t *cookie, const void *input)
|
|
{
|
|
progs_t *pr = cookie->pr;
|
|
PR_PushFrame (pr);
|
|
PR_RESET_PARAMS (pr);
|
|
P_POINTER (pr, 0) = cookie->data;
|
|
P_POINTER (pr, 1) = 0; // don't know the method name (selector)
|
|
P_POINTER (pr, 2) = PR_SetPointer (pr, input);//FIXME check input
|
|
pr->pr_argc = 3;
|
|
PR_ExecuteProgram (pr, cookie->func);
|
|
PR_PopFrame (pr);
|
|
}
|
|
|
|
static void
|
|
rua_axis_listener_func (void *data, const in_axis_t *axis)
|
|
{
|
|
rua_listener_func (data, axis);
|
|
}
|
|
|
|
static void
|
|
rua_axis_listener_method (void *data, const in_axis_t *axis)
|
|
{
|
|
rua_listener_method (data, axis);
|
|
}
|
|
|
|
static void
|
|
rua_button_listener_func (void *data, const in_button_t *button)
|
|
{
|
|
rua_listener_func (data, button);
|
|
}
|
|
|
|
static void
|
|
rua_button_listener_method (void *data, const in_button_t *button)
|
|
{
|
|
rua_listener_method (data, button);
|
|
}
|
|
|
|
static void
|
|
rua_IN_ButtonAddListener_func (progs_t *pr, void *_res)
|
|
{
|
|
input_resources_t *res = _res;
|
|
rua_add_button_listener (pr, res, rua_button_listener_func);
|
|
}
|
|
|
|
static void
|
|
rua_IN_ButtonRemoveListener_func (progs_t *pr, void *_res)
|
|
{
|
|
input_resources_t *res = _res;
|
|
rua_remove_button_listener (pr, res, rua_button_listener_func);
|
|
}
|
|
|
|
static void
|
|
rua_IN_AxisAddListener_func (progs_t *pr, void *_res)
|
|
{
|
|
input_resources_t *res = _res;
|
|
rua_add_axis_listener (pr, res, rua_axis_listener_func);
|
|
}
|
|
|
|
static void
|
|
rua_IN_AxisRemoveListener_func (progs_t *pr, void *_res)
|
|
{
|
|
input_resources_t *res = _res;
|
|
rua_remove_axis_listener (pr, res, rua_axis_listener_func);
|
|
}
|
|
|
|
static void
|
|
rua_IN_ButtonAddListener_method (progs_t *pr, void *_res)
|
|
{
|
|
input_resources_t *res = _res;
|
|
rua_add_button_listener (pr, res, rua_button_listener_method);
|
|
}
|
|
|
|
static void
|
|
rua_IN_ButtonRemoveListener_method (progs_t *pr, void *_res)
|
|
{
|
|
input_resources_t *res = _res;
|
|
rua_remove_button_listener (pr, res, rua_button_listener_method);
|
|
}
|
|
|
|
static void
|
|
rua_IN_AxisAddListener_method (progs_t *pr, void *_res)
|
|
{
|
|
input_resources_t *res = _res;
|
|
rua_add_axis_listener (pr, res, rua_axis_listener_method);
|
|
}
|
|
|
|
static void
|
|
rua_IN_AxisRemoveListener_method (progs_t *pr, void *_res)
|
|
{
|
|
input_resources_t *res = _res;
|
|
rua_remove_axis_listener (pr, res, rua_axis_listener_method);
|
|
}
|
|
|
|
static void
|
|
bi_IN_LoadConfig (progs_t *pr, void *_res)
|
|
{
|
|
IN_LoadConfig (Plist_GetItem (pr, P_INT (pr, 0)));
|
|
}
|
|
|
|
static void
|
|
bi_IMT_CreateContext (progs_t *pr, void *_res)
|
|
{
|
|
const char *name = P_GSTRING (pr, 0);
|
|
R_INT (pr) = IMT_CreateContext (name);
|
|
}
|
|
|
|
static void
|
|
bi_IMT_GetContext (progs_t *pr, void *_res)
|
|
{
|
|
R_INT (pr) = IMT_GetContext ();
|
|
}
|
|
|
|
static void
|
|
bi_IMT_SetContext (progs_t *pr, void *_res)
|
|
{
|
|
IMT_SetContext (P_INT (pr, 0));
|
|
}
|
|
|
|
static void
|
|
secured (progs_t *pr, void *_res)
|
|
{
|
|
PR_RunError (pr, "Secured function called");
|
|
}
|
|
|
|
#define p(type) PR_PARAM(type)
|
|
#define P(a, s) { .size = (s), .alignment = BITOP_LOG2 (a), }
|
|
#define bi(x,np,params...) {#x, secured, -1, np, {params}}
|
|
static builtin_t secure_builtins[] = {
|
|
bi(IN_CreateButton, 2, p(string), p(string)),
|
|
bi(IN_CreateAxis, 2, p(string), p(string)),
|
|
bi(IN_LoadConfig, 1, p(ptr)),
|
|
{0}
|
|
};
|
|
|
|
#undef bi
|
|
#define bi(x,np,params...) {#x, bi_##x, -1, np, {params}}
|
|
static builtin_t insecure_builtins[] = {
|
|
bi(IN_CreateButton, 2, p(string), p(string)),
|
|
bi(IN_CreateAxis, 2, p(string), p(string)),
|
|
bi(IN_LoadConfig, 1, p(ptr)),
|
|
{0}
|
|
};
|
|
static builtin_t builtins[] = {
|
|
bi(IN_FindDeviceId, 1, p(string)),
|
|
bi(IN_GetDeviceName, 1, p(int)),
|
|
bi(IN_GetDeviceId, 1, p(int)),
|
|
bi(IN_AxisInfo, 0), //FIXME
|
|
bi(IN_ButtonInfo, 0), //FIXME
|
|
bi(IN_GetAxisName, 2, p(int), p(int)),
|
|
bi(IN_GetButtonName, 2, p(int), p(int)),
|
|
bi(IN_GetAxisNumber, 2, p(int), p(string)),
|
|
bi(IN_GetButtonNumber, 2, p(int), p(string)),
|
|
bi(IN_ProcessEvents, 0),
|
|
bi(IN_ClearStates, 0),
|
|
bi(IN_GetAxisInfo, 3, p(int), p(int), p(ptr)),
|
|
bi(IN_GetButtonInfo, 3, p(int), p(int), p(ptr)),
|
|
{"IN_ButtonAddListener|^{tag in_button_s=}^(v^v^{tag in_button_s=})^v",
|
|
rua_IN_ButtonAddListener_func, -1, 3, {p(ptr), p(func), p(ptr)}},
|
|
{"IN_ButtonRemoveListener|^{tag in_button_s=}^(v^v^{tag in_button_s=})^v",
|
|
rua_IN_ButtonRemoveListener_func, -1, 3, {p(ptr), p(func), p(ptr)}},
|
|
{"IN_AxisAddListener|^{tag in_axis_s=}^(v^v^{tag in_axis_s=})^v",
|
|
rua_IN_AxisAddListener_func, -1, 3, {p(ptr), p(func), p(ptr)}},
|
|
{"IN_AxisRemoveListener|^{tag in_axis_s=}^(v^v^{tag in_axis_s=})^v",
|
|
rua_IN_AxisRemoveListener_func, -1, 3, {p(ptr), p(func), p(ptr)}},
|
|
{"IN_ButtonAddListener|^{tag in_button_s=}(@@:.)@",
|
|
rua_IN_ButtonAddListener_method, -1, 3, {p(ptr), p(func), p(ptr)}},
|
|
{"IN_ButtonRemoveListener|^{tag in_button_s=}(@@:.)@",
|
|
rua_IN_ButtonRemoveListener_method, -1, 3, {p(ptr), p(func), p(ptr)}},
|
|
{"IN_AxisAddListener|^{tag in_axis_s=}(@@:.)@",
|
|
rua_IN_AxisAddListener_method, -1, 3, {p(ptr), p(func), p(ptr)}},
|
|
{"IN_AxisRemoveListener|^{tag in_axis_s=}(@@:.)@",
|
|
rua_IN_AxisRemoveListener_method, -1, 3, {p(ptr), p(func), p(ptr)}},
|
|
|
|
bi(IMT_CreateContext, 1, p(string)),
|
|
bi(IMT_GetContext, 0),
|
|
bi(IMT_SetContext, 1, p(int)),
|
|
|
|
{0}
|
|
};
|
|
|
|
static void
|
|
bi_input_clear (progs_t *pr, void *_res)
|
|
{
|
|
input_resources_t *res = _res;
|
|
Hash_FlushTable (res->cookies);
|
|
}
|
|
|
|
static void
|
|
bi_input_destroy (progs_t *pr, void *_res)
|
|
{
|
|
input_resources_t *res = _res;
|
|
Hash_DelTable (res->cookies);
|
|
delete_memsuper (res->cookie_super);
|
|
}
|
|
|
|
static uintptr_t
|
|
rua_in_hash_cookie (const void *_cookie, void *_res)
|
|
{
|
|
const rua_in_cookie_t *cookie = _cookie;
|
|
return cookie->func + cookie->data;
|
|
}
|
|
|
|
static int
|
|
rua_in_cmp_cookies (const void *_a, const void *_b, void *_res)
|
|
{
|
|
const rua_in_cookie_t *a = _a;
|
|
const rua_in_cookie_t *b = _b;
|
|
return a->func == b->func && a->data == b->data;
|
|
}
|
|
|
|
static void
|
|
rua_in_free_cookie (void *_cookie, void *_res)
|
|
{
|
|
input_resources_t *res = _res;
|
|
rua_in_cookie_t *cookie = _cookie;
|
|
cmemfree (res->cookie_super, cookie);
|
|
}
|
|
|
|
void
|
|
RUA_Input_Init (progs_t *pr, int secure)
|
|
{
|
|
input_resources_t *res = calloc (sizeof (input_resources_t), 1);
|
|
|
|
res->cookie_super = new_memsuper ();
|
|
res->cookies = Hash_NewTable (251, 0, rua_in_free_cookie, res,
|
|
&res->hashctx);
|
|
Hash_SetHashCompare (res->cookies, rua_in_hash_cookie, rua_in_cmp_cookies);
|
|
|
|
PR_Resources_Register (pr, "input", res, bi_input_clear, bi_input_destroy);
|
|
if (secure & 2) {
|
|
PR_RegisterBuiltins (pr, secure_builtins, res);
|
|
} else {
|
|
PR_RegisterBuiltins (pr, insecure_builtins, res);
|
|
}
|
|
PR_RegisterBuiltins (pr, builtins, res);
|
|
}
|