summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVaxry <vaxry@vaxry.net>2024-05-06 02:15:26 +0100
committerVaxry <43317083+vaxerski@users.noreply.github.com>2024-05-09 13:37:39 +0100
commit84e8d1810d57b65a0bc3b5ac2998bc023df880f4 (patch)
treecd313a14333c06765dc149c9339d595c13eafd72
parented411f53bdcf8d455f9e6ca769693aa84dc8372f (diff)
Tablet: move to new impl
Ring and strip are not implemented. Will I implement this? God fucking knows. Nobody seems to have that anyways.
-rw-r--r--CMakeLists.txt2
-rw-r--r--protocols/meson.build2
-rw-r--r--protocols/tablet-unstable-v2.xml1178
-rw-r--r--src/Compositor.cpp2
-rw-r--r--src/Compositor.hpp1
-rw-r--r--src/debug/HyprCtl.cpp25
-rw-r--r--src/devices/IHID.hpp3
-rw-r--r--src/devices/Tablet.cpp326
-rw-r--r--src/devices/Tablet.hpp228
-rw-r--r--src/events/Devices.cpp4
-rw-r--r--src/helpers/Monitor.cpp14
-rw-r--r--src/helpers/WLClasses.hpp66
-rw-r--r--src/includes.hpp1
-rw-r--r--src/managers/PointerManager.cpp90
-rw-r--r--src/managers/PointerManager.hpp18
-rw-r--r--src/managers/ProtocolManager.cpp2
-rw-r--r--src/managers/input/InputManager.cpp131
-rw-r--r--src/managers/input/InputManager.hpp44
-rw-r--r--src/managers/input/Tablets.cpp437
-rw-r--r--src/protocols/Tablet.cpp656
-rw-r--r--src/protocols/Tablet.hpp236
21 files changed, 1893 insertions, 1573 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index f491e100..513f06bb 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -251,7 +251,6 @@ target_link_libraries(Hyprland
uuid
)
-protocol("protocols/tablet-unstable-v2.xml" "tablet-unstable-v2" true)
protocol("protocols/wlr-layer-shell-unstable-v1.xml" "wlr-layer-shell-unstable-v1" true)
protocol("protocols/wlr-screencopy-unstable-v1.xml" "wlr-screencopy-unstable-v1" true)
protocol("subprojects/hyprland-protocols/protocols/hyprland-global-shortcuts-v1.xml" "hyprland-global-shortcuts-v1" true)
@@ -285,6 +284,7 @@ protocolNew("unstable/pointer-constraints/pointer-constraints-unstable-v1.xml" "
protocolNew("staging/xdg-activation/xdg-activation-v1.xml" "xdg-activation-v1" false)
protocolNew("staging/ext-idle-notify/ext-idle-notify-v1.xml" "ext-idle-notify-v1" false)
protocolNew("staging/ext-session-lock/ext-session-lock-v1.xml" "ext-session-lock-v1" false)
+protocolNew("stable/tablet/tablet-v2.xml" "tablet-v2" false)
# tools
add_subdirectory(hyprctl)
diff --git a/protocols/meson.build b/protocols/meson.build
index 4c315797..345b47ee 100644
--- a/protocols/meson.build
+++ b/protocols/meson.build
@@ -29,7 +29,6 @@ protocols = [
[wl_protocol_dir, 'unstable/text-input/text-input-unstable-v1.xml'],
['wlr-layer-shell-unstable-v1.xml'],
['wlr-screencopy-unstable-v1.xml'],
- ['tablet-unstable-v2.xml'],
[hl_protocol_dir, 'protocols/hyprland-toplevel-export-v1.xml'],
[hl_protocol_dir, 'protocols/hyprland-global-shortcuts-v1.xml']
]
@@ -60,6 +59,7 @@ new_protocols = [
[wl_protocol_dir, 'staging/xdg-activation/xdg-activation-v1.xml'],
[wl_protocol_dir, 'staging/ext-idle-notify/ext-idle-notify-v1.xml'],
[wl_protocol_dir, 'staging/ext-session-lock/ext-session-lock-v1.xml'],
+ [wl_protocol_dir, 'stable/tablet/tablet-v2.xml'],
]
wl_protos_src = []
diff --git a/protocols/tablet-unstable-v2.xml b/protocols/tablet-unstable-v2.xml
deleted file mode 100644
index b286d964..00000000
--- a/protocols/tablet-unstable-v2.xml
+++ /dev/null
@@ -1,1178 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<protocol name="tablet_unstable_v2">
-
- <copyright>
- 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.
- </copyright>
-
- <description summary="Wayland protocol for graphics tablets">
- 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.
-
- Warning! The protocol described in this file is experimental and
- backward incompatible changes may be made. Backward compatible changes
- may be added together with the corresponding interface version bump.
- Backward incompatible changes are done by bumping the version number in
- the protocol and interface names and resetting the interface version.
- Once the protocol is to be declared stable, the 'z' prefix and the
- version number in the protocol and interface names are removed and the
- interface version number is reset.
- </description>
-
- <interface name="zwp_tablet_manager_v2" version="1">
- <description summary="controller object for graphic tablet devices">
- 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.
- </description>
-
- <request name="get_tablet_seat">
- <description summary="get the tablet seat">
- Get the wp_tablet_seat object for the given seat. This object
- provides access to all graphics tablets in this seat.
- </description>
- <arg name="tablet_seat" type="new_id" interface="zwp_tablet_seat_v2"/>
- <arg name="seat" type="object" interface="wl_seat" summary="The wl_seat object to retrieve the tablets for" />
- </request>
-
- <request name="destroy" type="destructor">
- <description summary="release the memory for the tablet manager object">
- Destroy the wp_tablet_manager object. Objects created from this
- object are unaffected and should be destroyed separately.
- </description>
- </request>
- </interface>
-
- <interface name="zwp_tablet_seat_v2" version="1">
- <description summary="controller object for graphic tablet devices of a seat">
- 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.
- </description>
-
- <request name="destroy" type="destructor">
- <description summary="release the memory for the tablet seat object">
- Destroy the wp_tablet_seat object. Objects created from this
- object are unaffected and should be destroyed separately.
- </description>
- </request>
-
- <event name="tablet_added">
- <description summary="new device notification">
- 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.
- </description>
- <arg name="id" type="new_id" interface="zwp_tablet_v2" summary="the newly added graphics tablet"/>
- </event>
-
- <event name="tool_added">
- <description summary="a new tool has been used with a tablet">
- 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.
- </description>
- <arg name="id" type="new_id" interface="zwp_tablet_tool_v2" summary="the newly added tablet tool"/>
- </event>
-
- <event name="pad_added">
- <description summary="new pad notification">
- 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.
- </description>
- <arg name="id" type="new_id" interface="zwp_tablet_pad_v2" summary="the newly added pad"/>
- </event>
- </interface>
-
- <interface name="zwp_tablet_tool_v2" version="1">
- <description summary="a physical tablet tool">
- 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.
- </description>
-
- <request name="set_cursor">
- <description summary="set the tablet tool's surface">
- 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.
- </description>
- <arg name="serial" type="uint" summary="serial of the enter event"/>
- <arg name="surface" type="object" interface="wl_surface" allow-null="true"/>
- <arg name="hotspot_x" type="int" summary="surface-local x coordinate"/>
- <arg name="hotspot_y" type="int" summary="surface-local y coordinate"/>
- </request>
-
- <request name="destroy" type="destructor">
- <description summary="destroy the tool object">
- This destroys the client's resource for this tool object.
- </description>
- </request>
-
- <enum name="type">
- <description summary="a physical tool type">
- 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.
- </description>
- <entry name="pen" value="0x140" summary="Pen"/>
- <entry name="eraser" value="0x141" summary="Eraser"/>
- <entry name="brush" value="0x142" summary="Brush"/>
- <entry name="pencil" value="0x143" summary="Pencil"/>
- <entry name="airbrush" value="0x144" summary="Airbrush"/>
- <entry name="finger" value="0x145" summary="Finger"/>
- <entry name="mouse" value="0x146" summary="Mouse"/>
- <entry name="lens" value="0x147" summary="Lens"/>
- </enum>
-
- <event name="type">
- <description summary="tool type">
- 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.
- </description>
- <arg name="tool_type" type="uint" enum="type" summary="the physical tool type"/>
- </event>
-
- <event name="hardware_serial">
- <description summary="unique hardware serial number of the tool">
- 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.
- </description>
- <arg name="hardware_serial_hi" type="uint" summary="the unique serial number of the tool, most significant bits"/>
- <arg name="hardware_serial_lo" type="uint" summary="the unique serial number of the tool, least significant bits"/>
- </event>
-
- <event name="hardware_id_wacom">
- <description summary="hardware id notification in Wacom's format">
- 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.
- </description>
- <arg name="hardware_id_hi" type="uint" summary="the hardware id, most significant bits"/>
- <arg name="hardware_id_lo" type="uint" summary="the hardware id, least significant bits"/>
- </event>
-
- <enum name="capability">
- <description summary="capability flags for a tool">
- Describes extra capabilities on a tablet.
-
- Any tool must provide x and y values, extra axes are
- device-specific.
- </description>
- <entry name="tilt" value="1" summary="Tilt axes"/>
- <entry name="pressure" value="2" summary="Pressure axis"/>
- <entry name="distance" value="3" summary="Distance axis"/>
- <entry name="rotation" value="4" summary="Z-rotation axis"/>
- <entry name="slider" value="5" summary="Slider axis"/>
- <entry name="wheel" value="6" summary="Wheel axis"/>
- </enum>
-
- <event name="capability">
- <description summary="tool capability notification">
- 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.
- </description>
- <arg name="capability" type="uint" enum="capability" summary="the capability"/>
- </event>
-
- <event name="done">
- <description summary="tool description events sequence complete">
- 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.
- </description>
- </event>
-
- <event name="removed">
- <description summary="tool removed">
- 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.
- </description>
- </event>
-
- <event name="proximity_in">
- <description summary="proximity in event">
- 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.
- </description>
- <arg name="serial" type="uint"/>
- <arg name="tablet" type="object" interface="zwp_tablet_v2" summary="The tablet the tool is in proximity of"/>
- <arg name="surface" type="object" interface="wl_surface" summary="The current surface the tablet tool is over"/>
- </event>
-
- <event name="proximity_out">
- <description summary="proximity out 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.
- </description>
- </event>
-
- <event name="down">
- <description summary="tablet tool is making contact">
- 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.
- </description>
- <arg name="serial" type="uint"/>
- </event>
-
- <event name="up">
- <description summary="tablet tool is no longer making contact">
- 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.
- </description>
- </event>
-
- <event name="motion">
- <description summary="motion event">
- Sent whenever a tablet tool moves.
- </description>
- <arg name="x" type="fixed" summary="surface-local x coordinate"/>
- <arg name="y" type="fixed" summary="surface-local y coordinate"/>
- </event>
-
- <event name="pressure">
- <description summary="pressure change event">
- 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.
- </description>
- <arg name="pressure" type="uint" summary="The current pressure value"/>
- </event>
-
- <event name="distance">
- <description summary="distance change event">
- 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.
- </description>
- <arg name="distance" type="uint" summary="The current distance value"/>
- </event>
-
- <event name="tilt">
- <description summary="tilt change event">
- 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.
- </description>
- <arg name="tilt_x" type="fixed" summary="The current value of the X tilt axis"/>
- <arg name="tilt_y" type="fixed" summary="The current value of the Y tilt axis"/>
- </event>
-
- <event name="rotation">
- <description summary="z-rotation change event">
- Sent whenever the z-rotation axis on the tool changes. The
- rotation value is in degrees clockwise from the tool's
- logical neutral position.
- </description>
- <arg name="degrees" type="fixed" summary="The current rotation of the Z axis"/>
- </event>
-
- <event name="slider">
- <description summary="Slider position change event">
- 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.
- </description>
- <arg name="position" type="int" summary="The current position of slider"/>
- </event>
-
- <event name="wheel">
- <description summary="Wheel delta event">
- 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.
- </description>
- <arg name="degrees" type="fixed" summary="The wheel delta in degrees"/>
- <arg name="clicks" type="int" summary="The wheel delta in discrete clicks"/>
- </event>
-
- <enum name="button_state">
- <description summary="physical button state">
- Describes the physical state of a button that produced the button event.
- </description>
- <entry name="released" value="0" summary="button is not pressed"/>
- <entry name="pressed" value="1" summary="button is pressed"/>
- </enum>
-
- <event name="button">
- <description summary="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.
- </description>
- <arg name="serial" type="uint"/>
- <arg name="button" type="uint" summary="The button whose state has changed"/>
- <arg name="state" type="uint" enum="button_state" summary="Whether the button was pressed or released"/>
- </event>
-
- <event name="frame">
- <description summary="frame event">
- 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.
- </description>
- <arg name="time" type="uint" summary="The time of the event with millisecond granularity"/>
- </event>
-
- <enum name="error">
- <entry name="role" value="0" summary="given wl_surface has another role"/>
- </enum>
- </interface>
-
- <interface name="zwp_tablet_v2" version="1">
- <description summary="graphics tablet device">
- 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.
- </description>
-
- <request name="destroy" type="destructor">
- <description summary="destroy the tablet object">
- This destroys the client's resource for this tablet object.
- </description>
- </request>
-
- <event name="name">
- <description summary="tablet device name">
- This event is sent in the initial burst of events before the
- wp_tablet.done event.
- </description>
- <arg name="name" type="string" summary="the device name"/>
- </event>
-
- <event name="id">
- <description summary="tablet device USB vendor/product id">
- This event is sent in the initial burst of events before the
- wp_tablet.done event.
- </description>
- <arg name="vid" type="uint" summary="USB vendor id"/>
- <arg name="pid" type="uint" summary="USB product id"/>
- </event>
-
- <event name="path">
- <description summary="path to the device">
- 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.
- </description>
- <arg name="path" type="string" summary="path to local device"/>
- </event>
-
- <event name="done">
- <description summary="tablet description events sequence complete">
- 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.
- </description>
- </event>
-
- <event name="removed">
- <description summary="tablet removed event">
- 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.
- </description>
- </event>
- </interface>
-
- <interface name="zwp_tablet_pad_ring_v2" version="1">
- <description summary="pad ring">
- 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.
- </description>
-
- <request name="set_feedback">
- <description summary="set compositor feedback">
- 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.
- </description>
- <arg name="description" type="string" summary="ring description"/>
- <arg name="serial" type="uint" summary="serial of the mode switch event"/>
- </request>
-
- <request name="destroy" type="destructor">
- <description summary="destroy the ring object">
- This destroys the client's resource for this ring object.
- </description>
- </request>
-
- <enum name="source">
- <description summary="ring axis source">
- 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.
- </description>
- <entry name="finger" value="1" summary="finger"/>
- </enum>
-
- <event name="source">
- <description summary="ring event source">
- 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.
- </description>
- <arg name="source" type="uint" enum="source" summary="the event source"/>
- </event>
-
- <event name="angle">
- <description summary="angle changed">
- 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.
- </description>
- <arg name="degrees" type="fixed" summary="the current angle in degrees"/>
- </event>
-
- <event name="stop">
- <description summary="interaction stopped">
- 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.
- </description>
- </event>
-
- <event name="frame">
- <description summary="end of a ring event sequence">
- 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.
- </description>
- <arg name="time" type="uint" summary="timestamp with millisecond granularity"/>
- </event>
- </interface>
-
- <interface name="zwp_tablet_pad_strip_v2" version="1">
- <description summary="pad strip">
- 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.
- </description>
-
- <request name="set_feedback">
- <description summary="set compositor feedback">
- 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.
- </description>
- <arg name="description" type="string" summary="strip description"/>
- <arg name="serial" type="uint" summary="serial of the mode switch event"/>
- </request>
-
- <request name="destroy" type="destructor">
- <description summary="destroy the strip object">
- This destroys the client's resource for this strip object.
- </description>
- </request>
-
- <enum name="source">
- <description summary="strip axis source">
- 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.
- </description>
- <entry name="finger" value="1" summary="finger"/>
- </enum>
-
- <event name="source">
- <description summary="strip event source">
- 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.
- </description>
- <arg name="source" type="uint" enum="source" summary="the event source"/>
- </event>
-
- <event name="position">
- <description summary="position changed">
- 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.
- </description>
- <arg name="position" type="uint" summary="the current position"/>
- </event>
-
- <event name="stop">
- <description summary="interaction stopped">
- 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.
- </description>
- </event>
-
- <event name="frame">
- <description summary="end of a strip event sequence">
- 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.
- </description>
- <arg name="time" type="uint" summary="timestamp with millisecond granularity"/>
- </event>
- </interface>
-
- <interface name="zwp_tablet_pad_group_v2" version="1">
- <description summary="a set of buttons, rings and strips">
- 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.
- </description>
-
- <request name="destroy" type="destructor">
- <description summary="destroy the pad object">
- Destroy the wp_tablet_pad_group object. Objects created from this object
- are unaffected and should be destroyed separately.
- </description>
- </request>
-
- <event name="buttons">
- <description summary="buttons announced">
- 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.
- </description>
- <arg name="buttons" type="array" summary="buttons in this group"/>
- </event>
-
- <event name="ring">
- <description summary="ring announced">
- 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.
- </description>
- <arg name="ring" type="new_id" interface="zwp_tablet_pad_ring_v2"/>
- </event>
-
- <event name="strip">
- <description summary="strip announced">
- 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.
- </description>
- <arg name="strip" type="new_id" interface="zwp_tablet_pad_strip_v2"/>
- </event>
-
- <event name="modes">
- <description summary="mode-switch ability announced">
- 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.
- </description>
- <arg name="modes" type="uint" summary="the number of modes"/>
- </event>
-
- <event name="done">
- <description summary="tablet group description events sequence complete">
- 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.
- </description>
- </event>
-
- <event name="mode_switch">
- <description summary="mode switch event">
- 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 client 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.
- </description>
- <arg name="time" type="uint" summary="the time of the event with millisecond granularity"/>
- <arg name="serial" type="uint"/>
- <arg name="mode" type="uint" summary="the new mode of the pad"/>
- </event>
- </interface>
-
- <interface name="zwp_tablet_pad_v2" version="1">
- <description summary="a set of buttons, rings and strips">
- 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.
- </description>
-
- <request name="set_feedback">
- <description summary="set compositor feedback">
- 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.
- </description>
- <arg name="button" type="uint" summary="button index"/>
- <arg name="description" type="string" summary="button description"/>
- <arg name="serial" type="uint" summary="serial of the mode switch event"/>
- </request>
-
- <request name="destroy" type="destructor">
- <description summary="destroy the pad object">
- Destroy the wp_tablet_pad object. Objects created from this object
- are unaffected and should be destroyed separately.
- </description>
- </request>
-
- <event name="group">
- <description summary="group announced">
- 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.
- </description>
- <arg name="pad_group" type="new_id" interface="zwp_tablet_pad_group_v2"/>
- </event>
-
- <event name="path">
- <description summary="path to the device">
- 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.
- </description>
- <arg name="path" type="string" summary="path to local device"/>
- </event>
-
- <event name="buttons">
- <description summary="buttons announced">
- 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.
- </description>
- <arg name="buttons" type="uint" summary="the number of buttons"/>
- </event>
-
- <event name="done">
- <description summary="pad description event sequence complete">
- 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.
- </description>
- </event>
-
- <enum name="button_state">
- <description summary="physical button state">
- Describes the physical state of a button that caused the button
- event.
- </description>
- <entry name="released" value="0" summary="the button is not pressed"/>
- <entry name="pressed" value="1" summary="the button is pressed"/>
- </enum>
-
- <event name="button">
- <description summary="physical button state">
- Sent whenever the physical state of a button changes.
- </description>
- <arg name="time" type="uint" summary="the time of the event with millisecond granularity"/>
- <arg name="button" type="uint" summary="the index of the button that changed state"/>
- <arg name="state" type="uint" enum="button_state"/>
- </event>
-
- <event name="enter">
- <description summary="enter event">
- Notification that this pad is focused on the specified surface.
- </description>
- <arg name="serial" type="uint" summary="serial number of the enter event"/>
- <arg name="tablet" type="object" interface="zwp_tablet_v2" summary="the tablet the pad is attached to"/>
- <arg name="surface" type="object" interface="wl_surface" summary="surface the pad is focused on"/>
- </event>
-
- <event name="leave">
- <description summary="enter event">
- Notification that this pad is no longer focused on the specified
- surface.
- </description>
- <arg name="serial" type="uint" summary="serial number of the leave event"/>
- <arg name="surface" type="object" interface="wl_surface" summary="surface the pad is no longer focused on"/>
- </event>
-
- <event name="removed">
- <description summary="pad removed event">
- 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.
- </description>
- </event>
- </interface>
-</protocol>
diff --git a/src/Compositor.cpp b/src/Compositor.cpp
index caf042bc..a31b1a57 100644
--- a/src/Compositor.cpp
+++ b/src/Compositor.cpp
@@ -242,8 +242,6 @@ void CCompositor::initServer() {
Debug::log(INFO, "VR will not be available");
}
- // m_sWLRTabletManager = wlr_tablet_v2_create(m_sWLDisplay);
-
m_sWLRForeignRegistry = wlr_xdg_foreign_registry_create(m_sWLDisplay);
wlr_xdg_foreign_v1_create(m_sWLDisplay, m_sWLRForeignRegistry);
diff --git a/src/Compositor.hpp b/src/Compositor.hpp
index d017949b..96557b8d 100644
--- a/src/Compositor.hpp
+++ b/src/Compositor.hpp
@@ -56,7 +56,6 @@ class CCompositor {
wlr_presentation* m_sWLRPresentation;
wlr_egl* m_sWLREGL;
int m_iDRMFD;
- wlr_tablet_manager_v2* m_sWLRTabletManager;
wlr_xdg_foreign_registry* m_sWLRForeignRegistry;
wlr_linux_dmabuf_v1* m_sWLRLinuxDMABuf;
wlr_backend* m_sWLRHeadlessBackend;
diff --git a/src/debug/HyprCtl.cpp b/src/debug/HyprCtl.cpp
index c0dc8d8e..56b7bd2b 100644
--- a/src/debug/HyprCtl.cpp
+++ b/src/debug/HyprCtl.cpp
@@ -22,6 +22,7 @@
#include "../devices/IPointer.hpp"
#include "../devices/IKeyboard.hpp"
#include "../devices/ITouch.hpp"
+#include "../devices/Tablet.hpp"
static void trimTrailingComma(std::string& str) {
if (!str.empty() && str.back() == ',')
@@ -557,7 +558,7 @@ std::string devicesRequest(eHyprCtlOutputFormat format, std::string request) {
result += "\"tablets\": [\n";
- for (auto& d : g_pInputManager->m_lTabletPads) {
+ for (auto& d : g_pInputManager->m_vTabletPads) {
result += std::format(
R"#( {{
"address": "0x{:x}",
@@ -567,26 +568,26 @@ std::string devicesRequest(eHyprCtlOutputFormat format, std::string request) {
"name": "{}"
}}
}},)#",
- (uintptr_t)&d, (uintptr_t)d.pTabletParent, escapeJSONStrings(d.pTabletParent ? d.pTabletParent->name : ""));
+ (uintptr_t)d.get(), (uintptr_t)d->parent.get(), escapeJSONStrings(d->parent ? d->parent->hlName : ""));
}
- for (auto& d : g_pInputManager->m_lTablets) {
+ for (auto& d : g_pInputManager->m_vTablets) {
result += std::format(
R"#( {{
"address": "0x{:x}",
"name": "{}"
}},)#",
- (uintptr_t)&d, escapeJSONStrings(d.name));
+ (uintptr_t)d.get(), escapeJSONStrings(d->hlName));
}
- for (auto& d : g_pInputManager->m_lTabletTools) {
+ for (auto& d : g_pInputManager->m_vTabletTools) {
result += std::format(
R"#( {{
"address": "0x{:x}",
"type": "tabletTool",
"belongsTo": "0x{:x}"
}},)#",
- (uintptr_t)&d, d.wlrTabletTool ? (uintptr_t)d.wlrTabletTool->data : 0);
+ (uintptr_t)d.get(), d->wlr() ? (uintptr_t)d->wlr()->data : 0);
}
trimTrailingComma(result);
@@ -643,16 +644,16 @@ std::string devicesRequest(eHyprCtlOutputFormat format, std::string request) {
result += "\n\nTablets:\n";
- for (auto& d : g_pInputManager->m_lTabletPads) {
- result += std::format("\tTablet Pad at {:x} (belongs to {:x} -> {})\n", (uintptr_t)&d, (uintptr_t)d.pTabletParent, d.pTabletParent ? d.pTabletParent->name : "");
+ for (auto& d : g_pInputManager->m_vTabletPads) {
+ result += std::format("\tTablet Pad at {:x} (belongs to {:x} -> {})\n", (uintptr_t)d.get(), (uintptr_t)d->parent.get(), d->parent ? d->parent->hlName : "");
}
- for (auto& d : g_pInputManager->m_lTablets) {
- result += std::format("\tTablet at {:x}:\n\t\t{}\n\t\t\tsize: {}x{}mm\n", (uintptr_t)&d, d.name, d.wlrTablet->width_mm, d.wlrTablet->height_mm);
+ for (auto& d : g_pInputManager->m_vTablets) {
+ result += std::format("\tTablet at {:x}:\n\t\t{}\n\t\t\tsize: {}x{}mm\n", (uintptr_t)d.get(), d->hlName, d->wlr()->width_mm, d->wlr()->height_mm);
}
- for (auto& d : g_pInputManager->m_lTabletTools) {
- result += std::format("\tTablet Tool at {:x} (belongs to {:x})\n", (uintptr_t)&d, d.wlrTabletTool ? (uintptr_t)d.wlrTabletTool->data : 0);
+ for (auto& d : g_pInputManager->m_vTabletTools) {
+ result += std::format("\tTablet Tool at {:x} (belongs to {:x})\n", (uintptr_t)d.get(), d->wlr() ? (uintptr_t)d->wlr()->data : 0);
}
result += "\n\nTouch:\n";
diff --git a/src/devices/IHID.hpp b/src/devices/IHID.hpp
index 2830864b..cac25112 100644
--- a/src/devices/IHID.hpp
+++ b/src/devices/IHID.hpp
@@ -8,6 +8,7 @@ enum eHIDCapabilityType : uint32_t {
HID_INPUT_CAPABILITY_KEYBOARD = (1 << 0),
HID_INPUT_CAPABILITY_POINTER = (1 << 1),
HID_INPUT_CAPABILITY_TOUCH = (1 << 2),
+ HID_INPUT_CAPABILITY_TABLET = (1 << 3),
};
enum eHIDType {
@@ -16,6 +17,8 @@ enum eHIDType {
HID_TYPE_KEYBOARD,
HID_TYPE_TOUCH,
HID_TYPE_TABLET,
+ HID_TYPE_TABLET_TOOL,
+ HID_TYPE_TABLET_PAD,
};
/*
diff --git a/src/devices/Tablet.cpp b/src/devices/Tablet.cpp
new file mode 100644
index 00000000..f5b610a9
--- /dev/null
+++ b/src/devices/Tablet.cpp
@@ -0,0 +1,326 @@
+#include "Tablet.hpp"
+#include "../defines.hpp"
+#include "../protocols/Tablet.hpp"
+
+SP<CTablet> CTablet::create(wlr_tablet* tablet) {
+ SP<CTablet> pTab = SP<CTablet>(new CTablet(tablet));
+
+ pTab->self = pTab;
+
+ PROTO::tablet->registerDevice(pTab);
+
+ return pTab;
+}
+
+SP<CTabletTool> CTabletTool::create(wlr_tablet_tool* tablet) {
+ SP<CTabletTool> pTab = SP<CTabletTool>(new CTabletTool(tablet));
+
+ pTab->self = pTab;
+
+ PROTO::tablet->registerDevice(pTab);
+
+ return pTab;
+}
+
+SP<CTabletPad> CTabletPad::create(wlr_tablet_pad* tablet) {
+ SP<CTabletPad> pTab = SP<CTabletPad>(new CTabletPad(tablet));
+
+ pTab->self = pTab;
+
+ PROTO::tablet->registerDevice(pTab);
+
+ return pTab;
+}
+
+SP<CTabletTool> CTabletTool::fromWlr(wlr_tablet_tool* tool) {
+ return ((CTabletTool*)tool->data)->self.lock();
+}
+
+SP<CTablet> CTablet::fromWlr(wlr_tablet* tablet) {
+ return ((CTablet*)tablet->data)->self.lock();
+}
+
+static uint32_t wlrUpdateToHl(uint32_t wlr) {
+ uint32_t result = 0;
+ if (wlr & WLR_TABLET_TOOL_AXIS_X)
+ result |= CTablet::eTabletToolAxes::HID_TABLET_TOOL_AXIS_X;
+ if (wlr & WLR_TABLET_TOOL_AXIS_Y)
+ result |= CTablet::eTabletToolAxes::HID_TABLET_TOOL_AXIS_Y;
+ if (wlr & WLR_TABLET_TOOL_AXIS_DISTANCE)
+ result |= CTablet::eTabletToolAxes::HID_TABLET_TOOL_AXIS_DISTANCE;
+ if (wlr & WLR_TABLET_TOOL_AXIS_PRESSURE)
+ result |= CTablet::eTabletToolAxes::HID_TABLET_TOOL_AXIS_PRESSURE;
+ if (wlr & WLR_TABLET_TOOL_AXIS_TILT_X)
+ result |= CTablet::eTabletToolAxes::HID_TABLET_TOOL_AXIS_TILT_X;
+ if (wlr & WLR_TABLET_TOOL_AXIS_TILT_Y)
+ result |= CTablet::eTabletToolAxes::HID_TABLET_TOOL_AXIS_TILT_Y;
+ if (wlr & WLR_TABLET_TOOL_AXIS_ROTATION)
+ result |= CTablet::eTabletToolAxes::HID_TABLET_TOOL_AXIS_ROTATION;
+ if (wlr & WLR_TABLET_TOOL_AXIS_SLIDER)
+ result |= CTablet::eTabletToolAxes::HID_TABLET_TOOL_AXIS_SLIDER;
+ if (wlr & WLR_TABLET_TOOL_AXIS_WHEEL)
+ result |= CTablet::eTabletToolAxes::HID_TABLET_TOOL_AXIS_WHEEL;
+ return result;
+}
+
+uint32_t CTablet::getCapabilities() {
+ return HID_INPUT_CAPABILITY_POINTER | HID_INPUT_CAPABILITY_TABLET;
+}
+
+wlr_tablet* CTablet::wlr() {
+ return tablet;
+}
+
+CTablet::CTablet(wlr_tablet* tablet_) : tablet(tablet_) {
+ if (!tablet)
+ return;
+
+ tablet->data = this;
+
+ // clang-format off
+ hyprListener_destroy.initCallback(&tablet->base.events.destroy, [this] (void* owner, void* data) {
+ tablet = nullptr;
+ disconnectCallbacks();
+ events.destroy.emit();
+ }, this, "CTablet");
+
+ hyprListener_axis.initCallback(&tablet->events.axis, [this] (void* owner, void* data) {
+ auto E = (wlr_tablet_tool_axis_event*)data;
+
+ tabletEvents.axis.emit(SAxisEvent{
+ .tool = E->tool,
+ .tablet = self.lock(),
+ .timeMs = E->time_msec,
+ .updatedAxes = wlrUpdateToHl(E->updated_axes),
+ .axis = {E->x, E->y},
+ .axisDelta = {E->dx, E->dy},
+ .tilt = {E->tilt_x, E->tilt_y},
+ .pressure = E->pressure,
+ .distance = E->distance,
+ .rotation = E->rotation,
+ .slider = E->slider,
+ .wheelDelta = E->wheel_delta,
+ });
+ }, this, "CTablet");
+
+ hyprListener_proximity.initCallback(&tablet->events.proximity, [this] (void* owner, void* data) {
+ auto E = (wlr_tablet_tool_proximity_event*)data;
+
+ tabletEvents.proximity.emit(SProximityEvent{
+ .tool = E->tool,
+ .tablet = self.lock(),
+ .timeMs = E->time_msec,
+ .proximity = {E->x, E->y},
+ .in = E->state == WLR_TABLET_TOOL_PROXIMITY_IN,
+ });
+ }, this, "CTablet");
+
+ hyprListener_tip.initCallback(&tablet->events.tip, [this] (void* owner, void* data) {
+ auto E = (wlr_tablet_tool_tip_event*)data;
+
+ tabletEvents.tip.emit(STipEvent{
+ .tool = E->tool,
+ .tablet = self.lock(),
+ .timeMs = E->time_msec,
+ .tip = {E->x, E->y},
+ .in = E->state == WLR_TABLET_TOOL_TIP_DOWN,
+ });
+ }, this, "CTablet");
+
+ hyprListener_button.initCallback(&tablet->events.button, [this] (void* owner, void* data) {
+ auto E = (wlr_tablet_tool_button_event*)data;
+
+ tabletEvents.button.emit(SButtonEvent{
+ .tool = E->tool,
+ .tablet = self.lock(),
+ .timeMs = E->time_msec,
+ .button = E->button,
+ .down = E->state == WLR_BUTTON_PRESSED,
+ });
+ }, this, "CTablet");
+ // clang-format on
+
+ deviceName = tablet->base.name ? tablet->base.name : "UNKNOWN";
+}
+
+CTablet::~CTablet() {
+ if (tablet)
+ tablet->data = nullptr;
+
+ PROTO::tablet->recheckRegisteredDevices();
+}
+
+void CTablet::disconnectCallbacks() {
+ hyprListener_axis.removeCallback();
+ hyprListener_button.removeCallback();
+ hyprListener_destroy.removeCallback();
+ hyprListener_proximity.removeCallback();
+ hyprListener_tip.removeCallback();
+}
+
+eHIDType CTablet::getType() {
+ return HID_TYPE_TABLET;
+}
+
+uint32_t CTabletPad::getCapabilities() {
+ return HID_INPUT_CAPABILITY_TABLET;
+}
+
+wlr_tablet_pad* CTabletPad::wlr() {
+ return pad;
+}
+
+eHIDType CTabletPad::getType() {
+ return HID_TYPE_TABLET_PAD;
+}
+
+CTabletPad::CTabletPad(wlr_tablet_pad* pad_) : pad(pad_) {
+ if (!pad)
+ return;
+
+ // clang-format off
+ hyprListener_destroy.initCallback(&pad->base.events.destroy, [this] (void* owner, void* data) {
+ pad = nullptr;
+ disconnectCallbacks();
+ events.destroy.emit();
+ }, this, "CTabletPad");
+
+ hyprListener_button.initCallback(&pad->events.button, [this] (void* owner, void* data) {
+ auto E = (wlr_tablet_pad_button_event*)data;
+
+ padEvents.button.emit(SButtonEvent{
+ .timeMs = E->time_msec,
+ .button = E->button,
+ .down = E->state == WLR_BUTTON_PRESSED,
+ .mode = E->mode,
+ .group = E->group,
+ });
+ }, this, "CTabletPad");
+
+ hyprListener_ring.initCallback(&pad->events.ring, [this] (void* owner, void* data) {
+ auto E = (wlr_tablet_pad_ring_event*)data;
+
+ padEvents.ring.emit(SRingEvent{
+ .timeMs = E->time_msec,
+ .finger = E->source == WLR_TABLET_PAD_RING_SOURCE_FINGER,
+ .ring = E->ring,
+ .position = E->position,
+ .mode = E->mode,
+ });
+ }, this, "CTabletPad");
+
+ hyprListener_strip.initCallback(&pad->events.strip, [this] (void* owner, void* data) {
+ auto E = (wlr_tablet_pad_strip_event*)data;
+
+ padEvents.strip.emit(SStripEvent{
+ .timeMs = E->time_msec,
+ .finger = E->source == WLR_TABLET_PAD_STRIP_SOURCE_FINGER,
+ .strip = E->strip,
+ .position = E->position,
+ .mode = E->mode,
+ });
+ }, this, "CTabletPad");
+
+ hyprListener_attach.initCallback(&pad->events.attach_tablet, [this] (void* owner, void* data) {
+ if (!data)
+ return;
+
+ padEvents.attach.emit(CTabletTool::fromWlr((wlr_tablet_tool*)data));
+ }, this, "CTabletPad");
+ // clang-format on
+
+ deviceName = pad->base.name ? pad->base.name : "UNKNOWN";
+}
+
+CTabletPad::~CTabletPad() {
+ PROTO::tablet->recheckRegisteredDevices();
+}
+
+void CTabletPad::disconnectCallbacks() {
+ hyprListener_ring.removeCallback();
+ hyprListener_button.removeCallback();
+ hyprListener_destroy.removeCallback();
+ hyprListener_strip.removeCallback();
+ hyprListener_attach.removeCallback();
+}
+
+uint32_t CTabletTool::getCapabilities() {
+ return HID_INPUT_CAPABILITY_POINTER | HID_INPUT_CAPABILITY_TABLET;
+}
+
+wlr_tablet_tool* CTabletTool::wlr() {
+ return tool;
+}
+
+eHIDType CTabletTool::getType() {
+ return HID_TYPE_TABLET_TOOL;
+}
+
+CTabletTool::CTabletTool(wlr_tablet_tool* tool_) : tool(tool_) {
+ if (!tool)
+ return;
+
+ // clang-format off
+ hyprListener_destroy.initCallback(&tool->events.destroy, [this] (void* owner, void* data) {
+ tool = nullptr;
+ disconnectCallbacks();
+ events.destroy.emit();
+ }, this, "CTabletTool");
+ // clang-format on
+
+ if (tool->tilt)
+ toolCapabilities |= HID_TABLET_TOOL_CAPABILITY_TILT;
+ if (tool->pressure)
+ toolCapabilities |= HID_TABLET_TOOL_CAPABILITY_PRESSURE;
+ if (tool->distance)
+ toolCapabilities |= HID_TABLET_TOOL_CAPABILITY_DISTANCE;
+ if (tool->rotation)
+ toolCapabilities |= HID_TABLET_TOOL_CAPABILITY_ROTATION;
+ if (tool->slider)
+ toolCapabilities |= HID_TABLET_TOOL_CAPABILITY_SLIDER;
+ if (tool->wheel)
+ toolCapabilities |= HID_TABLET_TOOL_CAPABILITY_WHEEL;
+
+ tool->data = this;
+
+ deviceName = std::to_string(tool->hardware_serial) + std::to_string(tool->hardware_wacom);
+}
+
+CTabletTool::~CTabletTool() {
+ if (tool)
+ tool->data = nullptr;
+
+ PROTO::tablet->recheckRegisteredDevices();
+}
+
+void CTabletTool::disconnectCallbacks() {
+ hyprListener_destroy.removeCallback();
+ hyprListener_destroySurface.removeCallback();
+}
+
+wlr_surface* CTabletTool::getSurface() {
+ return pSurface;
+}
+
+void CTabletTool::setSurface(wlr_surface* surf) {
+ if (surf == pSurface)
+ return;
+
+ if (pSurface) {
+ hyprListener_destroySurface.removeCallback();
+ pSurface = nullptr;
+ }
+
+ pSurface = surf;
+
+ if (surf) {
+ hyprListener_destroySurface.initCallback(
+ &surf->events.destroy,
+ [this](void* owner, void* data) {
+ PROTO::tablet->proximityOut(self.lock());
+ pSurface = nullptr;
+ hyprListener_destroySurface.removeCallback();
+ },
+ this, "CTabletTool");
+ }
+}
diff --git a/src/devices/Tablet.hpp b/src/devices/Tablet.hpp
new file mode 100644
index 00000000..f2444972
--- /dev/null
+++ b/src/devices/Tablet.hpp
@@ -0,0 +1,228 @@
+#pragma once
+
+#include "IHID.hpp"
+#include "../helpers/WLListener.hpp"
+#include "../macros.hpp"
+#include "../helpers/Vector2D.hpp"
+#include "../helpers/Box.hpp"
+
+struct wlr_tablet;
+struct wlr_tablet_tool;
+struct wlr_tablet_pad;
+
+class CTabletTool;
+class CTabletPad;
+
+/*
+ A tablet device
+ Tablets don't have an interface for now,
+ if there will be a need it's trivial to do.
+*/
+class CTablet : public IHID {
+ public:
+ static SP<CTablet> create(wlr_tablet* tablet);
+ static SP<CTablet> fromWlr(wlr_tablet* tablet);
+ ~CTablet();
+
+ virtual uint32_t getCapabilities();
+ virtual eHIDType getType();
+ wlr_tablet* wlr();
+
+ enum eTabletToolAxes {
+ HID_TABLET_TOOL_AXIS_X = (1 << 0),
+ HID_TABLET_TOOL_AXIS_Y = (1 << 1),
+ HID_TABLET_TOOL_AXIS_TILT_X = (1 << 2),
+ HID_TABLET_TOOL_AXIS_TILT_Y = (1 << 3),
+ HID_TABLET_TOOL_AXIS_DISTANCE = (1 << 4),
+ HID_TABLET_TOOL_AXIS_PRESSURE = (1 << 5),
+ HID_TABLET_TOOL_AXIS_ROTATION = (1 << 6),
+ HID_TABLET_TOOL_AXIS_SLIDER = (1 << 7),
+ HID_TABLET_TOOL_AXIS_WHEEL = (1 << 8),
+ };
+
+ struct SAxisEvent {
+ wlr_tablet_tool* tool;
+ SP<CTablet> tablet;
+
+ uint32_t timeMs = 0;
+ uint32_t updatedAxes = 0; // eTabletToolAxes
+ Vector2D axis;
+ Vector2D axisDelta;
+ Vector2D tilt;
+ double pressure = 0;
+ double distance = 0;
+ double rotation = 0;
+ double slider = 0;
+ double wheelDelta = 0;
+ };
+
+ struct SProximityEvent {
+ wlr_tablet_tool* tool;
+ SP<CTablet> tablet;
+
+ uint32_t timeMs = 0;
+ Vector2D proximity;
+ bool in = false;
+ };
+
+ struct STipEvent {
+ wlr_tablet_tool* tool;
+ SP<CTablet> tablet;
+
+ uint32_t timeMs = 0;
+ Vector2D tip;
+ bool in = false;
+ };
+
+ struct SButtonEvent {
+ wlr_tablet_tool* tool;
+ SP<CTablet> tablet;
+
+ uint32_t timeMs = 0;
+ uint32_t button;
+ bool down = false;
+ };
+
+ struct {
+ CSignal axis;
+ CSignal proximity;
+ CSignal tip;
+ CSignal button;
+ } tabletEvents;
+
+ WP<CTablet> self;
+
+ bool relativeInput = false;
+ std::string hlName = "";
+ std::string boundOutput = "";
+ CBox activeArea;
+ CBox boundBox; // output-local
+
+ private:
+ CTablet(wlr_tablet* tablet);
+
+ void disconnectCallbacks();
+
+ wlr_tablet* tablet = nullptr;
+
+ DYNLISTENER(destroy);
+ DYNLISTENER(axis);
+ DYNLISTENER(proximity);
+ DYNLISTENER(tip);
+ DYNLISTENER(button);
+};
+
+class CTabletPad : public IHID {
+ public:
+ static SP<CTabletPad> create(wlr_tablet_pad* pad);
+ ~CTabletPad();
+
+ virtual uint32_t getCapabilities();
+ virtual eHIDType getType();
+ wlr_tablet_pad* wlr();
+
+ struct SButtonEvent {
+ uint32_t timeMs = 0;
+ uint32_t button = 0;
+ bool down = false;
+ uint32_t mode = 0;
+ uint32_t group = 0;
+ };
+
+ struct SRingEvent {
+ uint32_t timeMs = 0;
+ bool finger = false;
+ uint32_t ring = 0;
+ double position = 0;
+ uint32_t mode = 0;
+ };
+
+ struct SStripEvent {
+ uint32_t timeMs = 0;
+ bool finger = false;
+ uint32_t strip = 0;
+ double position = 0;
+ uint32_t mode = 0;
+ };
+
+ struct {
+ CSignal button;
+ CSignal ring;
+ CSignal strip;
+ CSignal attach;
+ } padEvents;
+
+ WP<CTabletPad> self;
+ WP<CTabletTool> parent;
+
+ std::string hlName;
+
+ private:
+ CTabletPad(wlr_tablet_pad* pad);
+
+ void disconnectCallbacks();
+
+ wlr_tablet_pad* pad = nullptr;
+
+ DYNLISTENER(destroy);
+ DYNLISTENER(ring);
+ DYNLISTENER(strip);
+ DYNLISTENER(button);
+ DYNLISTENER(attach);
+};
+
+class CTabletTool : public IHID {
+ public:
+ static SP<CTabletTool> create(wlr_tablet_tool* tool);
+ static SP<CTabletTool> fromWlr(wlr_tablet_tool* tool);
+ ~CTabletTool();
+
+ enum eTabletToolType {
+ HID_TABLET_TOOL_TYPE_PEN = 1,
+ HID_TABLET_TOOL_TYPE_ERASER,
+ HID_TABLET_TOOL_TYPE_BRUSH,
+ HID_TABLET_TOOL_TYPE_PENCIL,
+ HID_TABLET_TOOL_TYPE_AIRBRUSH,
+ HID_TABLET_TOOL_TYPE_MOUSE,
+ HID_TABLET_TOOL_TYPE_LENS,
+ HID_TABLET_TOOL_TYPE_TOTEM,
+ };
+
+ enum eTabletToolCapabilities {
+ HID_TABLET_TOOL_CAPABILITY_TILT = (1 << 0),
+ HID_TABLET_TOOL_CAPABILITY_PRESSURE = (1 << 1),
+ HID_TABLET_TOOL_CAPABILITY_DISTANCE = (1 << 2),
+ HID_TABLET_TOOL_CAPABILITY_ROTATION = (1 << 3),
+ HID_TABLET_TOOL_CAPABILITY_SLIDER = (1 << 4),
+ HID_TABLET_TOOL_CAPABILITY_WHEEL = (1 << 5),
+ };
+
+ virtual uint32_t getCapabilities();
+ wlr_tablet_tool* wlr();
+ virtual eHIDType getType();
+ wlr_surface* getSurface();
+ void setSurface(wlr_surface*);
+
+ WP<CTabletTool> self;
+ Vector2D tilt;
+ bool active = false; // true if in proximity
+ uint32_t toolCapabilities = 0;
+
+ bool isDown = false;
+ std::vector<uint32_t> buttonsDown;
+ Vector2D absolutePos; // last known absolute position.
+
+ std::string hlName;
+
+ private:
+ CTabletTool(wlr_tablet_tool* tool);
+
+ void disconnectCallbacks();
+
+ wlr_surface* pSurface = nullptr;
+
+ wlr_tablet_tool* tool = nullptr;
+
+ DYNLISTENER(destroy);
+ DYNLISTENER(destroySurface);
+}; \ No newline at end of file
diff --git a/src/events/Devices.cpp b/src/events/Devices.cpp
index f36a95de..16a64559 100644
--- a/src/events/Devices.cpp
+++ b/src/events/Devices.cpp
@@ -38,8 +38,8 @@ void Events::listener_newInput(wl_listener* listener, void* data) {
g_pInputManager->newTouchDevice(DEVICE);
break;
case WLR_INPUT_DEVICE_TABLET:
- Debug::log(LOG, "Attached a tablet tool with name {}", DEVICE->name);
- g_pInputManager->newTabletTool(DEVICE);
+ Debug::log(LOG, "Attached a tablet with name {}", DEVICE->name);
+ g_pInputManager->newTablet(DEVICE);
break;
case WLR_INPUT_DEVICE_TABLET_PAD:
Debug::log(LOG, "Attached a tablet pad with name {}", DEVICE->name);
diff --git a/src/helpers/Monitor.cpp b/src/helpers/Monitor.cpp
index d0e777ed..fa68655f 100644
--- a/src/helpers/Monitor.cpp
+++ b/src/helpers/Monitor.cpp
@@ -146,20 +146,6 @@ void CMonitor::onConnect(bool noRule) {
if (!noRule)
g_pHyprRenderer->applyMonitorRule(this, &monitorRule, true);
- for (const auto& PTOUCHDEV : g_pInputManager->m_vTouches) {
- if (matchesStaticSelector(PTOUCHDEV->boundOutput)) {
- Debug::log(LOG, "Binding touch device {} to output {}", PTOUCHDEV->hlName, szName);
- // wlr_cursor_map_input_to_output(g_pCompositor->m_sWLRCursor, &PTOUCHDEV->wlr()->base, output);
- }
- }
-
- for (const auto& PTABLET : g_pInputManager->m_lTablets) {
- if (matchesStaticSelector(PTABLET.boundOutput)) {
- Debug::log(LOG, "Binding tablet {} to output {}", PTABLET.name, szName);
- // wlr_cursor_map_input_to_output(g_pCompositor->m_sWLRCursor, PTABLET.wlrDevice, output);
- }
- }
-
if (!state.commit())
Debug::log(WARN, "wlr_output_commit_state failed in CMonitor::onCommit");
diff --git a/src/helpers/WLClasses.hpp b/src/helpers/WLClasses.hpp
index bed2b915..bc07f6ba 100644
--- a/src/helpers/WLClasses.hpp
+++ b/src/helpers/WLClasses.hpp
@@ -85,72 +85,6 @@ struct SDrag {
DYNLISTENER(commitIcon);
};
-struct STablet {
- DYNLISTENER(Tip);
- DYNLISTENER(Axis);
- DYNLISTENER(Button);
- DYNLISTENER(Proximity);
- DYNLISTENER(Destroy);
-
- wlr_tablet* wlrTablet = nullptr;
- wlr_tablet_v2_tablet* wlrTabletV2 = nullptr;
- wlr_input_device* wlrDevice = nullptr;
-
- bool relativeInput = false;
-
- std::string name = "";
-
- std::string boundOutput = "";
-
- CBox activeArea;
-
- //
- bool operator==(const STablet& b) const {
- return wlrDevice == b.wlrDevice;
- }
-};
-
-struct STabletTool {
- wlr_tablet_tool* wlrTabletTool = nullptr;
- wlr_tablet_v2_tablet_tool* wlrTabletToolV2 = nullptr;
-
- wlr_tablet_v2_tablet* wlrTabletOwnerV2 = nullptr;
-
- wlr_surface* pSurface = nullptr;
-
- double tiltX = 0;
- double tiltY = 0;
-
- bool active = true;
-
- std::string name = "";
-
- DYNLISTENER(TabletToolDestroy);
- DYNLISTENER(TabletToolSetCursor);
-
- bool operator==(const STabletTool& b) const {
- return wlrTabletTool == b.wlrTabletTool;
- }
-};
-
-struct STabletPad {
- wlr_tablet_v2_tablet_pad* wlrTabletPadV2 = nullptr;
- STablet* pTabletParent = nullptr;
- wlr_input_device* pWlrDevice = nullptr;
-
- std::string name = "";
-
- DYNLISTENER(Attach);
- DYNLISTENER(Button);
- DYNLISTENER(Strip);
- DYNLISTENER(Ring);
- DYNLISTENER(Destroy);
-
- bool operator==(const STabletPad& b) const {
- return wlrTabletPadV2 == b.wlrTabletPadV2;
- }
-};
-
struct SSwipeGesture {
PHLWORKSPACE pWorkspaceBegin = nullptr;
diff --git a/src/includes.hpp b/src/includes.hpp
index 57ea5d54..080dbd4e 100644
--- a/src/includes.hpp
+++ b/src/includes.hpp
@@ -69,7 +69,6 @@ extern "C" {
#include <wlr/util/edges.h>
#include <wlr/types/wlr_tablet_pad.h>
#include <wlr/types/wlr_tablet_tool.h>
-#include <wlr/types/wlr_tablet_v2.h>
#include <xkbcommon/xkbcommon.h>
#include <wlr/render/egl.h>
#include <wlr/render/gles2.h>
diff --git a/src/managers/PointerManager.cpp b/src/managers/PointerManager.cpp
index caff8d22..c6478b9a 100644
--- a/src/managers/PointerManager.cpp
+++ b/src/managers/PointerManager.cpp
@@ -635,37 +635,60 @@ void CPointerManager::warpTo(const Vector2D& logical) {
void CPointerManager::move(const Vector2D& deltaLogical) {
const auto oldPos = pointerPos;
- auto newPos = oldPos + deltaLogical;
+ auto newPos = oldPos + Vector2D{std::isnan(deltaLogical.x) ? 0.0 : deltaLogical.x, std::isnan(deltaLogical.y) ? 0.0 : deltaLogical.y};
warpTo(newPos);
}
-void CPointerManager::warpAbsolute(const Vector2D& abs, SP<IHID> dev) {
+void CPointerManager::warpAbsolute(Vector2D abs, SP<IHID> dev) {
SP<CMonitor> currentMonitor = g_pCompositor->m_pLastMonitor.lock();
+ if (!currentMonitor)
+ return;
+
+ if (!std::isnan(abs.x))
+ abs.x = std::clamp(abs.x, 0.0, 1.0);
+ if (!std::isnan(abs.y))
+ abs.y = std::clamp(abs.y, 0.0, 1.0);
+
+ // in logical global
+ CBox mappedArea = currentMonitor->logicalBox();
switch (dev->getType()) {
- case HID_TYPE_TABLET:
- //TODO:
+ case HID_TYPE_TABLET: {
+ CTablet* TAB = reinterpret_cast<CTablet*>(dev.get());
+ if (!TAB->boundOutput.empty()) {
+ if (const auto PMONITOR = g_pCompositor->getMonitorFromString(TAB->boundOutput); PMONITOR) {
+ currentMonitor = PMONITOR->self.lock();
+ mappedArea = currentMonitor->logicalBox();
+ }
+ }
+
+ if (!TAB->boundBox.empty())
+ mappedArea = TAB->boundBox.translate(currentMonitor->vecPosition);
break;
+ }
case HID_TYPE_TOUCH: {
- auto TOUCH = SP<ITouch>(dev);
+ ITouch* TOUCH = reinterpret_cast<ITouch*>(dev.get());
if (!TOUCH->boundOutput.empty()) {
- const auto PMONITOR = g_pCompositor->getMonitorFromString(TOUCH->boundOutput);
- if (PMONITOR)
+ if (const auto PMONITOR = g_pCompositor->getMonitorFromString(TOUCH->boundOutput); PMONITOR) {
currentMonitor = PMONITOR->self.lock();
+ mappedArea = currentMonitor->logicalBox();
+ }
}
break;
}
default: break;
}
- if (!currentMonitor)
- return;
-
damageIfSoftware();
- pointerPos = currentMonitor->vecPosition + currentMonitor->vecSize * abs;
+ if (std::isnan(abs.x) || std::isnan(abs.y)) {
+ pointerPos.x = std::isnan(abs.x) ? pointerPos.x : mappedArea.x + mappedArea.w * abs.x;
+ pointerPos.y = std::isnan(abs.y) ? pointerPos.y : mappedArea.y + mappedArea.h * abs.y;
+ } else
+ pointerPos = mappedArea.pos() + mappedArea.size() * abs;
+
onCursorMoved();
recheckEnteredOutputs();
@@ -839,6 +862,47 @@ void CPointerManager::attachTouch(SP<ITouch> touch) {
Debug::log(LOG, "Attached touch {} to global", touch->hlName);
}
+void CPointerManager::attachTablet(SP<CTablet> tablet) {
+ if (!tablet)
+ return;
+
+ auto listener = tabletListeners.emplace_back(makeShared<STabletListener>());
+
+ listener->tablet = tablet;
+
+ // clang-format off
+ listener->destroy = tablet->events.destroy.registerListener([this] (std::any d) {
+ detachTablet(nullptr);
+ });
+
+ listener->axis = tablet->tabletEvents.axis.registerListener([this] (std::any e) {
+ auto E = std::any_cast<CTablet::SAxisEvent>(e);
+
+ g_pInputManager->onTabletAxis(E);
+ });
+
+ listener->proximity = tablet->tabletEvents.proximity.registerListener([this] (std::any e) {
+ auto E = std::any_cast<CTablet::SProximityEvent>(e);
+
+ g_pInputManager->onTabletProximity(E);
+ });
+
+ listener->tip = tablet->tabletEvents.tip.registerListener([this] (std::any e) {
+ auto E = std::any_cast<CTablet::STipEvent>(e);
+
+ g_pInputManager->onTabletTip(E);
+ });
+
+ listener->button = tablet->tabletEvents.button.registerListener([this] (std::any e) {
+ auto E = std::any_cast<CTablet::SButtonEvent>(e);
+
+ g_pInputManager->onTabletButton(E);
+ });
+ // clang-format on
+
+ Debug::log(LOG, "Attached tablet {} to global", tablet->hlName);
+}
+
void CPointerManager::detachPointer(SP<IPointer> pointer) {
std::erase_if(pointerListeners, [pointer](const auto& e) { return e->pointer.expired() || e->pointer == pointer; });
}
@@ -847,6 +911,10 @@ void CPointerManager::detachTouch(SP<ITouch> touch) {
std::erase_if(touchListeners, [touch](const auto& e) { return e->touch.expired() || e->touch == touch; });
}
+void CPointerManager::detachTablet(SP<CTablet> tablet) {
+ std::erase_if(tabletListeners, [tablet](const auto& e) { return e->tablet.expired() || e->tablet == tablet; });
+}
+
void CPointerManager::damageCursor(SP<CMonitor> pMonitor) {
for (auto& mw : monitorStates) {
if (mw->monitor != pMonitor)
diff --git a/src/managers/PointerManager.hpp b/src/managers/PointerManager.hpp
index 2446cde7..71ab0fc2 100644
--- a/src/managers/PointerManager.hpp
+++ b/src/managers/PointerManager.hpp
@@ -2,6 +2,7 @@
#include "../devices/IPointer.hpp"
#include "../devices/ITouch.hpp"
+#include "../devices/Tablet.hpp"
#include "../helpers/Box.hpp"
#include "../helpers/Region.hpp"
#include "../desktop/WLSurface.hpp"
@@ -22,18 +23,18 @@ class CPointerManager {
public:
CPointerManager();
- // pointers will move the cursor on their respective events
void attachPointer(SP<IPointer> pointer);
- // touch inputs won't move the cursor, it needs to be done manually
void attachTouch(SP<ITouch> touch);
+ void attachTablet(SP<CTablet> tablet);
void detachPointer(SP<IPointer> pointer);
void detachTouch(SP<ITouch> touch);
+ void detachTablet(SP<CTablet> tablet);
// only clamps to the layout.
void warpTo(const Vector2D& logical);
void move(const Vector2D& deltaLogical);
- void warpAbsolute(const Vector2D& abs, SP<IHID> dev);
+ void warpAbsolute(Vector2D abs, SP<IHID> dev);
void setCursorBuffer(wlr_buffer* buf, const Vector2D& hotspot, const float& scale);
void setCursorSurface(CWLSurface* buf, const Vector2D& hotspot);
@@ -111,6 +112,17 @@ class CPointerManager {
};
std::vector<SP<STouchListener>> touchListeners;
+ struct STabletListener {
+ CHyprSignalListener destroy;
+ CHyprSignalListener axis;
+ CHyprSignalListener proximity;
+ CHyprSignalListener tip;
+ CHyprSignalListener button;
+
+ WP<CTablet> tablet;
+ };
+ std::vector<SP<STabletListener>> tabletListeners;
+
struct {
std::vector<CBox> monitorBoxes;
} currentMonitorLayout;
diff --git a/src/managers/ProtocolManager.cpp b/src/managers/ProtocolManager.cpp
index ee27d35d..60fde1aa 100644
--- a/src/managers/ProtocolManager.cpp
+++ b/src/managers/ProtocolManager.cpp
@@ -25,6 +25,7 @@
#include "../protocols/OutputManagement.hpp"
#include "../protocols/ServerDecorationKDE.hpp"
#include "../protocols/FocusGrab.hpp"
+#include "../protocols/Tablet.hpp"
CProtocolManager::CProtocolManager() {
@@ -53,6 +54,7 @@ CProtocolManager::CProtocolManager() {
PROTO::outputManagement = std::make_unique<COutputManagementProtocol>(&zwlr_output_manager_v1_interface, 4, "OutputManagement");
PROTO::serverDecorationKDE = std::make_unique<CServerDecorationKDEProtocol>(&org_kde_kwin_server_decoration_manager_interface, 1, "ServerDecorationKDE");
PROTO::focusGrab = std::make_unique<CFocusGrabProtocol>(&hyprland_focus_grab_manager_v1_interface, 1, "FocusGrab");
+ PROTO::tablet = std::make_unique<CTabletV2Protocol>(&zwp_tablet_manager_v2_interface, 1, "TabletV2");
// Old protocol implementations.
// TODO: rewrite them to use hyprwayland-scanner.
diff --git a/src/managers/input/InputManager.cpp b/src/managers/input/InputManager.cpp
index 22fc24d4..7e746cfc 100644
--- a/src/managers/input/InputManager.cpp
+++ b/src/managers/input/InputManager.cpp
@@ -58,9 +58,9 @@ CInputManager::~CInputManager() {
m_vKeyboards.clear();
m_vPointers.clear();
m_vTouches.clear();
- m_lTablets.clear();
- m_lTabletTools.clear();
- m_lTabletPads.clear();
+ m_vTablets.clear();
+ m_vTabletTools.clear();
+ m_vTabletPads.clear();
m_vIdleInhibitors.clear();
m_lSwitches.clear();
}
@@ -777,6 +777,8 @@ void CInputManager::newVirtualKeyboard(SP<CVirtualKeyboardV1Resource> keyboard)
}
void CInputManager::setupKeyboard(SP<IKeyboard> keeb) {
+ m_vHIDs.push_back(keeb);
+
try {
keeb->hlName = getNameForNewDevice(keeb->wlr()->base.name);
} catch (std::exception& e) {
@@ -977,6 +979,8 @@ void CInputManager::newMouse(wlr_input_device* mouse) {
}
void CInputManager::setupMouse(SP<IPointer> mauz) {
+ m_vHIDs.push_back(mauz);
+
try {
mauz->hlName = getNameForNewDevice(mauz->wlr()->base.name);
} catch (std::exception& e) {
@@ -1165,6 +1169,11 @@ void CInputManager::setPointerConfigs() {
}
}
+static void removeFromHIDs(WP<IHID> hid) {
+ std::erase_if(g_pInputManager->m_vHIDs, [hid](const auto& e) { return e.expired() || e == hid; });
+ g_pInputManager->updateCapabilities();
+}
+
void CInputManager::destroyKeyboard(SP<IKeyboard> pKeyboard) {
if (pKeyboard->xkbTranslationState)
xkb_state_unref(pKeyboard->xkbTranslationState);
@@ -1180,6 +1189,8 @@ void CInputManager::destroyKeyboard(SP<IKeyboard> pKeyboard) {
g_pCompositor->m_sSeat.keyboard.reset();
wlr_seat_set_keyboard(g_pCompositor->m_sSeat.seat, nullptr);
}
+
+ removeFromHIDs(pKeyboard);
}
void CInputManager::destroyPointer(SP<IPointer> mouse) {
@@ -1189,12 +1200,40 @@ void CInputManager::destroyPointer(SP<IPointer> mouse) {
if (!g_pCompositor->m_sSeat.mouse.expired())
unconstrainMouse();
+
+ removeFromHIDs(mouse);
}
void CInputManager::destroyTouchDevice(SP<ITouch> touch) {
Debug::log(LOG, "Touch device at {:x} removed", (uintptr_t)touch.get());
std::erase_if(m_vTouches, [touch](const auto& other) { return other == touch; });
+
+ removeFromHIDs(touch);
+}
+
+void CInputManager::destroyTablet(SP<CTablet> tablet) {
+ Debug::log(LOG, "Tablet device at {:x} removed", (uintptr_t)tablet.get());
+
+ std::erase_if(m_vTablets, [tablet](const auto& other) { return other == tablet; });
+
+ removeFromHIDs(tablet);
+}
+
+void CInputManager::destroyTabletTool(SP<CTabletTool> tool) {
+ Debug::log(LOG, "Tablet tool at {:x} removed", (uintptr_t)tool.get());
+
+ std::erase_if(m_vTabletTools, [tool](const auto& other) { return other == tool; });
+
+ removeFromHIDs(tool);
+}
+
+void CInputManager::destroyTabletPad(SP<CTabletPad> pad) {
+ Debug::log(LOG, "Tablet pad at {:x} removed", (uintptr_t)pad.get());
+
+ std::erase_if(m_vTabletPads, [pad](const auto& other) { return other == pad; });
+
+ removeFromHIDs(pad);
}
void CInputManager::onKeyboardKey(std::any event, SP<IKeyboard> pKeyboard) {
@@ -1338,14 +1377,19 @@ bool CInputManager::isConstrained() {
void CInputManager::updateCapabilities() {
uint32_t caps = 0;
- if (!m_vKeyboards.empty())
- caps |= WL_SEAT_CAPABILITY_KEYBOARD;
- if (!m_vPointers.empty())
- caps |= WL_SEAT_CAPABILITY_POINTER;
- if (!m_vTouches.empty())
- caps |= WL_SEAT_CAPABILITY_TOUCH;
- if (!m_lTabletTools.empty())
- caps |= WL_SEAT_CAPABILITY_POINTER;
+ for (auto& h : m_vHIDs) {
+ if (h.expired())
+ continue;
+
+ auto cap = h->getCapabilities();
+
+ if (cap & HID_INPUT_CAPABILITY_KEYBOARD)
+ caps |= WL_SEAT_CAPABILITY_KEYBOARD;
+ if (cap & HID_INPUT_CAPABILITY_POINTER)
+ caps |= WL_SEAT_CAPABILITY_POINTER;
+ if (cap & HID_INPUT_CAPABILITY_TOUCH)
+ caps |= WL_SEAT_CAPABILITY_TOUCH;
+ }
wlr_seat_set_capabilities(g_pCompositor->m_sSeat.seat, caps);
m_uiCapabilities = caps;
@@ -1380,6 +1424,7 @@ void CInputManager::disableAllKeyboards(bool virt) {
void CInputManager::newTouchDevice(wlr_input_device* pDevice) {
const auto PNEWDEV = m_vTouches.emplace_back(CTouchDevice::create(wlr_touch_from_input_device(pDevice)));
+ m_vHIDs.push_back(PNEWDEV);
try {
PNEWDEV->hlName = getNameForNewDevice(pDevice->name);
@@ -1450,43 +1495,39 @@ void CInputManager::setTouchDeviceConfigs(SP<ITouch> dev) {
}
void CInputManager::setTabletConfigs() {
- for (auto& t : m_lTablets) {
- if (wlr_input_device_is_libinput(t.wlrDevice)) {
- const auto LIBINPUTDEV = (libinput_device*)wlr_libinput_get_device_handle(t.wlrDevice);
+ for (auto& t : m_vTablets) {
+ if (wlr_input_device_is_libinput(&t->wlr()->base)) {
+ const auto NAME = t->hlName;
+ const auto LIBINPUTDEV = (libinput_device*)wlr_libinput_get_device_handle(&t->wlr()->base);
- const auto RELINPUT = g_pConfigManager->getDeviceInt(t.name, "relative_input", "input:tablet:relative_input");
- t.relativeInput = RELINPUT;
+ const auto RELINPUT = g_pConfigManager->getDeviceInt(NAME, "relative_input", "input:tablet:relative_input");
+ t->relativeInput = RELINPUT;
- const int ROTATION = std::clamp(g_pConfigManager->getDeviceInt(t.name, "transform", "input:tablet:transform"), 0, 7);
- Debug::log(LOG, "Setting calibration matrix for device {}", t.name);
+ const int ROTATION = std::clamp(g_pConfigManager->getDeviceInt(NAME, "transform", "input:tablet:transform"), 0, 7);
+ Debug::log(LOG, "Setting calibration matrix for device {}", NAME);
libinput_device_config_calibration_set_matrix(LIBINPUTDEV, MATRICES[ROTATION]);
- if (g_pConfigManager->getDeviceInt(t.name, "left_handed", "input:tablet:left_handed") == 0)
+ if (g_pConfigManager->getDeviceInt(NAME, "left_handed", "input:tablet:left_handed") == 0)
libinput_device_config_left_handed_set(LIBINPUTDEV, 0);
else
libinput_device_config_left_handed_set(LIBINPUTDEV, 1);
- const auto OUTPUT = g_pConfigManager->getDeviceString(t.name, "output", "input:tablet:output");
- const auto PMONITOR = g_pCompositor->getMonitorFromString(OUTPUT);
- if (!OUTPUT.empty() && OUTPUT != STRVAL_EMPTY && PMONITOR) {
- Debug::log(LOG, "Binding tablet {} to output {}", t.name, PMONITOR->szName);
- // wlr_cursor_map_input_to_output(g_pCompositor->m_sWLRCursor, t.wlrDevice, PMONITOR->output);
- // wlr_cursor_map_input_to_region(g_pCompositor->m_sWLRCursor, t.wlrDevice, nullptr);
- t.boundOutput = OUTPUT;
- } else if (!PMONITOR)
- Debug::log(ERR, "Failed to bind tablet {} to output '{}': monitor not found", t.name, OUTPUT);
-
- const auto REGION_POS = g_pConfigManager->getDeviceVec(t.name, "region_position", "input:tablet:region_position");
- const auto REGION_SIZE = g_pConfigManager->getDeviceVec(t.name, "region_size", "input:tablet:region_size");
- //auto regionBox = CBox{REGION_POS.x, REGION_POS.y, REGION_SIZE.x, REGION_SIZE.y};
- // if (!regionBox.empty())
- // wlr_cursor_map_input_to_region(g_pCompositor->m_sWLRCursor, t.wlrDevice, regionBox.pWlr());
-
- const auto ACTIVE_AREA_SIZE = g_pConfigManager->getDeviceVec(t.name, "active_area_size", "input:tablet:active_area_size");
- const auto ACTIVE_AREA_POS = g_pConfigManager->getDeviceVec(t.name, "active_area_position", "input:tablet:active_area_position");
+ const auto OUTPUT = g_pConfigManager->getDeviceString(NAME, "output", "input:tablet:output");
+ if (OUTPUT != STRVAL_EMPTY) {
+ Debug::log(LOG, "Binding tablet {} to output {}", NAME, OUTPUT);
+ t->boundOutput = OUTPUT;
+ } else
+ t->boundOutput = "";
+
+ const auto REGION_POS = g_pConfigManager->getDeviceVec(NAME, "region_position", "input:tablet:region_position");
+ const auto REGION_SIZE = g_pConfigManager->getDeviceVec(NAME, "region_size", "input:tablet:region_size");
+ t->boundBox = {REGION_POS, REGION_SIZE};
+
+ const auto ACTIVE_AREA_SIZE = g_pConfigManager->getDeviceVec(NAME, "active_area_size", "input:tablet:active_area_size");
+ const auto ACTIVE_AREA_POS = g_pConfigManager->getDeviceVec(NAME, "active_area_position", "input:tablet:active_area_position");
if (ACTIVE_AREA_SIZE.x != 0 || ACTIVE_AREA_SIZE.y != 0) {
- t.activeArea = CBox{ACTIVE_AREA_POS.x / t.wlrTablet->width_mm, ACTIVE_AREA_POS.y / t.wlrTablet->height_mm,
- (ACTIVE_AREA_POS.x + ACTIVE_AREA_SIZE.x) / t.wlrTablet->width_mm, (ACTIVE_AREA_POS.y + ACTIVE_AREA_SIZE.y) / t.wlrTablet->height_mm};
+ t->activeArea = CBox{ACTIVE_AREA_POS.x / t->wlr()->width_mm, ACTIVE_AREA_POS.y / t->wlr()->height_mm, (ACTIVE_AREA_POS.x + ACTIVE_AREA_SIZE.x) / t->wlr()->width_mm,
+ (ACTIVE_AREA_POS.y + ACTIVE_AREA_SIZE.y) / t->wlr()->height_mm};
}
}
}
@@ -1575,16 +1616,16 @@ std::string CInputManager::getNameForNewDevice(std::string internalName) {
[&](const auto& other) { return other->hlName == proposedNewName + (dupeno == 0 ? "" : ("-" + std::to_string(dupeno))); }) != m_vTouches.end())
dupeno++;
- while (std::find_if(m_lTabletPads.begin(), m_lTabletPads.end(),
- [&](const STabletPad& other) { return other.name == proposedNewName + (dupeno == 0 ? "" : ("-" + std::to_string(dupeno))); }) != m_lTabletPads.end())
+ while (std::find_if(m_vTabletPads.begin(), m_vTabletPads.end(),
+ [&](const auto& other) { return other->hlName == proposedNewName + (dupeno == 0 ? "" : ("-" + std::to_string(dupeno))); }) != m_vTabletPads.end())
dupeno++;
- while (std::find_if(m_lTablets.begin(), m_lTablets.end(),
- [&](const STablet& other) { return other.name == proposedNewName + (dupeno == 0 ? "" : ("-" + std::to_string(dupeno))); }) != m_lTablets.end())
+ while (std::find_if(m_vTablets.begin(), m_vTablets.end(),
+ [&](const auto& other) { return other->hlName == proposedNewName + (dupeno == 0 ? "" : ("-" + std::to_string(dupeno))); }) != m_vTablets.end())
dupeno++;
- while (std::find_if(m_lTabletTools.begin(), m_lTabletTools.end(),
- [&](const STabletTool& other) { return other.name == proposedNewName + (dupeno == 0 ? "" : ("-" + std::to_string(dupeno))); }) != m_lTabletTools.end())
+ while (std::find_if(m_vTabletTools.begin(), m_vTabletTools.end(),
+ [&](const auto& other) { return other->hlName == proposedNewName + (dupeno == 0 ? "" : ("-" + std::to_string(dupeno))); }) != m_vTabletTools.end())
dupeno++;
return proposedNewName + (dupeno == 0 ? "" : ("-" + std::to_string(dupeno)));
diff --git a/src/managers/input/InputManager.hpp b/src/managers/input/InputManager.hpp
index a2caed46..970d53c8 100644
--- a/src/managers/input/InputManager.hpp
+++ b/src/managers/input/InputManager.hpp
@@ -9,6 +9,7 @@
#include "../../helpers/signal/Listener.hpp"
#include "../../devices/IPointer.hpp"
#include "../../devices/ITouch.hpp"
+#include "../../devices/Tablet.hpp"
class CPointerConstraint;
class CWindow;
@@ -87,9 +88,15 @@ class CInputManager {
void newVirtualMouse(SP<CVirtualPointerV1Resource>);
void newTouchDevice(wlr_input_device*);
void newSwitch(wlr_input_device*);
+ void newTabletTool(wlr_tablet_tool*);
+ void newTabletPad(wlr_input_device*);
+ void newTablet(wlr_input_device*);
void destroyTouchDevice(SP<ITouch>);
void destroyKeyboard(SP<IKeyboard>);
void destroyPointer(SP<IPointer>);
+ void destroyTablet(SP<CTablet>);
+ void destroyTabletTool(SP<CTabletTool>);
+ void destroyTabletPad(SP<CTabletPad>);
void destroySwitch(SSwitchDevice*);
void unconstrainMouse();
@@ -116,6 +123,15 @@ class CInputManager {
void onTouchUp(ITouch::SUpEvent);
void onTouchMove(ITouch::SMotionEvent);
+ void onSwipeBegin(IPointer::SSwipeBeginEvent);
+ void onSwipeEnd(IPointer::SSwipeEndEvent);
+ void onSwipeUpdate(IPointer::SSwipeUpdateEvent);
+
+ void onTabletAxis(CTablet::SAxisEvent);
+ void onTabletProximity(CTablet::SProximityEvent);
+ void onTabletTip(CTablet::STipEvent);
+ void onTabletButton(CTablet::SButtonEvent);
+
STouchData m_sTouchData;
// for dragging floating windows
@@ -124,18 +140,17 @@ class CInputManager {
bool m_bWasDraggingWindow = false;
// for refocus to be forced
- PHLWINDOWREF m_pForcedFocus;
+ PHLWINDOWREF m_pForcedFocus;
- SDrag m_sDrag;
+ SDrag m_sDrag;
- std::vector<SP<IKeyboard>> m_vKeyboards;
- std::vector<SP<IPointer>> m_vPointers;
- std::vector<SP<ITouch>> m_vTouches;
-
- // tablets
- std::list<STablet> m_lTablets;
- std::list<STabletTool> m_lTabletTools;
- std::list<STabletPad> m_lTabletPads;
+ std::vector<SP<IKeyboard>> m_vKeyboards;
+ std::vector<SP<IPointer>> m_vPointers;
+ std::vector<SP<ITouch>> m_vTouches;
+ std::vector<SP<CTablet>> m_vTablets;
+ std::vector<SP<CTabletTool>> m_vTabletTools;
+ std::vector<SP<CTabletPad>> m_vTabletPads;
+ std::vector<WP<IHID>> m_vHIDs; // general container for all HID devices connected to the input manager.
// Switches
std::list<SSwitchDevice> m_lSwitches;
@@ -147,16 +162,9 @@ class CInputManager {
std::vector<WP<CPointerConstraint>> m_vConstraints;
//
- void newTabletTool(wlr_input_device*);
- void newTabletPad(wlr_input_device*);
- void focusTablet(STablet*, wlr_tablet_tool*, bool motion = false);
void newIdleInhibitor(std::any);
void recheckIdleInhibitorStatus();
- void onSwipeBegin(IPointer::SSwipeBeginEvent);
- void onSwipeEnd(IPointer::SSwipeEndEvent);
- void onSwipeUpdate(IPointer::SSwipeUpdateEvent);
-
SSwipeGesture m_sActiveSwipe;
CTimer m_tmrLastCursorMovement;
@@ -223,7 +231,7 @@ class CInputManager {
void mouseMoveUnified(uint32_t, bool refocus = false);
- STabletTool* ensureTabletToolPresent(wlr_tablet_tool*);
+ SP<CTabletTool> ensureTabletToolPresent(wlr_tablet_tool*);
void applyConfigToKeyboard(SP<IKeyboard>);
diff --git a/src/managers/input/Tablets.cpp b/src/managers/input/Tablets.cpp
index a0bd81ce..cf666ec6 100644
--- a/src/managers/input/Tablets.cpp
+++ b/src/managers/input/Tablets.cpp
@@ -1,290 +1,291 @@
#include "InputManager.hpp"
#include "../../Compositor.hpp"
#include "../../protocols/IdleNotify.hpp"
+#include "../../protocols/Tablet.hpp"
+#include "../../devices/Tablet.hpp"
+#include "../../managers/PointerManager.hpp"
+#include "../../protocols/PointerConstraints.hpp"
+
+static void unfocusTool(SP<CTabletTool> tool) {
+ if (!tool->getSurface())
+ return;
+
+ tool->setSurface(nullptr);
+ if (tool->isDown)
+ PROTO::tablet->up(tool);
+ for (auto& b : tool->buttonsDown) {
+ PROTO::tablet->buttonTool(tool, b, false);
+ }
+ PROTO::tablet->proximityOut(tool);
+}
-void CInputManager::newTabletTool(wlr_input_device* pDevice) {
- const auto PNEWTABLET = &m_lTablets.emplace_back();
+static void focusTool(SP<CTabletTool> tool, SP<CTablet> tablet, wlr_surface* surf) {
+ if (tool->getSurface() == surf || !surf)
+ return;
- try {
- PNEWTABLET->name = deviceNameToInternalString(pDevice->name);
- } catch (std::exception& e) {
- Debug::log(ERR, "Tablet had no name???"); // logic error
+ if (tool->getSurface() && tool->getSurface() != surf)
+ unfocusTool(tool);
+
+ tool->setSurface(surf);
+ PROTO::tablet->proximityIn(tool, tablet, surf);
+ if (tool->isDown)
+ PROTO::tablet->down(tool);
+ for (auto& b : tool->buttonsDown) {
+ PROTO::tablet->buttonTool(tool, b, true);
}
+}
+
+static void refocusTablet(SP<CTablet> tab, SP<CTabletTool> tool, bool motion = false) {
+ const auto LASTHLSURFACE = CWLSurface::surfaceFromWlr(g_pInputManager->m_pLastMouseSurface);
- PNEWTABLET->wlrTablet = wlr_tablet_from_input_device(pDevice);
- PNEWTABLET->wlrDevice = pDevice;
- PNEWTABLET->wlrTabletV2 = wlr_tablet_create(g_pCompositor->m_sWLRTabletManager, g_pCompositor->m_sSeat.seat, pDevice);
- PNEWTABLET->wlrTablet->data = PNEWTABLET;
+ if (!LASTHLSURFACE || !tool->active) {
+ if (tool->getSurface())
+ unfocusTool(tool);
- Debug::log(LOG, "Attaching tablet to cursor!");
+ return;
+ }
- // wlr_cursor_attach_input_device(g_pCompositor->m_sWLRCursor, pDevice);
+ const auto BOX = LASTHLSURFACE->getSurfaceBoxGlobal();
- PNEWTABLET->hyprListener_Destroy.initCallback(
- &pDevice->events.destroy,
- [](void* owner, void* data) {
- const auto PTAB = (STablet*)owner;
+ if (!BOX.has_value()) {
+ if (tool->getSurface())
+ unfocusTool(tool);
- g_pInputManager->m_lTablets.remove(*PTAB);
+ return;
+ }
- Debug::log(LOG, "Removed a tablet");
- },
- PNEWTABLET, "Tablet");
-
- PNEWTABLET->hyprListener_Axis.initCallback(
- &wlr_tablet_from_input_device(pDevice)->events.axis,
- [](void* owner, void* data) {
- const auto EVENT = (wlr_tablet_tool_axis_event*)data;
- const auto PTAB = (STablet*)owner;
-
- switch (EVENT->tool->type) {
- case WLR_TABLET_TOOL_TYPE_MOUSE:
- // wlr_cursor_move(g_pCompositor->m_sWLRCursor, PTAB->wlrDevice, EVENT->dx, EVENT->dy);
- g_pInputManager->simulateMouseMovement();
- g_pInputManager->focusTablet(PTAB, EVENT->tool, true);
- g_pInputManager->m_tmrLastCursorMovement.reset();
- break;
- default:
- double x = (EVENT->updated_axes & WLR_TABLET_TOOL_AXIS_X) ? EVENT->x : NAN;
- double dx = (EVENT->updated_axes & WLR_TABLET_TOOL_AXIS_X) ? EVENT->dx : NAN;
- double y = (EVENT->updated_axes & WLR_TABLET_TOOL_AXIS_Y) ? EVENT->y : NAN;
- double dy = (EVENT->updated_axes & WLR_TABLET_TOOL_AXIS_Y) ? EVENT->dy : NAN;
-
- // if (PTAB->relativeInput)
- // wlr_cursor_move(g_pCompositor->m_sWLRCursor, PTAB->wlrDevice, dx, dy);
- // else {
- // Calculate transformations if active area is set
- // if (!PTAB->activeArea.empty()) {
- // x = (x - PTAB->activeArea.x) / (PTAB->activeArea.w - PTAB->activeArea.x);
- // y = (y - PTAB->activeArea.y) / (PTAB->activeArea.h - PTAB->activeArea.y);
- // }
- // wlr_cursor_warp_absolute(g_pCompositor->m_sWLRCursor, PTAB->wlrDevice, x, y);
- // }
-
- g_pInputManager->simulateMouseMovement();
- g_pInputManager->focusTablet(PTAB, EVENT->tool, true);
- g_pInputManager->m_tmrLastCursorMovement.reset();
- break;
- }
+ const auto CURSORPOS = g_pInputManager->getMouseCoordsInternal();
- const auto PTOOL = g_pInputManager->ensureTabletToolPresent(EVENT->tool);
+ focusTool(tool, tab, g_pInputManager->m_pLastMouseSurface);
- // TODO: this might be wrong
- if (PTOOL->active) {
- g_pInputManager->simulateMouseMovement();
+ if (!motion)
+ return;
- g_pInputManager->focusTablet(PTAB, EVENT->tool, true);
- }
+ if (LASTHLSURFACE->constraint() && tool->wlr()->type != WLR_TABLET_TOOL_TYPE_MOUSE) {
+ // cursor logic will completely break here as the cursor will be locked.
+ // let's just "map" the desired position to the constraint area.
- if (EVENT->updated_axes & WLR_TABLET_TOOL_AXIS_PRESSURE)
- wlr_tablet_v2_tablet_tool_notify_pressure(PTOOL->wlrTabletToolV2, EVENT->pressure);
+ Vector2D local;
- if (EVENT->updated_axes & WLR_TABLET_TOOL_AXIS_DISTANCE)
- wlr_tablet_v2_tablet_tool_notify_distance(PTOOL->wlrTabletToolV2, EVENT->distance);
+ // yes, this technically ignores any regions set by the app. Too bad!
+ if (LASTHLSURFACE->getWindow())
+ local = tool->absolutePos * LASTHLSURFACE->getWindow()->m_vRealSize.goal();
+ else
+ local = tool->absolutePos * BOX->size();
- if (EVENT->updated_axes & WLR_TABLET_TOOL_AXIS_ROTATION)
- wlr_tablet_v2_tablet_tool_notify_rotation(PTOOL->wlrTabletToolV2, EVENT->rotation);
+ if (LASTHLSURFACE->getWindow() && LASTHLSURFACE->getWindow()->m_bIsX11)
+ local = local * LASTHLSURFACE->getWindow()->m_fX11SurfaceScaledBy;
- if (EVENT->updated_axes & WLR_TABLET_TOOL_AXIS_SLIDER)
- wlr_tablet_v2_tablet_tool_notify_slider(PTOOL->wlrTabletToolV2, EVENT->slider);
+ PROTO::tablet->motion(tool, local);
+ return;
+ }
- if (EVENT->updated_axes & WLR_TABLET_TOOL_AXIS_WHEEL)
- wlr_tablet_v2_tablet_tool_notify_wheel(PTOOL->wlrTabletToolV2, EVENT->wheel_delta, 0);
+ auto local = CURSORPOS - BOX->pos();
- if (EVENT->updated_axes & WLR_TABLET_TOOL_AXIS_TILT_X)
- PTOOL->tiltX = EVENT->tilt_x;
+ if (LASTHLSURFACE->getWindow() && LASTHLSURFACE->getWindow()->m_bIsX11)
+ local = local * LASTHLSURFACE->getWindow()->m_fX11SurfaceScaledBy;
- if (EVENT->updated_axes & WLR_TABLET_TOOL_AXIS_TILT_Y)
- PTOOL->tiltY = EVENT->tilt_y;
+ PROTO::tablet->motion(tool, local);
+}
- if (EVENT->updated_axes & (WLR_TABLET_TOOL_AXIS_TILT_X | WLR_TABLET_TOOL_AXIS_TILT_Y))
- wlr_tablet_v2_tablet_tool_notify_tilt(PTOOL->wlrTabletToolV2, PTOOL->tiltX, PTOOL->tiltY);
+void CInputManager::onTabletAxis(CTablet::SAxisEvent e) {
+ const auto PTAB = e.tablet;
+ const auto PTOOL = ensureTabletToolPresent(e.tool);
- PROTO::idle->onActivity();
- },
- PNEWTABLET, "Tablet");
-
- PNEWTABLET->hyprListener_Tip.initCallback(
- &wlr_tablet_from_input_device(pDevice)->events.tip,
- [](void* owner, void* data) {
- const auto EVENT = (wlr_tablet_tool_tip_event*)data;
- const auto PTAB = (STablet*)owner;
-
- const auto PTOOL = g_pInputManager->ensureTabletToolPresent(EVENT->tool);
-
- // TODO: this might be wrong
- if (EVENT->state == WLR_TABLET_TOOL_TIP_DOWN) {
- g_pInputManager->simulateMouseMovement();
- g_pInputManager->focusTablet(PTAB, EVENT->tool);
- wlr_send_tablet_v2_tablet_tool_down(PTOOL->wlrTabletToolV2);
- } else {
- wlr_send_tablet_v2_tablet_tool_up(PTOOL->wlrTabletToolV2);
+ if (PTOOL->active && (e.updatedAxes & (CTablet::eTabletToolAxes::HID_TABLET_TOOL_AXIS_X | CTablet::eTabletToolAxes::HID_TABLET_TOOL_AXIS_Y))) {
+ double x = (e.updatedAxes & CTablet::eTabletToolAxes::HID_TABLET_TOOL_AXIS_X) ? e.axis.x : NAN;
+ double dx = (e.updatedAxes & CTablet::eTabletToolAxes::HID_TABLET_TOOL_AXIS_X) ? e.axisDelta.x : NAN;
+ double y = (e.updatedAxes & CTablet::eTabletToolAxes::HID_TABLET_TOOL_AXIS_Y) ? e.axis.y : NAN;
+ double dy = (e.updatedAxes & CTablet::eTabletToolAxes::HID_TABLET_TOOL_AXIS_Y) ? e.axisDelta.y : NAN;
+
+ Vector2D delta = {std::isnan(dx) ? 0.0 : dx, std::isnan(dy) ? 0.0 : dy};
+
+ switch (e.tool->type) {
+ case WLR_TABLET_TOOL_TYPE_MOUSE: {
+ g_pPointerManager->move(delta);
+ break;
+ }
+ default: {
+ if (!std::isnan(x))
+ PTOOL->absolutePos.x = x;
+ if (!std::isnan(y))
+ PTOOL->absolutePos.y = y;
+
+ if (PTAB->relativeInput)
+ g_pPointerManager->move(delta);
+ else {
+ //Calculate transformations if active area is set
+ if (!PTAB->activeArea.empty()) {
+ if (!std::isnan(x))
+ x = (x - PTAB->activeArea.x) / (PTAB->activeArea.w - PTAB->activeArea.x);
+ if (!std::isnan(y))
+ y = (y - PTAB->activeArea.y) / (PTAB->activeArea.h - PTAB->activeArea.y);
+ }
+ g_pPointerManager->warpAbsolute({x, y}, PTAB);
+ }
+ break;
}
+ }
- PROTO::idle->onActivity();
- },
- PNEWTABLET, "Tablet");
+ refocusTablet(PTAB, PTOOL, true);
+ m_tmrLastCursorMovement.reset();
+ }
- PNEWTABLET->hyprListener_Button.initCallback(
- &wlr_tablet_from_input_device(pDevice)->events.button,
- [](void* owner, void* data) {
- const auto EVENT = (wlr_tablet_tool_button_event*)data;
+ if (e.updatedAxes & CTablet::eTabletToolAxes::HID_TABLET_TOOL_AXIS_PRESSURE)
+ PROTO::tablet->pressure(PTOOL, e.pressure);
- const auto PTOOL = g_pInputManager->ensureTabletToolPresent(EVENT->tool);
+ if (e.updatedAxes & CTablet::eTabletToolAxes::HID_TABLET_TOOL_AXIS_DISTANCE)
+ PROTO::tablet->distance(PTOOL, e.distance);
- wlr_tablet_v2_tablet_tool_notify_button(PTOOL->wlrTabletToolV2, (zwp_tablet_pad_v2_button_state)EVENT->button, (zwp_tablet_pad_v2_button_state)EVENT->state);
- PROTO::idle->onActivity();
- },
- PNEWTABLET, "Tablet");
+ if (e.updatedAxes & CTablet::eTabletToolAxes::HID_TABLET_TOOL_AXIS_ROTATION)
+ PROTO::tablet->rotation(PTOOL, e.rotation);
- PNEWTABLET->hyprListener_Proximity.initCallback(
- &wlr_tablet_from_input_device(pDevice)->events.proximity,
- [](void* owner, void* data) {
- const auto EVENT = (wlr_tablet_tool_proximity_event*)data;
- const auto PTAB = (STablet*)owner;
+ if (e.updatedAxes & CTablet::eTabletToolAxes::HID_TABLET_TOOL_AXIS_SLIDER)
+ PROTO::tablet->slider(PTOOL, e.slider);
- const auto PTOOL = g_pInputManager->ensureTabletToolPresent(EVENT->tool);
+ if (e.updatedAxes & CTablet::eTabletToolAxes::HID_TABLET_TOOL_AXIS_WHEEL)
+ PROTO::tablet->wheel(PTOOL, e.wheelDelta);
- if (EVENT->state == WLR_TABLET_TOOL_PROXIMITY_OUT) {
- PTOOL->active = false;
+ if (e.updatedAxes & CTablet::eTabletToolAxes::HID_TABLET_TOOL_AXIS_TILT_X)
+ PTOOL->tilt.x = e.tilt.x;
- if (PTOOL->pSurface) {
- wlr_tablet_v2_tablet_tool_notify_proximity_out(PTOOL->wlrTabletToolV2);
- PTOOL->pSurface = nullptr;
- }
+ if (e.updatedAxes & CTablet::eTabletToolAxes::HID_TABLET_TOOL_AXIS_TILT_Y)
+ PTOOL->tilt.y = e.tilt.y;
- } else {
- PTOOL->active = true;
- g_pInputManager->simulateMouseMovement();
- g_pInputManager->focusTablet(PTAB, EVENT->tool);
- }
+ if (e.updatedAxes & (CTablet::eTabletToolAxes::HID_TABLET_TOOL_AXIS_TILT_X | CTablet::eTabletToolAxes::HID_TABLET_TOOL_AXIS_TILT_Y))
+ PROTO::tablet->tilt(PTOOL, PTOOL->tilt);
- PROTO::idle->onActivity();
- },
- PNEWTABLET, "Tablet");
+ PROTO::idle->onActivity();
+}
- setTabletConfigs();
+void CInputManager::onTabletTip(CTablet::STipEvent e) {
+ const auto PTAB = e.tablet;
+ const auto PTOOL = ensureTabletToolPresent(e.tool);
+
+ if (e.in) {
+ simulateMouseMovement();
+ refocusTablet(PTAB, PTOOL);
+ PROTO::tablet->down(PTOOL);
+ } else
+ PROTO::tablet->up(PTOOL);
+
+ PTOOL->isDown = e.in;
+
+ PROTO::idle->onActivity();
}
-STabletTool* CInputManager::ensureTabletToolPresent(wlr_tablet_tool* pTool) {
- if (pTool->data == nullptr) {
- const auto PTOOL = &m_lTabletTools.emplace_back();
+void CInputManager::onTabletButton(CTablet::SButtonEvent e) {
+ const auto PTOOL = ensureTabletToolPresent(e.tool);
- Debug::log(LOG, "Creating tablet tool v2 for {:x}", (uintptr_t)pTool);
+ PROTO::tablet->buttonTool(PTOOL, e.button, e.down);
- PTOOL->wlrTabletTool = pTool;
- pTool->data = PTOOL;
+ if (e.down)
+ PTOOL->buttonsDown.push_back(e.button);
+ else
+ std::erase(PTOOL->buttonsDown, e.button);
- PTOOL->wlrTabletToolV2 = wlr_tablet_tool_create(g_pCompositor->m_sWLRTabletManager, g_pCompositor->m_sSeat.seat, pTool);
+ PROTO::idle->onActivity();
+}
- PTOOL->hyprListener_TabletToolDestroy.initCallback(
- &pTool->events.destroy,
- [](void* owner, void* data) {
- const auto PTOOL = (STabletTool*)owner;
+void CInputManager::onTabletProximity(CTablet::SProximityEvent e) {
+ const auto PTAB = e.tablet;
+ const auto PTOOL = ensureTabletToolPresent(e.tool);
- PTOOL->wlrTabletTool->data = nullptr;
- g_pInputManager->m_lTabletTools.remove(*PTOOL);
- },
- PTOOL, "Tablet Tool V1");
+ PTOOL->active = e.in;
- //TODO: set cursor request
+ if (!e.in) {
+ if (PTOOL->getSurface())
+ unfocusTool(PTOOL);
+ } else {
+ simulateMouseMovement();
+ refocusTablet(PTAB, PTOOL);
}
- return (STabletTool*)pTool->data;
+ PROTO::idle->onActivity();
}
-void CInputManager::newTabletPad(wlr_input_device* pDevice) {
- const auto PNEWPAD = &m_lTabletPads.emplace_back();
+void CInputManager::newTablet(wlr_input_device* pDevice) {
+ const auto PNEWTABLET = m_vTablets.emplace_back(CTablet::create(wlr_tablet_from_input_device(pDevice)));
+ m_vHIDs.push_back(PNEWTABLET);
try {
- PNEWPAD->name = deviceNameToInternalString(pDevice->name);
+ PNEWTABLET->hlName = deviceNameToInternalString(pDevice->name);
} catch (std::exception& e) {
- Debug::log(ERR, "Pad had no name???"); // logic error
+ Debug::log(ERR, "Tablet had no name???"); // logic error
}
- PNEWPAD->wlrTabletPadV2 = wlr_tablet_pad_create(g_pCompositor->m_sWLRTabletManager, g_pCompositor->m_sSeat.seat, pDevice);
- PNEWPAD->pWlrDevice = pDevice;
+ g_pPointerManager->attachTablet(PNEWTABLET);
- PNEWPAD->hyprListener_Button.initCallback(
- &wlr_tablet_pad_from_input_device(pDevice)->events.button,
- [](void* owner, void* data) {
- const auto EVENT = (wlr_tablet_pad_button_event*)data;
- const auto PPAD = (STabletPad*)owner;
-
- wlr_tablet_v2_tablet_pad_notify_mode(PPAD->wlrTabletPadV2, EVENT->group, EVENT->mode, EVENT->time_msec);
- wlr_tablet_v2_tablet_pad_notify_button(PPAD->wlrTabletPadV2, EVENT->button, EVENT->time_msec, (zwp_tablet_pad_v2_button_state)EVENT->state);
+ PNEWTABLET->events.destroy.registerStaticListener(
+ [this](void* owner, std::any d) {
+ auto TABLET = ((CTablet*)owner)->self;
+ destroyTablet(TABLET.lock());
},
- PNEWPAD, "Tablet Pad");
-
- PNEWPAD->hyprListener_Strip.initCallback(
- &wlr_tablet_pad_from_input_device(pDevice)->events.strip,
- [](void* owner, void* data) {
- const auto EVENT = (wlr_tablet_pad_strip_event*)data;
- const auto PPAD = (STabletPad*)owner;
+ PNEWTABLET.get());
- wlr_tablet_v2_tablet_pad_notify_strip(PPAD->wlrTabletPadV2, EVENT->strip, EVENT->position, EVENT->source == WLR_TABLET_PAD_STRIP_SOURCE_FINGER, EVENT->time_msec);
- },
- PNEWPAD, "Tablet Pad");
+ setTabletConfigs();
+}
- PNEWPAD->hyprListener_Ring.initCallback(
- &wlr_tablet_pad_from_input_device(pDevice)->events.strip,
- [](void* owner, void* data) {
- const auto EVENT = (wlr_tablet_pad_ring_event*)data;
- const auto PPAD = (STabletPad*)owner;
+SP<CTabletTool> CInputManager::ensureTabletToolPresent(wlr_tablet_tool* pTool) {
+ if (pTool->data == nullptr) {
+ const auto PTOOL = m_vTabletTools.emplace_back(CTabletTool::create(pTool));
+ m_vHIDs.push_back(PTOOL);
- wlr_tablet_v2_tablet_pad_notify_ring(PPAD->wlrTabletPadV2, EVENT->ring, EVENT->position, EVENT->source == WLR_TABLET_PAD_RING_SOURCE_FINGER, EVENT->time_msec);
- },
- PNEWPAD, "Tablet Pad");
+ PTOOL->events.destroy.registerStaticListener(
+ [this](void* owner, std::any d) {
+ auto TOOL = ((CTabletTool*)owner)->self;
+ destroyTabletTool(TOOL.lock());
+ },
+ PTOOL.get());
+ }
- PNEWPAD->hyprListener_Attach.initCallback(
- &wlr_tablet_pad_from_input_device(pDevice)->events.strip,
- [](void* owner, void* data) {
- const auto TABLET = (wlr_tablet_tool*)data;
- const auto PPAD = (STabletPad*)owner;
+ return CTabletTool::fromWlr(pTool);
+}
- PPAD->pTabletParent = (STablet*)TABLET->data;
+void CInputManager::newTabletPad(wlr_input_device* pDevice) {
+ const auto PNEWPAD = m_vTabletPads.emplace_back(CTabletPad::create(wlr_tablet_pad_from_input_device(pDevice)));
+ m_vHIDs.push_back(PNEWPAD);
- if (!PPAD->pTabletParent)
- Debug::log(ERR, "tabletpad got attached to a nullptr tablet!! this might be bad.");
- },
- PNEWPAD, "Tablet Pad");
+ try {
+ PNEWPAD->hlName = deviceNameToInternalString(pDevice->name);
+ } catch (std::exception& e) {
+ Debug::log(ERR, "Pad had no name???"); // logic error
+ }
- PNEWPAD->hyprListener_Destroy.initCallback(
- &pDevice->events.destroy,
- [](void* owner, void* data) {
- const auto PPAD = (STabletPad*)owner;
+ // clang-format off
+ PNEWPAD->events.destroy.registerStaticListener([this](void* owner, std::any d) {
+ auto PAD = ((CTabletPad*)owner)->self;
+ destroyTabletPad(PAD.lock());
+ }, PNEWPAD.get());
- g_pInputManager->m_lTabletPads.remove(*PPAD);
+ PNEWPAD->padEvents.button.registerStaticListener([this](void* owner, std::any e) {
+ const auto E = std::any_cast<CTabletPad::SButtonEvent>(e);
+ const auto PPAD = ((CTabletPad*)owner)->self.lock();
- Debug::log(LOG, "Removed a tablet pad");
- },
- PNEWPAD, "Tablet Pad");
-}
+ PROTO::tablet->mode(PPAD, 0, E.mode, E.timeMs);
+ PROTO::tablet->buttonPad(PPAD, E.button, E.timeMs, E.down);
+ }, PNEWPAD.get());
-void CInputManager::focusTablet(STablet* pTab, wlr_tablet_tool* pTool, bool motion) {
- const auto PTOOL = g_pInputManager->ensureTabletToolPresent(pTool);
+ PNEWPAD->padEvents.strip.registerStaticListener([this](void* owner, std::any e) {
+ const auto E = std::any_cast<CTabletPad::SStripEvent>(e);
+ const auto PPAD = ((CTabletPad*)owner)->self.lock();
- if (const auto PWINDOW = g_pCompositor->m_pLastWindow.lock(); PWINDOW) {
- const auto CURSORPOS = g_pInputManager->getMouseCoordsInternal();
+ PROTO::tablet->strip(PPAD, E.strip, E.position, E.finger, E.timeMs);
+ }, PNEWPAD.get());
- if (PTOOL->pSurface != g_pInputManager->m_pLastMouseSurface)
- wlr_tablet_v2_tablet_tool_notify_proximity_out(PTOOL->wlrTabletToolV2);
+ PNEWPAD->padEvents.ring.registerStaticListener([this](void* owner, std::any e) {
+ const auto E = std::any_cast<CTabletPad::SRingEvent>(e);
+ const auto PPAD = ((CTabletPad*)owner)->self.lock();
- if (g_pInputManager->m_pLastMouseSurface) {
- PTOOL->pSurface = g_pCompositor->m_pLastFocus;
- wlr_tablet_v2_tablet_tool_notify_proximity_in(PTOOL->wlrTabletToolV2, pTab->wlrTabletV2, g_pInputManager->m_pLastMouseSurface);
- }
+ PROTO::tablet->ring(PPAD, E.ring, E.position, E.finger, E.timeMs);
+ }, PNEWPAD.get());
- if (motion) {
- auto local = CURSORPOS - PWINDOW->m_vRealPosition.goal();
+ PNEWPAD->padEvents.attach.registerStaticListener([this](void* owner, std::any e) {
+ const auto PPAD = ((CTabletPad*)owner)->self.lock();
+ const auto TOOL = std::any_cast<SP<CTabletTool>>(e);
- if (PWINDOW->m_bIsX11)
- local = local * PWINDOW->m_fX11SurfaceScaledBy;
+ PPAD->parent = TOOL;
+ }, PNEWPAD.get());
- wlr_tablet_v2_tablet_tool_notify_motion(PTOOL->wlrTabletToolV2, local.x, local.y);
- }
- } else {
- if (PTOOL->pSurface)
- wlr_tablet_v2_tablet_tool_notify_proximity_out(PTOOL->wlrTabletToolV2);
- }
+ // clang-format on
}
diff --git a/src/protocols/Tablet.cpp b/src/protocols/Tablet.cpp
new file mode 100644
index 00000000..b3ca76f5
--- /dev/null
+++ b/src/protocols/Tablet.cpp
@@ -0,0 +1,656 @@
+#include "Tablet.hpp"
+#include "../devices/Tablet.hpp"
+#include "../Compositor.hpp"
+#include <algorithm>
+
+#define LOGM PROTO::tablet->protoLog
+
+CTabletPadStripV2Resource::CTabletPadStripV2Resource(SP<CZwpTabletPadStripV2> resource_, uint32_t id_) : id(id_), resource(resource_) {
+ if (!good())
+ return;
+
+ resource->setDestroy([this](CZwpTabletPadStripV2* r) { PROTO::tablet->destroyResource(this); });
+ resource->setOnDestroy([this](CZwpTabletPadStripV2* r) { PROTO::tablet->destroyResource(this); });
+}
+
+bool CTabletPadStripV2Resource::good() {
+ return resource->resource();
+}
+
+CTabletPadRingV2Resource::CTabletPadRingV2Resource(SP<CZwpTabletPadRingV2> resource_, uint32_t id_) : id(id_), resource(resource_) {
+ if (!good())
+ return;
+
+ resource->setDestroy([this](CZwpTabletPadRingV2* r) { PROTO::tablet->destroyResource(this); });
+ resource->setOnDestroy([this](CZwpTabletPadRingV2* r) { PROTO::tablet->destroyResource(this); });
+}
+
+bool CTabletPadRingV2Resource::good() {
+ return resource->resource();
+}
+
+CTabletPadGroupV2Resource::CTabletPadGroupV2Resource(SP<CZwpTabletPadGroupV2> resource_, size_t idx_) : idx(idx_), resource(resource_) {
+ if (!good())
+ return;
+
+ resource->setDestroy([this](CZwpTabletPadGroupV2* r) { PROTO::tablet->destroyResource(this); });
+ resource->setOnDestroy([this](CZwpTabletPadGroupV2* r) { PROTO::tablet->destroyResource(this); });
+}
+
+bool CTabletPadGroupV2Resource::good() {
+ return resource->resource();
+}
+
+void CTabletPadGroupV2Resource::sendData(SP<CTabletPad> pad, wlr_tablet_pad_group* group) {
+ resource->sendModes(group->mode_count);
+
+ wl_array buttonArr;
+ wl_array_init(&buttonArr);
+ wl_array_add(&buttonArr, group->button_count * sizeof(int));
+ memcpy(buttonArr.data, group->buttons, group->button_count * sizeof(int));
+ resource->sendButtons(&buttonArr);
+ wl_array_release(&buttonArr);
+
+ for (size_t i = 0; i < group->strip_count; ++i) {
+ const auto RESOURCE =
+ PROTO::tablet->m_vStrips.emplace_back(makeShared<CTabletPadStripV2Resource>(makeShared<CZwpTabletPadStripV2>(resource->client(), resource->version(), 0), i));
+
+ if (!RESOURCE->good()) {
+ resource->noMemory();
+ PROTO::tablet->m_vStrips.pop_back();
+ return;
+ }
+
+ resource->sendStrip(RESOURCE->resource.get());
+ }
+
+ for (size_t i = 0; i < group->ring_count; ++i) {
+ const auto RESOURCE =
+ PROTO::tablet->m_vRings.emplace_back(makeShared<CTabletPadRingV2Resource>(makeShared<CZwpTabletPadRingV2>(resource->client(), resource->version(), 0), i));
+
+ if (!RESOURCE->good()) {
+ resource->noMemory();
+ PROTO::tablet->m_vRings.pop_back();
+ return;
+ }
+
+ resource->sendRing(RESOURCE->resource.get());
+ }
+
+ resource->sendDone();
+}
+
+CTabletPadV2Resource::CTabletPadV2Resource(SP<CZwpTabletPadV2> resource_, SP<CTabletPad> pad_, SP<CTabletSeat> seat_) : pad(pad_), seat(seat_), resource(resource_) {
+ if (!good())
+ return;
+
+ resource->setDestroy([this](CZwpTabletPadV2* r) { PROTO::tablet->destroyResource(this); });
+ resource->setOnDestroy([this](CZwpTabletPadV2* r) { PROTO::tablet->destroyResource(this); });
+}
+
+bool CTabletPadV2Resource::good() {
+ return resource->resource();
+}
+
+void CTabletPadV2Resource::sendData() {
+ // this is dodgy as fuck. I hate wl_array. it's expanded wl_array_for_each because C++ would complain about the implicit casts
+ const char** path_ptr;
+ for (path_ptr = (const char**)(&pad->wlr()->paths)->data; (const char*)path_ptr < ((const char*)(&pad->wlr()->paths)->data + (&pad->wlr()->paths)->size); (path_ptr)++) {
+ resource->sendPath(*path_ptr);
+ }
+
+ resource->sendButtons(pad->wlr()->button_count);
+
+ wlr_tablet_pad_group* group;
+ size_t i = 0;
+ wl_list_for_each(group, &pad->wlr()->groups, link) {
+ createGroup(group, i++);
+ }
+
+ resource->sendDone();
+}
+
+void CTabletPadV2Resource::createGroup(wlr_tablet_pad_group* group, size_t idx) {
+ const auto RESOURCE =
+ PROTO::tablet->m_vGroups.emplace_back(makeShared<CTabletPadGroupV2Resource>(makeShared<CZwpTabletPadGroupV2>(resource->client(), resource->version(), 0), idx));
+
+ if (!RESOURCE->good()) {
+ resource->noMemory();
+ PROTO::tablet->m_vGroups.pop_back();
+ return;
+ }
+
+ resource->sendGroup(RESOURCE->resource.get());
+
+ RESOURCE->sendData(pad.lock(), group);
+}
+
+CTabletV2Resource::CTabletV2Resource(SP<CZwpTabletV2> resource_, SP<CTablet> tablet_, SP<CTabletSeat> seat_) : tablet(tablet_), seat(seat_), resource(resource_) {
+ if (!good())
+ return;
+
+ resource->setDestroy([this](CZwpTabletV2* r) { PROTO::tablet->destroyResource(this); });
+ resource->setOnDestroy([this](CZwpTabletV2* r) { PROTO::tablet->destroyResource(this); });
+}
+
+bool CTabletV2Resource::good() {
+ return resource->resource();
+}
+
+void CTabletV2Resource::sendData() {
+ resource->sendName(tablet->deviceName.c_str());
+ resource->sendId(tablet->wlr()->usb_vendor_id, tablet->wlr()->usb_product_id);
+
+ // this is dodgy as fuck. I hate wl_array. it's expanded wl_array_for_each because C++ would complain about the implicit casts
+ const char** path_ptr;
+ for (path_ptr = (const char**)(&tablet->wlr()->paths)->data; (const char*)path_ptr < ((const char*)(&tablet->wlr()->paths)->data + (&tablet->wlr()->paths)->size);
+ (path_ptr)++) {
+ resource->sendPath(*path_ptr);
+ }
+
+ resource->sendDone();
+}
+
+CTabletToolV2Resource::CTabletToolV2Resource(SP<CZwpTabletToolV2> resource_, SP<CTabletTool> tool_, SP<CTabletSeat> seat_) : tool(tool_), seat(seat_), resource(resource_) {
+ if (!good())
+ return;
+
+ resource->setDestroy([this](CZwpTabletToolV2* r) { PROTO::tablet->destroyResource(this); });
+ resource->setOnDestroy([this](CZwpTabletToolV2* r) { PROTO::tablet->destroyResource(this); });
+
+ resource->setSetCursor([this](CZwpTabletToolV2* r, uint32_t serial, wl_resource* surf, int32_t hot_x, int32_t hot_y) {
+ wlr_seat_pointer_request_set_cursor_event e;
+ e.hotspot_x = hot_x;
+ e.hotspot_y = hot_y;
+ e.surface = surf ? wlr_surface_from_resource(surf) : nullptr;
+ e.serial = serial;
+ g_pInputManager->processMouseRequest(&e);
+ });
+}
+
+CTabletToolV2Resource::~CTabletToolV2Resource() {
+ if (frameSource)
+ wl_event_source_remove(frameSource);
+}
+
+bool CTabletToolV2Resource::good() {
+ return resource->resource();
+}
+
+void CTabletToolV2Resource::sendData() {
+ static auto WLR_TYPE_TO_PROTO = [](uint32_t wlr) -> zwpTabletToolV2Type {
+ switch (wlr) {
+ case WLR_TABLET_TOOL_TYPE_PEN: return ZWP_TABLET_TOOL_V2_TYPE_PEN;
+ case WLR_TABLET_TOOL_TYPE_ERASER: return ZWP_TABLET_TOOL_V2_TYPE_ERASER;
+ case WLR_TABLET_TOOL_TYPE_BRUSH: return ZWP_TABLET_TOOL_V2_TYPE_BRUSH;
+ case WLR_TABLET_TOOL_TYPE_PENCIL: return ZWP_TABLET_TOOL_V2_TYPE_PENCIL;
+ case WLR_TABLET_TOOL_TYPE_AIRBRUSH: return ZWP_TABLET_TOOL_V2_TYPE_AIRBRUSH;
+ case WLR_TABLET_TOOL_TYPE_MOUSE: return ZWP_TABLET_TOOL_V2_TYPE_MOUSE;
+ case WLR_TABLET_TOOL_TYPE_LENS: return ZWP_TABLET_TOOL_V2_TYPE_LENS;
+ default: ASSERT(false);
+ }
+ UNREACHABLE();
+ };
+
+ resource->sendType(WLR_TYPE_TO_PROTO(tool->wlr()->type));
+ resource->sendHardwareSerial(tool->wlr()->hardware_serial >> 32, tool->wlr()->hardware_serial & 0xFFFFFFFF);
+ resource->sendHardwareIdWacom(tool->wlr()->hardware_wacom >> 32, tool->wlr()->hardware_wacom & 0xFFFFFFFF);
+ if (tool->toolCapabilities & CTabletTool::eTabletToolCapabilities::HID_TABLET_TOOL_CAPABILITY_DISTANCE)
+ resource->sendCapability(zwpTabletToolV2Capability::ZWP_TABLET_TOOL_V2_CAPABILITY_DISTANCE);
+ if (tool->toolCapabilities & CTabletTool::eTabletToolCapabilities::HID_TABLET_TOOL_CAPABILITY_PRESSURE)
+ resource->sendCapability(zwpTabletToolV2Capability::ZWP_TABLET_TOOL_V2_CAPABILITY_PRESSURE);
+ if (tool->toolCapabilities & CTabletTool::eTabletToolCapabilities::HID_TABLET_TOOL_CAPABILITY_ROTATION)
+ resource->sendCapability(zwpTabletToolV2Capability::ZWP_TABLET_TOOL_V2_CAPABILITY_ROTATION);
+ if (tool->toolCapabilities & CTabletTool::eTabletToolCapabilities::HID_TABLET_TOOL_CAPABILITY_SLIDER)
+ resource->sendCapability(zwpTabletToolV2Capability::ZWP_TABLET_TOOL_V2_CAPABILITY_SLIDER);
+ if (tool->toolCapabilities & CTabletTool::eTabletToolCapabilities::HID_TABLET_TOOL_CAPABILITY_TILT)
+ resource->sendCapability(zwpTabletToolV2Capability::ZWP_TABLET_TOOL_V2_CAPABILITY_TILT);
+ if (tool->toolCapabilities & CTabletTool::eTabletToolCapabilities::HID_TABLET_TOOL_CAPABILITY_WHEEL)
+ resource->sendCapability(zwpTabletToolV2Capability::ZWP_TABLET_TOOL_V2_CAPABILITY_WHEEL);
+ resource->sendDone();
+}
+
+void CTabletToolV2Resource::queueFrame() {
+ if (frameSource)
+ return;
+
+ frameSource = wl_event_loop_add_idle(
+ g_pCompositor->m_sWLEventLoop, [](void* data) { ((CTabletToolV2Resource*)data)->sendFrame(false); }, this);
+}
+
+void CTabletToolV2Resource::sendFrame(bool removeSource) {
+ if (frameSource) {
+ if (removeSource)
+ wl_event_source_remove(frameSource);
+ frameSource = nullptr;
+ }
+
+ if (!current)
+ return;
+
+ timespec now;
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ resource->sendFrame(now.tv_sec * 1000 + now.tv_nsec / 1000000);
+}
+
+CTabletSeat::CTabletSeat(SP<CZwpTabletSeatV2> resource_) : resource(resource_) {
+ if (!good())
+ return;
+
+ resource->setDestroy([this](CZwpTabletSeatV2* r) { PROTO::tablet->destroyResource(this); });
+ resource->setOnDestroy([this](CZwpTabletSeatV2* r) { PROTO::tablet->destroyResource(this); });
+}
+
+bool CTabletSeat::good() {
+ return resource->resource();
+}
+
+void CTabletSeat::sendTool(SP<CTabletTool> tool) {
+ const auto RESOURCE =
+ PROTO::tablet->m_vTools.emplace_back(makeShared<CTabletToolV2Resource>(makeShared<CZwpTabletToolV2>(resource->client(), resource->version(), 0), tool, self.lock()));
+
+ if (!RESOURCE->good()) {
+ resource->noMemory();
+ PROTO::tablet->m_vTools.pop_back();
+ return;
+ }
+
+ resource->sendToolAdded(RESOURCE->resource.get());
+
+ RESOURCE->sendData();
+ tools.push_back(RESOURCE);
+}
+
+void CTabletSeat::sendPad(SP<CTabletPad> pad) {
+ const auto RESOURCE =
+ PROTO::tablet->m_vPads.emplace_back(makeShared<CTabletPadV2Resource>(makeShared<CZwpTabletPadV2>(resource->client(), resource->version(), 0), pad, self.lock()));
+
+ if (!RESOURCE->good()) {
+ resource->noMemory();
+ PROTO::tablet->m_vPads.pop_back();
+ return;
+ }
+
+ resource->sendPadAdded(RESOURCE->resource.get());
+
+ RESOURCE->sendData();
+ pads.push_back(RESOURCE);
+}
+
+void CTabletSeat::sendTablet(SP<CTablet> tablet) {
+ const auto RESOURCE =
+ PROTO::tablet->m_vTablets.emplace_back(makeShared<CTabletV2Resource>(makeShared<CZwpTabletV2>(resource->client(), resource->version(), 0), tablet, self.lock()));
+
+ if (!RESOURCE->good()) {
+ resource->noMemory();
+ PROTO::tablet->m_vTablets.pop_back();
+ return;
+ }
+
+ resource->sendTabletAdded(RESOURCE->resource.get());
+
+ RESOURCE->sendData();
+ tablets.push_back(RESOURCE);
+}
+
+void CTabletSeat::sendData() {
+ for (auto& tw : PROTO::tablet->tablets) {
+ if (tw.expired())
+ continue;
+
+ sendTablet(tw.lock());
+ }
+
+ for (auto& tw : PROTO::tablet->tools) {
+ if (tw.expired())
+ continue;
+
+ sendTool(tw.lock());
+ }
+
+ for (auto& tw : PROTO::tablet->pads) {
+ if (tw.expired())
+ continue;
+
+ sendPad(tw.lock());
+ }
+}
+
+CTabletV2Protocol::CTabletV2Protocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) {
+ ;
+}
+
+void CTabletV2Protocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) {
+ const auto RESOURCE = m_vManagers.emplace_back(std::make_unique<CZwpTabletManagerV2>(client, ver, id)).get();
+ RESOURCE->setOnDestroy([this](CZwpTabletManagerV2* p) { this->onManagerResourceDestroy(p->resource()); });
+
+ RESOURCE->setDestroy([this](CZwpTabletManagerV2* pMgr) { this->onManagerResourceDestroy(pMgr->resource()); });
+ RESOURCE->setGetTabletSeat([this](CZwpTabletManagerV2* pMgr, uint32_t id, wl_resource* seat) { this->onGetSeat(pMgr, id, seat); });
+}
+
+void CTabletV2Protocol::onManagerResourceDestroy(wl_resource* res) {
+ std::erase_if(m_vManagers, [&](const auto& other) { return other->resource() == res; });
+}
+
+void CTabletV2Protocol::destroyResource(CTabletSeat* resource) {
+ std::erase_if(m_vSeats, [&](const auto& other) { return other.get() == resource; });
+}
+
+void CTabletV2Protocol::destroyResource(CTabletToolV2Resource* resource) {
+ std::erase_if(m_vTools, [&](const auto& other) { return other.get() == resource; });
+}
+
+void CTabletV2Protocol::destroyResource(CTabletV2Resource* resource) {
+ std::erase_if(m_vTablets, [&](const auto& other) { return other.get() == resource; });
+}
+
+void CTabletV2Protocol::destroyResource(CTabletPadV2Resource* resource) {
+ std::erase_if(m_vPads, [&](const auto& other) { return other.get() == resource; });
+}
+
+void CTabletV2Protocol::destroyResource(CTabletPadGroupV2Resource* resource) {
+ std::erase_if(m_vGroups, [&](const auto& other) { return other.get() == resource; });
+}
+
+void CTabletV2Protocol::destroyResource(CTabletPadRingV2Resource* resource) {
+ std::erase_if(m_vRings, [&](const auto& other) { return other.get() == resource; });
+}
+
+void CTabletV2Protocol::destroyResource(CTabletPadStripV2Resource* resource) {
+ std::erase_if(m_vStrips, [&](const auto& other) { return other.get() == resource; });
+}
+
+void CTabletV2Protocol::onGetSeat(CZwpTabletManagerV2* pMgr, uint32_t id, wl_resource* seat) {
+ const auto RESOURCE = m_vSeats.emplace_back(makeShared<CTabletSeat>(makeShared<CZwpTabletSeatV2>(pMgr->client(), pMgr->version(), id)));
+
+ if (!RESOURCE->good()) {
+ pMgr->noMemory();
+ m_vSeats.pop_back();
+ return;
+ }
+
+ RESOURCE->self = RESOURCE;
+ RESOURCE->sendData();
+}
+
+void CTabletV2Protocol::registerDevice(SP<CTablet> tablet) {
+ for (auto& s : m_vSeats) {
+ s->sendTablet(tablet);
+ }
+
+ tablets.push_back(tablet);
+}
+
+void CTabletV2Protocol::registerDevice(SP<CTabletTool> tool) {
+ for (auto& s : m_vSeats) {
+ s->sendTool(tool);
+ }
+
+ tools.push_back(tool);
+}
+
+void CTabletV2Protocol::registerDevice(SP<CTabletPad> pad) {
+ for (auto& s : m_vSeats) {
+ s->sendPad(pad);
+ }
+
+ pads.push_back(pad);
+}
+
+void CTabletV2Protocol::unregisterDevice(SP<CTablet> tablet) {
+ for (auto& t : m_vTablets) {
+ if (t->tablet == tablet) {
+ t->resource->sendRemoved();
+ t->inert = true;
+ }
+ }
+ std::erase_if(tablets, [tablet](const auto& e) { return e.expired() || e == tablet; });
+}
+
+void CTabletV2Protocol::unregisterDevice(SP<CTabletTool> tool) {
+ for (auto& t : m_vTools) {
+ if (t->tool == tool) {
+ t->resource->sendRemoved();
+ t->inert = true;
+ }
+ }
+ std::erase_if(tools, [tool](const auto& e) { return e.expired() || e == tool; });
+}
+
+void CTabletV2Protocol::unregisterDevice(SP<CTabletPad> pad) {
+ for (auto& t : m_vPads) {
+ if (t->pad == pad) {
+ t->resource->sendRemoved();
+ t->inert = true;
+ }
+ }
+ std::erase_if(pads, [pad](const auto& e) { return e.expired() || e == pad; });
+}
+
+void CTabletV2Protocol::recheckRegisteredDevices() {
+ std::erase_if(tablets, [](const auto& e) { return e.expired(); });
+ std::erase_if(tools, [](const auto& e) { return e.expired(); });
+ std::erase_if(pads, [](const auto& e) { return e.expired(); });
+
+ // now we need to send removed events
+ for (auto& t : m_vTablets) {
+ if (!t->tablet.expired() || t->inert)
+ continue;
+
+ t->resource->sendRemoved();
+ t->inert = true;
+ }
+
+ for (auto& t : m_vTools) {
+ if (!t->tool.expired() || t->inert)
+ continue;
+
+ if (t->current) {
+ t->resource->sendProximityOut();
+ t->sendFrame();
+ t->lastSurf = nullptr;
+ }
+
+ t->resource->sendRemoved();
+ t->inert = true;
+ }
+
+ for (auto& t : m_vPads) {
+ if (!t->pad.expired() || t->inert)
+ continue;
+
+ t->resource->sendRemoved();
+ t->inert = true;
+ }
+}
+
+void CTabletV2Protocol::pressure(SP<CTabletTool> tool, double value) {
+ for (auto& t : m_vTools) {
+ if (t->tool != tool || !t->current)
+ continue;
+
+ t->resource->sendPressure(std::clamp(value * 65535, 0.0, 65535.0));
+ t->queueFrame();
+ }
+}
+
+void CTabletV2Protocol::distance(SP<CTabletTool> tool, double value) {
+ for (auto& t : m_vTools) {
+ if (t->tool != tool || !t->current)
+ continue;
+
+ t->resource->sendDistance(std::clamp(value * 65535, 0.0, 65535.0));
+ t->queueFrame();
+ }
+}
+
+void CTabletV2Protocol::rotation(SP<CTabletTool> tool, double value) {
+ for (auto& t : m_vTools) {
+ if (t->tool != tool || !t->current)
+ continue;
+
+ t->resource->sendRotation(wl_fixed_from_double(value));
+ t->queueFrame();
+ }
+}
+
+void CTabletV2Protocol::slider(SP<CTabletTool> tool, double value) {
+ for (auto& t : m_vTools) {
+ if (t->tool != tool || !t->current)
+ continue;
+
+ t->resource->sendSlider(std::clamp(value * 65535, -65535.0, 65535.0));
+ t->queueFrame();
+ }
+}
+
+void CTabletV2Protocol::wheel(SP<CTabletTool> tool, double value) {
+ for (auto& t : m_vTools) {
+ if (t->tool != tool || !t->current)
+ continue;
+
+ t->resource->sendWheel(wl_fixed_from_double(value), 0);
+ t->queueFrame();
+ }
+}
+
+void CTabletV2Protocol::tilt(SP<CTabletTool> tool, const Vector2D& value) {
+ for (auto& t : m_vTools) {
+ if (t->tool != tool || !t->current)
+ continue;
+
+ t->resource->sendTilt(wl_fixed_from_double(value.x), wl_fixed_from_double(value.y));
+ t->queueFrame();
+ }
+}
+
+void CTabletV2Protocol::up(SP<CTabletTool> tool) {
+ for (auto& t : m_vTools) {
+ if (t->tool != tool || !t->current)
+ continue;
+
+ t->resource->sendUp();
+ t->queueFrame();
+ }
+}
+
+void CTabletV2Protocol::down(SP<CTabletTool> tool) {
+ for (auto& t : m_vTools) {
+ if (t->tool != tool || !t->current)
+ continue;
+
+ auto serial = wlr_seat_client_next_serial(wlr_seat_client_for_wl_client(g_pCompositor->m_sSeat.seat, t->resource->client()));
+ t->resource->sendDown(serial);
+ t->queueFrame();
+ }
+}
+
+void CTabletV2Protocol::proximityIn(SP<CTabletTool> tool, SP<CTablet> tablet, wlr_surface* surf) {
+ proximityOut(tool);
+ const auto CLIENT = wl_resource_get_client(surf->resource);
+
+ SP<CTabletToolV2Resource> toolResource;
+ SP<CTabletV2Resource> tabletResource;
+
+ for (auto& t : m_vTools) {
+ if (t->tool != tool || t->resource->client() != CLIENT)
+ continue;
+
+ if (t->seat.expired()) {
+ LOGM(ERR, "proximityIn on a tool without a seat parent");
+ return;
+ }
+
+ if (t->lastSurf == surf)
+ return;
+
+ toolResource = t;
+
+ for (auto& tab : m_vTablets) {
+ if (tab->tablet != tablet)
+ continue;
+
+ if (tab->seat != t->seat || !tab->seat)
+ continue;
+
+ tabletResource = tab;
+ break;
+ }
+ }
+
+ if (!tabletResource || !toolResource) {
+ LOGM(ERR, "proximityIn on a tool and tablet without valid resource(s)??");
+ return;
+ }
+
+ toolResource->current = true;
+ toolResource->lastSurf = surf;
+
+ auto serial = wlr_seat_client_next_serial(wlr_seat_client_for_wl_client(g_pCompositor->m_sSeat.seat, toolResource->resource->client()));
+ toolResource->resource->sendProximityIn(serial, tabletResource->resource.get(), surf->resource);
+ toolResource->queueFrame();
+
+ LOGM(ERR, "proximityIn: found no resource to send enter");
+}
+
+void CTabletV2Protocol::proximityOut(SP<CTabletTool> tool) {
+ for (auto& t : m_vTools) {
+ if (t->tool != tool || !t->current)
+ continue;
+
+ t->current = false;
+ t->lastSurf = nullptr;
+ t->resource->sendProximityOut();
+ t->sendFrame();
+ }
+}
+
+void CTabletV2Protocol::buttonTool(SP<CTabletTool> tool, uint32_t button, uint32_t state) {
+ for (auto& t : m_vTools) {
+ if (t->tool != tool || !t->current)
+ continue;
+
+ auto serial = wlr_seat_client_next_serial(wlr_seat_client_for_wl_client(g_pCompositor->m_sSeat.seat, t->resource->client()));
+ t->resource->sendButton(serial, button, (zwpTabletToolV2ButtonState)state);
+ t->queueFrame();
+ }
+}
+
+void CTabletV2Protocol::motion(SP<CTabletTool> tool, const Vector2D& value) {
+ for (auto& t : m_vTools) {
+ if (t->tool != tool || !t->current)
+ continue;
+
+ t->resource->sendMotion(wl_fixed_from_double(value.x), wl_fixed_from_double(value.y));
+ t->queueFrame();
+ }
+}
+
+void CTabletV2Protocol::mode(SP<CTabletPad> pad, uint32_t group, uint32_t mode, uint32_t timeMs) {
+ for (auto& t : m_vPads) {
+ if (t->pad != pad)
+ continue;
+ if (t->groups.size() <= group) {
+ LOGM(ERR, "BUG THIS: group >= t->groups.size()");
+ return;
+ }
+ auto serial = wlr_seat_client_next_serial(wlr_seat_client_for_wl_client(g_pCompositor->m_sSeat.seat, t->resource->client()));
+ t->groups.at(group)->resource->sendModeSwitch(timeMs, serial, mode);
+ }
+}
+
+void CTabletV2Protocol::buttonPad(SP<CTabletPad> pad, uint32_t button, uint32_t timeMs, uint32_t state) {
+ for (auto& t : m_vPads) {
+ if (t->pad != pad)
+ continue;
+ t->resource->sendButton(timeMs, button, zwpTabletToolV2ButtonState{state});
+ }
+}
+
+void CTabletV2Protocol::strip(SP<CTabletPad> pad, uint32_t strip, double position, bool finger, uint32_t timeMs) {
+ LOGM(ERR, "FIXME: STUB: CTabletV2Protocol::strip not implemented");
+}
+
+void CTabletV2Protocol::ring(SP<CTabletPad> pad, uint32_t ring, double position, bool finger, uint32_t timeMs) {
+ LOGM(ERR, "FIXME: STUB: CTabletV2Protocol::ring not implemented");
+}
diff --git a/src/protocols/Tablet.hpp b/src/protocols/Tablet.hpp
new file mode 100644
index 00000000..74a45c63
--- /dev/null
+++ b/src/protocols/Tablet.hpp
@@ -0,0 +1,236 @@
+#pragma once
+
+#include <memory>
+#include <vector>
+#include <cstdint>
+#include "WaylandProtocol.hpp"
+#include "tablet-v2.hpp"
+#include "../helpers/Vector2D.hpp"
+
+class CTablet;
+class CTabletTool;
+class CTabletPad;
+class CEventLoopTimer;
+class CTabletSeat;
+
+class CTabletPadStripV2Resource {
+ public:
+ CTabletPadStripV2Resource(SP<CZwpTabletPadStripV2> resource_, uint32_t id);
+
+ bool good();
+
+ uint32_t id = 0;
+
+ private:
+ SP<CZwpTabletPadStripV2> resource;
+
+ friend class CTabletSeat;
+ friend class CTabletPadGroupV2Resource;
+ friend class CTabletV2Protocol;
+};
+
+class CTabletPadRingV2Resource {
+ public:
+ CTabletPadRingV2Resource(SP<CZwpTabletPadRingV2> resource_, uint32_t id);
+
+ bool good();
+
+ uint32_t id = 0;
+
+ private:
+ SP<CZwpTabletPadRingV2> resource;
+
+ friend class CTabletSeat;
+ friend class CTabletPadGroupV2Resource;
+ friend class CTabletV2Protocol;
+};
+
+class CTabletPadGroupV2Resource {
+ public:
+ CTabletPadGroupV2Resource(SP<CZwpTabletPadGroupV2> resource_, size_t idx);
+
+ bool good();
+ void sendData(SP<CTabletPad> pad, wlr_tablet_pad_group* group);
+
+ std::vector<WP<CTabletPadRingV2Resource>> rings;
+ std::vector<WP<CTabletPadStripV2Resource>> strips;
+
+ size_t idx = 0;
+
+ private:
+ SP<CZwpTabletPadGroupV2> resource;
+
+ friend class CTabletSeat;
+ friend class CTabletPadV2Resource;
+ friend class CTabletV2Protocol;
+};
+
+class CTabletPadV2Resource {
+ public:
+ CTabletPadV2Resource(SP<CZwpTabletPadV2> resource_, SP<CTabletPad> pad_, SP<CTabletSeat> seat_);
+
+ bool good();
+ void sendData();
+
+ std::vector<WP<CTabletPadGroupV2Resource>> groups;
+
+ WP<CTabletPad> pad;
+ WP<CTabletSeat> seat;
+
+ bool inert = false; // removed was sent
+
+ private:
+ SP<CZwpTabletPadV2> resource;
+
+ void createGroup(wlr_tablet_pad_group* group, size_t idx);
+
+ friend class CTabletSeat;
+ friend class CTabletV2Protocol;
+};
+
+class CTabletV2Resource {
+ public:
+ CTabletV2Resource(SP<CZwpTabletV2> resource_, SP<CTablet> tablet_, SP<CTabletSeat> seat_);
+
+ bool good();
+ void sendData();
+
+ WP<CTablet> tablet;
+ WP<CTabletSeat> seat;
+
+ bool inert = false; // removed was sent
+
+ private:
+ SP<CZwpTabletV2> resource;
+
+ friend class CTabletSeat;
+ friend class CTabletV2Protocol;
+};
+
+class CTabletToolV2Resource {
+ public:
+ CTabletToolV2Resource(SP<CZwpTabletToolV2> resource_, SP<CTabletTool> tool_, SP<CTabletSeat> seat_);
+ ~CTabletToolV2Resource();
+
+ bool good();
+ void sendData();
+ void queueFrame();
+ void sendFrame(bool removeSource = true);
+
+ bool current = false;
+ wlr_surface* lastSurf = nullptr; // READ-ONLY
+
+ WP<CTabletTool> tool;
+ WP<CTabletSeat> seat;
+ wl_event_source* frameSource = nullptr;
+
+ bool inert = false; // removed was sent
+
+ private:
+ SP<CZwpTabletToolV2> resource;
+
+ friend class CTabletSeat;
+ friend class CTabletV2Protocol;
+};
+
+class CTabletSeat {
+ public:
+ CTabletSeat(SP<CZwpTabletSeatV2> resource_);
+
+ bool good();
+ void sendData();
+
+ std::vector<WP<CTabletToolV2Resource>> tools;
+ std::vector<WP<CTabletPadV2Resource>> pads;
+ std::vector<WP<CTabletV2Resource>> tablets;
+
+ void sendTool(SP<CTabletTool> tool);
+ void sendPad(SP<CTabletPad> pad);
+ void sendTablet(SP<CTablet> tablet);
+
+ private:
+ SP<CZwpTabletSeatV2> resource;
+ WP<CTabletSeat> self;
+
+ friend class CTabletV2Protocol;
+};
+
+class CTabletV2Protocol : public IWaylandProtocol {
+ public:
+ CTabletV2Protocol(const wl_interface* iface, const int& ver, const std::string& name);
+
+ virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id);
+
+ void registerDevice(SP<CTablet> tablet);
+ void registerDevice(SP<CTabletTool> tool);
+ void registerDevice(SP<CTabletPad> pad);
+
+ void unregisterDevice(SP<CTablet> tablet);
+ void unregisterDevice(SP<CTabletTool> tool);
+ void unregisterDevice(SP<CTabletPad> pad);
+
+ void recheckRegisteredDevices();
+
+ // Tablet tool events
+ void pressure(SP<CTabletTool> tool, double value);
+ void distance(SP<CTabletTool> tool, double value);
+ void rotation(SP<CTabletTool> tool, double value);
+ void slider(SP<CTabletTool> tool, double value);
+ void wheel(SP<CTabletTool> tool, double value);
+ void tilt(SP<CTabletTool> tool, const Vector2D& value);
+ void up(SP<CTabletTool> tool);
+ void down(SP<CTabletTool> tool);
+ void proximityIn(SP<CTabletTool> tool, SP<CTablet> tablet, wlr_surface* surf);
+ void proximityOut(SP<CTabletTool> tool);
+ void buttonTool(SP<CTabletTool> tool, uint32_t button, uint32_t state);
+ void motion(SP<CTabletTool> tool, const Vector2D& value);
+
+ // Tablet pad events
+ void mode(SP<CTabletPad> pad, uint32_t group, uint32_t mode, uint32_t timeMs);
+ void buttonPad(SP<CTabletPad> pad, uint32_t button, uint32_t timeMs, uint32_t state);
+ void strip(SP<CTabletPad> pad, uint32_t strip, double position, bool finger, uint32_t timeMs);
+ void ring(SP<CTabletPad> pad, uint32_t ring, double position, bool finger, uint32_t timeMs);
+
+ private:
+ void onManagerResourceDestroy(wl_resource* res);
+ void destroyResource(CTabletSeat* resource);
+ void destroyResource(CTabletToolV2Resource* resource);
+ void destroyResource(CTabletV2Resource* resource);
+ void destroyResource(CTabletPadV2Resource* resource);
+ void destroyResource(CTabletPadGroupV2Resource* resource);
+ void destroyResource(CTabletPadRingV2Resource* resource);
+ void destroyResource(CTabletPadStripV2Resource* resource);
+ void onGetSeat(CZwpTabletManagerV2* pMgr, uint32_t id, wl_resource* seat);
+
+ //
+ std::vector<UP<CZwpTabletManagerV2>> m_vManagers;
+ std::vector<SP<CTabletSeat>> m_vSeats;
+ std::vector<SP<CTabletToolV2Resource>> m_vTools;
+ std::vector<SP<CTabletV2Resource>> m_vTablets;
+ std::vector<SP<CTabletPadV2Resource>> m_vPads;
+ std::vector<SP<CTabletPadGroupV2Resource>> m_vGroups;
+ std::vector<SP<CTabletPadRingV2Resource>> m_vRings;
+ std::vector<SP<CTabletPadStripV2Resource>> m_vStrips;
+
+ // registered
+ std::vector<WP<CTablet>> tablets;
+ std::vector<WP<CTabletTool>> tools;
+ std::vector<WP<CTabletPad>> pads;
+
+ // FIXME: rings and strips are broken, I don't understand how this shit works.
+ // It's 2am.
+ SP<CTabletPadRingV2Resource> ringForID(SP<CTabletPad> pad, uint32_t id);
+ SP<CTabletPadStripV2Resource> stripForID(SP<CTabletPad> pad, uint32_t id);
+
+ friend class CTabletSeat;
+ friend class CTabletToolV2Resource;
+ friend class CTabletV2Resource;
+ friend class CTabletPadV2Resource;
+ friend class CTabletPadGroupV2Resource;
+ friend class CTabletPadRingV2Resource;
+ friend class CTabletPadStripV2Resource;
+};
+
+namespace PROTO {
+ inline UP<CTabletV2Protocol> tablet;
+};