2023-12-01 22:12:00 +01:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
|
|
|
|
#include <assert.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <linux/input-event-codes.h>
|
|
|
|
|
#include <wlr/types/wlr_tablet_pad.h>
|
|
|
|
|
#include <wlr/types/wlr_tablet_tool.h>
|
|
|
|
|
#include <wlr/util/log.h>
|
2024-04-20 11:50:33 +02:00
|
|
|
#include <wlr/types/wlr_scene.h>
|
2023-12-01 22:12:00 +01:00
|
|
|
#include "common/macros.h"
|
|
|
|
|
#include "common/mem.h"
|
2024-04-20 11:50:33 +02:00
|
|
|
#include "common/scene-helpers.h"
|
2023-12-01 22:12:00 +01:00
|
|
|
#include "config/rcxml.h"
|
2024-04-20 11:50:33 +02:00
|
|
|
#include "config/mousebind.h"
|
2023-12-01 22:12:00 +01:00
|
|
|
#include "input/cursor.h"
|
2023-12-29 10:10:41 +01:00
|
|
|
#include "input/tablet.h"
|
2024-04-20 11:50:33 +02:00
|
|
|
#include "input/tablet-tool.h"
|
2024-06-04 20:21:12 +02:00
|
|
|
#include "input/tablet-pad.h"
|
2024-04-20 11:40:23 +02:00
|
|
|
#include "labwc.h"
|
2024-04-20 11:50:33 +02:00
|
|
|
#include "idle.h"
|
|
|
|
|
#include "action.h"
|
2023-12-01 22:12:00 +01:00
|
|
|
|
2024-07-05 06:17:29 +02:00
|
|
|
static enum motion
|
2024-07-05 06:17:39 +02:00
|
|
|
tool_motion_mode(enum motion motion, struct wlr_tablet_tool *tool)
|
2024-05-07 19:35:44 +02:00
|
|
|
{
|
2024-07-05 06:09:37 +02:00
|
|
|
/*
|
|
|
|
|
* Absolute positioning doesn't make sense
|
|
|
|
|
* for tablet mouses and lenses.
|
|
|
|
|
*/
|
2024-05-07 19:35:44 +02:00
|
|
|
switch (tool->type) {
|
|
|
|
|
case WLR_TABLET_TOOL_TYPE_MOUSE:
|
|
|
|
|
case WLR_TABLET_TOOL_TYPE_LENS:
|
2024-07-05 06:09:37 +02:00
|
|
|
return LAB_TABLET_MOTION_RELATIVE;
|
2024-05-07 19:35:44 +02:00
|
|
|
default:
|
2024-07-05 06:17:39 +02:00
|
|
|
return motion;
|
2024-05-07 19:35:44 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-01 18:26:44 +01:00
|
|
|
static void
|
|
|
|
|
adjust_for_tablet_area(double tablet_width, double tablet_height,
|
|
|
|
|
struct wlr_fbox box, double *x, double *y)
|
|
|
|
|
{
|
|
|
|
|
if ((!box.x && !box.y && !box.width && !box.height)
|
|
|
|
|
|| !tablet_width || !tablet_height) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!box.width) {
|
|
|
|
|
box.width = tablet_width - box.x;
|
|
|
|
|
}
|
|
|
|
|
if (!box.height) {
|
|
|
|
|
box.height = tablet_height - box.y;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (box.x + box.width <= tablet_width) {
|
|
|
|
|
const double max_x = 1;
|
|
|
|
|
double width_offset = max_x * box.x / tablet_width;
|
|
|
|
|
*x = (*x - width_offset) * tablet_width / box.width;
|
|
|
|
|
}
|
|
|
|
|
if (box.y + box.height <= tablet_height) {
|
|
|
|
|
const double max_y = 1;
|
|
|
|
|
double height_offset = max_y * box.y / tablet_height;
|
|
|
|
|
*y = (*y - height_offset) * tablet_height / box.height;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-29 22:38:05 +01:00
|
|
|
static void
|
|
|
|
|
adjust_for_rotation(enum rotation rotation, double *x, double *y)
|
|
|
|
|
{
|
|
|
|
|
double tmp;
|
|
|
|
|
switch (rotation) {
|
|
|
|
|
case LAB_ROTATE_NONE:
|
|
|
|
|
break;
|
|
|
|
|
case LAB_ROTATE_90:
|
|
|
|
|
tmp = *x;
|
|
|
|
|
*x = 1.0 - *y;
|
|
|
|
|
*y = tmp;
|
|
|
|
|
break;
|
|
|
|
|
case LAB_ROTATE_180:
|
|
|
|
|
*x = 1.0 - *x;
|
|
|
|
|
*y = 1.0 - *y;
|
|
|
|
|
break;
|
|
|
|
|
case LAB_ROTATE_270:
|
|
|
|
|
tmp = *x;
|
|
|
|
|
*x = *y;
|
|
|
|
|
*y = 1.0 - tmp;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-05 06:05:35 +02:00
|
|
|
static void
|
|
|
|
|
adjust_for_rotation_relative(enum rotation rotation, double *dx, double *dy)
|
|
|
|
|
{
|
|
|
|
|
double tmp;
|
|
|
|
|
switch (rotation) {
|
|
|
|
|
case LAB_ROTATE_NONE:
|
|
|
|
|
break;
|
|
|
|
|
case LAB_ROTATE_90:
|
|
|
|
|
tmp = *dx;
|
|
|
|
|
*dx = -*dy;
|
|
|
|
|
*dy = tmp;
|
|
|
|
|
break;
|
|
|
|
|
case LAB_ROTATE_180:
|
|
|
|
|
*dx = -*dx;
|
|
|
|
|
*dy = -*dy;
|
|
|
|
|
break;
|
|
|
|
|
case LAB_ROTATE_270:
|
|
|
|
|
tmp = *dx;
|
|
|
|
|
*dx = *dy;
|
|
|
|
|
*dy = -tmp;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-05 06:33:26 +02:00
|
|
|
static void
|
|
|
|
|
adjust_for_motion_sensitivity(double motion_sensitivity, double *dx, double *dy)
|
|
|
|
|
{
|
|
|
|
|
*dx = *dx * motion_sensitivity;
|
|
|
|
|
*dy = *dy * motion_sensitivity;
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-20 11:50:33 +02:00
|
|
|
static struct wlr_surface*
|
2024-07-05 06:09:37 +02:00
|
|
|
tablet_get_coords(struct drawing_tablet *tablet, double *x, double *y, double *dx, double *dy)
|
2024-04-20 11:50:33 +02:00
|
|
|
{
|
|
|
|
|
*x = tablet->x;
|
|
|
|
|
*y = tablet->y;
|
2024-07-05 06:09:37 +02:00
|
|
|
*dx = tablet->dx;
|
|
|
|
|
*dy = tablet->dy;
|
2024-04-20 11:50:33 +02:00
|
|
|
adjust_for_tablet_area(tablet->tablet->width_mm, tablet->tablet->height_mm,
|
|
|
|
|
rc.tablet.box, x, y);
|
|
|
|
|
adjust_for_rotation(rc.tablet.rotation, x, y);
|
2024-07-05 06:09:37 +02:00
|
|
|
adjust_for_rotation_relative(rc.tablet.rotation, dx, dy);
|
2024-07-05 06:33:26 +02:00
|
|
|
adjust_for_motion_sensitivity(rc.tablet_tool.relative_motion_sensitivity, dx, dy);
|
2024-04-20 11:50:33 +02:00
|
|
|
|
2024-04-22 17:21:55 +02:00
|
|
|
if (rc.tablet.force_mouse_emulation
|
|
|
|
|
|| !tablet->tablet_v2) {
|
2024-04-20 11:50:33 +02:00
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-05 06:09:37 +02:00
|
|
|
/* convert coordinates: first [0, 1] => layout, then layout => surface */
|
|
|
|
|
|
|
|
|
|
/* initialize here to avoid a maybe-uninitialized compiler warning */
|
|
|
|
|
double lx = -1, ly = -1;
|
|
|
|
|
switch (tablet->motion_mode) {
|
|
|
|
|
case LAB_TABLET_MOTION_ABSOLUTE:
|
|
|
|
|
wlr_cursor_absolute_to_layout_coords(tablet->seat->cursor,
|
|
|
|
|
tablet->wlr_input_device, *x, *y, &lx, &ly);
|
|
|
|
|
break;
|
|
|
|
|
case LAB_TABLET_MOTION_RELATIVE:
|
|
|
|
|
/*
|
|
|
|
|
* Deltas dx,dy will be directly passed into wlr_cursor_move,
|
|
|
|
|
* so we can add those directly here to determine our future
|
|
|
|
|
* position.
|
|
|
|
|
*/
|
|
|
|
|
lx = tablet->seat->cursor->x + *dx;
|
|
|
|
|
ly = tablet->seat->cursor->y + *dy;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2024-04-20 11:50:33 +02:00
|
|
|
|
|
|
|
|
double sx, sy;
|
|
|
|
|
struct wlr_scene_node *node =
|
|
|
|
|
wlr_scene_node_at(&tablet->seat->server->scene->tree.node, lx, ly, &sx, &sy);
|
|
|
|
|
|
2024-07-05 06:09:37 +02:00
|
|
|
/* find the surface and return it if it accepts tablet events */
|
2024-04-20 11:50:33 +02:00
|
|
|
struct wlr_surface *surface = lab_wlr_surface_from_node(node);
|
|
|
|
|
|
|
|
|
|
if (surface && !wlr_surface_accepts_tablet_v2(tablet->tablet_v2, surface)) {
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
return surface;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
notify_motion(struct drawing_tablet *tablet, struct drawing_tablet_tool *tool,
|
2024-07-05 06:09:37 +02:00
|
|
|
struct wlr_surface *surface, double x, double y, double dx, double dy,
|
|
|
|
|
uint32_t time)
|
2024-04-20 11:50:33 +02:00
|
|
|
{
|
|
|
|
|
idle_manager_notify_activity(tool->seat->seat);
|
|
|
|
|
|
2024-05-20 23:17:33 +02:00
|
|
|
bool enter_surface = false;
|
2024-05-25 08:58:53 +02:00
|
|
|
/* Postpone proximity-in on a new surface when the tip is down */
|
|
|
|
|
if (surface != tool->tool_v2->focused_surface && !tool->tool_v2->is_down) {
|
2024-05-20 23:17:33 +02:00
|
|
|
enter_surface = true;
|
2024-04-20 11:50:33 +02:00
|
|
|
wlr_tablet_v2_tablet_tool_notify_proximity_in(tool->tool_v2,
|
|
|
|
|
tablet->tablet_v2, surface);
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-05 06:09:37 +02:00
|
|
|
switch (tablet->motion_mode) {
|
|
|
|
|
case LAB_TABLET_MOTION_ABSOLUTE:
|
|
|
|
|
wlr_cursor_warp_absolute(tablet->seat->cursor,
|
|
|
|
|
tablet->wlr_input_device, x, y);
|
|
|
|
|
break;
|
|
|
|
|
case LAB_TABLET_MOTION_RELATIVE:
|
|
|
|
|
wlr_cursor_move(tablet->seat->cursor,
|
|
|
|
|
tablet->wlr_input_device, dx, dy);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2024-04-20 11:50:33 +02:00
|
|
|
|
|
|
|
|
double sx, sy;
|
|
|
|
|
bool notify = cursor_process_motion(tablet->seat->server, time, &sx, &sy);
|
|
|
|
|
if (notify) {
|
|
|
|
|
wlr_tablet_v2_tablet_tool_notify_motion(tool->tool_v2, sx, sy);
|
2024-05-20 23:17:33 +02:00
|
|
|
if (enter_surface) {
|
|
|
|
|
/*
|
|
|
|
|
* By re-using the existing cursor logic, we are also
|
|
|
|
|
* setting pointer focus with
|
|
|
|
|
* `wlr_seat_pointer_notify_clear_focus` when a tablet
|
|
|
|
|
* pen enters a surface. This is a good thing. A side
|
|
|
|
|
* effect is though, that a client will be notified
|
|
|
|
|
* about a pointer event (pointer enter at coordinate)
|
|
|
|
|
* and tablet events (proximity-in, move-to-coordinate)
|
|
|
|
|
* at the same time. Following tablet actions, as long
|
|
|
|
|
* as the pen stays on the surface, emit only tablet
|
|
|
|
|
* events. That said, the client still thinks that there
|
|
|
|
|
* is a pointer at the coordinate where the tablet
|
|
|
|
|
* entered the surface. This can conflict with e.g. menu
|
|
|
|
|
* or scrollbars because the client might be conflicted
|
|
|
|
|
* about which coordinates (pointer or tablet) have
|
|
|
|
|
* priority when both coordinates are on the same menu
|
|
|
|
|
* or scrollbar.
|
|
|
|
|
* It looks like that -1/-1 are valid coordinates (based
|
|
|
|
|
* solemnly on testing with several client). Notifying
|
|
|
|
|
* pointer motion to -1/-1, the pointer is sort-of out
|
|
|
|
|
* of the way and is never on the same control element
|
|
|
|
|
* with the tablet pen, so no conflicts anymore for the
|
|
|
|
|
* client.
|
|
|
|
|
* Note that Gnome/Mutter sends a pointer-leave
|
|
|
|
|
* notification on tablet proximity-in to the client to
|
|
|
|
|
* avoid this conflict. Going that way would probably
|
|
|
|
|
* involve much more refactoring in labwc, and I'm not
|
|
|
|
|
* sure what will break since I have the feeling that a
|
|
|
|
|
* lot of internals rely on correct pointer focus.
|
|
|
|
|
*/
|
|
|
|
|
wlr_seat_pointer_notify_motion(tool->seat->seat, time, -1, -1);
|
|
|
|
|
}
|
2024-04-20 11:50:33 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-07 19:35:44 +02:00
|
|
|
static void
|
|
|
|
|
handle_proximity(struct wl_listener *listener, void *data)
|
|
|
|
|
{
|
|
|
|
|
struct wlr_tablet_tool_proximity_event *ev = data;
|
2024-04-20 11:50:33 +02:00
|
|
|
struct drawing_tablet *tablet = ev->tablet->data;
|
|
|
|
|
struct drawing_tablet_tool *tool = ev->tool->data;
|
2024-05-07 19:35:44 +02:00
|
|
|
|
2024-07-05 06:09:37 +02:00
|
|
|
if (ev->state == WLR_TABLET_TOOL_PROXIMITY_IN) {
|
2024-07-05 06:17:39 +02:00
|
|
|
tablet->motion_mode =
|
|
|
|
|
tool_motion_mode(rc.tablet_tool.motion, ev->tool);
|
2024-04-20 11:50:33 +02:00
|
|
|
}
|
|
|
|
|
|
2024-07-05 06:09:37 +02:00
|
|
|
/*
|
|
|
|
|
* Reset relative coordinates, we don't want to move the
|
|
|
|
|
* cursor on proximity-in for relative positioning.
|
|
|
|
|
*/
|
|
|
|
|
tablet->dx = 0;
|
|
|
|
|
tablet->dy = 0;
|
|
|
|
|
|
2024-04-20 11:50:33 +02:00
|
|
|
tablet->x = ev->x;
|
|
|
|
|
tablet->y = ev->y;
|
|
|
|
|
|
2024-07-05 06:09:37 +02:00
|
|
|
double x, y, dx, dy;
|
|
|
|
|
struct wlr_surface *surface = tablet_get_coords(tablet, &x, &y, &dx, &dy);
|
2024-04-20 11:50:33 +02:00
|
|
|
|
2024-04-22 17:21:55 +02:00
|
|
|
if (!rc.tablet.force_mouse_emulation
|
|
|
|
|
&& tablet->seat->server->tablet_manager && !tool) {
|
2024-04-20 11:50:33 +02:00
|
|
|
/*
|
|
|
|
|
* Unfortunately `wlr_tool` is only present in the events, so
|
|
|
|
|
* use proximity for creating a `wlr_tablet_v2_tablet_tool`.
|
|
|
|
|
*/
|
|
|
|
|
tablet_tool_init(tablet->seat, ev->tool);
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-25 08:58:53 +02:00
|
|
|
/*
|
|
|
|
|
* We have a tablet tool (aka pen/stylus) and a tablet protocol capable
|
|
|
|
|
* surface, let's send tablet notifications.
|
|
|
|
|
*/
|
2024-04-20 11:50:33 +02:00
|
|
|
if (tool && surface) {
|
|
|
|
|
if (tool->tool_v2 && ev->state == WLR_TABLET_TOOL_PROXIMITY_IN) {
|
2024-07-05 06:09:37 +02:00
|
|
|
notify_motion(tablet, tool, surface, x, y, dx, dy, ev->time_msec);
|
2024-04-20 11:50:33 +02:00
|
|
|
}
|
|
|
|
|
if (tool->tool_v2 && ev->state == WLR_TABLET_TOOL_PROXIMITY_OUT) {
|
|
|
|
|
wlr_tablet_v2_tablet_tool_notify_proximity_out(tool->tool_v2);
|
|
|
|
|
}
|
2024-05-07 19:35:44 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-25 08:58:53 +02:00
|
|
|
static bool is_down_mouse_emulation = false;
|
|
|
|
|
|
2023-12-01 22:12:00 +01:00
|
|
|
static void
|
|
|
|
|
handle_axis(struct wl_listener *listener, void *data)
|
|
|
|
|
{
|
|
|
|
|
struct wlr_tablet_tool_axis_event *ev = data;
|
|
|
|
|
struct drawing_tablet *tablet = ev->tablet->data;
|
2024-04-20 11:50:33 +02:00
|
|
|
struct drawing_tablet_tool *tool = ev->tool->data;
|
2024-05-07 19:35:44 +02:00
|
|
|
|
2024-07-05 06:09:37 +02:00
|
|
|
/*
|
|
|
|
|
* Reset relative coordinates. If those axes aren't updated,
|
|
|
|
|
* the delta is zero.
|
|
|
|
|
*/
|
|
|
|
|
tablet->dx = 0;
|
|
|
|
|
tablet->dy = 0;
|
2024-07-05 06:05:35 +02:00
|
|
|
tablet->tilt_x = 0;
|
|
|
|
|
tablet->tilt_y = 0;
|
|
|
|
|
|
2024-04-20 11:50:33 +02:00
|
|
|
if (ev->updated_axes & WLR_TABLET_TOOL_AXIS_X) {
|
|
|
|
|
tablet->x = ev->x;
|
2024-07-05 06:09:37 +02:00
|
|
|
tablet->dx = ev->dx;
|
2024-04-20 11:50:33 +02:00
|
|
|
}
|
|
|
|
|
if (ev->updated_axes & WLR_TABLET_TOOL_AXIS_Y) {
|
|
|
|
|
tablet->y = ev->y;
|
2024-07-05 06:09:37 +02:00
|
|
|
tablet->dy = ev->dy;
|
2024-04-20 11:50:33 +02:00
|
|
|
}
|
2024-06-06 17:41:05 +02:00
|
|
|
if (ev->updated_axes & WLR_TABLET_TOOL_AXIS_DISTANCE) {
|
|
|
|
|
tablet->distance = ev->distance;
|
|
|
|
|
}
|
|
|
|
|
if (ev->updated_axes & WLR_TABLET_TOOL_AXIS_PRESSURE) {
|
|
|
|
|
tablet->pressure = ev->pressure;
|
|
|
|
|
}
|
2024-04-20 11:50:33 +02:00
|
|
|
if (ev->updated_axes & WLR_TABLET_TOOL_AXIS_TILT_X) {
|
|
|
|
|
tablet->tilt_x = ev->tilt_x;
|
|
|
|
|
}
|
|
|
|
|
if (ev->updated_axes & WLR_TABLET_TOOL_AXIS_TILT_Y) {
|
|
|
|
|
tablet->tilt_y = ev->tilt_y;
|
|
|
|
|
}
|
2024-06-06 17:41:05 +02:00
|
|
|
if (ev->updated_axes & WLR_TABLET_TOOL_AXIS_ROTATION) {
|
|
|
|
|
tablet->rotation = ev->rotation;
|
|
|
|
|
}
|
|
|
|
|
if (ev->updated_axes & WLR_TABLET_TOOL_AXIS_SLIDER) {
|
|
|
|
|
tablet->slider = ev->slider;
|
|
|
|
|
}
|
|
|
|
|
if (ev->updated_axes & WLR_TABLET_TOOL_AXIS_WHEEL) {
|
|
|
|
|
tablet->wheel_delta = ev->wheel_delta;
|
|
|
|
|
}
|
2024-04-20 11:50:33 +02:00
|
|
|
|
2024-07-05 06:09:37 +02:00
|
|
|
double x, y, dx, dy;
|
|
|
|
|
struct wlr_surface *surface = tablet_get_coords(tablet, &x, &y, &dx, &dy);
|
2024-04-20 11:50:33 +02:00
|
|
|
|
2024-05-25 08:58:53 +02:00
|
|
|
/*
|
|
|
|
|
* We are sending tablet notifications on the following conditions:
|
|
|
|
|
* - a tablet tool (aka pen/stylus) had been created earlier on
|
|
|
|
|
* proximity-in
|
|
|
|
|
* - there is no current tip or button press (e.g. from out-of-surface
|
|
|
|
|
* scrolling) that started on a non tablet capable surface
|
|
|
|
|
* - the surface below the tip understands the tablet protocol and is in
|
|
|
|
|
* pass through state (notifications are allowed to the client), or we
|
|
|
|
|
* don't have a tablet-capable surface but are still having an active
|
|
|
|
|
* grab (e.g. from out-of-surface scrolling).
|
|
|
|
|
*/
|
|
|
|
|
if (tool && !is_down_mouse_emulation && ((surface
|
2024-04-20 11:50:33 +02:00
|
|
|
&& tablet->seat->server->input_mode == LAB_INPUT_STATE_PASSTHROUGH)
|
|
|
|
|
|| wlr_tablet_tool_v2_has_implicit_grab(tool->tool_v2))) {
|
2024-06-06 17:41:05 +02:00
|
|
|
/* motion seems to be supported by all tools */
|
2024-07-05 06:09:37 +02:00
|
|
|
notify_motion(tablet, tool, surface, x, y, dx, dy, ev->time_msec);
|
2024-06-06 17:41:05 +02:00
|
|
|
|
|
|
|
|
/* notify about other axis based on tool capabilities */
|
|
|
|
|
if (ev->tool->distance) {
|
2024-04-20 11:50:33 +02:00
|
|
|
wlr_tablet_v2_tablet_tool_notify_distance(tool->tool_v2,
|
2024-06-06 17:41:05 +02:00
|
|
|
tablet->distance);
|
2023-12-01 22:12:00 +01:00
|
|
|
}
|
2024-06-06 17:41:05 +02:00
|
|
|
if (ev->tool->pressure) {
|
2024-04-20 11:50:33 +02:00
|
|
|
wlr_tablet_v2_tablet_tool_notify_pressure(tool->tool_v2,
|
2024-06-06 17:41:05 +02:00
|
|
|
tablet->pressure);
|
2024-04-20 11:50:33 +02:00
|
|
|
}
|
2024-06-06 17:41:05 +02:00
|
|
|
if (ev->tool->tilt) {
|
2024-04-20 11:50:33 +02:00
|
|
|
/*
|
|
|
|
|
* From https://gitlab.freedesktop.org/wayland/wayland-protocols/-/blob/main/stable/tablet/tablet-v2.xml
|
|
|
|
|
* "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.
|
|
|
|
|
* 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."
|
|
|
|
|
* Based on that we only need to apply rotation but no area transformation.
|
|
|
|
|
*/
|
|
|
|
|
double tilt_x = tablet->tilt_x;
|
|
|
|
|
double tilt_y = tablet->tilt_y;
|
2024-07-05 06:05:35 +02:00
|
|
|
adjust_for_rotation_relative(rc.tablet.rotation, &tilt_x, &tilt_y);
|
2023-12-01 22:12:00 +01:00
|
|
|
|
2024-04-20 11:50:33 +02:00
|
|
|
wlr_tablet_v2_tablet_tool_notify_tilt(tool->tool_v2,
|
|
|
|
|
tilt_x, tilt_y);
|
|
|
|
|
}
|
2024-06-06 17:41:05 +02:00
|
|
|
if (ev->tool->rotation) {
|
2024-04-20 11:50:33 +02:00
|
|
|
wlr_tablet_v2_tablet_tool_notify_rotation(tool->tool_v2,
|
2024-06-06 17:41:05 +02:00
|
|
|
tablet->rotation);
|
2024-04-20 11:50:33 +02:00
|
|
|
}
|
2024-06-06 17:41:05 +02:00
|
|
|
if (ev->tool->slider) {
|
2024-04-20 11:50:33 +02:00
|
|
|
wlr_tablet_v2_tablet_tool_notify_slider(tool->tool_v2,
|
2024-06-06 17:41:05 +02:00
|
|
|
tablet->slider);
|
2024-04-20 11:50:33 +02:00
|
|
|
}
|
2024-06-06 17:41:05 +02:00
|
|
|
if (ev->tool->wheel) {
|
2024-04-20 11:50:33 +02:00
|
|
|
wlr_tablet_v2_tablet_tool_notify_wheel(tool->tool_v2,
|
2024-06-06 17:41:05 +02:00
|
|
|
tablet->wheel_delta, 0);
|
2024-04-20 11:50:33 +02:00
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (ev->updated_axes & (WLR_TABLET_TOOL_AXIS_X | WLR_TABLET_TOOL_AXIS_Y)) {
|
|
|
|
|
if (tool && tool->tool_v2->focused_surface) {
|
|
|
|
|
wlr_tablet_v2_tablet_tool_notify_proximity_out(
|
|
|
|
|
tool->tool_v2);
|
|
|
|
|
}
|
2024-07-05 06:09:37 +02:00
|
|
|
|
|
|
|
|
switch (tablet->motion_mode) {
|
|
|
|
|
case LAB_TABLET_MOTION_ABSOLUTE:
|
|
|
|
|
cursor_emulate_move_absolute(tablet->seat,
|
|
|
|
|
&ev->tablet->base,
|
|
|
|
|
x, y, ev->time_msec);
|
|
|
|
|
break;
|
|
|
|
|
case LAB_TABLET_MOTION_RELATIVE:
|
|
|
|
|
cursor_emulate_move(tablet->seat,
|
|
|
|
|
&ev->tablet->base,
|
|
|
|
|
dx, dy, ev->time_msec);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2024-04-20 11:50:33 +02:00
|
|
|
}
|
2023-12-01 22:12:00 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-21 21:05:12 +02:00
|
|
|
static uint32_t
|
|
|
|
|
to_stylus_button(uint32_t button)
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* Use the mapping that is used by XWayland and GTK, even
|
|
|
|
|
* if it isn't the order one would expect. This is also
|
|
|
|
|
* consistent with the mapping for mouse emulation
|
|
|
|
|
*/
|
|
|
|
|
switch (button) {
|
|
|
|
|
case BTN_LEFT:
|
|
|
|
|
return BTN_TOOL_PEN;
|
|
|
|
|
case BTN_RIGHT:
|
|
|
|
|
return BTN_STYLUS2;
|
|
|
|
|
case BTN_MIDDLE:
|
|
|
|
|
return BTN_STYLUS;
|
|
|
|
|
case BTN_SIDE:
|
|
|
|
|
return BTN_STYLUS3;
|
|
|
|
|
default:
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-24 16:18:24 +02:00
|
|
|
static void
|
|
|
|
|
seat_pointer_end_grab(struct drawing_tablet_tool *tool,
|
|
|
|
|
struct wlr_surface *surface)
|
|
|
|
|
{
|
|
|
|
|
if (!surface || !wlr_seat_pointer_has_grab(tool->seat->seat)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct wlr_xdg_surface *xdg_surface =
|
|
|
|
|
wlr_xdg_surface_try_from_wlr_surface(surface);
|
|
|
|
|
if (!xdg_surface || xdg_surface->role != WLR_XDG_SURFACE_ROLE_POPUP) {
|
|
|
|
|
/*
|
|
|
|
|
* If we have an active popup grab (an open popup) and we are
|
|
|
|
|
* not on the popup itself, end that grab to close the popup.
|
|
|
|
|
* Contrary to pointer button notifications, a tablet button
|
|
|
|
|
* notification sometimes doesn't end grabs automatically on
|
|
|
|
|
* button notifications in another client (observed in GTK4),
|
|
|
|
|
* so end the grab manually.
|
|
|
|
|
*/
|
|
|
|
|
wlr_seat_pointer_end_grab(tool->seat->seat);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-01 22:12:00 +01:00
|
|
|
static void
|
|
|
|
|
handle_tip(struct wl_listener *listener, void *data)
|
|
|
|
|
{
|
|
|
|
|
struct wlr_tablet_tool_tip_event *ev = data;
|
|
|
|
|
struct drawing_tablet *tablet = ev->tablet->data;
|
2024-04-20 11:50:33 +02:00
|
|
|
struct drawing_tablet_tool *tool = ev->tool->data;
|
2023-12-01 22:12:00 +01:00
|
|
|
|
2024-07-05 06:09:37 +02:00
|
|
|
double x, y, dx, dy;
|
|
|
|
|
struct wlr_surface *surface = tablet_get_coords(tablet, &x, &y, &dx, &dy);
|
2024-04-20 11:50:33 +02:00
|
|
|
|
2024-04-21 21:05:12 +02:00
|
|
|
uint32_t button = tablet_get_mapped_button(BTN_TOOL_PEN);
|
|
|
|
|
|
2024-05-25 08:58:53 +02:00
|
|
|
/*
|
|
|
|
|
* We are sending tablet notifications on the following conditions:
|
|
|
|
|
* - a tablet tool (aka pen/stylus) had been created earlier on
|
|
|
|
|
* proximity-in
|
|
|
|
|
* - there is no current tip or button press (e.g. from out-of-surface
|
|
|
|
|
* scrolling) that started on a non tablet capable surface
|
|
|
|
|
* - the surface below tip understands the tablet protocol, or we don't
|
|
|
|
|
* have a tablet-capable surface but are still having an active grab.
|
|
|
|
|
*/
|
|
|
|
|
if (tool && !is_down_mouse_emulation && (surface
|
|
|
|
|
|| wlr_tablet_tool_v2_has_implicit_grab(tool->tool_v2))) {
|
2024-04-20 11:50:33 +02:00
|
|
|
idle_manager_notify_activity(tool->seat->seat);
|
|
|
|
|
|
2024-04-21 21:05:12 +02:00
|
|
|
uint32_t stylus_button = to_stylus_button(button);
|
|
|
|
|
if (stylus_button != BTN_TOOL_PEN) {
|
|
|
|
|
wlr_log(WLR_INFO, "ignoring stylus tool pen mapping for tablet mode");
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-20 11:50:33 +02:00
|
|
|
if (ev->state == WLR_TABLET_TOOL_TIP_DOWN) {
|
|
|
|
|
bool notify = cursor_process_button_press(tool->seat, BTN_LEFT,
|
|
|
|
|
ev->time_msec);
|
|
|
|
|
if (notify) {
|
2024-05-24 16:18:24 +02:00
|
|
|
seat_pointer_end_grab(tool, surface);
|
2024-04-20 11:50:33 +02:00
|
|
|
wlr_tablet_v2_tablet_tool_notify_down(tool->tool_v2);
|
|
|
|
|
wlr_tablet_tool_v2_start_implicit_grab(tool->tool_v2);
|
|
|
|
|
}
|
|
|
|
|
} else if (ev->state == WLR_TABLET_TOOL_TIP_UP) {
|
|
|
|
|
bool notify = cursor_process_button_release(tool->seat, BTN_LEFT,
|
|
|
|
|
ev->time_msec);
|
|
|
|
|
if (notify) {
|
|
|
|
|
wlr_tablet_v2_tablet_tool_notify_up(tool->tool_v2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool exit_interactive = cursor_finish_button_release(tool->seat);
|
2024-05-28 20:35:26 +02:00
|
|
|
if (exit_interactive && surface && tool->tool_v2->focused_surface) {
|
2024-04-20 11:50:33 +02:00
|
|
|
/*
|
|
|
|
|
* Re-enter the surface after a resize/move to ensure
|
2024-05-28 20:35:26 +02:00
|
|
|
* being back in tablet mode, but only if we are still
|
|
|
|
|
* above a tablet capable surface.
|
2024-04-20 11:50:33 +02:00
|
|
|
*/
|
|
|
|
|
wlr_tablet_v2_tablet_tool_notify_proximity_out(tool->tool_v2);
|
|
|
|
|
wlr_tablet_v2_tablet_tool_notify_proximity_in(tool->tool_v2,
|
|
|
|
|
tablet->tablet_v2, surface);
|
|
|
|
|
}
|
2023-12-29 10:06:59 +01:00
|
|
|
|
2024-04-20 11:50:33 +02:00
|
|
|
if (!surface) {
|
|
|
|
|
/* Out-of-surface movement ended on the desktop */
|
|
|
|
|
wlr_tablet_v2_tablet_tool_notify_proximity_out(tool->tool_v2);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (button) {
|
2024-05-25 08:58:53 +02:00
|
|
|
is_down_mouse_emulation = ev->state == WLR_TABLET_TOOL_TIP_DOWN;
|
2024-04-20 11:50:33 +02:00
|
|
|
cursor_emulate_button(tablet->seat,
|
|
|
|
|
button,
|
|
|
|
|
ev->state == WLR_TABLET_TOOL_TIP_DOWN
|
2024-03-19 01:32:05 +01:00
|
|
|
? WL_POINTER_BUTTON_STATE_PRESSED
|
|
|
|
|
: WL_POINTER_BUTTON_STATE_RELEASED,
|
2024-04-20 11:50:33 +02:00
|
|
|
ev->time_msec);
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-12-01 22:12:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
handle_button(struct wl_listener *listener, void *data)
|
|
|
|
|
{
|
|
|
|
|
struct wlr_tablet_tool_button_event *ev = data;
|
|
|
|
|
struct drawing_tablet *tablet = ev->tablet->data;
|
2024-04-20 11:50:33 +02:00
|
|
|
struct drawing_tablet_tool *tool = ev->tool->data;
|
2023-12-01 22:12:00 +01:00
|
|
|
|
2024-07-05 06:09:37 +02:00
|
|
|
double x, y, dx, dy;
|
|
|
|
|
struct wlr_surface *surface = tablet_get_coords(tablet, &x, &y, &dx, &dy);
|
2023-12-01 22:12:00 +01:00
|
|
|
|
2024-04-21 21:05:12 +02:00
|
|
|
uint32_t button = tablet_get_mapped_button(ev->button);
|
|
|
|
|
|
2024-05-25 08:58:53 +02:00
|
|
|
/*
|
|
|
|
|
* We are sending tablet notifications on the following conditions:
|
|
|
|
|
* - a tablet tool (aka pen/stylus) had been created earlier on
|
|
|
|
|
* proximity-in
|
|
|
|
|
* - there is no current tip or button press (e.g. out of surface
|
|
|
|
|
* scrolling) that started on a non tablet capable surface
|
|
|
|
|
* - the surface below the tip understands the tablet protocol.
|
|
|
|
|
*/
|
|
|
|
|
if (tool && !is_down_mouse_emulation && surface) {
|
2024-04-20 11:50:33 +02:00
|
|
|
idle_manager_notify_activity(tool->seat->seat);
|
|
|
|
|
|
2024-06-11 06:41:24 +02:00
|
|
|
if (button && ev->state == WLR_BUTTON_PRESSED) {
|
2024-04-21 21:05:12 +02:00
|
|
|
struct view *view = view_from_wlr_surface(surface);
|
|
|
|
|
struct mousebind *mousebind;
|
|
|
|
|
wl_list_for_each(mousebind, &rc.mousebinds, link) {
|
|
|
|
|
if (mousebind->mouse_event == MOUSE_ACTION_PRESS
|
|
|
|
|
&& mousebind->button == button
|
|
|
|
|
&& mousebind->context == LAB_SSD_CLIENT) {
|
|
|
|
|
actions_run(view, tool->seat->server,
|
|
|
|
|
&mousebind->actions, 0);
|
|
|
|
|
}
|
2024-04-20 11:50:33 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-21 21:05:12 +02:00
|
|
|
uint32_t stylus_button = to_stylus_button(button);
|
|
|
|
|
if (stylus_button && stylus_button != BTN_TOOL_PEN) {
|
2024-05-24 16:18:24 +02:00
|
|
|
if (ev->state == WLR_BUTTON_PRESSED) {
|
|
|
|
|
seat_pointer_end_grab(tool, surface);
|
|
|
|
|
}
|
2024-04-21 21:05:12 +02:00
|
|
|
wlr_tablet_v2_tablet_tool_notify_button(tool->tool_v2,
|
|
|
|
|
stylus_button,
|
|
|
|
|
ev->state == WLR_BUTTON_PRESSED
|
|
|
|
|
? ZWP_TABLET_PAD_V2_BUTTON_STATE_PRESSED
|
|
|
|
|
: ZWP_TABLET_PAD_V2_BUTTON_STATE_RELEASED);
|
|
|
|
|
} else {
|
|
|
|
|
wlr_log(WLR_INFO, "invalid stylus button mapping for tablet mode");
|
|
|
|
|
}
|
2024-04-20 11:50:33 +02:00
|
|
|
} else {
|
|
|
|
|
if (button) {
|
2024-05-25 08:58:53 +02:00
|
|
|
is_down_mouse_emulation = ev->state == WLR_BUTTON_PRESSED;
|
2024-03-19 01:32:05 +01:00
|
|
|
cursor_emulate_button(tablet->seat, button,
|
|
|
|
|
ev->state == WLR_BUTTON_PRESSED
|
|
|
|
|
? WL_POINTER_BUTTON_STATE_PRESSED
|
|
|
|
|
: WL_POINTER_BUTTON_STATE_RELEASED,
|
|
|
|
|
ev->time_msec);
|
2024-04-20 11:50:33 +02:00
|
|
|
}
|
|
|
|
|
}
|
2023-12-01 22:12:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
handle_destroy(struct wl_listener *listener, void *data)
|
|
|
|
|
{
|
|
|
|
|
struct drawing_tablet *tablet =
|
|
|
|
|
wl_container_of(listener, tablet, handlers.destroy);
|
2024-04-11 23:47:10 +02:00
|
|
|
|
2024-06-04 20:14:30 +02:00
|
|
|
wl_list_remove(&tablet->link);
|
2024-06-04 20:21:12 +02:00
|
|
|
tablet_pad_attach_tablet(tablet->seat);
|
|
|
|
|
|
2024-04-11 23:47:10 +02:00
|
|
|
wl_list_remove(&tablet->handlers.tip.link);
|
|
|
|
|
wl_list_remove(&tablet->handlers.button.link);
|
2024-05-07 19:35:44 +02:00
|
|
|
wl_list_remove(&tablet->handlers.proximity.link);
|
2024-04-11 23:47:10 +02:00
|
|
|
wl_list_remove(&tablet->handlers.axis.link);
|
|
|
|
|
wl_list_remove(&tablet->handlers.destroy.link);
|
2023-12-01 22:12:00 +01:00
|
|
|
free(tablet);
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-02 18:26:25 +01:00
|
|
|
void
|
|
|
|
|
tablet_init(struct seat *seat, struct wlr_input_device *wlr_device)
|
2023-12-01 22:12:00 +01:00
|
|
|
{
|
|
|
|
|
wlr_log(WLR_DEBUG, "setting up tablet");
|
|
|
|
|
struct drawing_tablet *tablet = znew(*tablet);
|
|
|
|
|
tablet->seat = seat;
|
2024-04-20 11:40:23 +02:00
|
|
|
tablet->wlr_input_device = wlr_device;
|
2023-12-01 22:12:00 +01:00
|
|
|
tablet->tablet = wlr_tablet_from_input_device(wlr_device);
|
|
|
|
|
tablet->tablet->data = tablet;
|
2024-04-20 11:40:23 +02:00
|
|
|
if (seat->server->tablet_manager) {
|
|
|
|
|
tablet->tablet_v2 = wlr_tablet_create(
|
|
|
|
|
seat->server->tablet_manager, seat->seat, wlr_device);
|
|
|
|
|
}
|
2023-12-01 22:12:00 +01:00
|
|
|
tablet->x = 0.0;
|
|
|
|
|
tablet->y = 0.0;
|
2024-06-06 17:41:05 +02:00
|
|
|
tablet->distance = 0.0;
|
|
|
|
|
tablet->pressure = 0.0;
|
2024-04-20 11:50:33 +02:00
|
|
|
tablet->tilt_x = 0.0;
|
|
|
|
|
tablet->tilt_y = 0.0;
|
2024-06-06 17:41:05 +02:00
|
|
|
tablet->rotation = 0.0;
|
|
|
|
|
tablet->slider = 0.0;
|
|
|
|
|
tablet->wheel_delta = 0.0;
|
2023-12-30 19:14:11 +01:00
|
|
|
wlr_log(WLR_INFO, "tablet dimensions: %.2fmm x %.2fmm",
|
|
|
|
|
tablet->tablet->width_mm, tablet->tablet->height_mm);
|
2023-12-01 22:12:00 +01:00
|
|
|
CONNECT_SIGNAL(tablet->tablet, &tablet->handlers, axis);
|
2024-05-07 19:35:44 +02:00
|
|
|
CONNECT_SIGNAL(tablet->tablet, &tablet->handlers, proximity);
|
2023-12-01 22:12:00 +01:00
|
|
|
CONNECT_SIGNAL(tablet->tablet, &tablet->handlers, tip);
|
|
|
|
|
CONNECT_SIGNAL(tablet->tablet, &tablet->handlers, button);
|
|
|
|
|
CONNECT_SIGNAL(wlr_device, &tablet->handlers, destroy);
|
2024-06-04 20:21:12 +02:00
|
|
|
|
2024-06-04 20:14:30 +02:00
|
|
|
wl_list_insert(&seat->tablets, &tablet->link);
|
2024-06-04 20:21:12 +02:00
|
|
|
tablet_pad_attach_tablet(tablet->seat);
|
2023-12-01 22:12:00 +01:00
|
|
|
}
|