grafthistory: comment about downloading the pack index
[elinks/elinks-j605.git] / src / main / event.c
blob5b8bab760ed8e3b897b176030784af7bf6e4745a
1 /* Event handling functions */
3 #ifdef HAVE_CONFIG_H
4 #include "config.h"
5 #endif
7 #include "elinks.h"
9 #include "main/event.h"
10 #include "util/error.h"
11 #include "util/hash.h"
12 #include "util/memory.h"
13 #include "util/snprintf.h"
14 #include "util/string.h"
17 /* First, we should set some terminology:
19 * o event -> Message being [triggerred] by some ELinks part and [catched]
20 * by various random other ones.
22 * o event hook -> Device being deployed by various ELinks parts,
23 * associated with certain [event It [catches] that event by having a
24 * handler executed when the [event] is triggerred.
26 * o event chain -> Line of [event hook]s associated with a given [event
27 * The [hook]s are ordered by [priority Each hook returns whenever should
28 * the chain continue or that no other events in the chain should be
29 * triggered (TODO).
32 struct event_handler {
33 /* The function to be called with the event data. */
34 event_hook_T callback;
36 /* The @priority of this handler. */
37 int priority;
39 /* Handler specific data. */
40 void *data;
43 struct event {
44 /* The event name has to be unique. */
45 unsigned char *name;
47 /* There are @count event @handlers all ordered by priority. */
48 struct event_handler *handlers;
49 unsigned int count;
51 /* The unique event id and position in events. */
52 int id;
55 static struct event *events = NULL;
56 static unsigned int eventssize = 0;
57 static struct hash *event_hash = NULL;
59 /* TODO: This should be tuned to the number of events. When we will have a lot
60 * of them, this should be some big enough number to reduce unnecessary
61 * slavery of CPU on the startup. Then after all main modules will be
62 * initialized and their events will be registered, we could call something
63 * like adjust_events_list() which will tune it to the exactly needed number.
64 * This should be also called after each new plugin loaded. */
65 #define EVENT_GRANULARITY 0x07
67 #define realloc_events(ptr, size) \
68 mem_align_alloc(ptr, (size), (size) + 1, struct event, EVENT_GRANULARITY)
70 static inline int
71 invalid_event_id(register int id)
73 return (id < 0 || id >= eventssize || id == EVENT_NONE);
76 int
77 register_event(unsigned char *name)
79 int id = get_event_id(name);
80 struct event *event;
81 int namelen;
83 if (id != EVENT_NONE) return id;
85 event = events;
86 if (!realloc_events(&events, eventssize)) return EVENT_NONE;
88 /* If @events got relocated update the hash. */
89 if (event != events) {
90 for (id = 0; id < eventssize; id++) {
91 struct hash_item *item;
92 int len = strlen(events[id].name);
94 item = get_hash_item(event_hash, events[id].name, len);
96 if (item) item->value = &events[id];
100 event = &events[eventssize];
102 namelen = strlen(name);
103 event->name = memacpy(name, namelen);
104 if (!event->name) return EVENT_NONE;
106 if (!add_hash_item(event_hash, event->name, namelen, event)) {
107 mem_free(event->name);
108 event->name = NULL;
109 return EVENT_NONE;
112 event->handlers = NULL;
113 event->count = 0;
114 event->id = eventssize++;
116 return event->id;
120 get_event_id(unsigned char *name)
122 struct hash_item *item;
123 int namelen;
125 assertm(name && name[0], "Empty or missing event name");
126 if_assert_failed return EVENT_NONE;
128 if (!event_hash) return EVENT_NONE;
130 namelen = strlen(name);
131 item = get_hash_item(event_hash, name, namelen);
132 if (item) {
133 struct event *event = item->value;
135 assertm(event, "Hash item with no value");
136 if_assert_failed return EVENT_NONE;
138 return event->id;
141 return EVENT_NONE;
144 unsigned char *
145 get_event_name(int id)
147 if (invalid_event_id(id)) return NULL;
149 return events[id].name;
152 static void
153 trigger_event_va(int id, va_list ap_init)
155 int i;
156 struct event_handler *ev_handler;
158 if (invalid_event_id(id)) return;
160 ev_handler = events[id].handlers;
161 for (i = 0; i < events[id].count; i++, ev_handler++) {
162 enum evhook_status ret;
163 va_list ap;
165 VA_COPY(ap, ap_init);
166 ret = ev_handler->callback(ap, ev_handler->data);
167 va_end(ap);
169 if (ret == EVENT_HOOK_STATUS_LAST) return;
173 void
174 trigger_event(int id, ...)
176 va_list ap;
178 va_start(ap, id);
179 trigger_event_va(id, ap);
182 void
183 trigger_event_name(unsigned char *name, ...)
185 va_list ap;
186 int id = get_event_id(name);
188 va_start(ap, name);
189 trigger_event_va(id, ap);
192 static inline void
193 move_event_handler(struct event *event, int to, int from)
195 int d = int_max(to, from);
197 memmove(&event->handlers[to], &event->handlers[from],
198 (event->count - d) * sizeof(*event->handlers));
202 register_event_hook(int id, event_hook_T callback, int priority, void *data)
204 struct event *event;
205 int i;
207 assert(callback);
208 if_assert_failed return EVENT_NONE;
210 if (invalid_event_id(id)) return EVENT_NONE;
212 event = &events[id];
214 for (i = 0; i < event->count; i++)
215 if (event->handlers[i].callback == callback) break;
217 if (i == event->count) {
218 struct event_handler *eh;
220 eh = mem_realloc(event->handlers,
221 (event->count + 1) * sizeof(*event->handlers));
223 if (!eh) return EVENT_NONE;
225 event->handlers = eh;
226 event->count++;
227 } else {
228 move_event_handler(event, i, i + 1);
231 for (i = 0; i < event->count - 1; i++)
232 if (event->handlers[i].priority < priority) break;
234 move_event_handler(event, i + 1, i);
236 event->handlers[i].callback = callback;
237 event->handlers[i].priority = priority;
238 event->handlers[i].data = data;
240 return id;
243 void
244 unregister_event_hook(int id, event_hook_T callback)
246 struct event *event;
248 assert(callback);
249 if_assert_failed return;
251 if (invalid_event_id(id)) return;
253 event = &events[id];
254 if (event->handlers) {
255 int i;
257 for (i = 0; i < event->count; i++) {
258 if (event->handlers[i].callback != callback)
259 continue;
261 move_event_handler(event, i, i + 1);
262 event->count--;
263 if (!event->count) {
264 mem_free(event->handlers);
265 event->handlers = NULL;
266 } else {
267 struct event_handler *eh;
269 eh = mem_realloc(event->handlers,
270 event->count * sizeof(*event->handlers));
271 if (eh) event->handlers = eh;
274 break;
279 void
280 register_event_hooks(struct event_hook_info *hooks)
282 int i;
284 for (i = 0; hooks[i].name; i++) {
285 int id = register_event(hooks[i].name);
287 if (id == EVENT_NONE) continue;
289 register_event_hook(id, hooks[i].callback, hooks[i].priority,
290 hooks[i].data);
294 void
295 unregister_event_hooks(struct event_hook_info *hooks)
297 int i;
299 for (i = 0; hooks[i].name; i++) {
300 int id = get_event_id(hooks[i].name);
302 if (id == EVENT_NONE) continue;
304 unregister_event_hook(id, hooks[i].callback);
308 void
309 init_event(void)
311 event_hash = init_hash(8, strhash);
314 void
315 done_event(void)
317 int i;
319 if (event_hash) free_hash(event_hash);
321 for (i = 0; i < eventssize; i++) {
322 mem_free_if(events[i].handlers);
323 mem_free(events[i].name);
326 mem_free_set(&events, NULL);
327 eventssize = 0;