diff --git a/common/events.c b/common/events.c new file mode 100644 index 00000000..1dbdf135 --- /dev/null +++ b/common/events.c @@ -0,0 +1,101 @@ +// 15 may 2019 +#include +#include "ui.h" +#include "uipriv.h" + +struct handler { + int id; + uiHandlerFunc f; + void *sender; + void *data; + bool blocked; +}; + +static int handlerCmp(const void *a, const void *b) +{ + const struct handler *ha = (const struct handler *) a; + const struct handler *hb = (const struct handler *) b; + + // This could be ha->id - hb->id, but let's do it the explicit way to avoid integer overflow/underflow. + if (ha->id < hb->id) + return -1; + if (ha->id > hb->id) + return 1; + return 0; +} + +static struct handler *handlerFind(struct handler *handlers, size_t len, int id) +{ + struct handler key; + + memset(&key, 0, sizeof (struct handler)); + key.id = id; + return (struct handler *) bsearch(&key, handlers, len, sizeof (struct handler), handlerCmp); +} + +static void handlerSort(struct handler *handlers, size_t len) +{ + qsort(handlers, len, sizeof (struct handler), handlerCmp); +} + +struct uiEvent { + uiEventOptions opts; + struct handler *handlers; + size_t len; + size_t cap; + bool firing; +}; + +uiEvent *uiNewEvent(uiEventOptions *options) +{ +} + +int uiEventAddHandler(uiEvent *e, uiEventHandler handler, void *sender, void *data) +{ +} + +void uiEventDeleteHandler(uiEvent *e, int id) +{ + struct handler *h; + + if (e == NULL) + TODO + if (e->firing) + TODO + if (e->len == 0) + TODO + h = handlerFind(e->handlers, e->len, id); + if (h == NULL) + TODO + e->len--; + memmove(h + 1, h, (e->len - (h - e->handlers)) * sizeof (struct handler)); +} + +void uiEventFire(uiEvent *e, void *sender, void *args) +{ + struct handler *h; + size_t i; + + if (e == NULL) + TODO + if (e->firing) + TODO + if (e->opts.Global && sender != NULL) + TODO + e->firing = true; + h = e->handlers; + for (i = 0; i < e->len; i++) { + if (h->sender == sender && !h->blocked) + (*(h->f))(sender, args, h->data); + h++; + } + e->firing = false; +} + +bool uiEventHandlerBlocked(const uiEvent *e, int id) +{ +} + +void uiEventSetHandlerBlocked(uiEvent *e, int id, bool blocked) +{ +} diff --git a/common/meson.build b/common/meson.build index ef3b188f..df85ce56 100644 --- a/common/meson.build +++ b/common/meson.build @@ -2,5 +2,6 @@ libui_sources += [ 'common/errors.c', + 'common/events.c', 'common/init.c', ] diff --git a/doc/events.md b/doc/events.md index 6035278c..168a6a8f 100644 --- a/doc/events.md +++ b/doc/events.md @@ -72,6 +72,8 @@ It is a programmer error to specify `NULL` for `e` or `handler`. It is also a pr void uiEventDeleteHandler(uiEvent *e, int id); ``` +`uiEventDeleteHandler()` removes an event handler registration; you specify which handler to unregister by passing in the ID returned from `uiEventAddHandler()`. + It is a programmer error to specify `NULL` for `e` or a currently unregistered value for `id`. It is also a programmer error to call `uiEventDeleteHandler()` on an event while that event is being fired. ### `uiEventFire()` diff --git a/ui.h b/ui.h index 90ea8203..5e234ce8 100644 --- a/ui.h +++ b/ui.h @@ -43,6 +43,24 @@ uiprivExtern void uiMain(void); uiprivExtern void uiQuit(void); uiprivExtern void uiQueueMain(void (*f)(void *data), void *data); +typedef struct uiEvent uiEvent; +typedef struct uiEventOptions uiEventOptions; + +typedef void (*uiEventHandler)(void *sender, void *args, void *data); + +struct uiEventOptions { + size_t Size; + bool Global; +}; + +uiprivExtern uiEvent *uiNewEvent(uiEventOptions *options); +// TODO uiFreeEvent() +uiprivExtern int uiEventAddHandler(uiEvent *e, uiEventHandler handler, void *sender, void *data); +uiprivExtern void uiEventDeleteHandler(uiEvent *e, int id); +uiprivExtern void uiEventFire(uiEvent *e, void *sender, void *args); +uiprivExtern bool uiEventHandlerBlocked(const uiEvent *e, int id); +uiprivExtern void uiEventSetHandlerBlocked(uiEvent *e, int id, bool blocked); + #ifdef __cplusplus } #endif