# Playbook: add an event handler `src/event/` mediates between `src/xmpp/` (and locally-triggered actions) and the rest of the codebase. Two directions: - **Server events** (`sv_ev_*`) — triggered by inbound stanzas. - **Client events** (`cl_ev_*`) — triggered by local actions (UI, command). See `patterns/events.md` for the conceptual split. ## Decide direction | Trigger | Direction | File | |---|---|---| | Inbound stanza parsed in `src/xmpp/`. | Server. | `src/event/server_events.c/h` | | User typed a command, UI key, or internal timer. | Client. | `src/event/client_events.c/h` | ## Add the handler — server example `src/event/server_events.h`: ```c void sv_ev_foo_received(const char* const from, const char* const payload); ``` `src/event/server_events.c`: ```c void sv_ev_foo_received(const char* const from, const char* const payload) { // 1. Update persistent state. chatlog_msg_in(from, payload); // 2. Push UI update. ProfChatWin* win = wins_get_chat(from); if (win) { chatwin_incoming_msg(win, payload, NULL, FALSE); } else { cons_show_incoming_message(from, payload); } // 3. Plugin callbacks last — state must be consistent. plugins_post_chat_message_received(from, payload); } ``` ## Wire the trigger site For a server event, the matching parser in `src/xmpp/` calls the new handler. For instance, if `` stanzas are parsed in `src/xmpp/message.c`: ```c // ... after parsing the stanza ... sv_ev_foo_received(from, payload); ``` For a client event, call from `cmd_funcs.c` (or wherever the user-facing trigger lives). ## Stub the handler in tests `tests/unittests/event/stub_*.c` — add a stub for the new function so units that exercise the trigger site link cleanly: ```c void sv_ev_foo_received(const char* const from, const char* const payload) { // pass-through (or check_expected, depending on test needs) } ``` If a test needs to verify the handler is called with specific args, switch the stub to `check_expected` and the test queues `expect_string` / `expect_value`. See `testing/stubs.md`. ## Plugin hook (if applicable) If the event should fire a plugin callback, add a dispatcher in `src/plugins/plugins.c/h` (e.g. `plugins_post_foo_received`) and call it **last** in the handler — after state and UI are consistent. Keep C and Python plugin APIs in sync (`c_api.h` / `python_api.h`). ## Unit test the handler `tests/unittests/test_server_events.c` (or `test_client_events.c`) holds event-side tests. Pattern: ```c void test_sv_ev_foo_received_writes_chatlog(void** state) { expect_string(chatlog_msg_in, jid, "alice@example.com"); expect_string(chatlog_msg_in, msg, "hello"); sv_ev_foo_received("alice@example.com", "hello"); } ``` ## Conventions - One handler, one event. Don't pile multiple unrelated events into the same function. - Order inside the handler: state → UI → plugins. Never call plugins before state is committed. - `sv_ev_*` and `cl_ev_*` should be straight-line — no XMPP protocol parsing, no UI rendering. They orchestrate; they don't implement.