summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohannes Herman <johannes.herman@gmail.com>2025-08-12 12:10:56 +0200
committerJohannes Herman <johannes.herman@gmail.com>2025-08-12 12:10:56 +0200
commitc58369db1c313ca3d62c5765c1ca0cbd61a3892d (patch)
tree112586f3f01833e44d661e080d6e0ecc21b61de2
parent5f7c6f8d08f336c5d18e67c145319a70abe4f61c (diff)
added tablet
-rw-r--r--Makefile6
-rw-r--r--config.def.h31
-rw-r--r--dwl.c228
-rw-r--r--patches/tablet-input-0.7.patch355
4 files changed, 611 insertions, 9 deletions
diff --git a/Makefile b/Makefile
index 3358bae..47a2bb2 100644
--- a/Makefile
+++ b/Makefile
@@ -21,7 +21,8 @@ dwl: dwl.o util.o
$(CC) dwl.o util.o $(DWLCFLAGS) $(LDFLAGS) $(LDLIBS) -o $@
dwl.o: dwl.c client.h config.h config.mk cursor-shape-v1-protocol.h \
pointer-constraints-unstable-v1-protocol.h wlr-layer-shell-unstable-v1-protocol.h \
- wlr-output-power-management-unstable-v1-protocol.h xdg-shell-protocol.h
+ wlr-output-power-management-unstable-v1-protocol.h xdg-shell-protocol.h \
+ tablet-v2-protocol.h
util.o: util.c util.h
# wayland-scanner is a tool which generates C headers and rigging for Wayland
@@ -45,6 +46,9 @@ wlr-output-power-management-unstable-v1-protocol.h:
xdg-shell-protocol.h:
$(WAYLAND_SCANNER) server-header \
$(WAYLAND_PROTOCOLS)/stable/xdg-shell/xdg-shell.xml $@
+tablet-v2-protocol.h:
+ $(WAYLAND_SCANNER) server-header \
+ $(WAYLAND_PROTOCOLS)/unstable/tablet/tablet-unstable-v2.xml $@
config.h:
cp config.def.h $@
diff --git a/config.def.h b/config.def.h
index 36fa943..fc74d4c 100644
--- a/config.def.h
+++ b/config.def.h
@@ -4,15 +4,16 @@
((hex >> 8) & 0xFF) / 255.0f, \
(hex & 0xFF) / 255.0f }
/* appearance */
+static const int tabletmaptosurface = 0; /* map tablet input to surface(1) or monitor(0) */
static const int sloppyfocus = 1; /* focus follows mouse */
static const int bypass_surface_visibility = 0; /* 1 means idle inhibitors will disable idle tracking even if it's surface isn't visible */
static const int smartborders = 1; /* 1 means no outer gap when there is only one window */
static const int smartgaps = 1; /* 1 means no outer gap when there is only one window */
static int gaps = 1; /* 1 means gaps between windows are added */
-static const unsigned int gappx = 10; /* gap pixel between windows */
+static const unsigned int gappx = 8; /* gap pixel between windows */
static const unsigned int borderpx = 3; /* border pixel of windows */
static const float rootcolor[] = COLOR(0x282828ff);
-static const float bordercolor[] = COLOR(0x7c6f64ff);
+static const float bordercolor[] = COLOR(0x282828ee);
static const float focuscolor[] = COLOR(0x458588ff);
static const float urgentcolor[] = COLOR(0xcc241dff);
/* This conforms to the xdg-protocol. Set the alpha to zero to restore the old behavior */
@@ -134,12 +135,16 @@ static const char *alttermcmd[] = { "foot", NULL };
static const char *browsercmd[] = { "firefox", NULL };
static const char *menucmd[] = { "bemenu-run", NULL };
+static const char *volume_raise[] = { "volume", "raise", NULL };
+static const char *volume_lower[] = { "volume", "lower", NULL };
+static const char *volume_mute[] = { "volume", "mute", NULL };
+
static const Key keys[] = {
/* Note that Shift changes certain key codes: c -> C, 2 -> at, etc. */
/* modifier key function argument */
{ MODKEY, XKB_KEY_d, spawn, {.v = menucmd} },
{ MODKEY, XKB_KEY_q, spawn, {.v = termcmd} },
- { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_q, spawn, {.v = alttermcmd} },
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Q, spawn, {.v = alttermcmd} },
{ MODKEY, XKB_KEY_w, spawn, {.v = browsercmd} },
{ MODKEY, XKB_KEY_j, focusstack, {.i = +1} },
{ MODKEY, XKB_KEY_k, focusstack, {.i = -1} },
@@ -147,18 +152,28 @@ static const Key keys[] = {
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_L, incnmaster, {.i = -1} },
{ MODKEY, XKB_KEY_h, setmfact, {.f = -0.05f} },
{ MODKEY, XKB_KEY_l, setmfact, {.f = +0.05f} },
+ { MODKEY, XKB_KEY_f, togglefullscreen, {0} },
{ MODKEY, XKB_KEY_Return, zoom, {0} },
{ MODKEY, XKB_KEY_Tab, view, {0} },
{ MODKEY, XKB_KEY_g, togglegaps, {0} },
{ MODKEY, XKB_KEY_p, togglesticky, {0} },
{ MODKEY, XKB_KEY_c, killclient, {0} },
+ { MODKEY, XKB_KEY_s, spawn, SHCMD("snap") },
+ // { MODKEY|ShiftMask, XK_s, spawn, SHCMD("snap -s") },
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_D, spawn, SHCMD("vis pop-cal 60") },
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_C, spawn, SHCMD("vis pop-clock 1") },
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_B, spawn, SHCMD("vis pop-bat 5") },
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_N, spawn, SHCMD("vis pop-net 10") },
+ { MODKEY, XKB_KEY_Escape, spawn, SHCMD("vis dismiss") },
+ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_v, spawn, {.v = volume_mute } },
+ { MODKEY, XKB_KEY_v, spawn, {.v = volume_raise } },
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_V, spawn, {.v = volume_lower } },
{ MODKEY, XKB_KEY_t, setlayout, {.v = &layouts[0]} },
// { MODKEY, XKB_KEY_f, setlayout, {.v = &layouts[1]} },
{ MODKEY, XKB_KEY_m, setlayout, {.v = &layouts[2]} },
- { MODKEY, XKB_KEY_s, setlayout, {.v = &layouts[3]} },
- { MODKEY, XKB_KEY_space, setlayout, {0} },
+ // { MODKEY, XKB_KEY_s, setlayout, {.v = &layouts[3]} },
+ // { MODKEY, XKB_KEY_space, setlayout, {0} },
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_space, togglefloating, {0} },
- { MODKEY, XKB_KEY_f, togglefullscreen, {0} },
{ MODKEY, XKB_KEY_0, view, {.ui = ~0} },
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_equal, tag, {.ui = ~0} },
{ MODKEY, XKB_KEY_comma, focusmon, {.i = WLR_DIRECTION_LEFT} },
@@ -175,6 +190,10 @@ static const Key keys[] = {
TAGKEYS( XKB_KEY_8, XKB_KEY_parenleft, 7),
TAGKEYS( XKB_KEY_9, XKB_KEY_parenright, 8),
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_M, quit, {0} },
+ { 0, XKB_KEY_XF86AudioMute, spawn, {.v = volume_mute } },
+ { 0, XKB_KEY_XF86AudioRaiseVolume, spawn, {.v = volume_raise } },
+ { 0, XKB_KEY_XF86AudioLowerVolume, spawn, {.v = volume_lower } },
+ { 0, XKB_KEY_XF86AudioMicMute, spawn, SHCMD("mic mute") },
/* Ctrl-Alt-Backspace and Ctrl-Alt-Fx used to be handled by X server */
{ WLR_MODIFIER_CTRL|WLR_MODIFIER_ALT,XKB_KEY_Terminate_Server, quit, {0} },
diff --git a/dwl.c b/dwl.c
index 396a566..684491a 100644
--- a/dwl.c
+++ b/dwl.c
@@ -50,6 +50,9 @@
#include <wlr/types/wlr_session_lock_v1.h>
#include <wlr/types/wlr_single_pixel_buffer_v1.h>
#include <wlr/types/wlr_subcompositor.h>
+#include <wlr/types/wlr_tablet_tool.h>
+#include <wlr/types/wlr_tablet_pad.h>
+#include <wlr/types/wlr_tablet_v2.h>
#include <wlr/types/wlr_viewporter.h>
#include <wlr/types/wlr_virtual_keyboard_v1.h>
#include <wlr/types/wlr_virtual_pointer_v1.h>
@@ -76,8 +79,6 @@
#define VISIBLEON(C, M) ((M) && (C)->mon == (M) && ((((C)->tags & (M)->tagset[(M)->seltags]) && !(C)->swallowedby) || C->issticky))
-// #define VISIBLEON(C, M) ((M) && (C)->mon == (M) && (((C)->tags & (M)->tagset[(M)->seltags]) || C->issticky))
-
#define LENGTH(X) (sizeof X / sizeof X[0])
#define END(A) ((A) + LENGTH(A))
#define TAGMASK ((1u << TAGCOUNT) - 1)
@@ -283,6 +284,7 @@ static void createnotify(struct wl_listener *listener, void *data);
static void createpointer(struct wlr_pointer *pointer);
static void createpointerconstraint(struct wl_listener *listener, void *data);
static void createpopup(struct wl_listener *listener, void *data);
+static void createtablet(struct wlr_input_device *device);
static void cursorconstrain(struct wlr_pointer_constraint_v1 *constraint);
static void cursorframe(struct wl_listener *listener, void *data);
static void cursorwarptohint(void);
@@ -297,6 +299,9 @@ static void destroypointerconstraint(struct wl_listener *listener, void *data);
static void destroysessionlock(struct wl_listener *listener, void *data);
static void destroysessionmgr(struct wl_listener *listener, void *data);
static void destroykeyboardgroup(struct wl_listener *listener, void *data);
+static void destroytablet(struct wl_listener *listener, void *data);
+static void destroytabletsurfacenotify(struct wl_listener *listener, void *data);
+static void destroytablettool(struct wl_listener *listener, void *data);
static Monitor *dirtomon(enum wlr_direction dir);
static void focusclient(Client *c, int lift);
static void focusmon(const Arg *arg);
@@ -356,6 +361,12 @@ static void startdrag(struct wl_listener *listener, void *data);
static void swallow(Client *c, Client *toswallow);
static void tag(const Arg *arg);
static void tagmon(const Arg *arg);
+static void tablettoolmotion(struct wlr_tablet_v2_tablet_tool *tool, bool change_x, bool change_y, double x, double y, double dx, double dy);
+static void tablettoolproximity(struct wl_listener *listener, void *data);
+static void tablettoolaxis(struct wl_listener *listener, void *data);
+static void tablettoolbutton(struct wl_listener *listener, void *data);
+static void tablettooltip(struct wl_listener *listener, void *data);
+
static Client *termforwin(Client *c);
static void tile(Monitor *m);
static void togglefloating(const Arg *arg);
@@ -428,6 +439,13 @@ static struct {
int hotspot_y;
} last_cursor;
+static struct wlr_tablet_manager_v2 *tablet_mgr;
+static struct wlr_tablet_v2_tablet *tablet = NULL;
+static struct wlr_tablet_v2_tablet_tool *tablet_tool = NULL;
+static struct wlr_tablet_v2_tablet_pad *tablet_pad = NULL;
+static struct wlr_surface *tablet_curr_surface = NULL;
+static struct wl_listener destroy_tablet_surface_listener = {.notify = destroytabletsurfacenotify};
+
static struct wlr_scene_rect *root_bg;
static struct wlr_session_lock_manager_v1 *session_lock_mgr;
static struct wlr_scene_rect *locked_bg;
@@ -1182,6 +1200,28 @@ createpopup(struct wl_listener *listener, void *data)
}
void
+createtablet(struct wlr_input_device *device)
+{
+ if (!tablet) {
+ struct libinput_device *device_handle = NULL;
+ if (!wlr_input_device_is_libinput(device) ||
+ !(device_handle = wlr_libinput_get_device_handle(device)))
+ return;
+
+ tablet = wlr_tablet_create(tablet_mgr, seat, device);
+ LISTEN_STATIC(&tablet->wlr_device->events.destroy, destroytablet);
+ if (libinput_device_config_send_events_get_modes(device_handle)) {
+ libinput_device_config_send_events_set_mode(device_handle, send_events_mode);
+ wlr_cursor_attach_input_device(cursor, device);
+ }
+ } else if (device == tablet->wlr_device) {
+ wlr_log(WLR_ERROR, "createtablet: duplicate device");
+ } else {
+ wlr_log(WLR_ERROR, "createtablet: already have one tablet");
+ }
+}
+
+void
cursorconstrain(struct wlr_pointer_constraint_v1 *constraint)
{
if (active_constraint == constraint)
@@ -1368,6 +1408,27 @@ destroykeyboardgroup(struct wl_listener *listener, void *data)
free(group);
}
+void
+destroytablet(struct wl_listener *listener, void *data)
+{
+ tablet = NULL;
+}
+
+void
+destroytabletsurfacenotify(struct wl_listener *listener, void *data)
+{
+ if (tablet_curr_surface)
+ wl_list_remove(&destroy_tablet_surface_listener.link);
+ tablet_curr_surface = NULL;
+}
+
+void
+destroytablettool(struct wl_listener *listener, void *data)
+{
+ destroytabletsurfacenotify(NULL, NULL);
+ tablet_tool = NULL;
+}
+
Monitor *
dirtomon(enum wlr_direction dir)
{
@@ -1625,6 +1686,12 @@ inputdevice(struct wl_listener *listener, void *data)
case WLR_INPUT_DEVICE_POINTER:
createpointer(wlr_pointer_from_input_device(device));
break;
+ case WLR_INPUT_DEVICE_TABLET:
+ createtablet(device);
+ break;
+ case WLR_INPUT_DEVICE_TABLET_PAD:
+ tablet_pad = wlr_tablet_pad_create(tablet_mgr, seat, device);
+ break;
default:
/* TODO handle other input device types */
break;
@@ -2706,6 +2773,8 @@ setup(void)
hide_source = wl_event_loop_add_timer(wl_display_get_event_loop(dpy),
hidecursor, cursor);
+ tablet_mgr = wlr_tablet_v2_create(dpy);
+
/*
* Creates a cursor, which is a wlroots utility for tracking the cursor
* image shown on screen.
@@ -2735,6 +2804,10 @@ setup(void)
LISTEN_STATIC(&cursor->events.button, buttonpress);
LISTEN_STATIC(&cursor->events.axis, axisnotify);
LISTEN_STATIC(&cursor->events.frame, cursorframe);
+ LISTEN_STATIC(&cursor->events.tablet_tool_proximity, tablettoolproximity);
+ LISTEN_STATIC(&cursor->events.tablet_tool_axis, tablettoolaxis);
+ LISTEN_STATIC(&cursor->events.tablet_tool_button, tablettoolbutton);
+ LISTEN_STATIC(&cursor->events.tablet_tool_tip, tablettooltip);
cursor_shape_mgr = wlr_cursor_shape_manager_v1_create(dpy, 1);
LISTEN_STATIC(&cursor_shape_mgr->events.request_set_shape, setcursorshape);
@@ -2903,6 +2976,7 @@ snail(Monitor *m)
}
}
+
void
spawn(const Arg *arg)
{
@@ -3019,6 +3093,156 @@ termforwin(Client *c)
}
void
+tabletapplymap(double x, double y, struct wlr_input_device *dev)
+{
+ Client *p;
+ struct wlr_box geom = {0};
+ if (tabletmaptosurface && tablet_curr_surface) {
+ toplevel_from_wlr_surface(tablet_curr_surface, &p, NULL);
+ if (p) {
+ for (; client_get_parent(p); p = client_get_parent(p));
+ geom.x = p->geom.x + p->bw;
+ geom.y = p->geom.y + p->bw;
+ geom.width = p->geom.width - 2 * p->bw;
+ geom.height = p->geom.height - 2 * p->bw;
+ }
+ }
+ wlr_cursor_map_input_to_region(cursor, dev, &geom);
+ wlr_cursor_map_input_to_output(cursor, dev, selmon->wlr_output);
+}
+
+void
+tablettoolmotion(struct wlr_tablet_v2_tablet_tool *tool, bool change_x, bool change_y,
+ double x, double y, double dx, double dy)
+{
+ struct wlr_surface *surface = NULL;
+ double sx, sy;
+
+ if (!change_x && !change_y)
+ return;
+
+ tabletapplymap(x, y, tablet->wlr_device);
+
+ // TODO: apply constraints
+ switch (tablet_tool->wlr_tool->type) {
+ case WLR_TABLET_TOOL_TYPE_LENS:
+ case WLR_TABLET_TOOL_TYPE_MOUSE:
+ wlr_cursor_move(cursor, tablet->wlr_device, dx, dy);
+ break;
+ default:
+ wlr_cursor_warp_absolute(cursor, tablet->wlr_device, change_x ? x : NAN, change_y ? y : NAN);
+ break;
+ }
+
+ motionnotify(0, NULL, 0, 0, 0, 0);
+
+ xytonode(cursor->x, cursor->y, &surface, NULL, NULL, &sx, &sy);
+ if (surface && !wlr_surface_accepts_tablet_v2(tablet, surface))
+ surface = NULL;
+
+ if (surface != tablet_curr_surface) {
+ if (tablet_curr_surface) {
+ // TODO: wait until all buttons released before leaving
+ if (tablet_tool)
+ wlr_tablet_v2_tablet_tool_notify_proximity_out(tablet_tool);
+ if (tablet_pad)
+ wlr_tablet_v2_tablet_pad_notify_leave(tablet_pad, tablet_curr_surface);
+ wl_list_remove(&destroy_tablet_surface_listener.link);
+ }
+ if (surface) {
+ if (tablet_pad)
+ wlr_tablet_v2_tablet_pad_notify_enter(tablet_pad, tablet, surface);
+ if (tablet_tool)
+ wlr_tablet_v2_tablet_tool_notify_proximity_in(tablet_tool, tablet, surface);
+ wl_signal_add(&surface->events.destroy, &destroy_tablet_surface_listener);
+ }
+ tablet_curr_surface = surface;
+ }
+
+ if (surface)
+ wlr_tablet_v2_tablet_tool_notify_motion(tablet_tool, sx, sy);
+}
+
+void
+tablettoolproximity(struct wl_listener *listener, void *data)
+{
+ struct wlr_tablet_tool_proximity_event *event = data;
+ struct wlr_tablet_tool *tool = event->tool;
+
+ if (!tablet_tool) {
+ tablet_tool = wlr_tablet_tool_create(tablet_mgr, seat, tool);
+ LISTEN_STATIC(&tablet_tool->wlr_tool->events.destroy, destroytablettool);
+ LISTEN_STATIC(&tablet_tool->events.set_cursor, setcursor);
+ }
+
+ switch (event->state) {
+ case WLR_TABLET_TOOL_PROXIMITY_OUT:
+ wlr_tablet_v2_tablet_tool_notify_proximity_out(tablet_tool);
+ destroytabletsurfacenotify(NULL, NULL);
+ break;
+ case WLR_TABLET_TOOL_PROXIMITY_IN:
+ tablettoolmotion(tablet_tool, true, true, event->x, event->y, 0, 0);
+ break;
+ }
+}
+
+void
+tablettoolaxis(struct wl_listener *listener, void *data)
+{
+ struct wlr_tablet_tool_axis_event *event = data;
+
+ tablettoolmotion(tablet_tool,
+ event->updated_axes & WLR_TABLET_TOOL_AXIS_X,
+ event->updated_axes & WLR_TABLET_TOOL_AXIS_Y,
+ event->x, event->y, event->dx, event->dy);
+
+ if (event->updated_axes & WLR_TABLET_TOOL_AXIS_PRESSURE)
+ wlr_tablet_v2_tablet_tool_notify_pressure(tablet_tool, event->pressure);
+ if (event->updated_axes & WLR_TABLET_TOOL_AXIS_DISTANCE)
+ wlr_tablet_v2_tablet_tool_notify_distance(tablet_tool, event->distance);
+ if (event->updated_axes & (WLR_TABLET_TOOL_AXIS_TILT_X | WLR_TABLET_TOOL_AXIS_TILT_Y))
+ wlr_tablet_v2_tablet_tool_notify_tilt(tablet_tool, event->tilt_x, event->tilt_y);
+ if (event->updated_axes & WLR_TABLET_TOOL_AXIS_ROTATION)
+ wlr_tablet_v2_tablet_tool_notify_rotation(tablet_tool, event->rotation);
+ if (event->updated_axes & WLR_TABLET_TOOL_AXIS_SLIDER)
+ wlr_tablet_v2_tablet_tool_notify_slider(tablet_tool, event->slider);
+ if (event->updated_axes & WLR_TABLET_TOOL_AXIS_WHEEL)
+ wlr_tablet_v2_tablet_tool_notify_wheel(tablet_tool, event->wheel_delta, 0);
+}
+
+void
+tablettoolbutton(struct wl_listener *listener, void *data)
+{
+ struct wlr_tablet_tool_button_event *event = data;
+ wlr_tablet_v2_tablet_tool_notify_button(tablet_tool, event->button,
+ (enum zwp_tablet_pad_v2_button_state)event->state);
+}
+
+void
+tablettooltip(struct wl_listener *listener, void *data)
+{
+ struct wlr_tablet_tool_tip_event *event = data;
+
+ if (!tablet_curr_surface) {
+ struct wlr_pointer_button_event fakeptrbtnevent = {
+ .button = BTN_LEFT,
+ .state = event->state == WLR_TABLET_TOOL_TIP_UP ?
+ WL_POINTER_BUTTON_STATE_RELEASED : WL_POINTER_BUTTON_STATE_PRESSED,
+ .time_msec = event->time_msec,
+ };
+ buttonpress(NULL, (void *)&fakeptrbtnevent);
+ }
+
+ if (event->state == WLR_TABLET_TOOL_TIP_UP) {
+ wlr_tablet_v2_tablet_tool_notify_up(tablet_tool);
+ return;
+ }
+
+ wlr_tablet_v2_tablet_tool_notify_down(tablet_tool);
+ wlr_tablet_tool_v2_start_implicit_grab(tablet_tool);
+}
+
+void
tile(Monitor *m)
{
unsigned int h, r, e = m->gaps, mw, my, ty, draw_borders = 1;
diff --git a/patches/tablet-input-0.7.patch b/patches/tablet-input-0.7.patch
new file mode 100644
index 0000000..37fdf8a
--- /dev/null
+++ b/patches/tablet-input-0.7.patch
@@ -0,0 +1,355 @@
+From e504dc0fccfc3994962f03dc824d8907c6afc64f Mon Sep 17 00:00:00 2001
+From: choc <notchoc@proton.me>
+Date: Sat, 4 May 2024 01:16:12 +0800
+Subject: [PATCH] implement wlr-tablet-v2
+
+---
+ Makefile | 6 +-
+ config.def.h | 1 +
+ dwl.c | 224 +++++++++++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 230 insertions(+), 1 deletion(-)
+
+diff --git a/Makefile b/Makefile
+index f955e7b..ce1b556 100644
+--- a/Makefile
++++ b/Makefile
+@@ -21,7 +21,8 @@ dwl: dwl.o util.o
+ $(CC) dwl.o util.o $(DWLCFLAGS) $(LDFLAGS) $(LDLIBS) -o $@
+ dwl.o: dwl.c client.h config.h config.mk cursor-shape-v1-protocol.h \
+ pointer-constraints-unstable-v1-protocol.h wlr-layer-shell-unstable-v1-protocol.h \
+- wlr-output-power-management-unstable-v1-protocol.h xdg-shell-protocol.h
++ wlr-output-power-management-unstable-v1-protocol.h xdg-shell-protocol.h \
++ tablet-v2-protocol.h
+ util.o: util.c util.h
+
+ # wayland-scanner is a tool which generates C headers and rigging for Wayland
+@@ -45,6 +46,9 @@ wlr-output-power-management-unstable-v1-protocol.h:
+ xdg-shell-protocol.h:
+ $(WAYLAND_SCANNER) server-header \
+ $(WAYLAND_PROTOCOLS)/stable/xdg-shell/xdg-shell.xml $@
++tablet-v2-protocol.h:
++ $(WAYLAND_SCANNER) server-header \
++ $(WAYLAND_PROTOCOLS)/unstable/tablet/tablet-unstable-v2.xml $@
+
+ config.h:
+ cp config.def.h $@
+diff --git a/config.def.h b/config.def.h
+index 22d2171..3ad98ef 100644
+--- a/config.def.h
++++ b/config.def.h
+@@ -4,6 +4,7 @@
+ ((hex >> 8) & 0xFF) / 255.0f, \
+ (hex & 0xFF) / 255.0f }
+ /* appearance */
++static const int tabletmaptosurface = 0; /* map tablet input to surface(1) or monitor(0) */
+ static const int sloppyfocus = 1; /* focus follows mouse */
+ static const int bypass_surface_visibility = 0; /* 1 means idle inhibitors will disable idle tracking even if it's surface isn't visible */
+ static const unsigned int borderpx = 1; /* border pixel of windows */
+diff --git a/dwl.c b/dwl.c
+index ac9c36b..b8d129f 100644
+--- a/dwl.c
++++ b/dwl.c
+@@ -50,6 +50,9 @@
+ #include <wlr/types/wlr_session_lock_v1.h>
+ #include <wlr/types/wlr_single_pixel_buffer_v1.h>
+ #include <wlr/types/wlr_subcompositor.h>
++#include <wlr/types/wlr_tablet_tool.h>
++#include <wlr/types/wlr_tablet_pad.h>
++#include <wlr/types/wlr_tablet_v2.h>
+ #include <wlr/types/wlr_viewporter.h>
+ #include <wlr/types/wlr_virtual_keyboard_v1.h>
+ #include <wlr/types/wlr_virtual_pointer_v1.h>
+@@ -270,6 +273,7 @@ static void createnotify(struct wl_listener *listener, void *data);
+ static void createpointer(struct wlr_pointer *pointer);
+ static void createpointerconstraint(struct wl_listener *listener, void *data);
+ static void createpopup(struct wl_listener *listener, void *data);
++static void createtablet(struct wlr_input_device *device);
+ static void cursorconstrain(struct wlr_pointer_constraint_v1 *constraint);
+ static void cursorframe(struct wl_listener *listener, void *data);
+ static void cursorwarptohint(void);
+@@ -284,6 +288,9 @@ static void destroypointerconstraint(struct wl_listener *listener, void *data);
+ static void destroysessionlock(struct wl_listener *listener, void *data);
+ static void destroysessionmgr(struct wl_listener *listener, void *data);
+ static void destroykeyboardgroup(struct wl_listener *listener, void *data);
++static void destroytablet(struct wl_listener *listener, void *data);
++static void destroytabletsurfacenotify(struct wl_listener *listener, void *data);
++static void destroytablettool(struct wl_listener *listener, void *data);
+ static Monitor *dirtomon(enum wlr_direction dir);
+ static void focusclient(Client *c, int lift);
+ static void focusmon(const Arg *arg);
+@@ -337,6 +344,11 @@ static void spawn(const Arg *arg);
+ static void startdrag(struct wl_listener *listener, void *data);
+ static void tag(const Arg *arg);
+ static void tagmon(const Arg *arg);
++static void tablettoolmotion(struct wlr_tablet_v2_tablet_tool *tool, bool change_x, bool change_y, double x, double y, double dx, double dy);
++static void tablettoolproximity(struct wl_listener *listener, void *data);
++static void tablettoolaxis(struct wl_listener *listener, void *data);
++static void tablettoolbutton(struct wl_listener *listener, void *data);
++static void tablettooltip(struct wl_listener *listener, void *data);
+ static void tile(Monitor *m);
+ static void togglefloating(const Arg *arg);
+ static void togglefullscreen(const Arg *arg);
+@@ -396,6 +408,13 @@ static struct wlr_pointer_constraint_v1 *active_constraint;
+ static struct wlr_cursor *cursor;
+ static struct wlr_xcursor_manager *cursor_mgr;
+
++static struct wlr_tablet_manager_v2 *tablet_mgr;
++static struct wlr_tablet_v2_tablet *tablet = NULL;
++static struct wlr_tablet_v2_tablet_tool *tablet_tool = NULL;
++static struct wlr_tablet_v2_tablet_pad *tablet_pad = NULL;
++static struct wlr_surface *tablet_curr_surface = NULL;
++static struct wl_listener destroy_tablet_surface_listener = {.notify = destroytabletsurfacenotify};
++
+ static struct wlr_scene_rect *root_bg;
+ static struct wlr_session_lock_manager_v1 *session_lock_mgr;
+ static struct wlr_scene_rect *locked_bg;
+@@ -1133,6 +1152,28 @@ createpopup(struct wl_listener *listener, void *data)
+ LISTEN_STATIC(&popup->base->surface->events.commit, commitpopup);
+ }
+
++void
++createtablet(struct wlr_input_device *device)
++{
++ if (!tablet) {
++ struct libinput_device *device_handle = NULL;
++ if (!wlr_input_device_is_libinput(device) ||
++ !(device_handle = wlr_libinput_get_device_handle(device)))
++ return;
++
++ tablet = wlr_tablet_create(tablet_mgr, seat, device);
++ LISTEN_STATIC(&tablet->wlr_device->events.destroy, destroytablet);
++ if (libinput_device_config_send_events_get_modes(device_handle)) {
++ libinput_device_config_send_events_set_mode(device_handle, send_events_mode);
++ wlr_cursor_attach_input_device(cursor, device);
++ }
++ } else if (device == tablet->wlr_device) {
++ wlr_log(WLR_ERROR, "createtablet: duplicate device");
++ } else {
++ wlr_log(WLR_ERROR, "createtablet: already have one tablet");
++ }
++}
++
+ void
+ cursorconstrain(struct wlr_pointer_constraint_v1 *constraint)
+ {
+@@ -1321,6 +1362,27 @@ destroykeyboardgroup(struct wl_listener *listener, void *data)
+ free(group);
+ }
+
++void
++destroytablet(struct wl_listener *listener, void *data)
++{
++ tablet = NULL;
++}
++
++void
++destroytabletsurfacenotify(struct wl_listener *listener, void *data)
++{
++ if (tablet_curr_surface)
++ wl_list_remove(&destroy_tablet_surface_listener.link);
++ tablet_curr_surface = NULL;
++}
++
++void
++destroytablettool(struct wl_listener *listener, void *data)
++{
++ destroytabletsurfacenotify(NULL, NULL);
++ tablet_tool = NULL;
++}
++
+ Monitor *
+ dirtomon(enum wlr_direction dir)
+ {
+@@ -1540,6 +1602,12 @@ inputdevice(struct wl_listener *listener, void *data)
+ case WLR_INPUT_DEVICE_POINTER:
+ createpointer(wlr_pointer_from_input_device(device));
+ break;
++ case WLR_INPUT_DEVICE_TABLET:
++ createtablet(device);
++ break;
++ case WLR_INPUT_DEVICE_TABLET_PAD:
++ tablet_pad = wlr_tablet_pad_create(tablet_mgr, seat, device);
++ break;
+ default:
+ /* TODO handle other input device types */
+ break;
+@@ -2567,6 +2635,8 @@ setup(void)
+
+ relative_pointer_mgr = wlr_relative_pointer_manager_v1_create(dpy);
+
++ tablet_mgr = wlr_tablet_v2_create(dpy);
++
+ /*
+ * Creates a cursor, which is a wlroots utility for tracking the cursor
+ * image shown on screen.
+@@ -2596,6 +2666,10 @@ setup(void)
+ LISTEN_STATIC(&cursor->events.button, buttonpress);
+ LISTEN_STATIC(&cursor->events.axis, axisnotify);
+ LISTEN_STATIC(&cursor->events.frame, cursorframe);
++ LISTEN_STATIC(&cursor->events.tablet_tool_proximity, tablettoolproximity);
++ LISTEN_STATIC(&cursor->events.tablet_tool_axis, tablettoolaxis);
++ LISTEN_STATIC(&cursor->events.tablet_tool_button, tablettoolbutton);
++ LISTEN_STATIC(&cursor->events.tablet_tool_tip, tablettooltip);
+
+ cursor_shape_mgr = wlr_cursor_shape_manager_v1_create(dpy, 1);
+ LISTEN_STATIC(&cursor_shape_mgr->events.request_set_shape, setcursorshape);
+@@ -2689,6 +2763,156 @@ tagmon(const Arg *arg)
+ setmon(sel, dirtomon(arg->i), 0);
+ }
+
++void
++tabletapplymap(double x, double y, struct wlr_input_device *dev)
++{
++ Client *p;
++ struct wlr_box geom = {0};
++ if (tabletmaptosurface && tablet_curr_surface) {
++ toplevel_from_wlr_surface(tablet_curr_surface, &p, NULL);
++ if (p) {
++ for (; client_get_parent(p); p = client_get_parent(p));
++ geom.x = p->geom.x + p->bw;
++ geom.y = p->geom.y + p->bw;
++ geom.width = p->geom.width - 2 * p->bw;
++ geom.height = p->geom.height - 2 * p->bw;
++ }
++ }
++ wlr_cursor_map_input_to_region(cursor, dev, &geom);
++ wlr_cursor_map_input_to_output(cursor, dev, selmon->wlr_output);
++}
++
++void
++tablettoolmotion(struct wlr_tablet_v2_tablet_tool *tool, bool change_x, bool change_y,
++ double x, double y, double dx, double dy)
++{
++ struct wlr_surface *surface = NULL;
++ double sx, sy;
++
++ if (!change_x && !change_y)
++ return;
++
++ tabletapplymap(x, y, tablet->wlr_device);
++
++ // TODO: apply constraints
++ switch (tablet_tool->wlr_tool->type) {
++ case WLR_TABLET_TOOL_TYPE_LENS:
++ case WLR_TABLET_TOOL_TYPE_MOUSE:
++ wlr_cursor_move(cursor, tablet->wlr_device, dx, dy);
++ break;
++ default:
++ wlr_cursor_warp_absolute(cursor, tablet->wlr_device, change_x ? x : NAN, change_y ? y : NAN);
++ break;
++ }
++
++ motionnotify(0, NULL, 0, 0, 0, 0);
++
++ xytonode(cursor->x, cursor->y, &surface, NULL, NULL, &sx, &sy);
++ if (surface && !wlr_surface_accepts_tablet_v2(tablet, surface))
++ surface = NULL;
++
++ if (surface != tablet_curr_surface) {
++ if (tablet_curr_surface) {
++ // TODO: wait until all buttons released before leaving
++ if (tablet_tool)
++ wlr_tablet_v2_tablet_tool_notify_proximity_out(tablet_tool);
++ if (tablet_pad)
++ wlr_tablet_v2_tablet_pad_notify_leave(tablet_pad, tablet_curr_surface);
++ wl_list_remove(&destroy_tablet_surface_listener.link);
++ }
++ if (surface) {
++ if (tablet_pad)
++ wlr_tablet_v2_tablet_pad_notify_enter(tablet_pad, tablet, surface);
++ if (tablet_tool)
++ wlr_tablet_v2_tablet_tool_notify_proximity_in(tablet_tool, tablet, surface);
++ wl_signal_add(&surface->events.destroy, &destroy_tablet_surface_listener);
++ }
++ tablet_curr_surface = surface;
++ }
++
++ if (surface)
++ wlr_tablet_v2_tablet_tool_notify_motion(tablet_tool, sx, sy);
++}
++
++void
++tablettoolproximity(struct wl_listener *listener, void *data)
++{
++ struct wlr_tablet_tool_proximity_event *event = data;
++ struct wlr_tablet_tool *tool = event->tool;
++
++ if (!tablet_tool) {
++ tablet_tool = wlr_tablet_tool_create(tablet_mgr, seat, tool);
++ LISTEN_STATIC(&tablet_tool->wlr_tool->events.destroy, destroytablettool);
++ LISTEN_STATIC(&tablet_tool->events.set_cursor, setcursor);
++ }
++
++ switch (event->state) {
++ case WLR_TABLET_TOOL_PROXIMITY_OUT:
++ wlr_tablet_v2_tablet_tool_notify_proximity_out(tablet_tool);
++ destroytabletsurfacenotify(NULL, NULL);
++ break;
++ case WLR_TABLET_TOOL_PROXIMITY_IN:
++ tablettoolmotion(tablet_tool, true, true, event->x, event->y, 0, 0);
++ break;
++ }
++}
++
++void
++tablettoolaxis(struct wl_listener *listener, void *data)
++{
++ struct wlr_tablet_tool_axis_event *event = data;
++
++ tablettoolmotion(tablet_tool,
++ event->updated_axes & WLR_TABLET_TOOL_AXIS_X,
++ event->updated_axes & WLR_TABLET_TOOL_AXIS_Y,
++ event->x, event->y, event->dx, event->dy);
++
++ if (event->updated_axes & WLR_TABLET_TOOL_AXIS_PRESSURE)
++ wlr_tablet_v2_tablet_tool_notify_pressure(tablet_tool, event->pressure);
++ if (event->updated_axes & WLR_TABLET_TOOL_AXIS_DISTANCE)
++ wlr_tablet_v2_tablet_tool_notify_distance(tablet_tool, event->distance);
++ if (event->updated_axes & (WLR_TABLET_TOOL_AXIS_TILT_X | WLR_TABLET_TOOL_AXIS_TILT_Y))
++ wlr_tablet_v2_tablet_tool_notify_tilt(tablet_tool, event->tilt_x, event->tilt_y);
++ if (event->updated_axes & WLR_TABLET_TOOL_AXIS_ROTATION)
++ wlr_tablet_v2_tablet_tool_notify_rotation(tablet_tool, event->rotation);
++ if (event->updated_axes & WLR_TABLET_TOOL_AXIS_SLIDER)
++ wlr_tablet_v2_tablet_tool_notify_slider(tablet_tool, event->slider);
++ if (event->updated_axes & WLR_TABLET_TOOL_AXIS_WHEEL)
++ wlr_tablet_v2_tablet_tool_notify_wheel(tablet_tool, event->wheel_delta, 0);
++}
++
++void
++tablettoolbutton(struct wl_listener *listener, void *data)
++{
++ struct wlr_tablet_tool_button_event *event = data;
++ wlr_tablet_v2_tablet_tool_notify_button(tablet_tool, event->button,
++ (enum zwp_tablet_pad_v2_button_state)event->state);
++}
++
++void
++tablettooltip(struct wl_listener *listener, void *data)
++{
++ struct wlr_tablet_tool_tip_event *event = data;
++
++ if (!tablet_curr_surface) {
++ struct wlr_pointer_button_event fakeptrbtnevent = {
++ .button = BTN_LEFT,
++ .state = event->state == WLR_TABLET_TOOL_TIP_UP ?
++ WL_POINTER_BUTTON_STATE_RELEASED : WL_POINTER_BUTTON_STATE_PRESSED,
++ .time_msec = event->time_msec,
++ };
++ buttonpress(NULL, (void *)&fakeptrbtnevent);
++ }
++
++ if (event->state == WLR_TABLET_TOOL_TIP_UP) {
++ wlr_tablet_v2_tablet_tool_notify_up(tablet_tool);
++ return;
++ }
++
++ wlr_tablet_v2_tablet_tool_notify_down(tablet_tool);
++ wlr_tablet_tool_v2_start_implicit_grab(tablet_tool);
++}
++
+ void
+ tile(Monitor *m)
+ {
+--
+2.43.0
+