mirror of
https://github.com/rkd77/elinks.git
synced 2024-12-04 14:46:47 -05:00
329 lines
6.6 KiB
C
329 lines
6.6 KiB
C
/* Event handling functions */
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include "elinks.h"
|
|
|
|
#include "main/event.h"
|
|
#include "util/error.h"
|
|
#include "util/hash.h"
|
|
#include "util/memory.h"
|
|
#include "util/snprintf.h"
|
|
#include "util/string.h"
|
|
|
|
|
|
/* First, we should set some terminology:
|
|
*
|
|
* o event -> Message being [triggerred] by some ELinks part and [catched]
|
|
* by various random other ones.
|
|
*
|
|
* o event hook -> Device being deployed by various ELinks parts,
|
|
* associated with certain [event It [catches] that event by having a
|
|
* handler executed when the [event] is triggerred.
|
|
*
|
|
* o event chain -> Line of [event hook]s associated with a given [event
|
|
* The [hook]s are ordered by [priority Each hook returns whenever should
|
|
* the chain continue or that no other events in the chain should be
|
|
* triggered (TODO).
|
|
*/
|
|
|
|
struct event_handler {
|
|
/* The function to be called with the event data. */
|
|
event_hook_T callback;
|
|
|
|
/* The @priority of this handler. */
|
|
int priority;
|
|
|
|
/* Handler specific data. */
|
|
void *data;
|
|
};
|
|
|
|
struct event {
|
|
/* The event name has to be unique. */
|
|
unsigned char *name;
|
|
|
|
/* There are @count event @handlers all ordered by priority. */
|
|
struct event_handler *handlers;
|
|
unsigned int count;
|
|
|
|
/* The unique event id and position in events. */
|
|
int id;
|
|
};
|
|
|
|
static struct event *events = NULL;
|
|
static unsigned int eventssize = 0;
|
|
static struct hash *event_hash = NULL;
|
|
|
|
/* TODO: This should be tuned to the number of events. When we will have a lot
|
|
* of them, this should be some big enough number to reduce unnecessary
|
|
* slavery of CPU on the startup. Then after all main modules will be
|
|
* initialized and their events will be registered, we could call something
|
|
* like adjust_events_list() which will tune it to the exactly needed number.
|
|
* This should be also called after each new plugin loaded. */
|
|
#define EVENT_GRANULARITY 0x07
|
|
|
|
#define realloc_events(ptr, size) \
|
|
mem_align_alloc(ptr, (size), (size) + 1, struct event, EVENT_GRANULARITY)
|
|
|
|
static inline int
|
|
invalid_event_id(register int id)
|
|
{
|
|
return (id < 0 || id >= eventssize || id == EVENT_NONE);
|
|
}
|
|
|
|
int
|
|
register_event(unsigned char *name)
|
|
{
|
|
int id = get_event_id(name);
|
|
struct event *event;
|
|
int namelen;
|
|
|
|
if (id != EVENT_NONE) return id;
|
|
|
|
event = events;
|
|
if (!realloc_events(&events, eventssize)) return EVENT_NONE;
|
|
|
|
/* If @events got relocated update the hash. */
|
|
if (event != events) {
|
|
for (id = 0; id < eventssize; id++) {
|
|
struct hash_item *item;
|
|
int len = strlen(events[id].name);
|
|
|
|
item = get_hash_item(event_hash, events[id].name, len);
|
|
|
|
if (item) item->value = &events[id];
|
|
}
|
|
}
|
|
|
|
event = &events[eventssize];
|
|
|
|
namelen = strlen(name);
|
|
event->name = memacpy(name, namelen);
|
|
if (!event->name) return EVENT_NONE;
|
|
|
|
if (!add_hash_item(event_hash, event->name, namelen, event)) {
|
|
mem_free(event->name);
|
|
event->name = NULL;
|
|
return EVENT_NONE;
|
|
}
|
|
|
|
event->handlers = NULL;
|
|
event->count = 0;
|
|
event->id = eventssize++;
|
|
|
|
return event->id;
|
|
}
|
|
|
|
int
|
|
get_event_id(unsigned char *name)
|
|
{
|
|
struct hash_item *item;
|
|
int namelen;
|
|
|
|
assertm(name && name[0], "Empty or missing event name");
|
|
if_assert_failed return EVENT_NONE;
|
|
|
|
if (!event_hash) return EVENT_NONE;
|
|
|
|
namelen = strlen(name);
|
|
item = get_hash_item(event_hash, name, namelen);
|
|
if (item) {
|
|
struct event *event = item->value;
|
|
|
|
assertm(event, "Hash item with no value");
|
|
if_assert_failed return EVENT_NONE;
|
|
|
|
return event->id;
|
|
}
|
|
|
|
return EVENT_NONE;
|
|
}
|
|
|
|
unsigned char *
|
|
get_event_name(int id)
|
|
{
|
|
if (invalid_event_id(id)) return NULL;
|
|
|
|
return events[id].name;
|
|
}
|
|
|
|
static inline void
|
|
trigger_event_va(int id, va_list ap_init)
|
|
{
|
|
int i;
|
|
struct event_handler *ev_handler;
|
|
|
|
if (invalid_event_id(id)) return;
|
|
|
|
ev_handler = events[id].handlers;
|
|
for (i = 0; i < events[id].count; i++, ev_handler++) {
|
|
enum evhook_status ret;
|
|
va_list ap;
|
|
|
|
VA_COPY(ap, ap_init);
|
|
ret = ev_handler->callback(ap, ev_handler->data);
|
|
va_end(ap);
|
|
|
|
if (ret == EVENT_HOOK_STATUS_LAST) return;
|
|
}
|
|
}
|
|
|
|
void
|
|
trigger_event(int id, ...)
|
|
{
|
|
va_list ap;
|
|
|
|
va_start(ap, id);
|
|
trigger_event_va(id, ap);
|
|
}
|
|
|
|
void
|
|
trigger_event_name(unsigned char *name, ...)
|
|
{
|
|
va_list ap;
|
|
int id = get_event_id(name);
|
|
|
|
va_start(ap, name);
|
|
trigger_event_va(id, ap);
|
|
}
|
|
|
|
static inline void
|
|
move_event_handler(struct event *event, int to, int from)
|
|
{
|
|
int d = int_max(to, from);
|
|
|
|
memmove(&event->handlers[to], &event->handlers[from],
|
|
(event->count - d) * sizeof(*event->handlers));
|
|
}
|
|
|
|
int
|
|
register_event_hook(int id, event_hook_T callback, int priority, void *data)
|
|
{
|
|
struct event *event;
|
|
int i;
|
|
|
|
assert(callback);
|
|
if_assert_failed return EVENT_NONE;
|
|
|
|
if (invalid_event_id(id)) return EVENT_NONE;
|
|
|
|
event = &events[id];
|
|
|
|
for (i = 0; i < event->count; i++)
|
|
if (event->handlers[i].callback == callback) break;
|
|
|
|
if (i == event->count) {
|
|
struct event_handler *eh;
|
|
|
|
eh = mem_realloc(event->handlers,
|
|
(event->count + 1) * sizeof(*event->handlers));
|
|
|
|
if (!eh) return EVENT_NONE;
|
|
|
|
event->handlers = eh;
|
|
event->count++;
|
|
} else {
|
|
move_event_handler(event, i, i + 1);
|
|
}
|
|
|
|
for (i = 0; i < event->count - 1; i++)
|
|
if (event->handlers[i].priority < priority) break;
|
|
|
|
move_event_handler(event, i + 1, i);
|
|
|
|
event->handlers[i].callback = callback;
|
|
event->handlers[i].priority = priority;
|
|
event->handlers[i].data = data;
|
|
|
|
return id;
|
|
}
|
|
|
|
void
|
|
unregister_event_hook(int id, event_hook_T callback)
|
|
{
|
|
struct event *event;
|
|
|
|
assert(callback);
|
|
if_assert_failed return;
|
|
|
|
if (invalid_event_id(id)) return;
|
|
|
|
event = &events[id];
|
|
if (event->handlers) {
|
|
int i;
|
|
|
|
for (i = 0; i < event->count; i++) {
|
|
if (event->handlers[i].callback != callback)
|
|
continue;
|
|
|
|
move_event_handler(event, i, i + 1);
|
|
event->count--;
|
|
if (!event->count) {
|
|
mem_free(event->handlers);
|
|
event->handlers = NULL;
|
|
} else {
|
|
struct event_handler *eh;
|
|
|
|
eh = mem_realloc(event->handlers,
|
|
event->count * sizeof(*event->handlers));
|
|
if (eh) event->handlers = eh;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
register_event_hooks(struct event_hook_info *hooks)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; hooks[i].name; i++) {
|
|
int id = register_event(hooks[i].name);
|
|
|
|
if (id == EVENT_NONE) continue;
|
|
|
|
register_event_hook(id, hooks[i].callback, hooks[i].priority,
|
|
hooks[i].data);
|
|
}
|
|
}
|
|
|
|
void
|
|
unregister_event_hooks(struct event_hook_info *hooks)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; hooks[i].name; i++) {
|
|
int id = get_event_id(hooks[i].name);
|
|
|
|
if (id == EVENT_NONE) continue;
|
|
|
|
unregister_event_hook(id, hooks[i].callback);
|
|
}
|
|
}
|
|
|
|
void
|
|
init_event(void)
|
|
{
|
|
event_hash = init_hash(8, strhash);
|
|
}
|
|
|
|
void
|
|
done_event(void)
|
|
{
|
|
int i;
|
|
|
|
if (event_hash) free_hash(event_hash);
|
|
|
|
for (i = 0; i < eventssize; i++) {
|
|
mem_free_if(events[i].handlers);
|
|
mem_free(events[i].name);
|
|
}
|
|
|
|
mem_free_set(&events, NULL);
|
|
eventssize = 0;
|
|
}
|