Pointer input
Using the wl_seat.get_pointer
request, clients may obtain a wl_pointer
object. The server will send events to it whenever the user moves their pointer,
presses mouse buttons, uses the scroll wheel, etc — whenever the pointer
is over one of your surfaces. We can determine if this condition is met with the
wl_pointer.enter
event:
<event name="enter">
<arg name="serial" type="uint" />
<arg name="surface" type="object" interface="wl_surface" />
<arg name="surface_x" type="fixed" />
<arg name="surface_y" type="fixed" />
</event>
The server sends this event when the pointer moves over one of our surfaces, and
specifies both the surface that was "entered", as well as the surface-local
coordinates (from the top-left corner) that the pointer is positioned over.
Coordinates here are specified with the "fixed" type, which you may remember
from chapter 2.1 represents a 24.8-bit fixed-precision number
(wl_fixed_to_double
will convert this to C's double
type).
When the pointer is moved away from your surface, the corresponding event is more brief:
<event name="leave">
<arg name="serial" type="uint" />
<arg name="surface" type="object" interface="wl_surface" />
</event>
Once a pointer has entered your surface, you'll start receiving additional events for it, which we'll discuss shortly. The first thing you will likely want to do, however, is provide a cursor image. The process is as such:
- Create a new
wl_surface
with thewl_compositor
. - Use
wl_pointer.set_cursor
to attach that surface to the pointer. - Attach a cursor image
wl_buffer
to the surface and commit it.
The only new API introduced here is wl_pointer.set_cursor
:
<request name="set_cursor">
<arg name="serial" type="uint" />
<arg name="surface" type="object" interface="wl_surface" allow-null="true" />
<arg name="hotspot_x" type="int" />
<arg name="hotspot_y" type="int" />
</request>
The serial
here has to come from the enter
event. The hotspot_x
and
hotspot_y
arguments specify the cursor-surface-local coordinates of the
"hotspot", or the effective position of the pointer within the cursor image
(e.g. at the tip of an arrow). Note also that the surface can be null —
use this to hide the cursor entirely.
If you're looking for a good source of cursor images, libwayland ships with a
separate wayland-cursor
library, which can load X cursor themes from disk and
create wl_buffers
for them. See wayland-cursor.h
for details, or the updates
to our example client in chapter 9.5.
After the cursor has entered your surface and you have attached an appropriate cursor, you're ready to start processing input events. There are motion, button, and axis events.
Pointer frames
A single frame of input processing on the server could carry information about lots of changes — for example, polling the mouse once could return, in a single packet, an updated position and the release of a button. The server sends these changes as separate Wayland events, and uses the "frame" event to group them together.
<event name="frame"></event>
Clients should accumulate all wl_pointer
events as they're received, then
process pending inputs as a single pointer event once the "frame" event is
received.
Motion events
Motion events are specified in the same coordinate space as the enter
event
uses, and are straightforward enough:
<event name="motion">
<arg name="time" type="uint" />
<arg name="surface_x" type="fixed" />
<arg name="surface_y" type="fixed" />
</event>
Like all input events which include a timestamp, the time
value is a
monotonically increasing millisecond-precision timestamp associated with this
input event.
Button events
Button events are mostly self-explanatory:
<enum name="button_state">
<entry name="released" value="0" />
<entry name="pressed" value="1" />
</enum>
<event name="button">
<arg name="serial" type="uint" />
<arg name="time" type="uint" />
<arg name="button" type="uint" />
<arg name="state" type="uint" enum="button_state" />
</event>
However, the button
argument merits some additional explanation. This number
is a platform-specific input event, though note that FreeBSD reuses the Linux
values. You can find these values for Linux in linux/input-event-codes.h
, and
the most useful ones will probably be represented by the constants BTN_LEFT
,
BTN_RIGHT
, and BTN_MIDDLE
. There are more, I'll leave you to peruse the
header at your leisure.
Axis events
The axis event is used for scrolling actions, such as rotating your scroll wheel or rocking it from left to right. The most basic form looks like this:
<enum name="axis">
<entry name="vertical_scroll" value="0" />
<entry name="horizontal_scroll" value="1" />
</enum>
<event name="axis">
<arg name="time" type="uint" />
<arg name="axis" type="uint" enum="axis" />
<arg name="value" type="fixed" />
</event>
However, axis events are complex, and this is the part of the wl_pointer
interface which has received the most attention over the years. Several
additional events exist which increase the specificity of the axis event:
<enum name="axis_source">
<entry name="wheel" value="0" />
<entry name="finger" value="1" />
<entry name="continuous" value="2" />
<entry name="wheel_tilt" value="3" />
</enum>
<event name="axis_source" since="5">
<arg name="axis_source" type="uint" enum="axis_source" />
</event>
The axis_source event tells you what kind of axis was actuated — a scroll wheel, or a finger on a touchpad, tilting a rocker to the side, or something more novel. This event is simple, but the remainder are less so:
<event name="axis_stop" since="5">
<arg name="time" type="uint" />
<arg name="axis" type="uint" enum="axis" />
</event>
<event name="axis_discrete" since="5">
<arg name="axis" type="uint" enum="axis" />
<arg name="discrete" type="int" />
</event>
The precise semantics of these two events are complex, and if you wish to
leverage them I recommend a careful reading of the summaries in wayland.xml
.
In short, the axis_discrete
event is used to disambiguate axis events on an
arbitrary scale from discrete steps of, for example, a scroll wheel where each
"click" of the wheel represents a single discrete change in the axis value. The
axis_stop
event signals that a discrete user motion has completed, and is used
when accounting for a scrolling event which takes place over several frames. Any
future events should be interpreted as a separate motion.