Created
August 26, 2025 07:12
-
-
Save Yappaholic/a34cbe3cc07d781ef8032795d112b2f0 to your computer and use it in GitHub Desktop.
Tablet patch for the Mangowc (DWL fork)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| From a649365d12b236af9a096d37484cfc82168d28b2 Mon Sep 17 00:00:00 2001 | |
| From: Yappaholic <[email protected]> | |
| Date: Tue, 26 Aug 2025 10:03:12 +0300 | |
| Subject: [PATCH] nothing | |
| --- | |
| src/ext-protocol/all.h | 3 +- | |
| src/ext-protocol/tablet.h | 220 ++++++++++++++++++++++++++++++++++++++ | |
| src/mango.c | 15 +++ | |
| 3 files changed, 237 insertions(+), 1 deletion(-) | |
| create mode 100644 src/ext-protocol/tablet.h | |
| diff --git a/src/ext-protocol/all.h b/src/ext-protocol/all.h | |
| index c657e0d..6b54c33 100644 | |
| --- a/src/ext-protocol/all.h | |
| +++ b/src/ext-protocol/all.h | |
| @@ -1,3 +1,4 @@ | |
| #include "dwl-ipc.h" | |
| #include "foreign-toplevel.h" | |
| -#include "text-input.h" | |
| \ No newline at end of file | |
| +#include "tablet.h" | |
| +#include "text-input.h" | |
| diff --git a/src/ext-protocol/tablet.h b/src/ext-protocol/tablet.h | |
| new file mode 100644 | |
| index 0000000..bd67973 | |
| --- /dev/null | |
| +++ b/src/ext-protocol/tablet.h | |
| @@ -0,0 +1,220 @@ | |
| +#include <wlr/types/wlr_tablet_pad.h> | |
| +#include <wlr/types/wlr_tablet_tool.h> | |
| +#include <wlr/types/wlr_tablet_v2.h> | |
| + | |
| +static const int tabletmaptosurface = | |
| + 0; /* map tablet input to surface(1) or monitor(0) */ | |
| + | |
| +static void createtablet(struct wlr_input_device *device); | |
| +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 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 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 wl_listener tablet_device_destroy = {.notify = destroytablet}; | |
| +static struct wl_listener tablet_tool_axis = {.notify = tablettoolaxis}; | |
| +static struct wl_listener tablet_tool_button = {.notify = tablettoolbutton}; | |
| +static struct wl_listener tablet_tool_destroy = {.notify = destroytablettool}; | |
| +static struct wl_listener tablet_tool_proximity = {.notify = | |
| + tablettoolproximity}; | |
| +static struct wl_listener tablet_tool_tip = {.notify = tablettooltip}; | |
| + | |
| +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); | |
| + wl_signal_add(&tablet->wlr_device->events.destroy, | |
| + &tablet_device_destroy); | |
| + 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 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; | |
| +} | |
| + | |
| +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(surface, tablet)) | |
| + 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); | |
| + wl_signal_add(&tablet_tool->wlr_tool->events.destroy, | |
| + &tablet_tool_destroy); | |
| + wl_signal_add(&tablet_tool->events.set_cursor, &request_cursor); | |
| + } | |
| + | |
| + 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)) { | |
| + printf("DEBUGGING: In axis event handling\n"); | |
| + 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); | |
| +} | |
| diff --git a/src/mango.c b/src/mango.c | |
| index 3700441..7e2e818 100644 | |
| --- a/src/mango.c | |
| +++ b/src/mango.c | |
| @@ -67,6 +67,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_pad.h> | |
| +#include <wlr/types/wlr_tablet_tool.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> | |
| @@ -3017,6 +3020,12 @@ void inputdevice(struct wl_listener *listener, void *data) { | |
| case WLR_INPUT_DEVICE_KEYBOARD: | |
| createkeyboard(wlr_keyboard_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; | |
| case WLR_INPUT_DEVICE_POINTER: | |
| createpointer(wlr_pointer_from_input_device(device)); | |
| break; | |
| @@ -4422,6 +4431,7 @@ void setup(void) { | |
| dpy = wl_display_create(); | |
| event_loop = wl_display_get_event_loop(dpy); | |
| pointer_manager = wlr_relative_pointer_manager_v1_create(dpy); | |
| + tablet_mgr = wlr_tablet_v2_create(dpy); | |
| /* The backend is a wlroots feature which abstracts the underlying input and | |
| * output hardware. The autocreate option will choose the most suitable | |
| * backend based on the current environment, such as opening an X11 window | |
| @@ -4594,6 +4604,11 @@ void setup(void) { | |
| wl_signal_add(&cursor->events.button, &cursor_button); | |
| wl_signal_add(&cursor->events.axis, &cursor_axis); | |
| wl_signal_add(&cursor->events.frame, &cursor_frame); | |
| + wl_signal_add(&cursor->events.tablet_tool_proximity, | |
| + &tablet_tool_proximity); | |
| + wl_signal_add(&cursor->events.tablet_tool_axis, &tablet_tool_axis); | |
| + wl_signal_add(&cursor->events.tablet_tool_button, &tablet_tool_button); | |
| + wl_signal_add(&cursor->events.tablet_tool_tip, &tablet_tool_tip); | |
| // 这两句代码会造成obs窗口里的鼠标光标消失,不知道注释有什么影响 | |
| cursor_shape_mgr = wlr_cursor_shape_manager_v1_create(dpy, 1); | |
| -- | |
| 2.49.1 | |
I tried this patch but if I remove the tablet from USB it crashes mango.
Aug 28 02:01:27 torment systemd-coredump[1002195]: Process 30397 (mango) of user 1000 terminated abnormally with signal 6/ABRT, processing... Aug 28 02:01:27 torment kernel: usb 3-3.4.1: USB disconnect, device number 13 Aug 28 02:01:22 torment kernel: input: Wacom Intuos Pro M Finger as /devices/pci0000:00/0000:00:08.1/0000:2d:00.3/usb3/3-3/3-3.4/3-3.4.1/3-3.4.1:1.1/0003:056A:0315.0013/input/input49 Aug 28 02:01:22 torment kernel: wacom 0003:056A:0315.0013: hidraw7: USB HID v1.10 Device [Wacom Co.,Ltd. Intuos5 touch M] on usb-0000:2d:00.3-3.4.1/input1 Aug 28 02:01:22 torment kernel: input: Wacom Intuos Pro M Pad as /devices/pci0000:00/0000:00:08.1/0000:2d:00.3/usb3/3-3/3-3.4/3-3.4.1/3-3.4.1:1.0/0003:056A:0315.0012/input/input47 Aug 28 02:01:22 torment kernel: input: Wacom Intuos Pro M Pen as /devices/pci0000:00/0000:00:08.1/0000:2d:00.3/usb3/3-3/3-3.4/3-3.4.1/3-3.4.1:1.0/0003:056A:0315.0012/input/input45 Aug 28 02:01:22 torment kernel: wacom 0003:056A:0315.0012: hidraw6: USB HID v1.10 Device [Wacom Co.,Ltd. Intuos5 touch M] on usb-0000:2d:00.3-3.4.1/input0 Aug 28 02:01:22 torment kernel: usb 3-3.4.1: Manufacturer: Wacom Co.,Ltd. Aug 28 02:01:22 torment kernel: usb 3-3.4.1: Product: Intuos5 touch M Aug 28 02:01:22 torment kernel: usb 3-3.4.1: New USB device strings: Mfr=1, Product=2, SerialNumber=0 Aug 28 02:01:22 torment kernel: usb 3-3.4.1: New USB device found, idVendor=056a, idProduct=0315, bcdDevice= 1.00 Aug 28 02:01:22 torment kernel: usb 3-3.4.1: new full-speed USB device number 13 using xhci_hcdAug 28 02:01:33 torment systemd-coredump[1002197]: [🡕] Process 30397 (mango) of user 1000 dumped core. Stack trace of thread 30397: #0 0x00007f6e1ac8274c n/a (libc.so.6 + 0x9774c) #1 0x00007f6e1ac28dc0 raise (libc.so.6 + 0x3ddc0) #2 0x00007f6e1ac1057a abort (libc.so.6 + 0x2557a) #3 0x00007f6e1ac104e3 n/a (libc.so.6 + 0x254e3) #4 0x00007f6e1af5d841 n/a (libwlroots-0.19.so + 0x24841) #5 0x00007f6e1afe0f74 wlr_tablet_finish (libwlroots-0.19.so + 0xa7f74) #6 0x00007f6e1af94a67 n/a (libwlroots-0.19.so + 0x5ba67) #7 0x00007f6e1af9578a n/a (libwlroots-0.19.so + 0x5c78a) #8 0x00007f6e1b07c112 wl_event_loop_dispatch (libwayland-server.so.0 + 0xa112) #9 0x00007f6e1b07e1f7 wl_display_run (libwayland-server.so.0 + 0xc1f7) #10 0x0000562018f0677f run (/usr/bin/mango + 0x4077f) #11 0x0000562018f0b5a9 main (/usr/bin/mango + 0x455a9) #12 0x00007f6e1ac126b5 n/a (libc.so.6 + 0x276b5) #13 0x00007f6e1ac12769 __libc_start_main (libc.so.6 + 0x27769) #14 0x0000562018ed8c45 _start (/usr/bin/mango + 0x12c45) ELF object binary architecture: AMD x86-64Could try to get more information about it but not sure what would be needed. does seem mostly related to this call
#5 0x00007f6e1afe0f74 wlr_tablet_finish (libwlroots-0.19.so + 0xa7f74)
Yes, this is a known bug from the DWL tablet patch and it is referenced in the Mangowc github issue. All I did was port the DWL patch and I don't have enough time right now to fix this issue.
Thanks for posting this patch though, just wanted to mention it if anyone was seeing this gist. I could try to poke at it as it seems somewhat easily replicatable but i don't know c.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I tried this patch but if I remove the tablet from USB it crashes mango.
Could try to get more information about it but not sure what would be needed. does seem mostly related to this call