4 Events
dxtr edited this page 2026-10-18 16:13:29 +02:00

Events

Reading events

Two functions read events:

(win:process-events window-system &key (timeout 0)) => count
(win:poll-event window-system event)                 => boolean

process-events reads what the window system sent, answers what needs an answer, and queues the events. It returns how many events it queued. timeout is how long it may wait for something to arrive:

timeout Behaviour
0, the default Does not wait. Handles what has arrived and returns. Use this in a loop that renders every frame.
a positive real, such as 0.05 Waits at most that many seconds. Use this on an event thread, so it can still notice that it should stop.
nil Waits until something arrives.

poll-event copies the oldest queued event into event and returns true, or returns false if no event is queued. It never waits and never allocates.

Make the event struct once, with make-event, and reuse it:

(let ((event (win:make-event)))
  (loop
    (win:process-events window-system :timeout 0.05)
    (loop while (win:poll-event window-system event)
          do (handle event))))

Call process-events regularly, from one thread. The window system expects answers, such as replies to its pings, and held keys repeat only while it runs. See Threads.

Events wait in a queue until they are polled. The queue grows when events come in faster than they are polled, and it keeps its size afterwards. Poll them all every time.

The event struct

An event is a struct with one field per thing an event can say. Which fields mean something depends on its type. The others are nil, 0, 0.0 or empty. A program reads events; backends write them. The readers' setf writers are exported for backends, and for tests that stand in for one.

Reader Type Set for
event-type event-type Every event.
event-window window Every event: the window it concerns.
event-width, event-height (integer 0) :resize: the new size in units.
event-scale scale :resize: the new scale. 1 for other events.
event-key key or nil :key-press, :key-release: the physical key.
event-symbol key or nil :key-press, :key-release: the logical key.
event-scancode (integer 0) :key-press, :key-release: the platform's code for the key.
event-repeat-p boolean :key-press: true when the key repeats because it is held.
event-text string :text: the text typed or chosen in an input method. :preedit: the text being composed.
event-caret-start, event-caret-end (integer 0) or nil :preedit: the input method's caret in the text being composed, or nil.
event-delete-before, event-delete-after (integer 0) :delete-surrounding: how many characters to delete before and after the caret.
event-button button or nil :button-press, :button-release.
event-x, event-y single-float Pointer, button and scroll events: the pointer's position in units, from the window's top left.
event-dx, event-dy single-float :pointer-motion: how far the pointer moved, in units; while the pointer is captured, the device's own motion.
event-scroll-x, event-scroll-y single-float :scroll: how far, in steps, positive right and down.

(event-modifier-p event modifier) says whether a modifier was held, for key, pointer, button and scroll events, and (event-modifiers event) is the mask of them all, in modifier-bit bits. See Keyboard.

The text is reused. The string event-text returns belongs to the event and is overwritten by the next poll-event into it. Copy it to keep it, (copy-seq (win:event-text event)), or copy the whole event:

(win:copy-event event kept)   ; every field of EVENT into KEPT, made with MAKE-EVENT

copy-event copies every field of one event into another, its text into the other's own string, and returns the other: to keep an event past the next poll-event, or to hand it on, as a queue of events does. It allocates nothing unless the text is longer than the other's string has room for.

An event struct belongs to the thread that polls into it.

Event types

Windows

Type Meaning Fields
:resize The window system proposes a new size, a new scale, or both. The first :resize reports the first size. width, height, scale
:close The user asked to close the window, for example with its close button, or the window system dismissed a popup. The window stays open until you destroy it.
:focus-in The window gained the keyboard.
:focus-out The window lost the keyboard.

A :resize event is an announcement. It does not mean the window has the new size yet: see Resizing and Scaling. When several resizes arrive before you poll, the window's pending size is the latest, so act on window-pending-size rather than on each event.

:close is a request. Ask the user to save their work, or ignore it, or destroy the window. For a popup the window system dismissed, popup-dismissed-p is true: destroy it. See Popups.

Keyboard

Type Meaning Fields
:key-press A key went down, or repeated because it is held. key, symbol, scancode, repeat-p, modifiers
:key-release A key came up. key, symbol, scancode, modifiers
:text Text was typed, or chosen in an input method. text

Keyboard events go to the window that has the keyboard focus, between :focus-in and :focus-out. See Keyboard.

Input methods

Type Meaning Fields
:preedit The text an input method is composing, to show at the caret; "" when nothing is composed. text, caret-start, caret-end
:delete-surrounding Delete characters around the caret. delete-before, delete-after

They come only while the window takes text from an input method, after start-text-input. See Input Methods.

Pointer

Type Meaning Fields
:pointer-enter The pointer came into the window. x, y, modifiers
:pointer-leave The pointer left the window. x, y (where it was last), modifiers
:pointer-motion The pointer moved over the window. x, y, dx, dy, modifiers
:button-press A button went down. button, x, y, modifiers
:button-release A button came up. button, x, y, modifiers
:scroll A wheel turned or a touchpad scrolled. scroll-x, scroll-y, x, y, modifiers

Pointer events go to the window under the pointer, between :pointer-enter and :pointer-leave. See Pointer.

A dispatch loop

(defun handle (event)
  (case (win:event-type event)
    (:close
     (setf *running* nil))
    (:key-press
     (cond ((and (win:event-modifier-p event :control)
                 (eq (win:event-symbol event) :z))
            (undo))
           ((eq (win:event-key event) :escape)
            (setf *running* nil))))
    (:text
     (insert-text (copy-seq (win:event-text event))))
    (:pointer-motion
     (hover (win:event-x event) (win:event-y event)))
    (:button-press
     (when (eq (win:event-button event) :left)
       (click (win:event-x event) (win:event-y event))))
    (:scroll
     (scroll-by (win:event-scroll-y event)))))

With several windows, event-window says which window an event is about.