[util] Add listeners to cvars

Listeners are separate to the main callback as listeners have only
read-only access to the objects, but the main callback is free to modify
the cvar and thus can act as a parser and validator. The listeners are
invoked after the main callback if the cvar is modified. There does not
need to be a main callback for the listeners to be invoked.
This commit is contained in:
Bill Currie 2021-11-25 17:46:16 +09:00
parent 3f299155a3
commit c4118a4bf1
2 changed files with 42 additions and 3 deletions

View file

@ -33,6 +33,7 @@
*/
///@{
#include "QF/listener.h"
#include "QF/qtypes.h"
#include "QF/quakeio.h"
@ -51,6 +52,7 @@ typedef struct cvar_s {
\param var This cvar.
*/
void (*callback)(struct cvar_s *var);
struct cvar_listener_set_s *listeners;
const char *description; ///< for "help" command
float value; ///< The current cvar value as a float
int int_val; ///< The current cvar value as an integer
@ -58,6 +60,10 @@ typedef struct cvar_s {
struct cvar_s *next; ///< \internal Linked list of cvars.
} cvar_t;
typedef struct cvar_listener_set_s LISTENER_SET_TYPE (cvar_t)
cvar_listener_set_t;
typedef void (*cvar_listener_t) (void *data, const cvar_t *cvar);
typedef struct cvar_alias_s {
char *name; ///< The name of the alias.
cvar_t *cvar; ///< The cvar to which this alias refers
@ -97,6 +103,8 @@ cvar_t *Cvar_FindAlias (const char *alias_name);
cvar_t *Cvar_MakeAlias (const char *name, cvar_t *cvar);
cvar_t *Cvar_RemoveAlias (const char *name);
void Cvar_AddListener (cvar_t *cvar, cvar_listener_t listener, void *data);
void Cvar_RemoveListener (cvar_t *cvar, cvar_listener_t listener, void *data);
// equivelants to "<name> <variable>" typed at the console
void Cvar_Set (cvar_t *var, const char *value);

View file

@ -249,6 +249,24 @@ Cvar_CompleteBuildList (const char *partial)
return buf;
}
VISIBLE void
Cvar_AddListener (cvar_t *cvar, cvar_listener_t listener, void *data)
{
if (!cvar->listeners) {
cvar->listeners = malloc (sizeof (*cvar->listeners));
LISTENER_SET_INIT (cvar->listeners, 8);
}
LISTENER_ADD (cvar->listeners, listener, data);
}
VISIBLE void
Cvar_RemoveListener (cvar_t *cvar, cvar_listener_t listener, void *data)
{
if (cvar->listeners) {
LISTENER_REMOVE (cvar->listeners, listener, data);
}
}
VISIBLE void
Cvar_Set (cvar_t *var, const char *value)
{
@ -279,6 +297,10 @@ Cvar_Set (cvar_t *var, const char *value)
if (var->callback)
var->callback (var);
if (var->listeners) {
LISTENER_INVOKE (var->listeners, var);
}
}
}
@ -710,7 +732,7 @@ VISIBLE cvar_t *
Cvar_Get (const char *name, const char *string, int cvarflags,
void (*callback)(cvar_t*), const char *description)
{
int changed = 0;
cvar_t *var;
if (Cmd_Exists (name)) {
@ -740,10 +762,13 @@ Cvar_Get (const char *name, const char *string, int cvarflags,
break;
var->next = *v;
*v = var;
changed = 1;
} else {
// Cvar does exist, so we update the flags and return.
var->flags &= ~CVAR_USER_CREATED;
var->flags |= cvarflags;
changed = !strequal (var->string, string) || var->callback != callback;
if (!var->callback)
var->callback = callback;
if (!var->description
@ -753,8 +778,14 @@ Cvar_Get (const char *name, const char *string, int cvarflags,
if (!var->default_string)
var->default_string = strdup (string);
}
if (var->callback)
var->callback (var);
if (changed) {
if (var->callback)
var->callback (var);
if (var->listeners) {
LISTENER_INVOKE (var->listeners, var);
}
}
return var;
}