Freeciv-3.1
Loading...
Searching...
No Matches
luascript_signal.c
Go to the documentation of this file.
1/*****************************************************************************
2 Freeciv - Copyright (C) 2005 - The Freeciv Project
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; either version 2, or (at your option)
6 any later version.
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12*****************************************************************************/
13
14/*****************************************************************************
15 Signals implementation.
16
17 New signal types can be declared with script_signal_create. Each
18 signal should have a unique name string.
19 All signal declarations are in signals_create, for convenience.
20
21 A signal may have any number of Lua callback functions connected to it
22 at any given time.
23
24 A signal emission invokes all associated callbacks in the order they were
25 connected:
26
27 * A callback can stop the current signal emission, preventing the callbacks
28 connected after it from being invoked.
29
30 * A callback can detach itself from its associated signal.
31
32 Lua callbacks functions are able to do these via their return values.
33
34 All Lua callback functions can return a value. Example:
35 return false
36
37 If the value is 'true' the current signal emission will be stopped.
38*****************************************************************************/
39
40#ifdef HAVE_CONFIG_H
41#include <fc_config.h>
42#endif
43
44#include <stdarg.h>
45
46/* utility */
47#include "deprecations.h"
48#include "log.h"
49
50/* common/scriptcore */
51#include "luascript.h"
52#include "luascript_types.h"
53
54#include "luascript_signal.h"
55
56struct signal;
57struct signal_callback;
58
59/* get 'struct signal_callback_list' and related functions: */
60#define SPECLIST_TAG signal_callback
61#define SPECLIST_TYPE struct signal_callback
62#include "speclist.h"
63
64#define signal_callback_list_iterate(list, pcallback) \
65 TYPED_LIST_ITERATE(struct signal_callback, list, pcallback)
66#define signal_callback_list_iterate_end \
67 LIST_ITERATE_END
68
69static struct signal_callback *signal_callback_new(const char *name);
70static void signal_callback_destroy(struct signal_callback *pcallback);
71static struct signal *signal_new(int nargs, enum api_types *parg_types);
72static void signal_destroy(struct signal *psignal);
73
74/* Signal datastructure. */
75struct signal {
76 int nargs; /* number of arguments to pass */
77 enum api_types *arg_types; /* argument types */
78 struct signal_callback_list *callbacks; /* connected callbacks */
79 char *depr_msg; /* deprecation message to show if handler added */
80};
81
82/* Signal callback datastructure. */
84 char *name; /* callback function name */
85};
86
87/*****************************************************************************
88 Signal hash table.
89*****************************************************************************/
90#define SPECHASH_TAG luascript_signal
91#define SPECHASH_ASTR_KEY_TYPE
92#define SPECHASH_IDATA_TYPE struct signal *
93#define SPECHASH_IDATA_FREE signal_destroy
94#include "spechash.h"
95
96#define signal_hash_iterate(phash, key, data) \
97 TYPED_HASH_ITERATE(char *, struct signal *, phash, key, data)
98#define signal_hash_iterate_end \
99 HASH_ITERATE_END
100
101/* get 'struct luascript_signal_name_list' and related functions: */
102#define SPECLIST_TAG luascript_signal_name
103#define SPECLIST_TYPE char
104#include "speclist.h"
105
106#define luascript_signal_name_list_iterate(list, pname) \
107 TYPED_LIST_ITERATE(struct signal_callback, list, pcallback)
108#define luascript_signal_name_list_iterate_end \
109 LIST_ITERATE_END
110
111/**********************************************************************/
114static struct signal_callback *signal_callback_new(const char *name)
115{
116 struct signal_callback *pcallback = fc_malloc(sizeof(*pcallback));
117
118 pcallback->name = fc_strdup(name);
119 return pcallback;
120}
121
122/**********************************************************************/
125static void signal_callback_destroy(struct signal_callback *pcallback)
126{
127 free(pcallback->name);
128 free(pcallback);
129}
130
131/**********************************************************************/
134static struct signal *signal_new(int nargs, enum api_types *parg_types)
135{
136 struct signal *psignal = fc_malloc(sizeof(*psignal));
137
138 psignal->nargs = nargs;
139 psignal->arg_types = parg_types;
140 psignal->callbacks
141 = signal_callback_list_new_full(signal_callback_destroy);
142 psignal->depr_msg = NULL;
143
144 return psignal;
145}
146
147/**********************************************************************/
150static void signal_destroy(struct signal *psignal)
151{
152 if (psignal->arg_types) {
153 free(psignal->arg_types);
154 }
155 if (psignal->depr_msg) {
156 free(psignal->depr_msg);
157 }
158 signal_callback_list_destroy(psignal->callbacks);
159 free(psignal);
160}
161
162/**********************************************************************/
166 const char *signal_name,
167 va_list args)
168{
169 struct signal *psignal;
170
171 fc_assert_ret(fcl);
173
174 if (luascript_signal_hash_lookup(fcl->signals, signal_name, &psignal)) {
175 signal_callback_list_iterate(psignal->callbacks, pcallback) {
176 va_list args_cb;
177
178 va_copy(args_cb, args);
179 if (luascript_callback_invoke(fcl, pcallback->name, psignal->nargs,
180 psignal->arg_types, args_cb)) {
181 va_end(args_cb);
182 break;
183 }
184 va_end(args_cb);
186 } else {
187 luascript_log(fcl, LOG_ERROR, "Signal \"%s\" does not exist, so cannot "
188 "be invoked.", signal_name);
189 }
190}
191
192/**********************************************************************/
196 const char *signal_name, ...)
197{
198 va_list args;
199
200 va_start(args, signal_name);
201 luascript_signal_emit_valist(fcl, signal_name, args);
202 va_end(args);
203}
204
205/**********************************************************************/
209 const char *signal_name,
210 int nargs, va_list args)
211{
212 struct signal *psignal;
213
214 fc_assert_ret_val(fcl, NULL);
215 fc_assert_ret_val(fcl->signals, NULL);
216
217 if (luascript_signal_hash_lookup(fcl->signals, signal_name, &psignal)) {
218 luascript_log(fcl, LOG_ERROR, "Signal \"%s\" was already created.",
219 signal_name);
220 return NULL;
221 } else {
222 enum api_types *parg_types;
223 char *sn = fc_malloc(strlen(signal_name) + 1);
224 struct signal *created;
225
226 if (nargs > 0) {
227 int i;
228
229 parg_types = fc_calloc(nargs, sizeof(*parg_types));
230
231 for (i = 0; i < nargs; i++) {
232 *(parg_types + i) = va_arg(args, int);
233 }
234 } else {
235 parg_types = NULL;
236 }
237
238 created = signal_new(nargs, parg_types);
239 luascript_signal_hash_insert(fcl->signals, signal_name,
240 created);
241 strcpy(sn, signal_name);
242 luascript_signal_name_list_append(fcl->signal_names, sn);
243
244 return created;
245 }
246}
247
248/**********************************************************************/
252 const char *signal_name,
253 int nargs, ...)
254{
255 va_list args;
256 struct signal *created;
257
258 va_start(args, nargs);
259 created = luascript_signal_create_valist(fcl, signal_name, nargs, args);
260 va_end(args);
261
262 if (created != NULL) {
263 return &(created->depr_msg);
264 }
265
266 return NULL;
267}
268
269/**********************************************************************/
272void deprecate_signal(signal_deprecator *deprecator, char *signal_name,
273 char *replacement, char *deprecated_since)
274{
275 if (deprecator != NULL) {
276 char buffer[1024];
277
278 if (deprecated_since != NULL && replacement != NULL) {
279 fc_snprintf(buffer, sizeof(buffer),
280 "Deprecated: lua signal \"%s\", deprecated since \"%s\", used. "
281 "Use \"%s\" instead", signal_name, deprecated_since, replacement);
282 } else if (replacement != NULL) {
283 fc_snprintf(buffer, sizeof(buffer),
284 "Deprecated: lua signal \"%s\" used. Use \"%s\" instead",
285 signal_name, replacement);
286 } else {
287 fc_snprintf(buffer, sizeof(buffer),
288 "Deprecated: lua signal \"%s\" used.", signal_name);
289 }
290
291 *deprecator = fc_strdup(buffer);
292 }
293}
294
295/**********************************************************************/
298void luascript_signal_callback(struct fc_lua *fcl, const char *signal_name,
299 const char *callback_name, bool create)
300{
301 struct signal *psignal;
302 struct signal_callback *pcallback_found = NULL;
303
304 fc_assert_ret(fcl != NULL);
305 fc_assert_ret(fcl->signals != NULL);
306
307 if (luascript_signal_hash_lookup(fcl->signals, signal_name, &psignal)) {
308 /* check for a duplicate callback */
309 signal_callback_list_iterate(psignal->callbacks, pcallback) {
310 if (!strcmp(pcallback->name, callback_name)) {
311 pcallback_found = pcallback;
312 break;
313 }
315
316 if (psignal->depr_msg != NULL) {
317 log_deprecation("%s", psignal->depr_msg);
318 }
319
320 if (create) {
321 if (pcallback_found) {
322 luascript_error(fcl->state, "Signal \"%s\" already has a callback "
323 "called \"%s\".", signal_name,
324 callback_name);
325 } else {
326 signal_callback_list_append(psignal->callbacks,
327 signal_callback_new(callback_name));
328 }
329 } else {
330 if (pcallback_found) {
331 signal_callback_list_remove(psignal->callbacks, pcallback_found);
332 }
333 }
334 } else {
335 luascript_error(fcl->state, "Signal \"%s\" does not exist.",
336 signal_name);
337 }
338}
339
340/**********************************************************************/
344 const char *signal_name,
345 const char *callback_name)
346{
347 struct signal *psignal;
348
349 fc_assert_ret_val(fcl != NULL, FALSE);
350 fc_assert_ret_val(fcl->signals != NULL, FALSE);
351
352 if (luascript_signal_hash_lookup(fcl->signals, signal_name, &psignal)) {
353 /* check for a duplicate callback */
354 signal_callback_list_iterate(psignal->callbacks, pcallback) {
355 if (!strcmp(pcallback->name, callback_name)) {
356 return TRUE;
357 }
359 }
360
361 return FALSE;
362}
363
364/**********************************************************************/
368static void sn_free(char *name)
369{
370 FC_FREE(name);
371}
372
373/**********************************************************************/
377{
378 fc_assert_ret(fcl != NULL);
379
380 if (NULL == fcl->signals) {
381 fcl->signals = luascript_signal_hash_new();
382 fcl->signal_names = luascript_signal_name_list_new_full(sn_free);
383 }
384}
385
386/**********************************************************************/
390{
391 if (NULL != fcl && NULL != fcl->signals) {
392 luascript_signal_hash_destroy(fcl->signals);
393
394 luascript_signal_name_list_destroy(fcl->signal_names);
395
396 fcl->signals = NULL;
397 }
398}
399
400/**********************************************************************/
403const char *luascript_signal_by_index(struct fc_lua *fcl, int sindex)
404{
405 fc_assert_ret_val(fcl != NULL, NULL);
406 fc_assert_ret_val(fcl->signal_names != NULL, NULL);
407
408 return luascript_signal_name_list_get(fcl->signal_names, sindex);
409}
410
411/**********************************************************************/
416 const char *signal_name,
417 int sindex)
418{
419 struct signal *psignal;
420
421 fc_assert_ret_val(fcl != NULL, NULL);
422 fc_assert_ret_val(fcl->signals != NULL, NULL);
423
424 if (luascript_signal_hash_lookup(fcl->signals, signal_name, &psignal)) {
425 struct signal_callback *pcallback
426 = signal_callback_list_get(psignal->callbacks, sindex);
427 if (pcallback) {
428 return pcallback->name;
429 }
430 }
431
432 return NULL;
433}
#define log_deprecation(message,...)
const char * name
Definition inputfile.c:127
#define fc_assert_ret(condition)
Definition log.h:191
#define fc_assert_ret_val(condition, val)
Definition log.h:194
@ LOG_ERROR
Definition log.h:30
bool luascript_callback_invoke(struct fc_lua *fcl, const char *callback_name, int nargs, enum api_types *parg_types, va_list args)
Definition luascript.c:657
int luascript_error(lua_State *L, const char *format,...)
Definition luascript.c:291
void luascript_log(struct fc_lua *fcl, enum log_level level, const char *format,...)
Definition luascript.c:409
void luascript_signal_free(struct fc_lua *fcl)
void luascript_signal_init(struct fc_lua *fcl)
static void signal_destroy(struct signal *psignal)
bool luascript_signal_callback_defined(struct fc_lua *fcl, const char *signal_name, const char *callback_name)
void luascript_signal_emit_valist(struct fc_lua *fcl, const char *signal_name, va_list args)
static struct signal * luascript_signal_create_valist(struct fc_lua *fcl, const char *signal_name, int nargs, va_list args)
const char * luascript_signal_by_index(struct fc_lua *fcl, int sindex)
#define signal_callback_list_iterate(list, pcallback)
void deprecate_signal(signal_deprecator *deprecator, char *signal_name, char *replacement, char *deprecated_since)
static struct signal_callback * signal_callback_new(const char *name)
static void signal_callback_destroy(struct signal_callback *pcallback)
const char * luascript_signal_callback_by_index(struct fc_lua *fcl, const char *signal_name, int sindex)
void luascript_signal_emit(struct fc_lua *fcl, const char *signal_name,...)
#define signal_callback_list_iterate_end
void luascript_signal_callback(struct fc_lua *fcl, const char *signal_name, const char *callback_name, bool create)
static void sn_free(char *name)
signal_deprecator * luascript_signal_create(struct fc_lua *fcl, const char *signal_name, int nargs,...)
static struct signal * signal_new(int nargs, enum api_types *parg_types)
char * signal_deprecator
#define fc_calloc(n, esz)
Definition mem.h:38
#define FC_FREE(ptr)
Definition mem.h:41
#define fc_strdup(str)
Definition mem.h:43
#define fc_malloc(sz)
Definition mem.h:34
struct luascript_signal_hash * signals
Definition luascript.h:53
struct luascript_signal_name_list * signal_names
Definition luascript.h:54
lua_State * state
Definition luascript.h:45
struct signal_callback_list * callbacks
enum api_types * arg_types
char * depr_msg
int fc_snprintf(char *str, size_t n, const char *format,...)
Definition support.c:969
#define TRUE
Definition support.h:46
#define FALSE
Definition support.h:47