// SPDX-FileCopyrightText: 2018 pancake // SPDX-License-Identifier: MIT #include #include typedef struct rz_event_callback_hook_t { RzEventCallback cb; void *user; int handle; } RzEventCallbackHook; RZ_API RzEvent *rz_event_new(void *user) { RzEvent *ev = RZ_NEW0(RzEvent); if (!ev) { return NULL; } ev->user = user; ev->next_handle = 0; ev->callbacks = ht_up_new(NULL, (HtUPFreeValue)rz_vector_free); if (!ev->callbacks) { goto err; } rz_vector_init(&ev->all_callbacks, sizeof(RzEventCallbackHook), NULL, NULL); rz_vector_init(&ev->pending_unhook, sizeof(RzEventCallbackHandle), NULL, NULL); return ev; err: rz_event_free(ev); return NULL; } RZ_API void rz_event_free(RzEvent *ev) { if (!ev) { return; } rz_vector_fini(&ev->pending_unhook); ht_up_free(ev->callbacks); rz_vector_fini(&ev->all_callbacks); free(ev); } static RzVector /**/ *get_cbs(RzEvent *ev, int type) { RzVector *cbs = ht_up_find(ev->callbacks, (ut64)type, NULL); if (!cbs) { cbs = rz_vector_new(sizeof(RzEventCallbackHook), NULL, NULL); if (cbs) { ht_up_insert(ev->callbacks, (ut64)type, cbs); } } return cbs; } RZ_API RzEventCallbackHandle rz_event_hook(RzEvent *ev, int type, RzEventCallback cb, void *user) { RzEventCallbackHandle handle = { 0 }; RzEventCallbackHook hook; rz_return_val_if_fail(ev, handle); hook.cb = cb; hook.user = user; hook.handle = ev->next_handle++; if (type == RZ_EVENT_ALL) { rz_vector_push(&ev->all_callbacks, &hook); } else { RzVector *cbs = get_cbs(ev, type); if (!cbs) { return handle; } rz_vector_push(cbs, &hook); } handle.handle = hook.handle; handle.type = type; return handle; } static bool del_hook(void *user, const ut64 k, const void *v) { int handle = *(int *)user; RzVector *cbs = (RzVector *)v; RzEventCallbackHook *hook; size_t i; rz_return_val_if_fail(cbs, false); rz_vector_enumerate (cbs, hook, i) { if (hook->handle == handle) { rz_vector_remove_at(cbs, i, NULL); break; } } return true; } RZ_API void rz_event_unhook(RzEvent *ev, RzEventCallbackHandle handle) { rz_return_if_fail(ev); if (ev->incall) { // when inside a call (meaning rz_event_send currently iterates over the callback vectors), // defer the unhook to after the rz_event_send is done. rz_vector_push(&ev->pending_unhook, &handle); return; } if (handle.type == RZ_EVENT_ALL) { // try to delete it both from each list of callbacks and from // the "all_callbacks" vector ht_up_foreach(ev->callbacks, del_hook, &handle.handle); del_hook(&handle.handle, 0, &ev->all_callbacks); } else { RzVector *cbs = ht_up_find(ev->callbacks, (ut64)handle.type, NULL); rz_return_if_fail(cbs); del_hook(&handle.handle, 0, cbs); } } RZ_API void rz_event_send(RzEvent *ev, int type, void *data) { RzEventCallbackHook *hook; rz_return_if_fail(ev && !ev->incall); // send to both the per-type callbacks and to the all_callbacks ev->incall = true; rz_vector_foreach (&ev->all_callbacks, hook) { hook->cb(ev, type, hook->user, data); } ev->incall = false; RzVector *cbs = ht_up_find(ev->callbacks, (ut64)type, NULL); if (cbs) { ev->incall = true; rz_vector_foreach (cbs, hook) { hook->cb(ev, type, hook->user, data); } ev->incall = false; } RzEventCallbackHandle *unhook_handle; rz_vector_foreach (&ev->pending_unhook, unhook_handle) { rz_event_unhook(ev, *unhook_handle); } rz_vector_clear(&ev->pending_unhook); }