2 * Copyright Neil Brown ©2015-2023 <neil@brown.name>
3 * May be distributed under terms of GPLv2 - see file:COPYING
5 * libevent support for edlib.
7 * Register command "attach-libevent".
8 * When that is called, register:
15 * When "event:deactivate" is called, cause event:run to abort.
22 #define PANE_DATA_TYPE struct event_info
27 EV_LIST, /* Events handled by libevent */
28 POLL_LIST, /* Events to poll before calling event_base_loop */
29 PRIO_0_LIST, /* background task - run one per loop */
30 PRIO_1_LIST, /* non-trivial follow-up tasks, line pane_refresh */
31 PRIO_2_LIST, /* fast follow-up tasks like freeing memory */
36 struct event_base *base;
37 struct list_head event_list[NR_LISTS];
38 struct pane *home safe;
41 struct command read, write, signal, timer, poll, on_idle,
42 run, deactivate, free, refresh, noblock;
44 #include "core-pane.h"
46 static struct map *libevent_map;
47 DEF_LOOKUP_CMD(libevent_handle, libevent_map);
51 struct pane *home safe;
53 struct command *comm safe;
55 int active; /* Don't delete or free this event, it is running */
56 int num; /* signal or mseconds or fd */
59 static void call_event(int thing, short sev, void *evv)
61 struct evt *ev safe = safe_cast evv;
71 if (comm_call(ev->comm, "callback:event", ev->home, thing) < 0 ||
76 command_put(ev->comm);
83 static void call_timeout_event(int thing, short sev, void *evv)
85 struct evt *ev safe = safe_cast evv;
88 time_start(TIME_TIMER);
89 if (comm_call(ev->comm, "callback:event", ev->home, thing) < 0 ||
93 command_put(ev->comm);
97 tv.tv_sec = ev->num / 1000;
98 tv.tv_usec = (ev->num % 1000) * 1000;
100 event_add(ev->l, &tv);
102 time_stop(TIME_TIMER);
105 DEF_CB(libevent_read)
107 struct event_info *ei = container_of(ci->comm, struct event_info, read);
113 /* If there is already an event with this 'fd', we need
114 * to remove it now, else libevent gets confused.
115 * Presumably call_event() is now running and will clean up
118 list_for_each_entry(ev, &ei->event_list[EV_LIST], lst) {
119 int fd = event_get_fd(ev->l);
120 if (fd >= 0 && ci->num >= 0 && fd == ci->num)
124 ev = malloc(sizeof(*ev));
127 ei->base = event_base_new();
129 ev->l = safe_cast event_new(ei->base, ci->num, EV_READ|EV_PERSIST,
131 ev->home = ci->focus;
132 ev->comm = command_get(ci->comm2);
135 ev->event = "event:read";
136 pane_add_notify(ei->home, ev->home, "Notify:Close");
137 list_add(&ev->lst, &ei->event_list[EV_LIST]);
138 event_add(ev->l, NULL);
142 DEF_CB(libevent_write)
144 struct event_info *ei = container_of(ci->comm, struct event_info, write);
150 /* If there is already an event with this 'fd', we need
151 * to remove it now, else libevent gets confused.
152 * Presumably call_event() is now running and will clean up
155 list_for_each_entry(ev, &ei->event_list[EV_LIST], lst) {
156 int fd = event_get_fd(ev->l);
157 if (fd >= 0 && ci->num >= 0 && fd == ci->num)
161 ev = malloc(sizeof(*ev));
164 ei->base = event_base_new();
166 ev->l = safe_cast event_new(ei->base, ci->num, EV_WRITE|EV_PERSIST,
168 ev->home = ci->focus;
169 ev->comm = command_get(ci->comm2);
172 ev->event = "event:write";
173 pane_add_notify(ei->home, ev->home, "Notify:Close");
174 list_add(&ev->lst, &ei->event_list[EV_LIST]);
175 event_add(ev->l, NULL);
179 DEF_CB(libevent_signal)
181 struct event_info *ei = container_of(ci->comm, struct event_info, signal);
187 ev = malloc(sizeof(*ev));
190 ei->base = event_base_new();
192 ev->l = safe_cast event_new(ei->base, ci->num, EV_SIGNAL|EV_PERSIST,
194 ev->home = ci->focus;
195 ev->comm = command_get(ci->comm2);
198 ev->event = "event:signal";
199 pane_add_notify(ei->home, ev->home, "Notify:Close");
200 list_add(&ev->lst, &ei->event_list[EV_LIST]);
201 event_add(ev->l, NULL);
205 DEF_CB(libevent_timer)
207 struct event_info *ei = container_of(ci->comm, struct event_info, timer);
214 ev = malloc(sizeof(*ev));
217 ei->base = event_base_new();
219 ev->l = safe_cast event_new(ei->base, -1, 0,
220 call_timeout_event, ev);
221 ev->home = ci->focus;
222 ev->comm = command_get(ci->comm2);
225 ev->event = "event:timer";
226 pane_add_notify(ei->home, ev->home, "Notify:Close");
227 list_add(&ev->lst, &ei->event_list[EV_LIST]);
228 tv.tv_sec = ev->num / 1000;
229 tv.tv_usec = (ev->num % 1000) * 1000;
230 event_add(ev->l, &tv);
234 DEF_CB(libevent_poll)
236 struct event_info *ei = container_of(ci->comm, struct event_info, poll);
242 ev = malloc(sizeof(*ev));
245 ei->base = event_base_new();
247 ev->home = ci->focus;
248 ev->comm = command_get(ci->comm2);
250 ev->event = "event:poll";
252 pane_add_notify(ei->home, ev->home, "Notify:Close");
253 list_add(&ev->lst, &ei->event_list[POLL_LIST]);
257 DEF_CMD(libevent_on_idle)
259 struct event_info *ei = container_of(ci->comm, struct event_info, on_idle);
266 ev = malloc(sizeof(*ev));
269 ei->base = event_base_new();
271 ev->home = ci->focus;
272 pane_add_notify(ei->home, ev->home, "Notify:Close");
273 ev->comm = command_get(ci->comm2);
275 ev->event = "event:on-idle";
281 list_add(&ev->lst, &ei->event_list[PRIO_0_LIST + prio]);
285 static int run_list(struct event_info *ei safe, int list, char *cb safe,
288 bool dont_block = False;
291 list_for_each_entry(ev, &ei->event_list[list], lst) {
293 if (comm_call(ev->comm, cb, ev->home, ev->num) >= 1)
295 if (ev->active == 2 || list >= PRIO_0_LIST) {
297 command_put(ev->comm);
302 if (dont_block && stop_on_first)
303 /* Other things might have been removed from list */
311 struct event_info *ei = container_of(ci->comm, struct event_info, run);
312 struct event_base *b = ei->base;
313 int dont_block = ei->dont_block;
322 /* No events to wait for.. */
328 /* First run any 'poll' events */
329 if (run_list(ei, POLL_LIST, "callback:poll", True))
332 for (i = PRIO_0_LIST ; i <= PRIO_2_LIST; i++)
333 if (!list_empty(&ei->event_list[i]))
336 /* Disable any alarm set by python (or other interpreter) */
338 event_base_loop(b, EVLOOP_ONCE | (dont_block ? EVLOOP_NONBLOCK : 0));
340 time_start(TIME_IDLE);
341 /* Prio 2 comes first - unconditional */
342 run_list(ei, PRIO_2_LIST, "callback:on-idle", False);
344 run_list(ei, PRIO_1_LIST, "callback:on-idle", False);
345 /* Repeat PRIO_2 just in case */
346 run_list(ei, PRIO_2_LIST, "callback:on-idle", False);
347 /* And do one background task */
348 run_list(ei, PRIO_0_LIST, "callback:on-idle", True);
349 time_stop(TIME_IDLE);
351 /* Check if we have been deactivated. */
355 for (i = 0 ; i < NR_LISTS; i++) {
356 while (!list_empty(&ei->event_list[i])) {
357 ev = list_first_entry(&ei->event_list[i], struct evt, lst);
363 command_put(ev->comm);
371 DEF_CB(libevent_deactivate)
373 struct event_info *ei = container_of(ci->comm, struct event_info, deactivate);
379 DEF_CB(libevent_free)
381 /* destroy for ci->focus and, if comm2 given, which activate
385 struct list_head *tmp;
386 struct event_info *ei = container_of(ci->comm, struct event_info, free);
389 for (i = 0; i < NR_LISTS; i++) {
390 list_for_each_entry_safe(ev, tmp, &ei->event_list[i], lst)
391 if (ev->home == ci->focus &&
392 (ci->comm2 == NULL || ev->comm == ci->comm2)) {
393 list_del_init(&ev->lst);
401 command_put(ev->comm);
409 DEF_CB(libevent_refresh)
412 struct list_head *tmp;
413 struct event_info *ei = container_of(ci->comm, struct event_info, refresh);
414 struct list_head old;
417 for (i = 0; i < NR_LISTS; i++) {
418 list_add(&old, &ei->event_list[i]);
419 list_del_init(&ei->event_list[i]);
420 list_for_each_entry_safe(ev, tmp, &old, lst) {
426 call_comm(ev->event, ev->home, ev->comm, ev->num);
427 command_put(ev->comm);
434 DEF_CB(libevent_noblock)
436 struct event_info *ei = container_of(ci->comm, struct event_info,
443 DEF_CMD(libevent_notify)
445 struct event_info *ei = ci->home->data;
447 home_comm_call(ci->home, &ei->free, "free", ci->focus);
451 DEF_CMD(libevent_activate)
453 struct event_info *ei;
457 p = pane_register(pane_root(ci->home), 0, &libevent_handle.c);
462 for (i = 0; i < NR_LISTS; i++)
463 INIT_LIST_HEAD(&ei->event_list[i]);
464 ei->read = libevent_read;
465 ei->write = libevent_write;
466 ei->signal = libevent_signal;
467 ei->timer = libevent_timer;
468 ei->poll = libevent_poll;
469 ei->on_idle = libevent_on_idle;
470 ei->run = libevent_run;
471 ei->deactivate = libevent_deactivate;
472 ei->free = libevent_free;
473 ei->refresh = libevent_refresh;
474 ei->noblock = libevent_noblock;
476 /* These are defaults, so make them sort late */
477 call_comm("global-set-command", ci->focus, &ei->read,
478 0, NULL, "event:read-zz");
479 call_comm("global-set-command", ci->focus, &ei->write,
480 0, NULL, "event:write-zz");
481 call_comm("global-set-command", ci->focus, &ei->signal,
482 0, NULL, "event:signal-zz");
483 call_comm("global-set-command", ci->focus, &ei->timer,
484 0, NULL, "event:timer-zz");
485 call_comm("global-set-command", ci->focus, &ei->poll,
486 0, NULL, "event:poll-zz");
487 call_comm("global-set-command", ci->focus, &ei->on_idle,
488 0, NULL, "event:on-idle-zz");
489 call_comm("global-set-command", ci->focus, &ei->run,
490 0, NULL, "event:run-zz");
491 call_comm("global-set-command", ci->focus, &ei->deactivate,
492 0, NULL, "event:deactivate-zz");
493 call_comm("global-set-command", ci->focus, &ei->free,
494 0, NULL, "event:free-zz");
495 call_comm("global-set-command", ci->focus, &ei->refresh,
496 0, NULL, "event:refresh-zz");
497 call_comm("global-set-command", ci->focus, &ei->noblock,
498 0, NULL, "event:noblock-zz");
499 call("event:refresh", ci->focus);
504 void edlib_init(struct pane *ed safe)
506 call_comm("global-set-command", ed, &libevent_activate,
507 0, NULL, "attach-libevent");
511 libevent_map = key_alloc();
512 key_add(libevent_map, "Notify:Close", &libevent_notify);