1 Cursors and Pointer Modes
dxtr edited this page 2026-10-14 04:26:09 +02:00

Cursors and Pointer Modes

Cursors

Each window has a cursor, the pointer's look while it is over the window, named as CSS names cursors:

(setf (win:window-cursor window) :text)        ; an I-beam, over text
(setf (win:window-cursor window) :ew-resize)   ; over a divider
(setf (win:window-cursor window) :hidden)      ; no cursor at all
(win:window-cursor window)                     ; => :HIDDEN

It is :default, the arrow, until set, and takes effect at once if the pointer is over the window. Setting the cursor a window has already costs nothing, so it may be set on every pointer motion, as the widget under the pointer changes.

The type cursor lists them all:

Cursors For
:default The arrow.
:text, :vertical-text Text that can be selected.
:pointer Links and buttons.
:wait, :progress Busy; busy in the background but still usable.
:help, :context-menu Help, and a menu to open.
:cell, :crosshair Selecting cells or points.
:alias, :copy, :move, :no-drop, :not-allowed, :grab, :grabbing Drag and drop.
:n-resize, :e-resize, :s-resize, :w-resize, :ne-resize, :nw-resize, :se-resize, :sw-resize Resizing by the edge or corner moved.
:ew-resize, :ns-resize, :nesw-resize, :nwse-resize Resizing both ways.
:col-resize, :row-resize Resizing columns and rows.
:all-scroll, :zoom-in, :zoom-out Scrolling and zooming.
:hidden No cursor.

Where the platform has no image for a cursor, the nearest it has is shown.

  • Wayland: the compositor draws the cursor from its name where it offers cursor-shape-v1. Elsewhere cl-windowing draws it from the user's cursor theme, named by $XCURSOR_THEME and $XCURSOR_SIZE as other toolkits read them, through libwayland-cursor, at the window's scale, and animates animated cursors. A cursor the theme lacks is shown as :default.
  • Windows: the cursors are the user's system cursors; where Windows has none for a cursor, the nearest it has is shown, such as the arrow for :zoom-in.

Pointer modes

A window holds the pointer as its pointer mode says, a pointer-mode:

(setf (win:window-pointer-mode window) :captured)   ; a camera follows the mouse
(setf (win:window-pointer-mode window) :confined)   ; a strategy game's edge scrolling
(setf (win:window-pointer-mode window) :free)       ; as usual, the default
Mode The pointer
:free moves as usual.
:confined cannot leave the window.
:captured is hidden and held where it is. Its motion comes as :pointer-motion events whose event-x and event-y stay put and whose event-dx and event-dy are the device's own motion.

The captured pointer's motion is without the platform's acceleration: about a unit for each count of the mouse, so a camera turns by event-dx and event-dy times a sensitivity:

(:pointer-motion
 (when (eq (win:window-pointer-mode window) :captured)
   (turn-camera (* sensitivity (win:event-dx event))
                (* sensitivity (win:event-dy event)))))

A window holds the pointer only while it has the focus and the pointer has come over it, and gives it back while another window or program has the focus. So the mode lasts until it is set again: a game sets it once, as play starts, and again on pausing.

window-system-capabilities includes :pointer-capture where the window system can hold the pointer. Setting a mode other than :free signals unsupported-error where it cannot.

  • Wayland: the compositor holds the pointer, through pointer-constraints-v1, and the captured pointer's motion comes through relative-pointer-v1. The compositor must offer both.
  • Windows: a window keeps the pointer within its client area, or at one point of it, with ClipCursor, and the captured pointer's motion is the mouse's raw input.