From b1de58df1a365bd850d4636c04fc1da2b2ff9e72 Mon Sep 17 00:00:00 2001 From: Ching Pei Yang Date: Wed, 11 Jun 2025 21:16:09 +0200 Subject: [PATCH 01/16] do not crash when using wayland --- syncscribble/linux/linuxtablet.c | 58 ++++++++++++++++++++++++++------ 1 file changed, 47 insertions(+), 11 deletions(-) diff --git a/syncscribble/linux/linuxtablet.c b/syncscribble/linux/linuxtablet.c index 8cf0ae9..075d305 100644 --- a/syncscribble/linux/linuxtablet.c +++ b/syncscribble/linux/linuxtablet.c @@ -1,4 +1,6 @@ +#include "SDL_video.h" #include +#include #include // for tolower #include @@ -40,11 +42,17 @@ static TabletData tabletData[MAX_TABLETS]; static size_t nTablets = 0; static struct { + bool valid; Atom absX, absY, absP, tiltX, tiltY, clipboard, imagePng, sdlSel, Incr, utf8String; } XAtoms; static int xinput2_opcode; +// TODO: union with XAtoms? +static struct { + bool valid; +} WlInfo; + static TabletData* findDevice(int sourceid) { @@ -380,8 +388,7 @@ static void processClipboardXEvent(XEvent* xevent) } } -void linuxProcessXEvent(SDL_Event* event) -{ +static void _linuxProcessXEvent(SDL_Event *event) { XEvent* xevent = &event->syswm.msg->msg.x11.event; if(xevent->type == GenericEvent) { XGenericEventCookie* cookie = &xevent->xcookie; @@ -399,14 +406,17 @@ void linuxProcessXEvent(SDL_Event* event) processClipboardXEvent(xevent); } -int linuxInitTablet(SDL_Window* sdlwin) +void linuxProcessXEvent(SDL_Event* event) { - SDL_SysWMinfo wmInfo; - SDL_VERSION(&wmInfo.version) - if(!SDL_GetWindowWMInfo(sdlwin, &wmInfo)) - return 0; - Display* xDisplay = wmInfo.info.x11.display; - Window xWindow = wmInfo.info.x11.window; + if (XAtoms.valid) { + _linuxProcessXEvent(event); + } +} + +static int initXAtoms(SDL_SysWMinfo* wmInfo) { + XAtoms.valid = true; + Display* xDisplay = wmInfo->info.x11.display; + Window xWindow = wmInfo->info.x11.window; XAtoms.clipboard = XInternAtom(xDisplay, "CLIPBOARD", 0); XAtoms.imagePng = XInternAtom(xDisplay, "image/png", 0); @@ -455,6 +465,25 @@ int linuxInitTablet(SDL_Window* sdlwin) return nTablets; } +int linuxInitTablet(SDL_Window* sdlwin) +{ + XAtoms.valid = false; + WlInfo.valid = false; + SDL_SysWMinfo wmInfo; + SDL_VERSION(&wmInfo.version) + if(!SDL_GetWindowWMInfo(sdlwin, &wmInfo)) + return 0; + + switch (wmInfo.subsystem) { + case SDL_SYSWM_X11: + return initXAtoms(&wmInfo); + case SDL_SYSWM_WAYLAND: + return 0; + default: + return 0; + } +} + #ifdef XINPUT2_TEST // gcc -DXINPUT2_TEST -o xitest linuxtablet.c -lSDL2 -lX11 -lXi // refs: /usr/include/X11/extensions/XInput2.h, XI2.h @@ -501,8 +530,7 @@ int main(int argc, char* argv[]) // * https://stackoverflow.com/questions/27378318/c-get-string-from-clipboard-on-linux/44992938#44992938 // * https://github.com/glfw/glfw/blob/master/src/x11_window.c -int requestClipboard(SDL_Window* sdlwin) -{ +static int requestXClipboard(SDL_Window* sdlwin) { SDL_SysWMinfo wmInfo; SDL_VERSION(&wmInfo.version) if(!SDL_GetWindowWMInfo(sdlwin, &wmInfo)) @@ -519,3 +547,11 @@ int requestClipboard(SDL_Window* sdlwin) XConvertSelection(xDisplay, XAtoms.clipboard, XAtoms.imagePng, XAtoms.sdlSel, xWindow, CurrentTime); return 1; } + +int requestClipboard(SDL_Window* sdlwin) +{ + if (XAtoms.valid) { + return requestXClipboard(sdlwin); + } + return 0; +} From 4802cdbcd163e76c9895c972b338bc983f74ce5c Mon Sep 17 00:00:00 2001 From: Ching Pei Yang Date: Fri, 13 Jun 2025 11:29:58 +0200 Subject: [PATCH 02/16] denormalize wayland touch events from SDL --- syncscribble/application.cpp | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/syncscribble/application.cpp b/syncscribble/application.cpp index 8aea7f5..969d43d 100644 --- a/syncscribble/application.cpp +++ b/syncscribble/application.cpp @@ -11,6 +11,7 @@ #include "windows/winhelper.h" #include "SDL_syswm.h" #elif PLATFORM_LINUX +#include "mainwindow.h" #include "linux/linuxtablet.h" #elif PLATFORM_OSX #include "macos/macoshelper.h" @@ -147,6 +148,27 @@ static void poolWait() static int sdlEventFilter(void* app, SDL_Event* event) { #if PLATFORM_LINUX + switch(event->type) { + case SDL_FINGERDOWN: + case SDL_FINGERMOTION: + case SDL_FINGERUP: { + // TODO: check is wayland before this + + // sdl touch event positions are between 0 and 1, ugui expects pixel position + // by default + int win_w, win_h; + SDL_GetWindowSize(static_cast(app)->win->sdlWindow, &win_w, &win_h); + event->tfinger.x *= win_w; + event->tfinger.y *= win_h; + event->tfinger.dx *= win_w; + event->tfinger.dy *= win_h; + const auto f = event->tfinger; + SDL_PeepEvents(event, 1, SDL_ADDEVENT, 0, 0); + return 0; + } + default: + break; + } if(event->type == SDL_SYSWMEVENT) { linuxProcessXEvent(event); return 0; // no further processing From 2cdbd3442ebe311c9d3573cefc3fda669690c2f5 Mon Sep 17 00:00:00 2001 From: Ching Pei Yang Date: Fri, 13 Jun 2025 12:53:11 +0200 Subject: [PATCH 03/16] generate wayland protocol code --- syncscribble/Makefile | 12 + syncscribble/Makefile.unix | 10 +- syncscribble/wayland-protocols/tablet-v2.xml | 1178 +++++++ syncscribble/wayland-protocols/wayland.xml | 3152 ++++++++++++++++++ 4 files changed, 4351 insertions(+), 1 deletion(-) create mode 100644 syncscribble/wayland-protocols/tablet-v2.xml create mode 100644 syncscribble/wayland-protocols/wayland.xml diff --git a/syncscribble/Makefile b/syncscribble/Makefile index e8770cf..b0bc350 100644 --- a/syncscribble/Makefile +++ b/syncscribble/Makefile @@ -283,6 +283,18 @@ else endif endif +WAYLAND ?= 1 +WAYLAND_PROTOCOL_DIR=$(BUILDDIR)/wayland-protocols +ifneq ($(WAYLAND), 0) + SOURCES += \ + $(WAYLAND_PROTOCOL_DIR)/wayland.c \ + $(WAYLAND_PROTOCOL_DIR)/tablet-v2.c + + INCSYS += $(WAYLAND_PROTOCOL_DIR) + + PKGS += wayland-client +endif + # must use this instead of just -lpthread so the defines needed by force_glibc.h are created CFLAGS = -pthread # X11 and Xi needed for linuxtablet.c diff --git a/syncscribble/Makefile.unix b/syncscribble/Makefile.unix index 773c12a..aee2e2b 100644 --- a/syncscribble/Makefile.unix +++ b/syncscribble/Makefile.unix @@ -74,7 +74,7 @@ DEPS=$(SRCBASE:%=$(OBJDIR)/%.d) TGT=$(BUILDDIR)/$(TARGET) # gcc will not create directories, so depend on existence of all directories in output folder # sort removes duplicates (which cause make error) -BUILDDIRS=$(sort $(dir $(OBJ))) +BUILDDIRS=$(sort $(dir $(OBJ)) $(WAYLAND_PROTOCOL_DIR)) .PHONY: all tgz clean distclean sourcelist @@ -96,6 +96,14 @@ $(OBJDIR)/%.o: %.c $(TGT): $(OBJ) $(LD) -o $@ $^ $(LDFLAGS) $(LIBS) +$(WAYLAND_PROTOCOL_DIR)/%.h: wayland-protocols/%.xml + wayland-scanner client-header < $< > $@ + +# TODO: private-code does not depend on client-header, I should figure out where exactly to +# put the headers +$(WAYLAND_PROTOCOL_DIR)/%.c: wayland-protocols/%.xml $(WAYLAND_PROTOCOL_DIR)/%.h + wayland-scanner private-code < $< > $@ + $(TGZ): $(TGT) $(DISTRES) strings $(TGT) | grep "^GLIBC_" mkdir -p $(BUILDDIR)/.dist diff --git a/syncscribble/wayland-protocols/tablet-v2.xml b/syncscribble/wayland-protocols/tablet-v2.xml new file mode 100644 index 0000000..58329f7 --- /dev/null +++ b/syncscribble/wayland-protocols/tablet-v2.xml @@ -0,0 +1,1178 @@ + + + + + Copyright 2014 © Stephen "Lyude" Chandler Paul + Copyright 2015-2016 © Red Hat, Inc. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice (including the + next paragraph) shall be included in all copies or substantial + portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + + + + This description provides a high-level overview of the interplay between + the interfaces defined this protocol. For details, see the protocol + specification. + + More than one tablet may exist, and device-specifics matter. Tablets are + not represented by a single virtual device like wl_pointer. A client + binds to the tablet manager object which is just a proxy object. From + that, the client requests wp_tablet_manager.get_tablet_seat(wl_seat) + and that returns the actual interface that has all the tablets. With + this indirection, we can avoid merging wp_tablet into the actual Wayland + protocol, a long-term benefit. + + The wp_tablet_seat sends a "tablet added" event for each tablet + connected. That event is followed by descriptive events about the + hardware; currently that includes events for name, vid/pid and + a wp_tablet.path event that describes a local path. This path can be + used to uniquely identify a tablet or get more information through + libwacom. Emulated or nested tablets can skip any of those, e.g. a + virtual tablet may not have a vid/pid. The sequence of descriptive + events is terminated by a wp_tablet.done event to signal that a client + may now finalize any initialization for that tablet. + + Events from tablets require a tool in proximity. Tools are also managed + by the tablet seat; a "tool added" event is sent whenever a tool is new + to the compositor. That event is followed by a number of descriptive + events about the hardware; currently that includes capabilities, + hardware id and serial number, and tool type. Similar to the tablet + interface, a wp_tablet_tool.done event is sent to terminate that initial + sequence. + + Any event from a tool happens on the wp_tablet_tool interface. When the + tool gets into proximity of the tablet, a proximity_in event is sent on + the wp_tablet_tool interface, listing the tablet and the surface. That + event is followed by a motion event with the coordinates. After that, + it's the usual motion, axis, button, etc. events. The protocol's + serialisation means events are grouped by wp_tablet_tool.frame events. + + Two special events (that don't exist in X) are down and up. They signal + "tip touching the surface". For tablets without real proximity + detection, the sequence is: proximity_in, motion, down, frame. + + When the tool leaves proximity, a proximity_out event is sent. If any + button is still down, a button release event is sent before this + proximity event. These button events are sent in the same frame as the + proximity event to signal to the client that the buttons were held when + the tool left proximity. + + If the tool moves out of the surface but stays in proximity (i.e. + between windows), compositor-specific grab policies apply. This usually + means that the proximity-out is delayed until all buttons are released. + + Moving a tool physically from one tablet to the other has no real effect + on the protocol, since we already have the tool object from the "tool + added" event. All the information is already there and the proximity + events on both tablets are all a client needs to reconstruct what + happened. + + Some extra axes are normalized, i.e. the client knows the range as + specified in the protocol (e.g. [0, 65535]), the granularity however is + unknown. The current normalized axes are pressure, distance, and slider. + + Other extra axes are in physical units as specified in the protocol. + The current extra axes with physical units are tilt, rotation and + wheel rotation. + + Since tablets work independently of the pointer controlled by the mouse, + the focus handling is independent too and controlled by proximity. + The wp_tablet_tool.set_cursor request sets a tool-specific cursor. + This cursor surface may be the same as the mouse cursor, and it may be + the same across tools but it is possible to be more fine-grained. For + example, a client may set different cursors for the pen and eraser. + + Tools are generally independent of tablets and it is + compositor-specific policy when a tool can be removed. Common approaches + will likely include some form of removing a tool when all tablets the + tool was used on are removed. + + + + + An object that provides access to the graphics tablets available on this + system. All tablets are associated with a seat, to get access to the + actual tablets, use wp_tablet_manager.get_tablet_seat. + + + + + Get the wp_tablet_seat object for the given seat. This object + provides access to all graphics tablets in this seat. + + + + + + + + Destroy the wp_tablet_manager object. Objects created from this + object are unaffected and should be destroyed separately. + + + + + + + An object that provides access to the graphics tablets available on this + seat. After binding to this interface, the compositor sends a set of + wp_tablet_seat.tablet_added and wp_tablet_seat.tool_added events. + + + + + Destroy the wp_tablet_seat object. Objects created from this + object are unaffected and should be destroyed separately. + + + + + + This event is sent whenever a new tablet becomes available on this + seat. This event only provides the object id of the tablet, any + static information about the tablet (device name, vid/pid, etc.) is + sent through the wp_tablet interface. + + + + + + + This event is sent whenever a tool that has not previously been used + with a tablet comes into use. This event only provides the object id + of the tool; any static information about the tool (capabilities, + type, etc.) is sent through the wp_tablet_tool interface. + + + + + + + This event is sent whenever a new pad is known to the system. Typically, + pads are physically attached to tablets and a pad_added event is + sent immediately after the wp_tablet_seat.tablet_added. + However, some standalone pad devices logically attach to tablets at + runtime, and the client must wait for wp_tablet_pad.enter to know + the tablet a pad is attached to. + + This event only provides the object id of the pad. All further + features (buttons, strips, rings) are sent through the wp_tablet_pad + interface. + + + + + + + + An object that represents a physical tool that has been, or is + currently in use with a tablet in this seat. Each wp_tablet_tool + object stays valid until the client destroys it; the compositor + reuses the wp_tablet_tool object to indicate that the object's + respective physical tool has come into proximity of a tablet again. + + A wp_tablet_tool object's relation to a physical tool depends on the + tablet's ability to report serial numbers. If the tablet supports + this capability, then the object represents a specific physical tool + and can be identified even when used on multiple tablets. + + A tablet tool has a number of static characteristics, e.g. tool type, + hardware_serial and capabilities. These capabilities are sent in an + event sequence after the wp_tablet_seat.tool_added event before any + actual events from this tool. This initial event sequence is + terminated by a wp_tablet_tool.done event. + + Tablet tool events are grouped by wp_tablet_tool.frame events. + Any events received before a wp_tablet_tool.frame event should be + considered part of the same hardware state change. + + + + + Sets the surface of the cursor used for this tool on the given + tablet. This request only takes effect if the tool is in proximity + of one of the requesting client's surfaces or the surface parameter + is the current pointer surface. If there was a previous surface set + with this request it is replaced. If surface is NULL, the cursor + image is hidden. + + The parameters hotspot_x and hotspot_y define the position of the + pointer surface relative to the pointer location. Its top-left corner + is always at (x, y) - (hotspot_x, hotspot_y), where (x, y) are the + coordinates of the pointer location, in surface-local coordinates. + + On surface.attach requests to the pointer surface, hotspot_x and + hotspot_y are decremented by the x and y parameters passed to the + request. Attach must be confirmed by wl_surface.commit as usual. + + The hotspot can also be updated by passing the currently set pointer + surface to this request with new values for hotspot_x and hotspot_y. + + The current and pending input regions of the wl_surface are cleared, + and wl_surface.set_input_region is ignored until the wl_surface is no + longer used as the cursor. When the use as a cursor ends, the current + and pending input regions become undefined, and the wl_surface is + unmapped. + + This request gives the surface the role of a wp_tablet_tool cursor. A + surface may only ever be used as the cursor surface for one + wp_tablet_tool. If the surface already has another role or has + previously been used as cursor surface for a different tool, a + protocol error is raised. + + + + + + + + + + This destroys the client's resource for this tool object. + + + + + + Describes the physical type of a tool. The physical type of a tool + generally defines its base usage. + + The mouse tool represents a mouse-shaped tool that is not a relative + device but bound to the tablet's surface, providing absolute + coordinates. + + The lens tool is a mouse-shaped tool with an attached lens to + provide precision focus. + + + + + + + + + + + + + + The tool type is the high-level type of the tool and usually decides + the interaction expected from this tool. + + This event is sent in the initial burst of events before the + wp_tablet_tool.done event. + + + + + + + If the physical tool can be identified by a unique 64-bit serial + number, this event notifies the client of this serial number. + + If multiple tablets are available in the same seat and the tool is + uniquely identifiable by the serial number, that tool may move + between tablets. + + Otherwise, if the tool has no serial number and this event is + missing, the tool is tied to the tablet it first comes into + proximity with. Even if the physical tool is used on multiple + tablets, separate wp_tablet_tool objects will be created, one per + tablet. + + This event is sent in the initial burst of events before the + wp_tablet_tool.done event. + + + + + + + + This event notifies the client of a hardware id available on this tool. + + The hardware id is a device-specific 64-bit id that provides extra + information about the tool in use, beyond the wl_tool.type + enumeration. The format of the id is specific to tablets made by + Wacom Inc. For example, the hardware id of a Wacom Grip + Pen (a stylus) is 0x802. + + This event is sent in the initial burst of events before the + wp_tablet_tool.done event. + + + + + + + + Describes extra capabilities on a tablet. + + Any tool must provide x and y values, extra axes are + device-specific. + + + + + + + + + + + + This event notifies the client of any capabilities of this tool, + beyond the main set of x/y axes and tip up/down detection. + + One event is sent for each extra capability available on this tool. + + This event is sent in the initial burst of events before the + wp_tablet_tool.done event. + + + + + + + This event signals the end of the initial burst of descriptive + events. A client may consider the static description of the tool to + be complete and finalize initialization of the tool. + + + + + + This event is sent when the tool is removed from the system and will + send no further events. Should the physical tool come back into + proximity later, a new wp_tablet_tool object will be created. + + It is compositor-dependent when a tool is removed. A compositor may + remove a tool on proximity out, tablet removal or any other reason. + A compositor may also keep a tool alive until shutdown. + + If the tool is currently in proximity, a proximity_out event will be + sent before the removed event. See wp_tablet_tool.proximity_out for + the handling of any buttons logically down. + + When this event is received, the client must wp_tablet_tool.destroy + the object. + + + + + + Notification that this tool is focused on a certain surface. + + This event can be received when the tool has moved from one surface to + another, or when the tool has come back into proximity above the + surface. + + If any button is logically down when the tool comes into proximity, + the respective button event is sent after the proximity_in event but + within the same frame as the proximity_in event. + + + + + + + + + Notification that this tool has either left proximity, or is no + longer focused on a certain surface. + + When the tablet tool leaves proximity of the tablet, button release + events are sent for each button that was held down at the time of + leaving proximity. These events are sent before the proximity_out + event but within the same wp_tablet.frame. + + If the tool stays within proximity of the tablet, but the focus + changes from one surface to another, a button release event may not + be sent until the button is actually released or the tool leaves the + proximity of the tablet. + + + + + + Sent whenever the tablet tool comes in contact with the surface of the + tablet. + + If the tool is already in contact with the tablet when entering the + input region, the client owning said region will receive a + wp_tablet.proximity_in event, followed by a wp_tablet.down + event and a wp_tablet.frame event. + + Note that this event describes logical contact, not physical + contact. On some devices, a compositor may not consider a tool in + logical contact until a minimum physical pressure threshold is + exceeded. + + + + + + + Sent whenever the tablet tool stops making contact with the surface of + the tablet, or when the tablet tool moves out of the input region + and the compositor grab (if any) is dismissed. + + If the tablet tool moves out of the input region while in contact + with the surface of the tablet and the compositor does not have an + ongoing grab on the surface, the client owning said region will + receive a wp_tablet.up event, followed by a wp_tablet.proximity_out + event and a wp_tablet.frame event. If the compositor has an ongoing + grab on this device, this event sequence is sent whenever the grab + is dismissed in the future. + + Note that this event describes logical contact, not physical + contact. On some devices, a compositor may not consider a tool out + of logical contact until physical pressure falls below a specific + threshold. + + + + + + Sent whenever a tablet tool moves. + + + + + + + + Sent whenever the pressure axis on a tool changes. The value of this + event is normalized to a value between 0 and 65535. + + Note that pressure may be nonzero even when a tool is not in logical + contact. See the down and up events for more details. + + + + + + + Sent whenever the distance axis on a tool changes. The value of this + event is normalized to a value between 0 and 65535. + + Note that distance may be nonzero even when a tool is not in logical + contact. See the down and up events for more details. + + + + + + + Sent whenever one or both of the tilt axes on a tool change. Each tilt + value is in degrees, relative to the z-axis of the tablet. + The angle is positive when the top of a tool tilts along the + positive x or y axis. + + + + + + + + Sent whenever the z-rotation axis on the tool changes. The + rotation value is in degrees clockwise from the tool's + logical neutral position. + + + + + + + Sent whenever the slider position on the tool changes. The + value is normalized between -65535 and 65535, with 0 as the logical + neutral position of the slider. + + The slider is available on e.g. the Wacom Airbrush tool. + + + + + + + Sent whenever the wheel on the tool emits an event. This event + contains two values for the same axis change. The degrees value is + in the same orientation as the wl_pointer.vertical_scroll axis. The + clicks value is in discrete logical clicks of the mouse wheel. This + value may be zero if the movement of the wheel was less + than one logical click. + + Clients should choose either value and avoid mixing degrees and + clicks. The compositor may accumulate values smaller than a logical + click and emulate click events when a certain threshold is met. + Thus, wl_tablet_tool.wheel events with non-zero clicks values may + have different degrees values. + + + + + + + + Describes the physical state of a button that produced the button event. + + + + + + + + Sent whenever a button on the tool is pressed or released. + + If a button is held down when the tool moves in or out of proximity, + button events are generated by the compositor. See + wp_tablet_tool.proximity_in and wp_tablet_tool.proximity_out for + details. + + + + + + + + + Marks the end of a series of axis and/or button updates from the + tablet. The Wayland protocol requires axis updates to be sent + sequentially, however all events within a frame should be considered + one hardware event. + + + + + + + + + + + + The wp_tablet interface represents one graphics tablet device. The + tablet interface itself does not generate events; all events are + generated by wp_tablet_tool objects when in proximity above a tablet. + + A tablet has a number of static characteristics, e.g. device name and + pid/vid. These capabilities are sent in an event sequence after the + wp_tablet_seat.tablet_added event. This initial event sequence is + terminated by a wp_tablet.done event. + + + + + This destroys the client's resource for this tablet object. + + + + + + A descriptive name for the tablet device. + + If the device has no descriptive name, this event is not sent. + + This event is sent in the initial burst of events before the + wp_tablet.done event. + + + + + + + The USB vendor and product IDs for the tablet device. + + If the device has no USB vendor/product ID, this event is not sent. + This can happen for virtual devices or non-USB devices, for instance. + + This event is sent in the initial burst of events before the + wp_tablet.done event. + + + + + + + + A system-specific device path that indicates which device is behind + this wp_tablet. This information may be used to gather additional + information about the device, e.g. through libwacom. + + A device may have more than one device path. If so, multiple + wp_tablet.path events are sent. A device may be emulated and not + have a device path, and in that case this event will not be sent. + + The format of the path is unspecified, it may be a device node, a + sysfs path, or some other identifier. It is up to the client to + identify the string provided. + + This event is sent in the initial burst of events before the + wp_tablet.done event. + + + + + + + This event is sent immediately to signal the end of the initial + burst of descriptive events. A client may consider the static + description of the tablet to be complete and finalize initialization + of the tablet. + + + + + + Sent when the tablet has been removed from the system. When a tablet + is removed, some tools may be removed. + + When this event is received, the client must wp_tablet.destroy + the object. + + + + + + + A circular interaction area, such as the touch ring on the Wacom Intuos + Pro series tablets. + + Events on a ring are logically grouped by the wl_tablet_pad_ring.frame + event. + + + + + Request that the compositor use the provided feedback string + associated with this ring. This request should be issued immediately + after a wp_tablet_pad_group.mode_switch event from the corresponding + group is received, or whenever the ring is mapped to a different + action. See wp_tablet_pad_group.mode_switch for more details. + + Clients are encouraged to provide context-aware descriptions for + the actions associated with the ring; compositors may use this + information to offer visual feedback about the button layout + (eg. on-screen displays). + + The provided string 'description' is a UTF-8 encoded string to be + associated with this ring, and is considered user-visible; general + internationalization rules apply. + + The serial argument will be that of the last + wp_tablet_pad_group.mode_switch event received for the group of this + ring. Requests providing other serials than the most recent one will be + ignored. + + + + + + + + This destroys the client's resource for this ring object. + + + + + + Describes the source types for ring events. This indicates to the + client how a ring event was physically generated; a client may + adjust the user interface accordingly. For example, events + from a "finger" source may trigger kinetic scrolling. + + + + + + + Source information for ring events. + + This event does not occur on its own. It is sent before a + wp_tablet_pad_ring.frame event and carries the source information + for all events within that frame. + + The source specifies how this event was generated. If the source is + wp_tablet_pad_ring.source.finger, a wp_tablet_pad_ring.stop event + will be sent when the user lifts the finger off the device. + + This event is optional. If the source is unknown for an interaction, + no event is sent. + + + + + + + Sent whenever the angle on a ring changes. + + The angle is provided in degrees clockwise from the logical + north of the ring in the pad's current rotation. + + + + + + + Stop notification for ring events. + + For some wp_tablet_pad_ring.source types, a wp_tablet_pad_ring.stop + event is sent to notify a client that the interaction with the ring + has terminated. This enables the client to implement kinetic scrolling. + See the wp_tablet_pad_ring.source documentation for information on + when this event may be generated. + + Any wp_tablet_pad_ring.angle events with the same source after this + event should be considered as the start of a new interaction. + + + + + + Indicates the end of a set of ring events that logically belong + together. A client is expected to accumulate the data in all events + within the frame before proceeding. + + All wp_tablet_pad_ring events before a wp_tablet_pad_ring.frame event belong + logically together. For example, on termination of a finger interaction + on a ring the compositor will send a wp_tablet_pad_ring.source event, + a wp_tablet_pad_ring.stop event and a wp_tablet_pad_ring.frame event. + + A wp_tablet_pad_ring.frame event is sent for every logical event + group, even if the group only contains a single wp_tablet_pad_ring + event. Specifically, a client may get a sequence: angle, frame, + angle, frame, etc. + + + + + + + + A linear interaction area, such as the strips found in Wacom Cintiq + models. + + Events on a strip are logically grouped by the wl_tablet_pad_strip.frame + event. + + + + + Requests the compositor to use the provided feedback string + associated with this strip. This request should be issued immediately + after a wp_tablet_pad_group.mode_switch event from the corresponding + group is received, or whenever the strip is mapped to a different + action. See wp_tablet_pad_group.mode_switch for more details. + + Clients are encouraged to provide context-aware descriptions for + the actions associated with the strip, and compositors may use this + information to offer visual feedback about the button layout + (eg. on-screen displays). + + The provided string 'description' is a UTF-8 encoded string to be + associated with this ring, and is considered user-visible; general + internationalization rules apply. + + The serial argument will be that of the last + wp_tablet_pad_group.mode_switch event received for the group of this + strip. Requests providing other serials than the most recent one will be + ignored. + + + + + + + + This destroys the client's resource for this strip object. + + + + + + Describes the source types for strip events. This indicates to the + client how a strip event was physically generated; a client may + adjust the user interface accordingly. For example, events + from a "finger" source may trigger kinetic scrolling. + + + + + + + Source information for strip events. + + This event does not occur on its own. It is sent before a + wp_tablet_pad_strip.frame event and carries the source information + for all events within that frame. + + The source specifies how this event was generated. If the source is + wp_tablet_pad_strip.source.finger, a wp_tablet_pad_strip.stop event + will be sent when the user lifts their finger off the device. + + This event is optional. If the source is unknown for an interaction, + no event is sent. + + + + + + + Sent whenever the position on a strip changes. + + The position is normalized to a range of [0, 65535], the 0-value + represents the top-most and/or left-most position of the strip in + the pad's current rotation. + + + + + + + Stop notification for strip events. + + For some wp_tablet_pad_strip.source types, a wp_tablet_pad_strip.stop + event is sent to notify a client that the interaction with the strip + has terminated. This enables the client to implement kinetic + scrolling. See the wp_tablet_pad_strip.source documentation for + information on when this event may be generated. + + Any wp_tablet_pad_strip.position events with the same source after this + event should be considered as the start of a new interaction. + + + + + + Indicates the end of a set of events that represent one logical + hardware strip event. A client is expected to accumulate the data + in all events within the frame before proceeding. + + All wp_tablet_pad_strip events before a wp_tablet_pad_strip.frame event belong + logically together. For example, on termination of a finger interaction + on a strip the compositor will send a wp_tablet_pad_strip.source event, + a wp_tablet_pad_strip.stop event and a wp_tablet_pad_strip.frame + event. + + A wp_tablet_pad_strip.frame event is sent for every logical event + group, even if the group only contains a single wp_tablet_pad_strip + event. Specifically, a client may get a sequence: position, frame, + position, frame, etc. + + + + + + + + A pad group describes a distinct (sub)set of buttons, rings and strips + present in the tablet. The criteria of this grouping is usually positional, + eg. if a tablet has buttons on the left and right side, 2 groups will be + presented. The physical arrangement of groups is undisclosed and may + change on the fly. + + Pad groups will announce their features during pad initialization. Between + the corresponding wp_tablet_pad.group event and wp_tablet_pad_group.done, the + pad group will announce the buttons, rings and strips contained in it, + plus the number of supported modes. + + Modes are a mechanism to allow multiple groups of actions for every element + in the pad group. The number of groups and available modes in each is + persistent across device plugs. The current mode is user-switchable, it + will be announced through the wp_tablet_pad_group.mode_switch event both + whenever it is switched, and after wp_tablet_pad.enter. + + The current mode logically applies to all elements in the pad group, + although it is at clients' discretion whether to actually perform different + actions, and/or issue the respective .set_feedback requests to notify the + compositor. See the wp_tablet_pad_group.mode_switch event for more details. + + + + + Destroy the wp_tablet_pad_group object. Objects created from this object + are unaffected and should be destroyed separately. + + + + + + Sent on wp_tablet_pad_group initialization to announce the available + buttons in the group. Button indices start at 0, a button may only be + in one group at a time. + + This event is first sent in the initial burst of events before the + wp_tablet_pad_group.done event. + + Some buttons are reserved by the compositor. These buttons may not be + assigned to any wp_tablet_pad_group. Compositors may broadcast this + event in the case of changes to the mapping of these reserved buttons. + If the compositor happens to reserve all buttons in a group, this event + will be sent with an empty array. + + + + + + + Sent on wp_tablet_pad_group initialization to announce available rings. + One event is sent for each ring available on this pad group. + + This event is sent in the initial burst of events before the + wp_tablet_pad_group.done event. + + + + + + + Sent on wp_tablet_pad initialization to announce available strips. + One event is sent for each strip available on this pad group. + + This event is sent in the initial burst of events before the + wp_tablet_pad_group.done event. + + + + + + + Sent on wp_tablet_pad_group initialization to announce that the pad + group may switch between modes. A client may use a mode to store a + specific configuration for buttons, rings and strips and use the + wl_tablet_pad_group.mode_switch event to toggle between these + configurations. Mode indices start at 0. + + Switching modes is compositor-dependent. See the + wp_tablet_pad_group.mode_switch event for more details. + + This event is sent in the initial burst of events before the + wp_tablet_pad_group.done event. This event is only sent when more than + more than one mode is available. + + + + + + + This event is sent immediately to signal the end of the initial + burst of descriptive events. A client may consider the static + description of the tablet to be complete and finalize initialization + of the tablet group. + + + + + + Notification that the mode was switched. + + A mode applies to all buttons, rings and strips in a group + simultaneously, but a client is not required to assign different actions + for each mode. For example, a client may have mode-specific button + mappings but map the ring to vertical scrolling in all modes. Mode + indices start at 0. + + Switching modes is compositor-dependent. The compositor may provide + visual cues to the user about the mode, e.g. by toggling LEDs on + the tablet device. Mode-switching may be software-controlled or + controlled by one or more physical buttons. For example, on a Wacom + Intuos Pro, the button inside the ring may be assigned to switch + between modes. + + The compositor will also send this event after wp_tablet_pad.enter on + each group in order to notify of the current mode. Groups that only + feature one mode will use mode=0 when emitting this event. + + If a button action in the new mode differs from the action in the + previous mode, the client should immediately issue a + wp_tablet_pad.set_feedback request for each changed button. + + If a ring or strip action in the new mode differs from the action + in the previous mode, the client should immediately issue a + wp_tablet_ring.set_feedback or wp_tablet_strip.set_feedback request + for each changed ring or strip. + + + + + + + + + + A pad device is a set of buttons, rings and strips + usually physically present on the tablet device itself. Some + exceptions exist where the pad device is physically detached, e.g. the + Wacom ExpressKey Remote. + + Pad devices have no axes that control the cursor and are generally + auxiliary devices to the tool devices used on the tablet surface. + + A pad device has a number of static characteristics, e.g. the number + of rings. These capabilities are sent in an event sequence after the + wp_tablet_seat.pad_added event before any actual events from this pad. + This initial event sequence is terminated by a wp_tablet_pad.done + event. + + All pad features (buttons, rings and strips) are logically divided into + groups and all pads have at least one group. The available groups are + notified through the wp_tablet_pad.group event; the compositor will + emit one event per group before emitting wp_tablet_pad.done. + + Groups may have multiple modes. Modes allow clients to map multiple + actions to a single pad feature. Only one mode can be active per group, + although different groups may have different active modes. + + + + + Requests the compositor to use the provided feedback string + associated with this button. This request should be issued immediately + after a wp_tablet_pad_group.mode_switch event from the corresponding + group is received, or whenever a button is mapped to a different + action. See wp_tablet_pad_group.mode_switch for more details. + + Clients are encouraged to provide context-aware descriptions for + the actions associated with each button, and compositors may use + this information to offer visual feedback on the button layout + (e.g. on-screen displays). + + Button indices start at 0. Setting the feedback string on a button + that is reserved by the compositor (i.e. not belonging to any + wp_tablet_pad_group) does not generate an error but the compositor + is free to ignore the request. + + The provided string 'description' is a UTF-8 encoded string to be + associated with this ring, and is considered user-visible; general + internationalization rules apply. + + The serial argument will be that of the last + wp_tablet_pad_group.mode_switch event received for the group of this + button. Requests providing other serials than the most recent one will + be ignored. + + + + + + + + + Destroy the wp_tablet_pad object. Objects created from this object + are unaffected and should be destroyed separately. + + + + + + Sent on wp_tablet_pad initialization to announce available groups. + One event is sent for each pad group available. + + This event is sent in the initial burst of events before the + wp_tablet_pad.done event. At least one group will be announced. + + + + + + + A system-specific device path that indicates which device is behind + this wp_tablet_pad. This information may be used to gather additional + information about the device, e.g. through libwacom. + + The format of the path is unspecified, it may be a device node, a + sysfs path, or some other identifier. It is up to the client to + identify the string provided. + + This event is sent in the initial burst of events before the + wp_tablet_pad.done event. + + + + + + + Sent on wp_tablet_pad initialization to announce the available + buttons. + + This event is sent in the initial burst of events before the + wp_tablet_pad.done event. This event is only sent when at least one + button is available. + + + + + + + This event signals the end of the initial burst of descriptive + events. A client may consider the static description of the pad to + be complete and finalize initialization of the pad. + + + + + + Describes the physical state of a button that caused the button + event. + + + + + + + + Sent whenever the physical state of a button changes. + + + + + + + + + Notification that this pad is focused on the specified surface. + + + + + + + + + Notification that this pad is no longer focused on the specified + surface. + + + + + + + + Sent when the pad has been removed from the system. When a tablet + is removed its pad(s) will be removed too. + + When this event is received, the client must destroy all rings, strips + and groups that were offered by this pad, and issue wp_tablet_pad.destroy + the pad itself. + + + + diff --git a/syncscribble/wayland-protocols/wayland.xml b/syncscribble/wayland-protocols/wayland.xml new file mode 100644 index 0000000..1d1a55c --- /dev/null +++ b/syncscribble/wayland-protocols/wayland.xml @@ -0,0 +1,3152 @@ + + + + + Copyright © 2008-2011 Kristian Høgsberg + Copyright © 2010-2011 Intel Corporation + Copyright © 2012-2013 Collabora, Ltd. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice (including the + next paragraph) shall be included in all copies or substantial + portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + + + + + The core global object. This is a special singleton object. It + is used for internal Wayland protocol features. + + + + + The sync request asks the server to emit the 'done' event + on the returned wl_callback object. Since requests are + handled in-order and events are delivered in-order, this can + be used as a barrier to ensure all previous requests and the + resulting events have been handled. + + The object returned by this request will be destroyed by the + compositor after the callback is fired and as such the client must not + attempt to use it after that point. + + The callback_data passed in the callback is the event serial. + + + + + + + This request creates a registry object that allows the client + to list and bind the global objects available from the + compositor. + + It should be noted that the server side resources consumed in + response to a get_registry request can only be released when the + client disconnects, not when the client side proxy is destroyed. + Therefore, clients should invoke get_registry as infrequently as + possible to avoid wasting memory. + + + + + + + The error event is sent out when a fatal (non-recoverable) + error has occurred. The object_id argument is the object + where the error occurred, most often in response to a request + to that object. The code identifies the error and is defined + by the object interface. As such, each interface defines its + own set of error codes. The message is a brief description + of the error, for (debugging) convenience. + + + + + + + + + These errors are global and can be emitted in response to any + server request. + + + + + + + + + + This event is used internally by the object ID management + logic. When a client deletes an object that it had created, + the server will send this event to acknowledge that it has + seen the delete request. When the client receives this event, + it will know that it can safely reuse the object ID. + + + + + + + + The singleton global registry object. The server has a number of + global objects that are available to all clients. These objects + typically represent an actual object in the server (for example, + an input device) or they are singleton objects that provide + extension functionality. + + When a client creates a registry object, the registry object + will emit a global event for each global currently in the + registry. Globals come and go as a result of device or + monitor hotplugs, reconfiguration or other events, and the + registry will send out global and global_remove events to + keep the client up to date with the changes. To mark the end + of the initial burst of events, the client can use the + wl_display.sync request immediately after calling + wl_display.get_registry. + + A client can bind to a global object by using the bind + request. This creates a client-side handle that lets the object + emit events to the client and lets the client invoke requests on + the object. + + + + + Binds a new, client-created object to the server using the + specified name as the identifier. + + + + + + + + Notify the client of global objects. + + The event notifies the client that a global object with + the given name is now available, and it implements the + given version of the given interface. + + + + + + + + + Notify the client of removed global objects. + + This event notifies the client that the global identified + by name is no longer available. If the client bound to + the global using the bind request, the client should now + destroy that object. + + The object remains valid and requests to the object will be + ignored until the client destroys it, to avoid races between + the global going away and a client sending a request to it. + + + + + + + + Clients can handle the 'done' event to get notified when + the related request is done. + + Note, because wl_callback objects are created from multiple independent + factory interfaces, the wl_callback interface is frozen at version 1. + + + + + Notify the client when the related request is done. + + + + + + + + A compositor. This object is a singleton global. The + compositor is in charge of combining the contents of multiple + surfaces into one displayable output. + + + + + Ask the compositor to create a new surface. + + + + + + + Ask the compositor to create a new region. + + + + + + + + The wl_shm_pool object encapsulates a piece of memory shared + between the compositor and client. Through the wl_shm_pool + object, the client can allocate shared memory wl_buffer objects. + All objects created through the same pool share the same + underlying mapped memory. Reusing the mapped memory avoids the + setup/teardown overhead and is useful when interactively resizing + a surface or for many small buffers. + + + + + Create a wl_buffer object from the pool. + + The buffer is created offset bytes into the pool and has + width and height as specified. The stride argument specifies + the number of bytes from the beginning of one row to the beginning + of the next. The format is the pixel format of the buffer and + must be one of those advertised through the wl_shm.format event. + + A buffer will keep a reference to the pool it was created from + so it is valid to destroy the pool immediately after creating + a buffer from it. + + + + + + + + + + + + Destroy the shared memory pool. + + The mmapped memory will be released when all + buffers that have been created from this pool + are gone. + + + + + + This request will cause the server to remap the backing memory + for the pool from the file descriptor passed when the pool was + created, but using the new size. This request can only be + used to make the pool bigger. + + This request only changes the amount of bytes that are mmapped + by the server and does not touch the file corresponding to the + file descriptor passed at creation time. It is the client's + responsibility to ensure that the file is at least as big as + the new pool size. + + + + + + + + A singleton global object that provides support for shared + memory. + + Clients can create wl_shm_pool objects using the create_pool + request. + + On binding the wl_shm object one or more format events + are emitted to inform clients about the valid pixel formats + that can be used for buffers. + + + + + These errors can be emitted in response to wl_shm requests. + + + + + + + + + This describes the memory layout of an individual pixel. + + All renderers should support argb8888 and xrgb8888 but any other + formats are optional and may not be supported by the particular + renderer in use. + + The drm format codes match the macros defined in drm_fourcc.h, except + argb8888 and xrgb8888. The formats actually supported by the compositor + will be reported by the format event. + + For all wl_shm formats and unless specified in another protocol + extension, pre-multiplied alpha is used for pixel values. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Create a new wl_shm_pool object. + + The pool can be used to create shared memory based buffer + objects. The server will mmap size bytes of the passed file + descriptor, to use as backing memory for the pool. + + + + + + + + + Informs the client about a valid pixel format that + can be used for buffers. Known formats include + argb8888 and xrgb8888. + + + + + + + + A buffer provides the content for a wl_surface. Buffers are + created through factory interfaces such as wl_shm, wp_linux_buffer_params + (from the linux-dmabuf protocol extension) or similar. It has a width and + a height and can be attached to a wl_surface, but the mechanism by which a + client provides and updates the contents is defined by the buffer factory + interface. + + If the buffer uses a format that has an alpha channel, the alpha channel + is assumed to be premultiplied in the color channels unless otherwise + specified. + + Note, because wl_buffer objects are created from multiple independent + factory interfaces, the wl_buffer interface is frozen at version 1. + + + + + Destroy a buffer. If and how you need to release the backing + storage is defined by the buffer factory interface. + + For possible side-effects to a surface, see wl_surface.attach. + + + + + + Sent when this wl_buffer is no longer used by the compositor. + The client is now free to reuse or destroy this buffer and its + backing storage. + + If a client receives a release event before the frame callback + requested in the same wl_surface.commit that attaches this + wl_buffer to a surface, then the client is immediately free to + reuse the buffer and its backing storage, and does not need a + second buffer for the next surface content update. Typically + this is possible, when the compositor maintains a copy of the + wl_surface contents, e.g. as a GL texture. This is an important + optimization for GL(ES) compositors with wl_shm clients. + + + + + + + A wl_data_offer represents a piece of data offered for transfer + by another client (the source client). It is used by the + copy-and-paste and drag-and-drop mechanisms. The offer + describes the different mime types that the data can be + converted to and provides the mechanism for transferring the + data directly from the source client. + + + + + + + + + + + + Indicate that the client can accept the given mime type, or + NULL for not accepted. + + For objects of version 2 or older, this request is used by the + client to give feedback whether the client can receive the given + mime type, or NULL if none is accepted; the feedback does not + determine whether the drag-and-drop operation succeeds or not. + + For objects of version 3 or newer, this request determines the + final result of the drag-and-drop operation. If the end result + is that no mime types were accepted, the drag-and-drop operation + will be cancelled and the corresponding drag source will receive + wl_data_source.cancelled. Clients may still use this event in + conjunction with wl_data_source.action for feedback. + + + + + + + + To transfer the offered data, the client issues this request + and indicates the mime type it wants to receive. The transfer + happens through the passed file descriptor (typically created + with the pipe system call). The source client writes the data + in the mime type representation requested and then closes the + file descriptor. + + The receiving client reads from the read end of the pipe until + EOF and then closes its end, at which point the transfer is + complete. + + This request may happen multiple times for different mime types, + both before and after wl_data_device.drop. Drag-and-drop destination + clients may preemptively fetch data or examine it more closely to + determine acceptance. + + + + + + + + Destroy the data offer. + + + + + + Sent immediately after creating the wl_data_offer object. One + event per offered mime type. + + + + + + + + + Notifies the compositor that the drag destination successfully + finished the drag-and-drop operation. + + Upon receiving this request, the compositor will emit + wl_data_source.dnd_finished on the drag source client. + + It is a client error to perform other requests than + wl_data_offer.destroy after this one. It is also an error to perform + this request after a NULL mime type has been set in + wl_data_offer.accept or no action was received through + wl_data_offer.action. + + If wl_data_offer.finish request is received for a non drag and drop + operation, the invalid_finish protocol error is raised. + + + + + + Sets the actions that the destination side client supports for + this operation. This request may trigger the emission of + wl_data_source.action and wl_data_offer.action events if the compositor + needs to change the selected action. + + This request can be called multiple times throughout the + drag-and-drop operation, typically in response to wl_data_device.enter + or wl_data_device.motion events. + + This request determines the final result of the drag-and-drop + operation. If the end result is that no action is accepted, + the drag source will receive wl_data_source.cancelled. + + The dnd_actions argument must contain only values expressed in the + wl_data_device_manager.dnd_actions enum, and the preferred_action + argument must only contain one of those values set, otherwise it + will result in a protocol error. + + While managing an "ask" action, the destination drag-and-drop client + may perform further wl_data_offer.receive requests, and is expected + to perform one last wl_data_offer.set_actions request with a preferred + action other than "ask" (and optionally wl_data_offer.accept) before + requesting wl_data_offer.finish, in order to convey the action selected + by the user. If the preferred action is not in the + wl_data_offer.source_actions mask, an error will be raised. + + If the "ask" action is dismissed (e.g. user cancellation), the client + is expected to perform wl_data_offer.destroy right away. + + This request can only be made on drag-and-drop offers, a protocol error + will be raised otherwise. + + + + + + + + This event indicates the actions offered by the data source. It + will be sent immediately after creating the wl_data_offer object, + or anytime the source side changes its offered actions through + wl_data_source.set_actions. + + + + + + + This event indicates the action selected by the compositor after + matching the source/destination side actions. Only one action (or + none) will be offered here. + + This event can be emitted multiple times during the drag-and-drop + operation in response to destination side action changes through + wl_data_offer.set_actions. + + This event will no longer be emitted after wl_data_device.drop + happened on the drag-and-drop destination, the client must + honor the last action received, or the last preferred one set + through wl_data_offer.set_actions when handling an "ask" action. + + Compositors may also change the selected action on the fly, mainly + in response to keyboard modifier changes during the drag-and-drop + operation. + + The most recent action received is always the valid one. Prior to + receiving wl_data_device.drop, the chosen action may change (e.g. + due to keyboard modifiers being pressed). At the time of receiving + wl_data_device.drop the drag-and-drop destination must honor the + last action received. + + Action changes may still happen after wl_data_device.drop, + especially on "ask" actions, where the drag-and-drop destination + may choose another action afterwards. Action changes happening + at this stage are always the result of inter-client negotiation, the + compositor shall no longer be able to induce a different action. + + Upon "ask" actions, it is expected that the drag-and-drop destination + may potentially choose a different action and/or mime type, + based on wl_data_offer.source_actions and finally chosen by the + user (e.g. popping up a menu with the available options). The + final wl_data_offer.set_actions and wl_data_offer.accept requests + must happen before the call to wl_data_offer.finish. + + + + + + + + The wl_data_source object is the source side of a wl_data_offer. + It is created by the source client in a data transfer and + provides a way to describe the offered data and a way to respond + to requests to transfer the data. + + + + + + + + + + This request adds a mime type to the set of mime types + advertised to targets. Can be called several times to offer + multiple types. + + + + + + + Destroy the data source. + + + + + + Sent when a target accepts pointer_focus or motion events. If + a target does not accept any of the offered types, type is NULL. + + Used for feedback during drag-and-drop. + + + + + + + Request for data from the client. Send the data as the + specified mime type over the passed file descriptor, then + close it. + + + + + + + + This data source is no longer valid. There are several reasons why + this could happen: + + - The data source has been replaced by another data source. + - The drag-and-drop operation was performed, but the drop destination + did not accept any of the mime types offered through + wl_data_source.target. + - The drag-and-drop operation was performed, but the drop destination + did not select any of the actions present in the mask offered through + wl_data_source.action. + - The drag-and-drop operation was performed but didn't happen over a + surface. + - The compositor cancelled the drag-and-drop operation (e.g. compositor + dependent timeouts to avoid stale drag-and-drop transfers). + + The client should clean up and destroy this data source. + + For objects of version 2 or older, wl_data_source.cancelled will + only be emitted if the data source was replaced by another data + source. + + + + + + + + Sets the actions that the source side client supports for this + operation. This request may trigger wl_data_source.action and + wl_data_offer.action events if the compositor needs to change the + selected action. + + The dnd_actions argument must contain only values expressed in the + wl_data_device_manager.dnd_actions enum, otherwise it will result + in a protocol error. + + This request must be made once only, and can only be made on sources + used in drag-and-drop, so it must be performed before + wl_data_device.start_drag. Attempting to use the source other than + for drag-and-drop will raise a protocol error. + + + + + + + The user performed the drop action. This event does not indicate + acceptance, wl_data_source.cancelled may still be emitted afterwards + if the drop destination does not accept any mime type. + + However, this event might however not be received if the compositor + cancelled the drag-and-drop operation before this event could happen. + + Note that the data_source may still be used in the future and should + not be destroyed here. + + + + + + The drop destination finished interoperating with this data + source, so the client is now free to destroy this data source and + free all associated data. + + If the action used to perform the operation was "move", the + source can now delete the transferred data. + + + + + + This event indicates the action selected by the compositor after + matching the source/destination side actions. Only one action (or + none) will be offered here. + + This event can be emitted multiple times during the drag-and-drop + operation, mainly in response to destination side changes through + wl_data_offer.set_actions, and as the data device enters/leaves + surfaces. + + It is only possible to receive this event after + wl_data_source.dnd_drop_performed if the drag-and-drop operation + ended in an "ask" action, in which case the final wl_data_source.action + event will happen immediately before wl_data_source.dnd_finished. + + Compositors may also change the selected action on the fly, mainly + in response to keyboard modifier changes during the drag-and-drop + operation. + + The most recent action received is always the valid one. The chosen + action may change alongside negotiation (e.g. an "ask" action can turn + into a "move" operation), so the effects of the final action must + always be applied in wl_data_offer.dnd_finished. + + Clients can trigger cursor surface changes from this point, so + they reflect the current action. + + + + + + + + There is one wl_data_device per seat which can be obtained + from the global wl_data_device_manager singleton. + + A wl_data_device provides access to inter-client data transfer + mechanisms such as copy-and-paste and drag-and-drop. + + + + + + + + + This request asks the compositor to start a drag-and-drop + operation on behalf of the client. + + The source argument is the data source that provides the data + for the eventual data transfer. If source is NULL, enter, leave + and motion events are sent only to the client that initiated the + drag and the client is expected to handle the data passing + internally. If source is destroyed, the drag-and-drop session will be + cancelled. + + The origin surface is the surface where the drag originates and + the client must have an active implicit grab that matches the + serial. + + The icon surface is an optional (can be NULL) surface that + provides an icon to be moved around with the cursor. Initially, + the top-left corner of the icon surface is placed at the cursor + hotspot, but subsequent wl_surface.attach request can move the + relative position. Attach requests must be confirmed with + wl_surface.commit as usual. The icon surface is given the role of + a drag-and-drop icon. If the icon surface already has another role, + it raises a protocol error. + + The input region is ignored for wl_surfaces with the role of a + drag-and-drop icon. + + + + + + + + + + This request asks the compositor to set the selection + to the data from the source on behalf of the client. + + To unset the selection, set the source to NULL. + + + + + + + + The data_offer event introduces a new wl_data_offer object, + which will subsequently be used in either the + data_device.enter event (for drag-and-drop) or the + data_device.selection event (for selections). Immediately + following the data_device.data_offer event, the new data_offer + object will send out data_offer.offer events to describe the + mime types it offers. + + + + + + + This event is sent when an active drag-and-drop pointer enters + a surface owned by the client. The position of the pointer at + enter time is provided by the x and y arguments, in surface-local + coordinates. + + + + + + + + + + + This event is sent when the drag-and-drop pointer leaves the + surface and the session ends. The client must destroy the + wl_data_offer introduced at enter time at this point. + + + + + + This event is sent when the drag-and-drop pointer moves within + the currently focused surface. The new position of the pointer + is provided by the x and y arguments, in surface-local + coordinates. + + + + + + + + + The event is sent when a drag-and-drop operation is ended + because the implicit grab is removed. + + The drag-and-drop destination is expected to honor the last action + received through wl_data_offer.action, if the resulting action is + "copy" or "move", the destination can still perform + wl_data_offer.receive requests, and is expected to end all + transfers with a wl_data_offer.finish request. + + If the resulting action is "ask", the action will not be considered + final. The drag-and-drop destination is expected to perform one last + wl_data_offer.set_actions request, or wl_data_offer.destroy in order + to cancel the operation. + + + + + + The selection event is sent out to notify the client of a new + wl_data_offer for the selection for this device. The + data_device.data_offer and the data_offer.offer events are + sent out immediately before this event to introduce the data + offer object. The selection event is sent to a client + immediately before receiving keyboard focus and when a new + selection is set while the client has keyboard focus. The + data_offer is valid until a new data_offer or NULL is received + or until the client loses keyboard focus. Switching surface with + keyboard focus within the same client doesn't mean a new selection + will be sent. The client must destroy the previous selection + data_offer, if any, upon receiving this event. + + + + + + + + + This request destroys the data device. + + + + + + + The wl_data_device_manager is a singleton global object that + provides access to inter-client data transfer mechanisms such as + copy-and-paste and drag-and-drop. These mechanisms are tied to + a wl_seat and this interface lets a client get a wl_data_device + corresponding to a wl_seat. + + Depending on the version bound, the objects created from the bound + wl_data_device_manager object will have different requirements for + functioning properly. See wl_data_source.set_actions, + wl_data_offer.accept and wl_data_offer.finish for details. + + + + + Create a new data source. + + + + + + + Create a new data device for a given seat. + + + + + + + + + + This is a bitmask of the available/preferred actions in a + drag-and-drop operation. + + In the compositor, the selected action is a result of matching the + actions offered by the source and destination sides. "action" events + with a "none" action will be sent to both source and destination if + there is no match. All further checks will effectively happen on + (source actions ∩ destination actions). + + In addition, compositors may also pick different actions in + reaction to key modifiers being pressed. One common design that + is used in major toolkits (and the behavior recommended for + compositors) is: + + - If no modifiers are pressed, the first match (in bit order) + will be used. + - Pressing Shift selects "move", if enabled in the mask. + - Pressing Control selects "copy", if enabled in the mask. + + Behavior beyond that is considered implementation-dependent. + Compositors may for example bind other modifiers (like Alt/Meta) + or drags initiated with other buttons than BTN_LEFT to specific + actions (e.g. "ask"). + + + + + + + + + + + This interface is implemented by servers that provide + desktop-style user interfaces. + + It allows clients to associate a wl_shell_surface with + a basic surface. + + Note! This protocol is deprecated and not intended for production use. + For desktop-style user interfaces, use xdg_shell. Compositors and clients + should not implement this interface. + + + + + + + + + Create a shell surface for an existing surface. This gives + the wl_surface the role of a shell surface. If the wl_surface + already has another role, it raises a protocol error. + + Only one shell surface can be associated with a given surface. + + + + + + + + + An interface that may be implemented by a wl_surface, for + implementations that provide a desktop-style user interface. + + It provides requests to treat surfaces like toplevel, fullscreen + or popup windows, move, resize or maximize them, associate + metadata like title and class, etc. + + On the server side the object is automatically destroyed when + the related wl_surface is destroyed. On the client side, + wl_shell_surface_destroy() must be called before destroying + the wl_surface object. + + + + + A client must respond to a ping event with a pong request or + the client may be deemed unresponsive. + + + + + + + Start a pointer-driven move of the surface. + + This request must be used in response to a button press event. + The server may ignore move requests depending on the state of + the surface (e.g. fullscreen or maximized). + + + + + + + + These values are used to indicate which edge of a surface + is being dragged in a resize operation. The server may + use this information to adapt its behavior, e.g. choose + an appropriate cursor image. + + + + + + + + + + + + + + + Start a pointer-driven resizing of the surface. + + This request must be used in response to a button press event. + The server may ignore resize requests depending on the state of + the surface (e.g. fullscreen or maximized). + + + + + + + + + Map the surface as a toplevel surface. + + A toplevel surface is not fullscreen, maximized or transient. + + + + + + These flags specify details of the expected behaviour + of transient surfaces. Used in the set_transient request. + + + + + + + Map the surface relative to an existing surface. + + The x and y arguments specify the location of the upper left + corner of the surface relative to the upper left corner of the + parent surface, in surface-local coordinates. + + The flags argument controls details of the transient behaviour. + + + + + + + + + + Hints to indicate to the compositor how to deal with a conflict + between the dimensions of the surface and the dimensions of the + output. The compositor is free to ignore this parameter. + + + + + + + + + + Map the surface as a fullscreen surface. + + If an output parameter is given then the surface will be made + fullscreen on that output. If the client does not specify the + output then the compositor will apply its policy - usually + choosing the output on which the surface has the biggest surface + area. + + The client may specify a method to resolve a size conflict + between the output size and the surface size - this is provided + through the method parameter. + + The framerate parameter is used only when the method is set + to "driver", to indicate the preferred framerate. A value of 0 + indicates that the client does not care about framerate. The + framerate is specified in mHz, that is framerate of 60000 is 60Hz. + + A method of "scale" or "driver" implies a scaling operation of + the surface, either via a direct scaling operation or a change of + the output mode. This will override any kind of output scaling, so + that mapping a surface with a buffer size equal to the mode can + fill the screen independent of buffer_scale. + + A method of "fill" means we don't scale up the buffer, however + any output scale is applied. This means that you may run into + an edge case where the application maps a buffer with the same + size of the output mode but buffer_scale 1 (thus making a + surface larger than the output). In this case it is allowed to + downscale the results to fit the screen. + + The compositor must reply to this request with a configure event + with the dimensions for the output on which the surface will + be made fullscreen. + + + + + + + + + Map the surface as a popup. + + A popup surface is a transient surface with an added pointer + grab. + + An existing implicit grab will be changed to owner-events mode, + and the popup grab will continue after the implicit grab ends + (i.e. releasing the mouse button does not cause the popup to + be unmapped). + + The popup grab continues until the window is destroyed or a + mouse button is pressed in any other client's window. A click + in any of the client's surfaces is reported as normal, however, + clicks in other clients' surfaces will be discarded and trigger + the callback. + + The x and y arguments specify the location of the upper left + corner of the surface relative to the upper left corner of the + parent surface, in surface-local coordinates. + + + + + + + + + + + + Map the surface as a maximized surface. + + If an output parameter is given then the surface will be + maximized on that output. If the client does not specify the + output then the compositor will apply its policy - usually + choosing the output on which the surface has the biggest surface + area. + + The compositor will reply with a configure event telling + the expected new surface size. The operation is completed + on the next buffer attach to this surface. + + A maximized surface typically fills the entire output it is + bound to, except for desktop elements such as panels. This is + the main difference between a maximized shell surface and a + fullscreen shell surface. + + The details depend on the compositor implementation. + + + + + + + Set a short title for the surface. + + This string may be used to identify the surface in a task bar, + window list, or other user interface elements provided by the + compositor. + + The string must be encoded in UTF-8. + + + + + + + Set a class for the surface. + + The surface class identifies the general class of applications + to which the surface belongs. A common convention is to use the + file name (or the full path if it is a non-standard location) of + the application's .desktop file as the class. + + + + + + + Ping a client to check if it is receiving events and sending + requests. A client is expected to reply with a pong request. + + + + + + + The configure event asks the client to resize its surface. + + The size is a hint, in the sense that the client is free to + ignore it if it doesn't resize, pick a smaller size (to + satisfy aspect ratio or resize in steps of NxM pixels). + + The edges parameter provides a hint about how the surface + was resized. The client may use this information to decide + how to adjust its content to the new size (e.g. a scrolling + area might adjust its content position to leave the viewable + content unmoved). + + The client is free to dismiss all but the last configure + event it received. + + The width and height arguments specify the size of the window + in surface-local coordinates. + + + + + + + + + The popup_done event is sent out when a popup grab is broken, + that is, when the user clicks a surface that doesn't belong + to the client owning the popup surface. + + + + + + + A surface is a rectangular area that may be displayed on zero + or more outputs, and shown any number of times at the compositor's + discretion. They can present wl_buffers, receive user input, and + define a local coordinate system. + + The size of a surface (and relative positions on it) is described + in surface-local coordinates, which may differ from the buffer + coordinates of the pixel content, in case a buffer_transform + or a buffer_scale is used. + + A surface without a "role" is fairly useless: a compositor does + not know where, when or how to present it. The role is the + purpose of a wl_surface. Examples of roles are a cursor for a + pointer (as set by wl_pointer.set_cursor), a drag icon + (wl_data_device.start_drag), a sub-surface + (wl_subcompositor.get_subsurface), and a window as defined by a + shell protocol (e.g. wl_shell.get_shell_surface). + + A surface can have only one role at a time. Initially a + wl_surface does not have a role. Once a wl_surface is given a + role, it is set permanently for the whole lifetime of the + wl_surface object. Giving the current role again is allowed, + unless explicitly forbidden by the relevant interface + specification. + + Surface roles are given by requests in other interfaces such as + wl_pointer.set_cursor. The request should explicitly mention + that this request gives a role to a wl_surface. Often, this + request also creates a new protocol object that represents the + role and adds additional functionality to wl_surface. When a + client wants to destroy a wl_surface, they must destroy this role + object before the wl_surface, otherwise a defunct_role_object error is + sent. + + Destroying the role object does not remove the role from the + wl_surface, but it may stop the wl_surface from "playing the role". + For instance, if a wl_subsurface object is destroyed, the wl_surface + it was created for will be unmapped and forget its position and + z-order. It is allowed to create a wl_subsurface for the same + wl_surface again, but it is not allowed to use the wl_surface as + a cursor (cursor is a different role than sub-surface, and role + switching is not allowed). + + + + + These errors can be emitted in response to wl_surface requests. + + + + + + + + + + + Deletes the surface and invalidates its object ID. + + + + + + Set a buffer as the content of this surface. + + The new size of the surface is calculated based on the buffer + size transformed by the inverse buffer_transform and the + inverse buffer_scale. This means that at commit time the supplied + buffer size must be an integer multiple of the buffer_scale. If + that's not the case, an invalid_size error is sent. + + The x and y arguments specify the location of the new pending + buffer's upper left corner, relative to the current buffer's upper + left corner, in surface-local coordinates. In other words, the + x and y, combined with the new surface size define in which + directions the surface's size changes. Setting anything other than 0 + as x and y arguments is discouraged, and should instead be replaced + with using the separate wl_surface.offset request. + + When the bound wl_surface version is 5 or higher, passing any + non-zero x or y is a protocol violation, and will result in an + 'invalid_offset' error being raised. The x and y arguments are ignored + and do not change the pending state. To achieve equivalent semantics, + use wl_surface.offset. + + Surface contents are double-buffered state, see wl_surface.commit. + + The initial surface contents are void; there is no content. + wl_surface.attach assigns the given wl_buffer as the pending + wl_buffer. wl_surface.commit makes the pending wl_buffer the new + surface contents, and the size of the surface becomes the size + calculated from the wl_buffer, as described above. After commit, + there is no pending buffer until the next attach. + + Committing a pending wl_buffer allows the compositor to read the + pixels in the wl_buffer. The compositor may access the pixels at + any time after the wl_surface.commit request. When the compositor + will not access the pixels anymore, it will send the + wl_buffer.release event. Only after receiving wl_buffer.release, + the client may reuse the wl_buffer. A wl_buffer that has been + attached and then replaced by another attach instead of committed + will not receive a release event, and is not used by the + compositor. + + If a pending wl_buffer has been committed to more than one wl_surface, + the delivery of wl_buffer.release events becomes undefined. A well + behaved client should not rely on wl_buffer.release events in this + case. Alternatively, a client could create multiple wl_buffer objects + from the same backing storage or use wp_linux_buffer_release. + + Destroying the wl_buffer after wl_buffer.release does not change + the surface contents. Destroying the wl_buffer before wl_buffer.release + is allowed as long as the underlying buffer storage isn't re-used (this + can happen e.g. on client process termination). However, if the client + destroys the wl_buffer before receiving the wl_buffer.release event and + mutates the underlying buffer storage, the surface contents become + undefined immediately. + + If wl_surface.attach is sent with a NULL wl_buffer, the + following wl_surface.commit will remove the surface content. + + + + + + + + + This request is used to describe the regions where the pending + buffer is different from the current surface contents, and where + the surface therefore needs to be repainted. The compositor + ignores the parts of the damage that fall outside of the surface. + + Damage is double-buffered state, see wl_surface.commit. + + The damage rectangle is specified in surface-local coordinates, + where x and y specify the upper left corner of the damage rectangle. + + The initial value for pending damage is empty: no damage. + wl_surface.damage adds pending damage: the new pending damage + is the union of old pending damage and the given rectangle. + + wl_surface.commit assigns pending damage as the current damage, + and clears pending damage. The server will clear the current + damage as it repaints the surface. + + Note! New clients should not use this request. Instead damage can be + posted with wl_surface.damage_buffer which uses buffer coordinates + instead of surface coordinates. + + + + + + + + + + Request a notification when it is a good time to start drawing a new + frame, by creating a frame callback. This is useful for throttling + redrawing operations, and driving animations. + + When a client is animating on a wl_surface, it can use the 'frame' + request to get notified when it is a good time to draw and commit the + next frame of animation. If the client commits an update earlier than + that, it is likely that some updates will not make it to the display, + and the client is wasting resources by drawing too often. + + The frame request will take effect on the next wl_surface.commit. + The notification will only be posted for one frame unless + requested again. For a wl_surface, the notifications are posted in + the order the frame requests were committed. + + The server must send the notifications so that a client + will not send excessive updates, while still allowing + the highest possible update rate for clients that wait for the reply + before drawing again. The server should give some time for the client + to draw and commit after sending the frame callback events to let it + hit the next output refresh. + + A server should avoid signaling the frame callbacks if the + surface is not visible in any way, e.g. the surface is off-screen, + or completely obscured by other opaque surfaces. + + The object returned by this request will be destroyed by the + compositor after the callback is fired and as such the client must not + attempt to use it after that point. + + The callback_data passed in the callback is the current time, in + milliseconds, with an undefined base. + + + + + + + This request sets the region of the surface that contains + opaque content. + + The opaque region is an optimization hint for the compositor + that lets it optimize the redrawing of content behind opaque + regions. Setting an opaque region is not required for correct + behaviour, but marking transparent content as opaque will result + in repaint artifacts. + + The opaque region is specified in surface-local coordinates. + + The compositor ignores the parts of the opaque region that fall + outside of the surface. + + Opaque region is double-buffered state, see wl_surface.commit. + + wl_surface.set_opaque_region changes the pending opaque region. + wl_surface.commit copies the pending region to the current region. + Otherwise, the pending and current regions are never changed. + + The initial value for an opaque region is empty. Setting the pending + opaque region has copy semantics, and the wl_region object can be + destroyed immediately. A NULL wl_region causes the pending opaque + region to be set to empty. + + + + + + + This request sets the region of the surface that can receive + pointer and touch events. + + Input events happening outside of this region will try the next + surface in the server surface stack. The compositor ignores the + parts of the input region that fall outside of the surface. + + The input region is specified in surface-local coordinates. + + Input region is double-buffered state, see wl_surface.commit. + + wl_surface.set_input_region changes the pending input region. + wl_surface.commit copies the pending region to the current region. + Otherwise the pending and current regions are never changed, + except cursor and icon surfaces are special cases, see + wl_pointer.set_cursor and wl_data_device.start_drag. + + The initial value for an input region is infinite. That means the + whole surface will accept input. Setting the pending input region + has copy semantics, and the wl_region object can be destroyed + immediately. A NULL wl_region causes the input region to be set + to infinite. + + + + + + + Surface state (input, opaque, and damage regions, attached buffers, + etc.) is double-buffered. Protocol requests modify the pending state, + as opposed to the current state in use by the compositor. A commit + request atomically applies all pending state, replacing the current + state. After commit, the new pending state is as documented for each + related request. + + On commit, a pending wl_buffer is applied first, and all other state + second. This means that all coordinates in double-buffered state are + relative to the new wl_buffer coming into use, except for + wl_surface.attach itself. If there is no pending wl_buffer, the + coordinates are relative to the current surface contents. + + All requests that need a commit to become effective are documented + to affect double-buffered state. + + Other interfaces may add further double-buffered surface state. + + + + + + This is emitted whenever a surface's creation, movement, or resizing + results in some part of it being within the scanout region of an + output. + + Note that a surface may be overlapping with zero or more outputs. + + + + + + + This is emitted whenever a surface's creation, movement, or resizing + results in it no longer having any part of it within the scanout region + of an output. + + Clients should not use the number of outputs the surface is on for frame + throttling purposes. The surface might be hidden even if no leave event + has been sent, and the compositor might expect new surface content + updates even if no enter event has been sent. The frame event should be + used instead. + + + + + + + + + This request sets an optional transformation on how the compositor + interprets the contents of the buffer attached to the surface. The + accepted values for the transform parameter are the values for + wl_output.transform. + + Buffer transform is double-buffered state, see wl_surface.commit. + + A newly created surface has its buffer transformation set to normal. + + wl_surface.set_buffer_transform changes the pending buffer + transformation. wl_surface.commit copies the pending buffer + transformation to the current one. Otherwise, the pending and current + values are never changed. + + The purpose of this request is to allow clients to render content + according to the output transform, thus permitting the compositor to + use certain optimizations even if the display is rotated. Using + hardware overlays and scanning out a client buffer for fullscreen + surfaces are examples of such optimizations. Those optimizations are + highly dependent on the compositor implementation, so the use of this + request should be considered on a case-by-case basis. + + Note that if the transform value includes 90 or 270 degree rotation, + the width of the buffer will become the surface height and the height + of the buffer will become the surface width. + + If transform is not one of the values from the + wl_output.transform enum the invalid_transform protocol error + is raised. + + + + + + + + + This request sets an optional scaling factor on how the compositor + interprets the contents of the buffer attached to the window. + + Buffer scale is double-buffered state, see wl_surface.commit. + + A newly created surface has its buffer scale set to 1. + + wl_surface.set_buffer_scale changes the pending buffer scale. + wl_surface.commit copies the pending buffer scale to the current one. + Otherwise, the pending and current values are never changed. + + The purpose of this request is to allow clients to supply higher + resolution buffer data for use on high resolution outputs. It is + intended that you pick the same buffer scale as the scale of the + output that the surface is displayed on. This means the compositor + can avoid scaling when rendering the surface on that output. + + Note that if the scale is larger than 1, then you have to attach + a buffer that is larger (by a factor of scale in each dimension) + than the desired surface size. + + If scale is not positive the invalid_scale protocol error is + raised. + + + + + + + + This request is used to describe the regions where the pending + buffer is different from the current surface contents, and where + the surface therefore needs to be repainted. The compositor + ignores the parts of the damage that fall outside of the surface. + + Damage is double-buffered state, see wl_surface.commit. + + The damage rectangle is specified in buffer coordinates, + where x and y specify the upper left corner of the damage rectangle. + + The initial value for pending damage is empty: no damage. + wl_surface.damage_buffer adds pending damage: the new pending + damage is the union of old pending damage and the given rectangle. + + wl_surface.commit assigns pending damage as the current damage, + and clears pending damage. The server will clear the current + damage as it repaints the surface. + + This request differs from wl_surface.damage in only one way - it + takes damage in buffer coordinates instead of surface-local + coordinates. While this generally is more intuitive than surface + coordinates, it is especially desirable when using wp_viewport + or when a drawing library (like EGL) is unaware of buffer scale + and buffer transform. + + Note: Because buffer transformation changes and damage requests may + be interleaved in the protocol stream, it is impossible to determine + the actual mapping between surface and buffer damage until + wl_surface.commit time. Therefore, compositors wishing to take both + kinds of damage into account will have to accumulate damage from the + two requests separately and only transform from one to the other + after receiving the wl_surface.commit. + + + + + + + + + + + + The x and y arguments specify the location of the new pending + buffer's upper left corner, relative to the current buffer's upper + left corner, in surface-local coordinates. In other words, the + x and y, combined with the new surface size define in which + directions the surface's size changes. + + Surface location offset is double-buffered state, see + wl_surface.commit. + + This request is semantically equivalent to and the replaces the x and y + arguments in the wl_surface.attach request in wl_surface versions prior + to 5. See wl_surface.attach for details. + + + + + + + + + + This event indicates the preferred buffer scale for this surface. It is + sent whenever the compositor's preference changes. + + It is intended that scaling aware clients use this event to scale their + content and use wl_surface.set_buffer_scale to indicate the scale they + have rendered with. This allows clients to supply a higher detail + buffer. + + + + + + + This event indicates the preferred buffer transform for this surface. + It is sent whenever the compositor's preference changes. + + It is intended that transform aware clients use this event to apply the + transform to their content and use wl_surface.set_buffer_transform to + indicate the transform they have rendered with. + + + + + + + + A seat is a group of keyboards, pointer and touch devices. This + object is published as a global during start up, or when such a + device is hot plugged. A seat typically has a pointer and + maintains a keyboard focus and a pointer focus. + + + + + This is a bitmask of capabilities this seat has; if a member is + set, then it is present on the seat. + + + + + + + + + These errors can be emitted in response to wl_seat requests. + + + + + + + This is emitted whenever a seat gains or loses the pointer, + keyboard or touch capabilities. The argument is a capability + enum containing the complete set of capabilities this seat has. + + When the pointer capability is added, a client may create a + wl_pointer object using the wl_seat.get_pointer request. This object + will receive pointer events until the capability is removed in the + future. + + When the pointer capability is removed, a client should destroy the + wl_pointer objects associated with the seat where the capability was + removed, using the wl_pointer.release request. No further pointer + events will be received on these objects. + + In some compositors, if a seat regains the pointer capability and a + client has a previously obtained wl_pointer object of version 4 or + less, that object may start sending pointer events again. This + behavior is considered a misinterpretation of the intended behavior + and must not be relied upon by the client. wl_pointer objects of + version 5 or later must not send events if created before the most + recent event notifying the client of an added pointer capability. + + The above behavior also applies to wl_keyboard and wl_touch with the + keyboard and touch capabilities, respectively. + + + + + + + The ID provided will be initialized to the wl_pointer interface + for this seat. + + This request only takes effect if the seat has the pointer + capability, or has had the pointer capability in the past. + It is a protocol violation to issue this request on a seat that has + never had the pointer capability. The missing_capability error will + be sent in this case. + + + + + + + The ID provided will be initialized to the wl_keyboard interface + for this seat. + + This request only takes effect if the seat has the keyboard + capability, or has had the keyboard capability in the past. + It is a protocol violation to issue this request on a seat that has + never had the keyboard capability. The missing_capability error will + be sent in this case. + + + + + + + The ID provided will be initialized to the wl_touch interface + for this seat. + + This request only takes effect if the seat has the touch + capability, or has had the touch capability in the past. + It is a protocol violation to issue this request on a seat that has + never had the touch capability. The missing_capability error will + be sent in this case. + + + + + + + + + In a multi-seat configuration the seat name can be used by clients to + help identify which physical devices the seat represents. + + The seat name is a UTF-8 string with no convention defined for its + contents. Each name is unique among all wl_seat globals. The name is + only guaranteed to be unique for the current compositor instance. + + The same seat names are used for all clients. Thus, the name can be + shared across processes to refer to a specific wl_seat global. + + The name event is sent after binding to the seat global. This event is + only sent once per seat object, and the name does not change over the + lifetime of the wl_seat global. + + Compositors may re-use the same seat name if the wl_seat global is + destroyed and re-created later. + + + + + + + + + Using this request a client can tell the server that it is not going to + use the seat object anymore. + + + + + + + + The wl_pointer interface represents one or more input devices, + such as mice, which control the pointer location and pointer_focus + of a seat. + + The wl_pointer interface generates motion, enter and leave + events for the surfaces that the pointer is located over, + and button and axis events for button presses, button releases + and scrolling. + + + + + + + + + Set the pointer surface, i.e., the surface that contains the + pointer image (cursor). This request gives the surface the role + of a cursor. If the surface already has another role, it raises + a protocol error. + + The cursor actually changes only if the pointer + focus for this device is one of the requesting client's surfaces + or the surface parameter is the current pointer surface. If + there was a previous surface set with this request it is + replaced. If surface is NULL, the pointer image is hidden. + + The parameters hotspot_x and hotspot_y define the position of + the pointer surface relative to the pointer location. Its + top-left corner is always at (x, y) - (hotspot_x, hotspot_y), + where (x, y) are the coordinates of the pointer location, in + surface-local coordinates. + + On surface.attach requests to the pointer surface, hotspot_x + and hotspot_y are decremented by the x and y parameters + passed to the request. Attach must be confirmed by + wl_surface.commit as usual. + + The hotspot can also be updated by passing the currently set + pointer surface to this request with new values for hotspot_x + and hotspot_y. + + The input region is ignored for wl_surfaces with the role of + a cursor. When the use as a cursor ends, the wl_surface is + unmapped. + + The serial parameter must match the latest wl_pointer.enter + serial number sent to the client. Otherwise the request will be + ignored. + + + + + + + + + + Notification that this seat's pointer is focused on a certain + surface. + + When a seat's focus enters a surface, the pointer image + is undefined and a client should respond to this event by setting + an appropriate pointer image with the set_cursor request. + + + + + + + + + + Notification that this seat's pointer is no longer focused on + a certain surface. + + The leave notification is sent before the enter notification + for the new focus. + + + + + + + + Notification of pointer location change. The arguments + surface_x and surface_y are the location relative to the + focused surface. + + + + + + + + + Describes the physical state of a button that produced the button + event. + + + + + + + + Mouse button click and release notifications. + + The location of the click is given by the last motion or + enter event. + The time argument is a timestamp with millisecond + granularity, with an undefined base. + + The button is a button code as defined in the Linux kernel's + linux/input-event-codes.h header file, e.g. BTN_LEFT. + + Any 16-bit button code value is reserved for future additions to the + kernel's event code list. All other button codes above 0xFFFF are + currently undefined but may be used in future versions of this + protocol. + + + + + + + + + + Describes the axis types of scroll events. + + + + + + + + Scroll and other axis notifications. + + For scroll events (vertical and horizontal scroll axes), the + value parameter is the length of a vector along the specified + axis in a coordinate space identical to those of motion events, + representing a relative movement along the specified axis. + + For devices that support movements non-parallel to axes multiple + axis events will be emitted. + + When applicable, for example for touch pads, the server can + choose to emit scroll events where the motion vector is + equivalent to a motion event vector. + + When applicable, a client can transform its content relative to the + scroll distance. + + + + + + + + + + + Using this request a client can tell the server that it is not going to + use the pointer object anymore. + + This request destroys the pointer proxy object, so clients must not call + wl_pointer_destroy() after using this request. + + + + + + + + Indicates the end of a set of events that logically belong together. + A client is expected to accumulate the data in all events within the + frame before proceeding. + + All wl_pointer events before a wl_pointer.frame event belong + logically together. For example, in a diagonal scroll motion the + compositor will send an optional wl_pointer.axis_source event, two + wl_pointer.axis events (horizontal and vertical) and finally a + wl_pointer.frame event. The client may use this information to + calculate a diagonal vector for scrolling. + + When multiple wl_pointer.axis events occur within the same frame, + the motion vector is the combined motion of all events. + When a wl_pointer.axis and a wl_pointer.axis_stop event occur within + the same frame, this indicates that axis movement in one axis has + stopped but continues in the other axis. + When multiple wl_pointer.axis_stop events occur within the same + frame, this indicates that these axes stopped in the same instance. + + A wl_pointer.frame event is sent for every logical event group, + even if the group only contains a single wl_pointer event. + Specifically, a client may get a sequence: motion, frame, button, + frame, axis, frame, axis_stop, frame. + + The wl_pointer.enter and wl_pointer.leave events are logical events + generated by the compositor and not the hardware. These events are + also grouped by a wl_pointer.frame. When a pointer moves from one + surface to another, a compositor should group the + wl_pointer.leave event within the same wl_pointer.frame. + However, a client must not rely on wl_pointer.leave and + wl_pointer.enter being in the same wl_pointer.frame. + Compositor-specific policies may require the wl_pointer.leave and + wl_pointer.enter event being split across multiple wl_pointer.frame + groups. + + + + + + Describes the source types for axis events. This indicates to the + client how an axis event was physically generated; a client may + adjust the user interface accordingly. For example, scroll events + from a "finger" source may be in a smooth coordinate space with + kinetic scrolling whereas a "wheel" source may be in discrete steps + of a number of lines. + + The "continuous" axis source is a device generating events in a + continuous coordinate space, but using something other than a + finger. One example for this source is button-based scrolling where + the vertical motion of a device is converted to scroll events while + a button is held down. + + The "wheel tilt" axis source indicates that the actual device is a + wheel but the scroll event is not caused by a rotation but a + (usually sideways) tilt of the wheel. + + + + + + + + + + Source information for scroll and other axes. + + This event does not occur on its own. It is sent before a + wl_pointer.frame event and carries the source information for + all events within that frame. + + The source specifies how this event was generated. If the source is + wl_pointer.axis_source.finger, a wl_pointer.axis_stop event will be + sent when the user lifts the finger off the device. + + If the source is wl_pointer.axis_source.wheel, + wl_pointer.axis_source.wheel_tilt or + wl_pointer.axis_source.continuous, a wl_pointer.axis_stop event may + or may not be sent. Whether a compositor sends an axis_stop event + for these sources is hardware-specific and implementation-dependent; + clients must not rely on receiving an axis_stop event for these + scroll sources and should treat scroll sequences from these scroll + sources as unterminated by default. + + This event is optional. If the source is unknown for a particular + axis event sequence, no event is sent. + Only one wl_pointer.axis_source event is permitted per frame. + + The order of wl_pointer.axis_discrete and wl_pointer.axis_source is + not guaranteed. + + + + + + + Stop notification for scroll and other axes. + + For some wl_pointer.axis_source types, a wl_pointer.axis_stop event + is sent to notify a client that the axis sequence has terminated. + This enables the client to implement kinetic scrolling. + See the wl_pointer.axis_source documentation for information on when + this event may be generated. + + Any wl_pointer.axis events with the same axis_source after this + event should be considered as the start of a new axis motion. + + The timestamp is to be interpreted identical to the timestamp in the + wl_pointer.axis event. The timestamp value may be the same as a + preceding wl_pointer.axis event. + + + + + + + + Discrete step information for scroll and other axes. + + This event carries the axis value of the wl_pointer.axis event in + discrete steps (e.g. mouse wheel clicks). + + This event is deprecated with wl_pointer version 8 - this event is not + sent to clients supporting version 8 or later. + + This event does not occur on its own, it is coupled with a + wl_pointer.axis event that represents this axis value on a + continuous scale. The protocol guarantees that each axis_discrete + event is always followed by exactly one axis event with the same + axis number within the same wl_pointer.frame. Note that the protocol + allows for other events to occur between the axis_discrete and + its coupled axis event, including other axis_discrete or axis + events. A wl_pointer.frame must not contain more than one axis_discrete + event per axis type. + + This event is optional; continuous scrolling devices + like two-finger scrolling on touchpads do not have discrete + steps and do not generate this event. + + The discrete value carries the directional information. e.g. a value + of -2 is two steps towards the negative direction of this axis. + + The axis number is identical to the axis number in the associated + axis event. + + The order of wl_pointer.axis_discrete and wl_pointer.axis_source is + not guaranteed. + + + + + + + + Discrete high-resolution scroll information. + + This event carries high-resolution wheel scroll information, + with each multiple of 120 representing one logical scroll step + (a wheel detent). For example, an axis_value120 of 30 is one quarter of + a logical scroll step in the positive direction, a value120 of + -240 are two logical scroll steps in the negative direction within the + same hardware event. + Clients that rely on discrete scrolling should accumulate the + value120 to multiples of 120 before processing the event. + + The value120 must not be zero. + + This event replaces the wl_pointer.axis_discrete event in clients + supporting wl_pointer version 8 or later. + + Where a wl_pointer.axis_source event occurs in the same + wl_pointer.frame, the axis source applies to this event. + + The order of wl_pointer.axis_value120 and wl_pointer.axis_source is + not guaranteed. + + + + + + + + + + This specifies the direction of the physical motion that caused a + wl_pointer.axis event, relative to the wl_pointer.axis direction. + + + + + + + + Relative directional information of the entity causing the axis + motion. + + For a wl_pointer.axis event, the wl_pointer.axis_relative_direction + event specifies the movement direction of the entity causing the + wl_pointer.axis event. For example: + - if a user's fingers on a touchpad move down and this + causes a wl_pointer.axis vertical_scroll down event, the physical + direction is 'identical' + - if a user's fingers on a touchpad move down and this causes a + wl_pointer.axis vertical_scroll up scroll up event ('natural + scrolling'), the physical direction is 'inverted'. + + A client may use this information to adjust scroll motion of + components. Specifically, enabling natural scrolling causes the + content to change direction compared to traditional scrolling. + Some widgets like volume control sliders should usually match the + physical direction regardless of whether natural scrolling is + active. This event enables clients to match the scroll direction of + a widget to the physical direction. + + This event does not occur on its own, it is coupled with a + wl_pointer.axis event that represents this axis value. + The protocol guarantees that each axis_relative_direction event is + always followed by exactly one axis event with the same + axis number within the same wl_pointer.frame. Note that the protocol + allows for other events to occur between the axis_relative_direction + and its coupled axis event. + + The axis number is identical to the axis number in the associated + axis event. + + The order of wl_pointer.axis_relative_direction, + wl_pointer.axis_discrete and wl_pointer.axis_source is not + guaranteed. + + + + + + + + + The wl_keyboard interface represents one or more keyboards + associated with a seat. + + + + + This specifies the format of the keymap provided to the + client with the wl_keyboard.keymap event. + + + + + + + + This event provides a file descriptor to the client which can be + memory-mapped in read-only mode to provide a keyboard mapping + description. + + From version 7 onwards, the fd must be mapped with MAP_PRIVATE by + the recipient, as MAP_SHARED may fail. + + + + + + + + + Notification that this seat's keyboard focus is on a certain + surface. + + The compositor must send the wl_keyboard.modifiers event after this + event. + + + + + + + + + Notification that this seat's keyboard focus is no longer on + a certain surface. + + The leave notification is sent before the enter notification + for the new focus. + + After this event client must assume that all keys, including modifiers, + are lifted and also it must stop key repeating if there's some going on. + + + + + + + + Describes the physical state of a key that produced the key event. + + + + + + + + A key was pressed or released. + The time argument is a timestamp with millisecond + granularity, with an undefined base. + + The key is a platform-specific key code that can be interpreted + by feeding it to the keyboard mapping (see the keymap event). + + If this event produces a change in modifiers, then the resulting + wl_keyboard.modifiers event must be sent after this event. + + + + + + + + + + Notifies clients that the modifier and/or group state has + changed, and it should update its local state. + + + + + + + + + + + + + + + + + + + Informs the client about the keyboard's repeat rate and delay. + + This event is sent as soon as the wl_keyboard object has been created, + and is guaranteed to be received by the client before any key press + event. + + Negative values for either rate or delay are illegal. A rate of zero + will disable any repeating (regardless of the value of delay). + + This event can be sent later on as well with a new value if necessary, + so clients should continue listening for the event past the creation + of wl_keyboard. + + + + + + + + + The wl_touch interface represents a touchscreen + associated with a seat. + + Touch interactions can consist of one or more contacts. + For each contact, a series of events is generated, starting + with a down event, followed by zero or more motion events, + and ending with an up event. Events relating to the same + contact point can be identified by the ID of the sequence. + + + + + A new touch point has appeared on the surface. This touch point is + assigned a unique ID. Future events from this touch point reference + this ID. The ID ceases to be valid after a touch up event and may be + reused in the future. + + + + + + + + + + + + The touch point has disappeared. No further events will be sent for + this touch point and the touch point's ID is released and may be + reused in a future touch down event. + + + + + + + + + A touch point has changed coordinates. + + + + + + + + + + Indicates the end of a set of events that logically belong together. + A client is expected to accumulate the data in all events within the + frame before proceeding. + + A wl_touch.frame terminates at least one event but otherwise no + guarantee is provided about the set of events within a frame. A client + must assume that any state not updated in a frame is unchanged from the + previously known state. + + + + + + Sent if the compositor decides the touch stream is a global + gesture. No further events are sent to the clients from that + particular gesture. Touch cancellation applies to all touch points + currently active on this client's surface. The client is + responsible for finalizing the touch points, future touch points on + this surface may reuse the touch point ID. + + + + + + + + + + + + + + Sent when a touchpoint has changed its shape. + + This event does not occur on its own. It is sent before a + wl_touch.frame event and carries the new shape information for + any previously reported, or new touch points of that frame. + + Other events describing the touch point such as wl_touch.down, + wl_touch.motion or wl_touch.orientation may be sent within the + same wl_touch.frame. A client should treat these events as a single + logical touch point update. The order of wl_touch.shape, + wl_touch.orientation and wl_touch.motion is not guaranteed. + A wl_touch.down event is guaranteed to occur before the first + wl_touch.shape event for this touch ID but both events may occur within + the same wl_touch.frame. + + A touchpoint shape is approximated by an ellipse through the major and + minor axis length. The major axis length describes the longer diameter + of the ellipse, while the minor axis length describes the shorter + diameter. Major and minor are orthogonal and both are specified in + surface-local coordinates. The center of the ellipse is always at the + touchpoint location as reported by wl_touch.down or wl_touch.move. + + This event is only sent by the compositor if the touch device supports + shape reports. The client has to make reasonable assumptions about the + shape if it did not receive this event. + + + + + + + + + Sent when a touchpoint has changed its orientation. + + This event does not occur on its own. It is sent before a + wl_touch.frame event and carries the new shape information for + any previously reported, or new touch points of that frame. + + Other events describing the touch point such as wl_touch.down, + wl_touch.motion or wl_touch.shape may be sent within the + same wl_touch.frame. A client should treat these events as a single + logical touch point update. The order of wl_touch.shape, + wl_touch.orientation and wl_touch.motion is not guaranteed. + A wl_touch.down event is guaranteed to occur before the first + wl_touch.orientation event for this touch ID but both events may occur + within the same wl_touch.frame. + + The orientation describes the clockwise angle of a touchpoint's major + axis to the positive surface y-axis and is normalized to the -180 to + +180 degree range. The granularity of orientation depends on the touch + device, some devices only support binary rotation values between 0 and + 90 degrees. + + This event is only sent by the compositor if the touch device supports + orientation reports. + + + + + + + + + An output describes part of the compositor geometry. The + compositor works in the 'compositor coordinate system' and an + output corresponds to a rectangular area in that space that is + actually visible. This typically corresponds to a monitor that + displays part of the compositor space. This object is published + as global during start up, or when a monitor is hotplugged. + + + + + This enumeration describes how the physical + pixels on an output are laid out. + + + + + + + + + + + + This describes the transform that a compositor will apply to a + surface to compensate for the rotation or mirroring of an + output device. + + The flipped values correspond to an initial flip around a + vertical axis followed by rotation. + + The purpose is mainly to allow clients to render accordingly and + tell the compositor, so that for fullscreen surfaces, the + compositor will still be able to scan out directly from client + surfaces. + + + + + + + + + + + + + + The geometry event describes geometric properties of the output. + The event is sent when binding to the output object and whenever + any of the properties change. + + The physical size can be set to zero if it doesn't make sense for this + output (e.g. for projectors or virtual outputs). + + The geometry event will be followed by a done event (starting from + version 2). + + Note: wl_output only advertises partial information about the output + position and identification. Some compositors, for instance those not + implementing a desktop-style output layout or those exposing virtual + outputs, might fake this information. Instead of using x and y, clients + should use xdg_output.logical_position. Instead of using make and model, + clients should use name and description. + + + + + + + + + + + + + + These flags describe properties of an output mode. + They are used in the flags bitfield of the mode event. + + + + + + + + The mode event describes an available mode for the output. + + The event is sent when binding to the output object and there + will always be one mode, the current mode. The event is sent + again if an output changes mode, for the mode that is now + current. In other words, the current mode is always the last + mode that was received with the current flag set. + + Non-current modes are deprecated. A compositor can decide to only + advertise the current mode and never send other modes. Clients + should not rely on non-current modes. + + The size of a mode is given in physical hardware units of + the output device. This is not necessarily the same as + the output size in the global compositor space. For instance, + the output may be scaled, as described in wl_output.scale, + or transformed, as described in wl_output.transform. Clients + willing to retrieve the output size in the global compositor + space should use xdg_output.logical_size instead. + + The vertical refresh rate can be set to zero if it doesn't make + sense for this output (e.g. for virtual outputs). + + The mode event will be followed by a done event (starting from + version 2). + + Clients should not use the refresh rate to schedule frames. Instead, + they should use the wl_surface.frame event or the presentation-time + protocol. + + Note: this information is not always meaningful for all outputs. Some + compositors, such as those exposing virtual outputs, might fake the + refresh rate or the size. + + + + + + + + + + + + This event is sent after all other properties have been + sent after binding to the output object and after any + other property changes done after that. This allows + changes to the output properties to be seen as + atomic, even if they happen via multiple events. + + + + + + This event contains scaling geometry information + that is not in the geometry event. It may be sent after + binding the output object or if the output scale changes + later. If it is not sent, the client should assume a + scale of 1. + + A scale larger than 1 means that the compositor will + automatically scale surface buffers by this amount + when rendering. This is used for very high resolution + displays where applications rendering at the native + resolution would be too small to be legible. + + It is intended that scaling aware clients track the + current output of a surface, and if it is on a scaled + output it should use wl_surface.set_buffer_scale with + the scale of the output. That way the compositor can + avoid scaling the surface, and the client can supply + a higher detail image. + + The scale event will be followed by a done event. + + + + + + + + + Using this request a client can tell the server that it is not going to + use the output object anymore. + + + + + + + + Many compositors will assign user-friendly names to their outputs, show + them to the user, allow the user to refer to an output, etc. The client + may wish to know this name as well to offer the user similar behaviors. + + The name is a UTF-8 string with no convention defined for its contents. + Each name is unique among all wl_output globals. The name is only + guaranteed to be unique for the compositor instance. + + The same output name is used for all clients for a given wl_output + global. Thus, the name can be shared across processes to refer to a + specific wl_output global. + + The name is not guaranteed to be persistent across sessions, thus cannot + be used to reliably identify an output in e.g. configuration files. + + Examples of names include 'HDMI-A-1', 'WL-1', 'X11-1', etc. However, do + not assume that the name is a reflection of an underlying DRM connector, + X11 connection, etc. + + The name event is sent after binding the output object. This event is + only sent once per output object, and the name does not change over the + lifetime of the wl_output global. + + Compositors may re-use the same output name if the wl_output global is + destroyed and re-created later. Compositors should avoid re-using the + same name if possible. + + The name event will be followed by a done event. + + + + + + + Many compositors can produce human-readable descriptions of their + outputs. The client may wish to know this description as well, e.g. for + output selection purposes. + + The description is a UTF-8 string with no convention defined for its + contents. The description is not guaranteed to be unique among all + wl_output globals. Examples might include 'Foocorp 11" Display' or + 'Virtual X11 output via :1'. + + The description event is sent after binding the output object and + whenever the description changes. The description is optional, and may + not be sent at all. + + The description event will be followed by a done event. + + + + + + + + A region object describes an area. + + Region objects are used to describe the opaque and input + regions of a surface. + + + + + Destroy the region. This will invalidate the object ID. + + + + + + Add the specified rectangle to the region. + + + + + + + + + + Subtract the specified rectangle from the region. + + + + + + + + + + + The global interface exposing sub-surface compositing capabilities. + A wl_surface, that has sub-surfaces associated, is called the + parent surface. Sub-surfaces can be arbitrarily nested and create + a tree of sub-surfaces. + + The root surface in a tree of sub-surfaces is the main + surface. The main surface cannot be a sub-surface, because + sub-surfaces must always have a parent. + + A main surface with its sub-surfaces forms a (compound) window. + For window management purposes, this set of wl_surface objects is + to be considered as a single window, and it should also behave as + such. + + The aim of sub-surfaces is to offload some of the compositing work + within a window from clients to the compositor. A prime example is + a video player with decorations and video in separate wl_surface + objects. This should allow the compositor to pass YUV video buffer + processing to dedicated overlay hardware when possible. + + + + + Informs the server that the client will not be using this + protocol object anymore. This does not affect any other + objects, wl_subsurface objects included. + + + + + + + + + + + Create a sub-surface interface for the given surface, and + associate it with the given parent surface. This turns a + plain wl_surface into a sub-surface. + + The to-be sub-surface must not already have another role, and it + must not have an existing wl_subsurface object. Otherwise the + bad_surface protocol error is raised. + + Adding sub-surfaces to a parent is a double-buffered operation on the + parent (see wl_surface.commit). The effect of adding a sub-surface + becomes visible on the next time the state of the parent surface is + applied. + + The parent surface must not be one of the child surface's descendants, + and the parent must be different from the child surface, otherwise the + bad_parent protocol error is raised. + + This request modifies the behaviour of wl_surface.commit request on + the sub-surface, see the documentation on wl_subsurface interface. + + + + + + + + + + An additional interface to a wl_surface object, which has been + made a sub-surface. A sub-surface has one parent surface. A + sub-surface's size and position are not limited to that of the parent. + Particularly, a sub-surface is not automatically clipped to its + parent's area. + + A sub-surface becomes mapped, when a non-NULL wl_buffer is applied + and the parent surface is mapped. The order of which one happens + first is irrelevant. A sub-surface is hidden if the parent becomes + hidden, or if a NULL wl_buffer is applied. These rules apply + recursively through the tree of surfaces. + + The behaviour of a wl_surface.commit request on a sub-surface + depends on the sub-surface's mode. The possible modes are + synchronized and desynchronized, see methods + wl_subsurface.set_sync and wl_subsurface.set_desync. Synchronized + mode caches the wl_surface state to be applied when the parent's + state gets applied, and desynchronized mode applies the pending + wl_surface state directly. A sub-surface is initially in the + synchronized mode. + + Sub-surfaces also have another kind of state, which is managed by + wl_subsurface requests, as opposed to wl_surface requests. This + state includes the sub-surface position relative to the parent + surface (wl_subsurface.set_position), and the stacking order of + the parent and its sub-surfaces (wl_subsurface.place_above and + .place_below). This state is applied when the parent surface's + wl_surface state is applied, regardless of the sub-surface's mode. + As the exception, set_sync and set_desync are effective immediately. + + The main surface can be thought to be always in desynchronized mode, + since it does not have a parent in the sub-surfaces sense. + + Even if a sub-surface is in desynchronized mode, it will behave as + in synchronized mode, if its parent surface behaves as in + synchronized mode. This rule is applied recursively throughout the + tree of surfaces. This means, that one can set a sub-surface into + synchronized mode, and then assume that all its child and grand-child + sub-surfaces are synchronized, too, without explicitly setting them. + + Destroying a sub-surface takes effect immediately. If you need to + synchronize the removal of a sub-surface to the parent surface update, + unmap the sub-surface first by attaching a NULL wl_buffer, update parent, + and then destroy the sub-surface. + + If the parent wl_surface object is destroyed, the sub-surface is + unmapped. + + + + + The sub-surface interface is removed from the wl_surface object + that was turned into a sub-surface with a + wl_subcompositor.get_subsurface request. The wl_surface's association + to the parent is deleted. The wl_surface is unmapped immediately. + + + + + + + + + + This schedules a sub-surface position change. + The sub-surface will be moved so that its origin (top left + corner pixel) will be at the location x, y of the parent surface + coordinate system. The coordinates are not restricted to the parent + surface area. Negative values are allowed. + + The scheduled coordinates will take effect whenever the state of the + parent surface is applied. When this happens depends on whether the + parent surface is in synchronized mode or not. See + wl_subsurface.set_sync and wl_subsurface.set_desync for details. + + If more than one set_position request is invoked by the client before + the commit of the parent surface, the position of a new request always + replaces the scheduled position from any previous request. + + The initial position is 0, 0. + + + + + + + + This sub-surface is taken from the stack, and put back just + above the reference surface, changing the z-order of the sub-surfaces. + The reference surface must be one of the sibling surfaces, or the + parent surface. Using any other surface, including this sub-surface, + will cause a protocol error. + + The z-order is double-buffered. Requests are handled in order and + applied immediately to a pending state. The final pending state is + copied to the active state the next time the state of the parent + surface is applied. When this happens depends on whether the parent + surface is in synchronized mode or not. See wl_subsurface.set_sync and + wl_subsurface.set_desync for details. + + A new sub-surface is initially added as the top-most in the stack + of its siblings and parent. + + + + + + + The sub-surface is placed just below the reference surface. + See wl_subsurface.place_above. + + + + + + + Change the commit behaviour of the sub-surface to synchronized + mode, also described as the parent dependent mode. + + In synchronized mode, wl_surface.commit on a sub-surface will + accumulate the committed state in a cache, but the state will + not be applied and hence will not change the compositor output. + The cached state is applied to the sub-surface immediately after + the parent surface's state is applied. This ensures atomic + updates of the parent and all its synchronized sub-surfaces. + Applying the cached state will invalidate the cache, so further + parent surface commits do not (re-)apply old state. + + See wl_subsurface for the recursive effect of this mode. + + + + + + Change the commit behaviour of the sub-surface to desynchronized + mode, also described as independent or freely running mode. + + In desynchronized mode, wl_surface.commit on a sub-surface will + apply the pending state directly, without caching, as happens + normally with a wl_surface. Calling wl_surface.commit on the + parent surface has no effect on the sub-surface's wl_surface + state. This mode allows a sub-surface to be updated on its own. + + If cached state exists when wl_surface.commit is called in + desynchronized mode, the pending state is added to the cached + state, and applied as a whole. This invalidates the cache. + + Note: even if a sub-surface is set to desynchronized, a parent + sub-surface may override it to behave as synchronized. For details, + see wl_subsurface. + + If a surface's parent surface behaves as desynchronized, then + the cached state is applied on set_desync. + + + + + + From 4ae3e05d4f91925b3c30c5f37bbb2de86c542a50 Mon Sep 17 00:00:00 2001 From: Ching Pei Yang Date: Fri, 13 Jun 2025 14:34:55 +0200 Subject: [PATCH 04/16] add basic wayland seat handler --- nanovgXC | 2 +- syncscribble/Makefile | 2 +- syncscribble/Makefile.custom | 7 +++ syncscribble/Makefile.unix | 7 +-- syncscribble/application.cpp | 2 + syncscribble/linux/linuxwayland.c | 71 +++++++++++++++++++++++++++++++ syncscribble/linux/linuxwayland.h | 15 +++++++ syncscribble/temp.c | 1 + ugui | 2 +- ulib | 2 +- usvg | 2 +- 11 files changed, 105 insertions(+), 8 deletions(-) create mode 100644 syncscribble/Makefile.custom create mode 100644 syncscribble/linux/linuxwayland.c create mode 100644 syncscribble/linux/linuxwayland.h create mode 100644 syncscribble/temp.c diff --git a/nanovgXC b/nanovgXC index 972b14d..96a4b18 160000 --- a/nanovgXC +++ b/nanovgXC @@ -1 +1 @@ -Subproject commit 972b14deb84decc1be9d94203db663623e31dfc8 +Subproject commit 96a4b18ad8b6f5d388494cacd1ee2fc4847dbae9 diff --git a/syncscribble/Makefile b/syncscribble/Makefile index b0bc350..6244b65 100644 --- a/syncscribble/Makefile +++ b/syncscribble/Makefile @@ -264,7 +264,7 @@ include Makefile.wasm else -SOURCES += linux/linuxtablet.c ../nanovgXC/glad/glad.c +SOURCES += linux/linuxtablet.c linux/linuxwayland.c ../nanovgXC/glad/glad.c DEFS += SCRIBBLE_TEST_PATH='"$(CURDIR)/../scribbletest"' DEBUG ?= 0 USE_SYSTEM_SDL ?= 0 diff --git a/syncscribble/Makefile.custom b/syncscribble/Makefile.custom new file mode 100644 index 0000000..aaafed4 --- /dev/null +++ b/syncscribble/Makefile.custom @@ -0,0 +1,7 @@ +.PHONY: build run + +build: + $(MAKE) -j8 USE_SYSTEM_SDL=1 DEBUG=0 CXXFLAGS=-g + +run: build + SDL_VIDEODRIVER=wayland ./Release/Write diff --git a/syncscribble/Makefile.unix b/syncscribble/Makefile.unix index aee2e2b..f03e2d2 100644 --- a/syncscribble/Makefile.unix +++ b/syncscribble/Makefile.unix @@ -99,11 +99,12 @@ $(TGT): $(OBJ) $(WAYLAND_PROTOCOL_DIR)/%.h: wayland-protocols/%.xml wayland-scanner client-header < $< > $@ -# TODO: private-code does not depend on client-header, I should figure out where exactly to -# put the headers -$(WAYLAND_PROTOCOL_DIR)/%.c: wayland-protocols/%.xml $(WAYLAND_PROTOCOL_DIR)/%.h +$(WAYLAND_PROTOCOL_DIR)/%.c: wayland-protocols/%.xml wayland-scanner private-code < $< > $@ +# TODO: there should be a better way... +linux/linuxwayland.c: $(WAYLAND_PROTOCOL_DIR)/wayland.h $(WAYLAND_PROTOCOL_DIR)/tablet-v2.h + $(TGZ): $(TGT) $(DISTRES) strings $(TGT) | grep "^GLIBC_" mkdir -p $(BUILDDIR)/.dist diff --git a/syncscribble/application.cpp b/syncscribble/application.cpp index 969d43d..70e6c76 100644 --- a/syncscribble/application.cpp +++ b/syncscribble/application.cpp @@ -1,4 +1,5 @@ #include "application.h" +#include "linux/linuxwayland.h" #include "usvg/svgpainter.h" #include "usvg/svgwriter.h" #include "usvg/svgparser.h" @@ -434,6 +435,7 @@ int SDL_main(int argc, char* argv[]) #elif PLATFORM_OSX macosDisableMouseCoalescing(); // get all tablet input points #elif PLATFORM_LINUX + linuxInitWayland(sdlWindow); linuxInitTablet(sdlWindow); SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE); // linuxtablet.c handles touch events even if no pen #elif PLATFORM_EMSCRIPTEN diff --git a/syncscribble/linux/linuxwayland.c b/syncscribble/linux/linuxwayland.c new file mode 100644 index 0000000..7163851 --- /dev/null +++ b/syncscribble/linux/linuxwayland.c @@ -0,0 +1,71 @@ +#include "linuxwayland.h" +#include "SDL_syswm.h" +#include "tablet-v2.h" +#include "wayland-client-protocol.h" +#include "SDL_video.h" +#include +#include + +typedef struct WlState { + struct wl_seat* seat; +} WlState; + +static WlState wlState; + +static void handleSeatCapabilities(void* data, struct wl_seat* seat, + uint32_t capabilities) +{ + // TODO +} + +static void handleSeatName(void* data, struct wl_seat* seat, const char* name) +{ + printf("handleSeatName: %s\n", name); +} + +static const struct wl_seat_listener seatListener = { + .capabilities = handleSeatCapabilities, + .name = handleSeatName, +}; + +static void registryHandleGlobal(void* data, struct wl_registry* registry, + uint32_t name, const char* interface, uint32_t version) +{ + if(strcmp(interface, wl_seat_interface.name) == 0) { + // TODO: multi-seat? + wlState.seat = wl_registry_bind(registry, name, &wl_seat_interface, 9); + wl_seat_add_listener(wlState.seat, &seatListener, NULL); + } else if(strcmp(interface, zwp_tablet_manager_v2_interface.name) == 0); +} + +static void registryHandleGlobalRemove(void *data, + struct wl_registry *registry, uint32_t name) +{ + // TODO +} + +static const struct wl_registry_listener registry_listener = { + .global = registryHandleGlobal, + .global_remove = registryHandleGlobalRemove, +}; + +int linuxInitWayland(SDL_Window* sdlwin) +{ + SDL_SysWMinfo wmInfo; + SDL_VERSION(&wmInfo.version); + if(!SDL_GetWindowWMInfo(sdlwin, &wmInfo)) + return 0; + + if (wmInfo.subsystem != SDL_SYSWM_WAYLAND) { + return 0; + } + + // TODO: doesn SDL expose wl_registry directly? + struct wl_registry* registry = wl_display_get_registry(wmInfo.info.wl.display); + wl_registry_add_listener(registry, ®istry_listener, NULL); + + // do I need this? + wl_display_roundtrip(wmInfo.info.wl.display); + + return 0; +} diff --git a/syncscribble/linux/linuxwayland.h b/syncscribble/linux/linuxwayland.h new file mode 100644 index 0000000..2cd457e --- /dev/null +++ b/syncscribble/linux/linuxwayland.h @@ -0,0 +1,15 @@ +#ifndef LINUXWAYLAND_H +#define LINUXWAYLAND_H + +#ifdef __cplusplus +extern "C" { +#endif +struct SDL_Window; +union SDL_Event; +int linuxInitWayland(struct SDL_Window* sdlwin); +// int requestClipboard(struct SDL_Window* sdlwin); +#ifdef __cplusplus +} +#endif + +#endif diff --git a/syncscribble/temp.c b/syncscribble/temp.c new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/syncscribble/temp.c @@ -0,0 +1 @@ + diff --git a/ugui b/ugui index 2bb38d7..c160717 160000 --- a/ugui +++ b/ugui @@ -1 +1 @@ -Subproject commit 2bb38d708cbfb6d22e1868d005d6628baf208161 +Subproject commit c16071795a1f6357acff4c5a892a4b1b964916e3 diff --git a/ulib b/ulib index 6562c3d..cb20a9c 160000 --- a/ulib +++ b/ulib @@ -1 +1 @@ -Subproject commit 6562c3d79a5d2e0142638c56320fa80b758b21d8 +Subproject commit cb20a9cdcbd5df95a293011ecfd65244a4099ab2 diff --git a/usvg b/usvg index 86d8de3..a151a65 160000 --- a/usvg +++ b/usvg @@ -1 +1 @@ -Subproject commit 86d8de3fd9f30a23102d6da81859930e2c41199d +Subproject commit a151a65f1db512503697dcd0d9a152f4a1760b27 From d651abc4d0929fd12a390b657bc87f6292183942 Mon Sep 17 00:00:00 2001 From: Ching Pei Yang Date: Fri, 13 Jun 2025 15:28:41 +0200 Subject: [PATCH 05/16] add boilerplate for wayland tablet tool --- syncscribble/linux/linuxwayland.c | 152 +++++++++++++++++++++++++++++- 1 file changed, 149 insertions(+), 3 deletions(-) diff --git a/syncscribble/linux/linuxwayland.c b/syncscribble/linux/linuxwayland.c index 7163851..1d9feb4 100644 --- a/syncscribble/linux/linuxwayland.c +++ b/syncscribble/linux/linuxwayland.c @@ -8,9 +8,11 @@ typedef struct WlState { struct wl_seat* seat; + struct zwp_tablet_manager_v2* tabletManager; + struct zwp_tablet_seat_v2* tabletSeat; } WlState; -static WlState wlState; +static WlState wlState = {0}; static void handleSeatCapabilities(void* data, struct wl_seat* seat, uint32_t capabilities) @@ -20,7 +22,6 @@ static void handleSeatCapabilities(void* data, struct wl_seat* seat, static void handleSeatName(void* data, struct wl_seat* seat, const char* name) { - printf("handleSeatName: %s\n", name); } static const struct wl_seat_listener seatListener = { @@ -28,6 +29,146 @@ static const struct wl_seat_listener seatListener = { .name = handleSeatName, }; +static void handleTabletToolType(void* , struct zwp_tablet_tool_v2* , uint32_t ) +{ +} + +static void handleHardwareSerial(void* data, struct zwp_tablet_tool_v2* tool, uint32_t hi, uint32_t lo) +{ +} + +static void handleHardwareIdWacom(void* data, struct zwp_tablet_tool_v2* tool, uint32_t hi, uint32_t lo) +{ +} + +void handleTabletToolCapability(void *data, + struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, + uint32_t capability) +{ +} +void handleTabletToolDone(void *data, + struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2) +{ +} +void handleTabletToolRemoved(void *data, + struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2) +{ +} +void handleTabletToolProximityIn(void *data, + struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, + uint32_t serial, + struct zwp_tablet_v2 *tablet, + struct wl_surface *surface) +{ +} +void handleTabletToolProximityOut(void *data, + struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2) +{ +} +void handleTabletToolDown(void *data, + struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, + uint32_t serial) +{ +} +void handleTabletToolUp(void *data, + struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2) +{ +} +void handleTabletToolMotion(void *data, + struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, + wl_fixed_t x, + wl_fixed_t y) +{ +} +void handleTabletToolPressure(void *data, + struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, + uint32_t pressure) +{ +} +void handleTabletToolDistance(void *data, + struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, + uint32_t distance) +{ +} +void handleTabletToolTilt(void *data, + struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, + wl_fixed_t tilt_x, + wl_fixed_t tilt_y) +{ +} +void handleTabletToolRotation(void *data, + struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, + wl_fixed_t degrees) +{ +} +void handleTabletToolSlider(void *data, + struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, + int32_t position) +{ +} +void handleTabletToolWheel(void *data, + struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, + wl_fixed_t degrees, + int32_t clicks) +{ +} +void handleTabletToolButton(void *data, + struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, + uint32_t serial, + uint32_t button, + uint32_t state) +{ +} +void handleTabletToolFrame(void *data, + struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, + uint32_t time) +{ +} + +static const struct zwp_tablet_tool_v2_listener tabletToolListener = { + .type = handleTabletToolType, + .hardware_serial = handleHardwareSerial, + .hardware_id_wacom = handleHardwareIdWacom, + .capability = handleTabletToolCapability, + .done = handleTabletToolDone, + .removed = handleTabletToolRemoved, + .proximity_in = handleTabletToolProximityIn, + .proximity_out = handleTabletToolProximityOut, + .down = handleTabletToolDown, + .up = handleTabletToolUp, + .motion = handleTabletToolMotion, + .pressure = handleTabletToolPressure, + .distance = handleTabletToolDistance, + .tilt = handleTabletToolTilt, + .rotation = handleTabletToolRotation, + .slider = handleTabletToolSlider, + .wheel = handleTabletToolWheel, + .button = handleTabletToolButton, + .frame = handleTabletToolFrame, +}; + +static void handleTabletAdded(void* data, struct zwp_tablet_seat_v2* tabSeat, + struct zwp_tablet_v2 * tablet) +{ + // TODO: should I care +} + +static void handleToolAdded(void* data, struct zwp_tablet_seat_v2* tabletSeat, + struct zwp_tablet_tool_v2* tool) +{ + zwp_tablet_tool_v2_add_listener(tool, &tabletToolListener, NULL); +} + +static void handlePadAdded(void* data, struct zwp_tablet_seat_v2* tabletSeat, struct zwp_tablet_pad_v2* pad) +{ +} + +static const struct zwp_tablet_seat_v2_listener tabletSeatListener = { + .tablet_added = handleTabletAdded, + .tool_added = handleToolAdded, + .pad_added = handlePadAdded, +}; + static void registryHandleGlobal(void* data, struct wl_registry* registry, uint32_t name, const char* interface, uint32_t version) { @@ -35,7 +176,12 @@ static void registryHandleGlobal(void* data, struct wl_registry* registry, // TODO: multi-seat? wlState.seat = wl_registry_bind(registry, name, &wl_seat_interface, 9); wl_seat_add_listener(wlState.seat, &seatListener, NULL); - } else if(strcmp(interface, zwp_tablet_manager_v2_interface.name) == 0); + } else if(strcmp(interface, zwp_tablet_manager_v2_interface.name) == 0) { + wlState.tabletManager = wl_registry_bind(registry, name, &zwp_tablet_manager_v2_interface, 1); + // TODO: is wlState.seat guaranteed to be valid at this point? + wlState.tabletSeat = zwp_tablet_manager_v2_get_tablet_seat(wlState.tabletManager, wlState.seat); + zwp_tablet_seat_v2_add_listener(wlState.tabletSeat, &tabletSeatListener, NULL); + } } static void registryHandleGlobalRemove(void *data, From d9d9239482d4d75b4dc803ced7a4f1419d35f9ab Mon Sep 17 00:00:00 2001 From: Ching Pei Yang Date: Fri, 13 Jun 2025 16:40:15 +0200 Subject: [PATCH 06/16] add basic pen down/up/move handlers --- syncscribble/linux/linuxwayland.c | 70 ++++++++++++++++++++++++++++++- 1 file changed, 68 insertions(+), 2 deletions(-) diff --git a/syncscribble/linux/linuxwayland.c b/syncscribble/linux/linuxwayland.c index 1d9feb4..f80c27f 100644 --- a/syncscribble/linux/linuxwayland.c +++ b/syncscribble/linux/linuxwayland.c @@ -1,19 +1,59 @@ #include "linuxwayland.h" +#include "SDL_events.h" #include "SDL_syswm.h" +#include "SDL_timer.h" #include "tablet-v2.h" +#include "ugui/svggui_platform.h" #include "wayland-client-protocol.h" #include "SDL_video.h" +#include "wayland-util.h" #include #include +#define MAX_TOOLS 32 + +typedef struct FrameInfo { + uint32_t type; +} FrameInfo; + +typedef struct ToolState { + struct zwp_tablet_tool_v2* tool; + SDL_Window* window; // TODO: move somewhere else + float x; + float y; + FrameInfo frame; +} ToolState; + typedef struct WlState { + SDL_Window* window; struct wl_seat* seat; struct zwp_tablet_manager_v2* tabletManager; struct zwp_tablet_seat_v2* tabletSeat; + ToolState tools[MAX_TOOLS]; } WlState; static WlState wlState = {0}; +static void wlReportTabletEvent(uint32_t type, float x, float y) +{ + SDL_Event event = { + .tfinger = { + .type = type, + .timestamp = SDL_GetTicks(), + .touchId = PenPointerPen, // TODO + .fingerId = 0, // TODO + .x = x, + .y = y, + .dx = 0, // TODO + .dy = 0, // TODO + .pressure = 1.0, // TODO + // .windowID = 0, /**< The window underneath the finger, if any */ + } + }; + + SDL_PeepEvents(&event, 1, SDL_ADDEVENT, 0, 0); +} + static void handleSeatCapabilities(void* data, struct wl_seat* seat, uint32_t capabilities) { @@ -66,19 +106,26 @@ void handleTabletToolProximityOut(void *data, { } void handleTabletToolDown(void *data, - struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, + struct zwp_tablet_tool_v2* tool, uint32_t serial) { + ToolState* toolState = data; + toolState->frame.type = SDL_FINGERDOWN; } void handleTabletToolUp(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2) { + ToolState* toolState = data; + toolState->frame.type = SDL_FINGERUP; } void handleTabletToolMotion(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, wl_fixed_t x, wl_fixed_t y) { + ToolState* toolState = data; + toolState->x = wl_fixed_to_double(x); + toolState->y = wl_fixed_to_double(y); } void handleTabletToolPressure(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, @@ -123,6 +170,18 @@ void handleTabletToolFrame(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, uint32_t time) { + ToolState* toolState = data; + if(toolState->frame.type == 0) { + // TODO: log? + return; + } + + int win_w, win_h; + SDL_GetWindowSize(wlState.window, &win_w, &win_h); + + wlReportTabletEvent(toolState->frame.type, toolState->x * win_w, + toolState->y * win_h); + toolState->frame.type = 0; } static const struct zwp_tablet_tool_v2_listener tabletToolListener = { @@ -156,7 +215,13 @@ static void handleTabletAdded(void* data, struct zwp_tablet_seat_v2* tabSeat, static void handleToolAdded(void* data, struct zwp_tablet_seat_v2* tabletSeat, struct zwp_tablet_tool_v2* tool) { - zwp_tablet_tool_v2_add_listener(tool, &tabletToolListener, NULL); + // TODO: multiple tools + for(int i = 0; i < MAX_TOOLS; i++) { + if(wlState.tools[i].tool) + continue; + + zwp_tablet_tool_v2_add_listener(tool, &tabletToolListener, &wlState.tools[i]); + } } static void handlePadAdded(void* data, struct zwp_tablet_seat_v2* tabletSeat, struct zwp_tablet_pad_v2* pad) @@ -197,6 +262,7 @@ static const struct wl_registry_listener registry_listener = { int linuxInitWayland(SDL_Window* sdlwin) { + wlState.window = sdlwin; SDL_SysWMinfo wmInfo; SDL_VERSION(&wmInfo.version); if(!SDL_GetWindowWMInfo(sdlwin, &wmInfo)) From 624589c317bdd13ad27e5cef07542cd35a7dff3a Mon Sep 17 00:00:00 2001 From: Ching Pei Yang Date: Fri, 13 Jun 2025 18:04:48 +0200 Subject: [PATCH 07/16] propagate wayland pen down/up/move events --- syncscribble/linux/linuxwayland.c | 32 ++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/syncscribble/linux/linuxwayland.c b/syncscribble/linux/linuxwayland.c index f80c27f..0c914af 100644 --- a/syncscribble/linux/linuxwayland.c +++ b/syncscribble/linux/linuxwayland.c @@ -9,6 +9,7 @@ #include "wayland-util.h" #include #include +#include #define MAX_TOOLS 32 @@ -91,8 +92,16 @@ void handleTabletToolDone(void *data, { } void handleTabletToolRemoved(void *data, - struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2) + struct zwp_tablet_tool_v2 *tool) { + for(int i = 0; i < MAX_TOOLS; i++) { + if(wlState.tools[i].tool == tool) { + wlState.tools[i] = (ToolState){0}; + break; + } + } + + zwp_tablet_tool_v2_destroy(tool); } void handleTabletToolProximityIn(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, @@ -124,8 +133,11 @@ void handleTabletToolMotion(void *data, wl_fixed_t y) { ToolState* toolState = data; + // TODO: needs scaling by logical scale toolState->x = wl_fixed_to_double(x); toolState->y = wl_fixed_to_double(y); + if(toolState->frame.type == 0) + toolState->frame.type = SDL_FINGERMOTION; } void handleTabletToolPressure(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, @@ -172,15 +184,16 @@ void handleTabletToolFrame(void *data, { ToolState* toolState = data; if(toolState->frame.type == 0) { + printf("bad frame: type == 0\n"); // TODO: log? return; } - int win_w, win_h; - SDL_GetWindowSize(wlState.window, &win_w, &win_h); + // not sure if this is needed + if((toolState->frame.type & SDL_FINGERMOTION) == 0) + wlReportTabletEvent(SDL_FINGERMOTION, toolState->x, toolState->y); - wlReportTabletEvent(toolState->frame.type, toolState->x * win_w, - toolState->y * win_h); + wlReportTabletEvent(toolState->frame.type, toolState->x, toolState->y); toolState->frame.type = 0; } @@ -215,13 +228,10 @@ static void handleTabletAdded(void* data, struct zwp_tablet_seat_v2* tabSeat, static void handleToolAdded(void* data, struct zwp_tablet_seat_v2* tabletSeat, struct zwp_tablet_tool_v2* tool) { - // TODO: multiple tools - for(int i = 0; i < MAX_TOOLS; i++) { - if(wlState.tools[i].tool) - continue; + if(wlState.tools[0].tool == tool) + return; - zwp_tablet_tool_v2_add_listener(tool, &tabletToolListener, &wlState.tools[i]); - } + zwp_tablet_tool_v2_add_listener(tool, &tabletToolListener, &wlState.tools[0]); } static void handlePadAdded(void* data, struct zwp_tablet_seat_v2* tabletSeat, struct zwp_tablet_pad_v2* pad) From e95c4596d72618807072919003b83418b8534bcb Mon Sep 17 00:00:00 2001 From: Ching Pei Yang Date: Sat, 14 Jun 2025 01:29:26 +0200 Subject: [PATCH 08/16] scale stylus position by display scaling --- syncscribble/linux/linuxwayland.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/syncscribble/linux/linuxwayland.c b/syncscribble/linux/linuxwayland.c index 0c914af..5d86120 100644 --- a/syncscribble/linux/linuxwayland.c +++ b/syncscribble/linux/linuxwayland.c @@ -1,7 +1,9 @@ #include "linuxwayland.h" +#include "SDL_error.h" #include "SDL_events.h" #include "SDL_syswm.h" #include "SDL_timer.h" +#include "SDL_version.h" #include "tablet-v2.h" #include "ugui/svggui_platform.h" #include "wayland-client-protocol.h" @@ -35,6 +37,18 @@ typedef struct WlState { static WlState wlState = {0}; +static float getDisplayScaleFactor(SDL_Window* window) { + int displayIdx = SDL_GetWindowDisplayIndex(window); + float ddpi, hdpi, vdpi; + if (SDL_GetDisplayDPI(displayIdx, &ddpi, &hdpi, &vdpi) == 0) { + return ddpi/96.f; // 96 DPI is the baseline scale + } + + printf("%s", SDL_GetError()); + // TODO: is there something else I can do? + return 1.f; +} + static void wlReportTabletEvent(uint32_t type, float x, float y) { SDL_Event event = { @@ -133,9 +147,10 @@ void handleTabletToolMotion(void *data, wl_fixed_t y) { ToolState* toolState = data; - // TODO: needs scaling by logical scale - toolState->x = wl_fixed_to_double(x); - toolState->y = wl_fixed_to_double(y); + float scale = getDisplayScaleFactor(wlState.window); + + toolState->x = wl_fixed_to_double(x) * scale; + toolState->y = wl_fixed_to_double(y) * scale; if(toolState->frame.type == 0) toolState->frame.type = SDL_FINGERMOTION; } From c00cd25fbdbb47ea010a5304dbef6f8ad604d720 Mon Sep 17 00:00:00 2001 From: Ching Pei Yang Date: Sun, 15 Jun 2025 02:11:21 +0200 Subject: [PATCH 09/16] support stylus buttons --- syncscribble/linux/linuxwayland.c | 44 ++++++++++++++++++++++++------- 1 file changed, 35 insertions(+), 9 deletions(-) diff --git a/syncscribble/linux/linuxwayland.c b/syncscribble/linux/linuxwayland.c index 5d86120..9b157d0 100644 --- a/syncscribble/linux/linuxwayland.c +++ b/syncscribble/linux/linuxwayland.c @@ -1,6 +1,7 @@ #include "linuxwayland.h" #include "SDL_error.h" #include "SDL_events.h" +#include "SDL_mouse.h" #include "SDL_syswm.h" #include "SDL_timer.h" #include "SDL_version.h" @@ -17,6 +18,7 @@ typedef struct FrameInfo { uint32_t type; + unsigned int buttons; } FrameInfo; typedef struct ToolState { @@ -49,26 +51,44 @@ static float getDisplayScaleFactor(SDL_Window* window) { return 1.f; } -static void wlReportTabletEvent(uint32_t type, float x, float y) +static void wlReportTabletEvent(ToolState* state) { SDL_Event event = { .tfinger = { - .type = type, + .type = state->frame.type, .timestamp = SDL_GetTicks(), - .touchId = PenPointerPen, // TODO - .fingerId = 0, // TODO - .x = x, - .y = y, + .touchId = state->frame.toolType == ZWP_TABLET_TOOL_V2_TYPE_ERASER ? PenPointerEraser : PenPointerPen, + .fingerId = state->frame.buttons, + .x = state->x, + .y = state->y, .dx = 0, // TODO .dy = 0, // TODO .pressure = 1.0, // TODO - // .windowID = 0, /**< The window underneath the finger, if any */ + // .windowID = 0, } }; SDL_PeepEvents(&event, 1, SDL_ADDEVENT, 0, 0); } +static int wlToSDLButton(uint32_t b) +{ + // SDL_BUTTON_LMASK is used to represent pen down/pen up + // SDL_BUTTON_MMASK represents side button pressed state + // SDL_BUTTON_MMASK represents second side button pressed state + switch (b) { + // see %{_includedir}/linux/input-event-codes.h + case 0x14b: // BTN_STYLUS + return SDL_BUTTON_MMASK; + case 0x14c: // BTN_STYLUS2 + return SDL_BUTTON_RMASK; + // case 0x149: // BTN_STYLUS3 + // return SDL_BUTTON_RMASK; + default: + return 0; + } +} + static void handleSeatCapabilities(void* data, struct wl_seat* seat, uint32_t capabilities) { @@ -192,6 +212,12 @@ void handleTabletToolButton(void *data, uint32_t button, uint32_t state) { + ToolState* toolState = data; + + if(state == ZWP_TABLET_PAD_V2_BUTTON_STATE_PRESSED) + toolState->frame.buttons |= wlToSDLButton(button); + else // TODO: linuxtablet.c does a ^= , which seems weird to me + toolState->frame.buttons &= ~wlToSDLButton(button); } void handleTabletToolFrame(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, @@ -206,9 +232,9 @@ void handleTabletToolFrame(void *data, // not sure if this is needed if((toolState->frame.type & SDL_FINGERMOTION) == 0) - wlReportTabletEvent(SDL_FINGERMOTION, toolState->x, toolState->y); + wlReportTabletEvent(toolState); - wlReportTabletEvent(toolState->frame.type, toolState->x, toolState->y); + wlReportTabletEvent(toolState); toolState->frame.type = 0; } From bf98a5ff353f02874d606cca42acf27e1e32c61b Mon Sep 17 00:00:00 2001 From: Ching Pei Yang Date: Sun, 15 Jun 2025 02:23:28 +0200 Subject: [PATCH 10/16] support eraser on wayland --- syncscribble/linux/linuxwayland.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/syncscribble/linux/linuxwayland.c b/syncscribble/linux/linuxwayland.c index 9b157d0..601ee36 100644 --- a/syncscribble/linux/linuxwayland.c +++ b/syncscribble/linux/linuxwayland.c @@ -17,7 +17,8 @@ #define MAX_TOOLS 32 typedef struct FrameInfo { - uint32_t type; + uint32_t toolType; + uint32_t eventType; unsigned int buttons; } FrameInfo; @@ -55,7 +56,7 @@ static void wlReportTabletEvent(ToolState* state) { SDL_Event event = { .tfinger = { - .type = state->frame.type, + .type = state->frame.eventType, .timestamp = SDL_GetTicks(), .touchId = state->frame.toolType == ZWP_TABLET_TOOL_V2_TYPE_ERASER ? PenPointerEraser : PenPointerPen, .fingerId = state->frame.buttons, @@ -104,8 +105,10 @@ static const struct wl_seat_listener seatListener = { .name = handleSeatName, }; -static void handleTabletToolType(void* , struct zwp_tablet_tool_v2* , uint32_t ) +static void handleTabletToolType(void* data, struct zwp_tablet_tool_v2* tool, uint32_t type) { + ToolState* toolState = data; + toolState->frame.toolType = type; } static void handleHardwareSerial(void* data, struct zwp_tablet_tool_v2* tool, uint32_t hi, uint32_t lo) @@ -128,6 +131,7 @@ void handleTabletToolDone(void *data, void handleTabletToolRemoved(void *data, struct zwp_tablet_tool_v2 *tool) { + // TODO: use pointer artihmetic to find index for(int i = 0; i < MAX_TOOLS; i++) { if(wlState.tools[i].tool == tool) { wlState.tools[i] = (ToolState){0}; @@ -153,13 +157,13 @@ void handleTabletToolDown(void *data, uint32_t serial) { ToolState* toolState = data; - toolState->frame.type = SDL_FINGERDOWN; + toolState->frame.eventType = SDL_FINGERDOWN; } void handleTabletToolUp(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2) { ToolState* toolState = data; - toolState->frame.type = SDL_FINGERUP; + toolState->frame.eventType = SDL_FINGERUP; } void handleTabletToolMotion(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, @@ -171,8 +175,8 @@ void handleTabletToolMotion(void *data, toolState->x = wl_fixed_to_double(x) * scale; toolState->y = wl_fixed_to_double(y) * scale; - if(toolState->frame.type == 0) - toolState->frame.type = SDL_FINGERMOTION; + if(toolState->frame.eventType == 0) + toolState->frame.eventType = SDL_FINGERMOTION; } void handleTabletToolPressure(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, @@ -224,18 +228,18 @@ void handleTabletToolFrame(void *data, uint32_t time) { ToolState* toolState = data; - if(toolState->frame.type == 0) { + if(toolState->frame.eventType == 0) { printf("bad frame: type == 0\n"); // TODO: log? return; } // not sure if this is needed - if((toolState->frame.type & SDL_FINGERMOTION) == 0) + if((toolState->frame.eventType & SDL_FINGERMOTION) == 0) wlReportTabletEvent(toolState); wlReportTabletEvent(toolState); - toolState->frame.type = 0; + toolState->frame.eventType = 0; } static const struct zwp_tablet_tool_v2_listener tabletToolListener = { From b8faa22d605d37d29c4af7f802cc39cfcb4b3580 Mon Sep 17 00:00:00 2001 From: Ching Pei Yang Date: Sun, 15 Jun 2025 02:34:54 +0200 Subject: [PATCH 11/16] support stylus pressure --- syncscribble/linux/linuxwayland.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/syncscribble/linux/linuxwayland.c b/syncscribble/linux/linuxwayland.c index 601ee36..7192728 100644 --- a/syncscribble/linux/linuxwayland.c +++ b/syncscribble/linux/linuxwayland.c @@ -19,6 +19,7 @@ typedef struct FrameInfo { uint32_t toolType; uint32_t eventType; + float pressure; // normalized to 0..1 unsigned int buttons; } FrameInfo; @@ -64,7 +65,7 @@ static void wlReportTabletEvent(ToolState* state) .y = state->y, .dx = 0, // TODO .dy = 0, // TODO - .pressure = 1.0, // TODO + .pressure = state->frame.pressure, // .windowID = 0, } }; @@ -182,6 +183,10 @@ void handleTabletToolPressure(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, uint32_t pressure) { + ToolState* toolState = data; + + // according to spec, pressure is normalized to a value between 0 and 65535 + toolState->frame.pressure = (float)pressure / 65535; } void handleTabletToolDistance(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, From 313e930cd034fd1511672735b425ce1d51d4251d Mon Sep 17 00:00:00 2001 From: Ching Pei Yang Date: Sun, 15 Jun 2025 13:49:07 +0200 Subject: [PATCH 12/16] support tablet tool tilt --- syncscribble/linux/linuxwayland.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/syncscribble/linux/linuxwayland.c b/syncscribble/linux/linuxwayland.c index 7192728..c8d6069 100644 --- a/syncscribble/linux/linuxwayland.c +++ b/syncscribble/linux/linuxwayland.c @@ -19,6 +19,8 @@ typedef struct FrameInfo { uint32_t toolType; uint32_t eventType; + float tiltX; // normalized to -1 .. +1 + float tiltY; // normalized to -1 .. +1 float pressure; // normalized to 0..1 unsigned int buttons; } FrameInfo; @@ -63,8 +65,8 @@ static void wlReportTabletEvent(ToolState* state) .fingerId = state->frame.buttons, .x = state->x, .y = state->y, - .dx = 0, // TODO - .dy = 0, // TODO + .dx = state->frame.tiltX, + .dy = state->frame.tiltY, .pressure = state->frame.pressure, // .windowID = 0, } @@ -198,6 +200,13 @@ void handleTabletToolTilt(void *data, wl_fixed_t tilt_x, wl_fixed_t tilt_y) { + ToolState* toolState = data; + // Wayland tilt is in degrees, relative to the z-axis of the tablet, + // and is positive when the top of a tool tilts along the positive x + // or y axis. + // Our internal representation should be normalized to -1 .. 1 + toolState->frame.tiltX = wl_fixed_to_double(tilt_x) / 90.f; + toolState->frame.tiltY = wl_fixed_to_double(tilt_y) / 90.f; } void handleTabletToolRotation(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, From a9868de68223c2701ce3f3b480364c143dd41375 Mon Sep 17 00:00:00 2001 From: Ching Pei Yang Date: Sun, 15 Jun 2025 13:50:16 +0200 Subject: [PATCH 13/16] add debug logging --- syncscribble/linux/linuxwayland.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/syncscribble/linux/linuxwayland.c b/syncscribble/linux/linuxwayland.c index c8d6069..293ecab 100644 --- a/syncscribble/linux/linuxwayland.c +++ b/syncscribble/linux/linuxwayland.c @@ -68,11 +68,17 @@ static void wlReportTabletEvent(ToolState* state) .dx = state->frame.tiltX, .dy = state->frame.tiltY, .pressure = state->frame.pressure, - // .windowID = 0, + .windowID = 0, // unused } }; SDL_PeepEvents(&event, 1, SDL_ADDEVENT, 0, 0); + + // fprintf(stderr, + // "Wayland tablet event: touchId: %ld, x,y: (%f, %f), buttons: %ld," + // "tilt: (%f, %f), pressure: %f", + // event.tfinger.touchId, event.tfinger.x, event.tfinger.y, event.tfinger.fingerId, + // event.tfinger.dx, event.tfinger.dy, event.tfinger.pressure); } static int wlToSDLButton(uint32_t b) From b5ad5dd2643cfc9989a13f08e54e2664355a43c8 Mon Sep 17 00:00:00 2001 From: Ching Pei Yang Date: Sun, 15 Jun 2025 14:52:59 +0200 Subject: [PATCH 14/16] separate pen down and motion flags --- syncscribble/linux/linuxwayland.c | 33 ++++++++++++++++--------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/syncscribble/linux/linuxwayland.c b/syncscribble/linux/linuxwayland.c index 293ecab..e45b8a9 100644 --- a/syncscribble/linux/linuxwayland.c +++ b/syncscribble/linux/linuxwayland.c @@ -18,7 +18,13 @@ typedef struct FrameInfo { uint32_t toolType; - uint32_t eventType; + + // 0 means not set this frame + // 1 means pen up + // 2 means pen down + int penDown; + + bool moved; float tiltX; // normalized to -1 .. +1 float tiltY; // normalized to -1 .. +1 float pressure; // normalized to 0..1 @@ -57,9 +63,13 @@ static float getDisplayScaleFactor(SDL_Window* window) { static void wlReportTabletEvent(ToolState* state) { + uint32_t eventType = SDL_FINGERMOTION; + if(state->frame.penDown != 0) + eventType = state->frame.penDown == 1 ? SDL_FINGERUP : SDL_FINGERDOWN; + SDL_Event event = { .tfinger = { - .type = state->frame.eventType, + .type = eventType, .timestamp = SDL_GetTicks(), .touchId = state->frame.toolType == ZWP_TABLET_TOOL_V2_TYPE_ERASER ? PenPointerEraser : PenPointerPen, .fingerId = state->frame.buttons, @@ -166,13 +176,13 @@ void handleTabletToolDown(void *data, uint32_t serial) { ToolState* toolState = data; - toolState->frame.eventType = SDL_FINGERDOWN; + toolState->frame.penDown = 2; } void handleTabletToolUp(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2) { ToolState* toolState = data; - toolState->frame.eventType = SDL_FINGERUP; + toolState->frame.penDown = 1; } void handleTabletToolMotion(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, @@ -184,8 +194,7 @@ void handleTabletToolMotion(void *data, toolState->x = wl_fixed_to_double(x) * scale; toolState->y = wl_fixed_to_double(y) * scale; - if(toolState->frame.eventType == 0) - toolState->frame.eventType = SDL_FINGERMOTION; + toolState->frame.moved = true; } void handleTabletToolPressure(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, @@ -248,18 +257,10 @@ void handleTabletToolFrame(void *data, uint32_t time) { ToolState* toolState = data; - if(toolState->frame.eventType == 0) { - printf("bad frame: type == 0\n"); - // TODO: log? - return; - } - - // not sure if this is needed - if((toolState->frame.eventType & SDL_FINGERMOTION) == 0) - wlReportTabletEvent(toolState); wlReportTabletEvent(toolState); - toolState->frame.eventType = 0; + toolState->frame.penDown = 0; + toolState->frame.moved = false; } static const struct zwp_tablet_tool_v2_listener tabletToolListener = { From 33baf21a68f6bfb7e3c6b592a8aa205855177c1e Mon Sep 17 00:00:00 2001 From: Ching Pei Yang Date: Sun, 15 Jun 2025 15:14:30 +0200 Subject: [PATCH 15/16] clean up unneeded types --- syncscribble/linux/linuxwayland.c | 60 ++++++++++++++----------------- 1 file changed, 27 insertions(+), 33 deletions(-) diff --git a/syncscribble/linux/linuxwayland.c b/syncscribble/linux/linuxwayland.c index e45b8a9..ad270d5 100644 --- a/syncscribble/linux/linuxwayland.c +++ b/syncscribble/linux/linuxwayland.c @@ -16,27 +16,21 @@ #define MAX_TOOLS 32 -typedef struct FrameInfo { +typedef struct ToolState { + struct zwp_tablet_tool_v2* tool; uint32_t toolType; - - // 0 means not set this frame - // 1 means pen up - // 2 means pen down - int penDown; - - bool moved; + float x; + float y; float tiltX; // normalized to -1 .. +1 float tiltY; // normalized to -1 .. +1 float pressure; // normalized to 0..1 unsigned int buttons; -} FrameInfo; -typedef struct ToolState { - struct zwp_tablet_tool_v2* tool; - SDL_Window* window; // TODO: move somewhere else - float x; - float y; - FrameInfo frame; + // 0 means not set this frame. + // 1 means pen up. + // 2 means pen down. + int framePenDown; + bool frameMotionSet; } ToolState; typedef struct WlState { @@ -64,20 +58,20 @@ static float getDisplayScaleFactor(SDL_Window* window) { static void wlReportTabletEvent(ToolState* state) { uint32_t eventType = SDL_FINGERMOTION; - if(state->frame.penDown != 0) - eventType = state->frame.penDown == 1 ? SDL_FINGERUP : SDL_FINGERDOWN; + if(state->framePenDown != 0) + eventType = state->framePenDown == 1 ? SDL_FINGERUP : SDL_FINGERDOWN; SDL_Event event = { .tfinger = { .type = eventType, .timestamp = SDL_GetTicks(), - .touchId = state->frame.toolType == ZWP_TABLET_TOOL_V2_TYPE_ERASER ? PenPointerEraser : PenPointerPen, - .fingerId = state->frame.buttons, + .touchId = state->toolType == ZWP_TABLET_TOOL_V2_TYPE_ERASER ? PenPointerEraser : PenPointerPen, + .fingerId = state->buttons, .x = state->x, .y = state->y, - .dx = state->frame.tiltX, - .dy = state->frame.tiltY, - .pressure = state->frame.pressure, + .dx = state->tiltX, + .dy = state->tiltY, + .pressure = state->pressure, .windowID = 0, // unused } }; @@ -127,7 +121,7 @@ static const struct wl_seat_listener seatListener = { static void handleTabletToolType(void* data, struct zwp_tablet_tool_v2* tool, uint32_t type) { ToolState* toolState = data; - toolState->frame.toolType = type; + toolState->toolType = type; } static void handleHardwareSerial(void* data, struct zwp_tablet_tool_v2* tool, uint32_t hi, uint32_t lo) @@ -176,13 +170,13 @@ void handleTabletToolDown(void *data, uint32_t serial) { ToolState* toolState = data; - toolState->frame.penDown = 2; + toolState->framePenDown = 2; } void handleTabletToolUp(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2) { ToolState* toolState = data; - toolState->frame.penDown = 1; + toolState->framePenDown = 1; } void handleTabletToolMotion(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, @@ -194,7 +188,7 @@ void handleTabletToolMotion(void *data, toolState->x = wl_fixed_to_double(x) * scale; toolState->y = wl_fixed_to_double(y) * scale; - toolState->frame.moved = true; + toolState->frameMotionSet = true; } void handleTabletToolPressure(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, @@ -203,7 +197,7 @@ void handleTabletToolPressure(void *data, ToolState* toolState = data; // according to spec, pressure is normalized to a value between 0 and 65535 - toolState->frame.pressure = (float)pressure / 65535; + toolState->pressure = (float)pressure / 65535; } void handleTabletToolDistance(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, @@ -220,8 +214,8 @@ void handleTabletToolTilt(void *data, // and is positive when the top of a tool tilts along the positive x // or y axis. // Our internal representation should be normalized to -1 .. 1 - toolState->frame.tiltX = wl_fixed_to_double(tilt_x) / 90.f; - toolState->frame.tiltY = wl_fixed_to_double(tilt_y) / 90.f; + toolState->tiltX = wl_fixed_to_double(tilt_x) / 90.f; + toolState->tiltY = wl_fixed_to_double(tilt_y) / 90.f; } void handleTabletToolRotation(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, @@ -248,9 +242,9 @@ void handleTabletToolButton(void *data, ToolState* toolState = data; if(state == ZWP_TABLET_PAD_V2_BUTTON_STATE_PRESSED) - toolState->frame.buttons |= wlToSDLButton(button); + toolState->buttons |= wlToSDLButton(button); else // TODO: linuxtablet.c does a ^= , which seems weird to me - toolState->frame.buttons &= ~wlToSDLButton(button); + toolState->buttons &= ~wlToSDLButton(button); } void handleTabletToolFrame(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, @@ -259,8 +253,8 @@ void handleTabletToolFrame(void *data, ToolState* toolState = data; wlReportTabletEvent(toolState); - toolState->frame.penDown = 0; - toolState->frame.moved = false; + toolState->framePenDown = 0; + toolState->frameMotionSet = false; } static const struct zwp_tablet_tool_v2_listener tabletToolListener = { From ef7b657587b4d8cd23fb34bf108838905ab8cea9 Mon Sep 17 00:00:00 2001 From: Ching Pei Yang Date: Thu, 13 Nov 2025 22:30:17 +0100 Subject: [PATCH 16/16] fix out-of-order initialization of tablet seat --- syncscribble/linux/linuxwayland.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/syncscribble/linux/linuxwayland.c b/syncscribble/linux/linuxwayland.c index ad270d5..f20249c 100644 --- a/syncscribble/linux/linuxwayland.c +++ b/syncscribble/linux/linuxwayland.c @@ -313,9 +313,6 @@ static void registryHandleGlobal(void* data, struct wl_registry* registry, wl_seat_add_listener(wlState.seat, &seatListener, NULL); } else if(strcmp(interface, zwp_tablet_manager_v2_interface.name) == 0) { wlState.tabletManager = wl_registry_bind(registry, name, &zwp_tablet_manager_v2_interface, 1); - // TODO: is wlState.seat guaranteed to be valid at this point? - wlState.tabletSeat = zwp_tablet_manager_v2_get_tablet_seat(wlState.tabletManager, wlState.seat); - zwp_tablet_seat_v2_add_listener(wlState.tabletSeat, &tabletSeatListener, NULL); } } @@ -330,6 +327,13 @@ static const struct wl_registry_listener registry_listener = { .global_remove = registryHandleGlobalRemove, }; +static void initTabletSeat() { + if (wlState.tabletManager && wlState.seat) { + wlState.tabletSeat = zwp_tablet_manager_v2_get_tablet_seat(wlState.tabletManager, wlState.seat); + zwp_tablet_seat_v2_add_listener(wlState.tabletSeat, &tabletSeatListener, NULL); + } +} + int linuxInitWayland(SDL_Window* sdlwin) { wlState.window = sdlwin; @@ -349,5 +353,9 @@ int linuxInitWayland(SDL_Window* sdlwin) // do I need this? wl_display_roundtrip(wmInfo.info.wl.display); + initTabletSeat(); + // TODO: should I force another roundtrip? or call initTabletSeat as soon as + // seat and tablet_manager are both available? + return 0; }