Two live channels from the box: physical input (Catch) and device log lines (Logs & counters). Subscribe, then pull fixed-size POD events off the handle.
medius_device_catch_events(dev, mask, &stream) ── subscribe to live input
medius_device_logs(dev, &stream) ── subscribe to log lines
│
▼ a background reader thread fills a host-side queue
medius_event_stream_recv(stream, &event) ── pull one (blocks)
medius_log_stream_recv(stream, &line) ── the same, for logs
│
▼ loop until MEDIUS_STATUS_ERR_DISCONNECTED
medius_event_stream_free(stream) ── unsubscribe
medius_log_stream_free(stream) ── the same, for logsBoth return a MediusStatus and write an opaque handle through an out-param. Pick the input classes with a MediusCatchMask.
MediusStatus medius_device_catch_events(struct MediusDevice *dev, MediusCatchMask mask, struct MediusEventStream **out); MediusStatus medius_device_logs(struct MediusDevice *dev, struct MediusLogStream **out);
| Call | Does |
|---|
medius_device_catch_events(dev, mask, &out) | Subscribe to physical input for the class mask. See Catch. |
medius_device_logs(dev, &out) | Open the device LOG channel. See Logs. |
medius_event_stream_clone(stream) / medius_log_stream_clone(stream) | Another handle to the same subscription. Null in → null out. |
medius_event_stream_free(stream) / medius_log_stream_free(stream) | Release a handle (unsubscribes when the last clone drops). Null is a no-op. |
CATCH MASK BITS
| Constant | Value | Class |
|---|
MEDIUS_CATCH_MASK_MOTION | 1 | cursor motion (dx / dy) |
MEDIUS_CATCH_MASK_WHEEL | 2 | wheel |
MEDIUS_CATCH_MASK_BUTTONS | 4 | mouse buttons |
MEDIUS_CATCH_MASK_KEYS | 8 | keyboard keys |
MEDIUS_CATCH_MASK_MEDIA | 16 | media keys |
MEDIUS_CATCH_MASK_ALL | 31 | everything (1 | 2 | 4 | 8 | 16) |
There's no iterator. Loop a receive call until it returns MEDIUS_STATUS_ERR_DISCONNECTED (the stream closes after a reset or link loss). Each writes one event through *out.
| Function | Returns | Blocks? |
|---|
medius_event_stream_recv(stream, &out) | MediusStatus (MEDIUS_STATUS_ERR_DISCONNECTED on close) | Yes, until the next event |
medius_event_stream_try_recv(stream, &out) | bool (false if the queue is empty) | No, returns at once |
medius_event_stream_recv_timeout(stream, timeout_ms, &out) | bool (false on timeout or close) | Up to timeout_ms |
LOGS MIRROR THIS
| Function | Returns |
|---|
medius_log_stream_recv(stream, &out) | MediusStatus (MEDIUS_STATUS_ERR_DISCONNECTED on close) |
medius_log_stream_try_recv(stream, &out) | bool (false if none queued) |
medius_log_stream_recv_timeout(stream, timeout_ms, &out) | bool (false on timeout or close) |
medius_event_stream_recv fills a MediusCatchEvent: a kind tag plus a union. Read the union arm that matches kind. The usage list is length-prefixed by an inline n count; the backing array is fixed at MEDIUS_MAX_USAGES (256), so nothing truncates. It holds class-tagged MediusUsage usages (a button, key, or media HID usage).
typedef struct MediusCatchEvent {
MediusCatchEventKind kind;
union MediusCatchEventData data;
} MediusCatchEvent;
struct MediusMotionEvent { int16_t dx, dy, dz; };
struct MediusUsageEvent { uint16_t n; MediusUsage usages[256]; };
typedef struct MediusLogLine {
MediusLogLevel level;
char text[512];
} MediusLogLine;
When kind is | Read | Fields |
|---|
MEDIUS_CATCH_EVENT_KIND_MOTION | data.motion | dx, dy, dz (cursor and wheel deltas) |
MEDIUS_CATCH_EVENT_KIND_USAGES | data.usages | usages[0..n], each a class-tagged MediusUsage |
INSPECTORS
| Helper | Does |
|---|
medius_usage_event_is_held(&ev.data.usages, usage) | bool: true if that MediusUsage usage (button, key, or media) is held. |
medius_event_stream_dropped(stream) | uint64_t: events dropped because the consumer fell behind (host-side back-pressure). |
#include <medius.h>
#include <stdio.h>
MediusEventStream *events = NULL;
if (medius_device_catch_events(dev, MEDIUS_CATCH_MASK_ALL, &events) != MEDIUS_STATUS_OK) {
char msg[256];
medius_last_error_message(msg, sizeof msg);
fprintf(stderr, "subscribe failed: %s\n", msg);
return 1;
}
MediusCatchEvent ev;
while (medius_event_stream_recv(events, &ev) == MEDIUS_STATUS_OK) {
switch (ev.kind) {
case MEDIUS_CATCH_EVENT_KIND_MOTION:
printf("motion dx=%d dy=%d dz=%d\n",
ev.data.motion.dx, ev.data.motion.dy, ev.data.motion.dz);
break;
case MEDIUS_CATCH_EVENT_KIND_USAGES:
printf("held usages=%u LMB=%d W=%d\n", ev.data.usages.n,
medius_usage_event_is_held(&ev.data.usages, medius_usage_button(MEDIUS_BUTTON_LEFT)),
medius_usage_event_is_held(&ev.data.usages, medius_usage_key(MEDIUS_KEY_W)));
break;
}
}
printf("dropped while behind: %llu\n",
(unsigned long long)medius_event_stream_dropped(events));
medius_event_stream_free(events);
The C ABI is synchronous; there's no async API. For a single-threaded event loop, poll with medius_event_stream_try_recv or block with a budget using medius_event_stream_recv_timeout; or run the blocking recv loop on its own thread. A stream handle is clonable, so each thread can hold its own.