User Input

User input is defined by three main categories: Pointer, Key, Scroll. Drag & Drop events are synthesized from pointer events.


Pointer events are used to represent a user interacting with a UI with a mouse, sylus, finger etc. Whereas key events propagate through the view tree until captured, pointer events use the draw order.

When a pointer event is fed to the runtime, it finds the top-most view it intersects with. That view can handle it or not, the event will be processed by the next view that intersects unless captured. This allows multiple views to handle motion events for example, allowing each of them to show a hovered state. The Block Pointer Events will prevent any pointer events from falling-through a view.


Down events have a slightly special behavior: when a view captures a down event, any Up event will ONLY be sent to that specific view. To avoid surprising behavior in your UI, widgets you create that only do an action on an Up event should also capture the Down event even though they do nothing with it.


enum ink_pointer_type {

enum ink_pointer_action {

struct ink_event_pointer {
    enum ink_pointer_type pointer;
    enum ink_pointer_action action;
    int pointer_id;
    int x, y;

Example API Usage#

struct ink_event ev;
ev.event_type = INK_EVENT_TYPE_POINTER; = 0; = ...; = ...; = INK_MOUSE_LEFT; = INK_POINTER_DOWN;

ink_event_feed_event(0, &ev);


Key events are made up of two types, Actions and Text. Action events are used to represent special key presses, such as the left shift key on a keyboard or a button on a game pad.

Text key events are used to represent UTF-8 input, like the user typing on a keyboard or using text to speech. Furthermore, text events are divided up into KEY_DOWN, KEY_REPEAT, KEY_UP, and TEXT_INPUT. Down, Repeat, and Up key events can be captured by the Event Filter Node and inline event filters whereas TEXT_INPUT cannot and are automatically sent the first focused editable text views.


struct ink_event_text_input {
    ink_str text;
    enum ink_key_state key_state;

    // Modifiers are invalid/unused when key_state == INK_TEXT_INPUT
    bool shift: 1;
    bool ctrl: 1;
    bool super: 1;
    bool alt: 1;
    bool padding_0: 1;
    bool padding_1: 1;
    bool padding_2: 1;
    bool padding_3: 1;

Example API Usage#

struct ink_event ev;
ev.event_type = INK_EVENT_TYPE_TEXT; = ink_str_empty();
ink_str_assign_hold(&, ink_str_from_copy(&'a', 1)); = INK_KEY_DOWN; = false; = false; = false; = false;

ink_event_feed_event(0, &ev);


Scroll events are either explicitly created from a mouse wheel for example, or implicitly when a user drags using a pointer in a scrollable view.


struct ink_event_scroll {
    int dx, dy;

Example API Usage#

struct ink_event ev;
ev.event_type = INK_EVENT_TYPE_SCROLL; = 40; = 0;

ink_event_feed_event(0, &ev);

dx and dy are in pixel units.


All events regardless of type have a concept of being captured. Events propagate throughout a UI (how exactly depends on their type) until a view captures or blocks them. Event filters can match an event and fire an impulse without capturing, allowing any number of filters to respond to an event.