The complete API lives in include/agfy_rfb.h.
This page summarises the surface; the header itself is the source of
truth and the doc-comments there cover the per-function contracts.
agfy_rfb_server_t *agfy_rfb_create(int width, int height,
agfy_rfb_pixfmt_t pixfmt);
void agfy_rfb_destroy(agfy_rfb_server_t *s);
int agfy_rfb_listen(agfy_rfb_server_t *s, int port_v4, int port_v6);
int agfy_rfb_run_async(agfy_rfb_server_t *s);
void agfy_rfb_stop(agfy_rfb_server_t *s);create allocates a server with an initial framebuffer geometry but
no backing buffer; install one with set_frame_buffer. listen
binds to loopback and returns 0 on success or -1 on bind failure.
run_async returns immediately; the accept loop and per-connection
service all run on internal dispatch queues. stop is idempotent and
safe to call from any thread; it cancels every connected client and
the listener. destroy does an implicit stop first.
void agfy_rfb_set_frame_buffer(agfy_rfb_server_t *s, void *buffer,
size_t padded_row_bytes);
void agfy_rfb_mark_dirty(agfy_rfb_server_t *s,
int x1, int y1, int x2, int y2);
void agfy_rfb_set_size(agfy_rfb_server_t *s, int width, int height);Caller owns the buffer; the library never copies or frees it.
padded_row_bytes lets you point at the middle of a larger surface
that has its own row stride. Pass 0 to use the natural
width × 4 stride.
mark_dirty is the only method clients have for triggering a
FramebufferUpdate. Coordinates are half-open: [x1, x2) × [y1, y2).
Coordinates are clamped to the framebuffer bounds; out-of-range or
empty rectangles are no-ops.
set_size triggers a DesktopSize pseudo-encoding push to every
client that negotiated it. Clients without DesktopSize support stay
at the original dimensions until they reconnect (per spec).
void agfy_rfb_set_new_client_hook(agfy_rfb_server_t *s,
agfy_rfb_new_client_fn fn, void *user);
void agfy_rfb_set_pointer_event_hook(agfy_rfb_server_t *s,
agfy_rfb_pointer_event_fn fn, void *user);
void agfy_rfb_set_keyboard_event_hook(agfy_rfb_server_t *s,
agfy_rfb_keyboard_event_fn fn, void *user);
void agfy_rfb_set_release_all_keys_hook(agfy_rfb_server_t *s,
agfy_rfb_release_all_keys_fn fn, void *user);
void agfy_rfb_set_cuttext_hook(agfy_rfb_server_t *s,
agfy_rfb_cuttext_fn fn, void *user);
void agfy_rfb_set_display_hooks(agfy_rfb_server_t *s,
agfy_rfb_display_fn before, void *bu,
agfy_rfb_display_fn after, void *au);
void agfy_rfb_set_client_gone_hook(agfy_rfb_server_t *s,
agfy_rfb_client_gone_fn fn, void *user);Every hook fires on the server's internal serial dispatch queue. Hook implementations must not block on caller-side mutexes that any agfy_rfb_* call from the same thread might also be holding.
new_client returns a decision: REJECT, ACCEPT, or
ACCEPT_VIEW_ONLY. Returning view-only suppresses pointer and
keyboard event delivery for that client.
client_gone fires before the underlying client state is freed.
The cl pointer is still valid for the duration of the hook.
display.before / display.after fire around each FramebufferUpdate
write — useful for frame-rate metrics or coalescing logging.
void agfy_rfb_set_password(agfy_rfb_server_t *s, const char *pwd);
void agfy_rfb_set_password_check(agfy_rfb_server_t *s,
agfy_rfb_password_check_fn fn, void *user);set_password(NULL) or empty string puts the server in No-Auth mode
(security type 1). Any non-empty password switches to VncAuth
(security type 2).
The custom check, when registered, runs after the default DES verification succeeds — both must pass. This lets you layer policy (host filters, rate limits, lookups) without re-implementing the crypto.
void agfy_rfb_send_cut_text(agfy_rfb_server_t *s,
const char *text, size_t len);Broadcasts a server cut-text (latin1) to every connected client (RFC 6143 §7.6.4). No-op when no clients are connected. Caller-owned buffer; the library copies internally.
const char *agfy_rfb_client_host(agfy_rfb_client_t *cl);
int agfy_rfb_client_socket(agfy_rfb_client_t *cl);Pointers returned by client_host are valid only for the lifetime of
the hook invocation; copy if you need to retain the value.
client_socket returns the underlying TCP fd when one is available,
or -1 if the connection runs over a higher-level transport.
Embedders that need a stable per-client identity should derive their
own value inside new_client and stash it via the user pointer.
void agfy_rfb_set_cursor(agfy_rfb_server_t *s,
agfy_rfb_cursor_kind_t kind,
const void *pixels,
int width, int height,
int hot_x, int hot_y);The current release records the state but does not push cursor pseudo-encodings to clients. The signature is stable and the data is preserved for a future encoder; do not rely on visible behaviour yet.
- Public API methods are safe to call from any thread once
run_asynchas returned. - Hooks fire on a single serial dispatch queue. Two hooks never run
concurrently on the same
Server. mark_dirtyenqueues a fan-out onto the server queue and returns immediately; per-client send happens asynchronously.stopblocks long enough to cancel each connection but does not wait for in-flight bytes to drain; pair with a short sleep if you need a quiescent state.
The C API uses simple sentinel returns: NULL from constructors,
-1 from listen / run_async. There is no error code enum.
Failure causes are limited (bad arguments, bind failure, no listener
configured); reading the source for the specific call is cheaper
than an error API.