From 2d05cd098f134ede4678d6506a00afd29a7807ec Mon Sep 17 00:00:00 2001 From: Zsolt Harsing Date: Fri, 22 May 2026 19:53:01 +0100 Subject: [PATCH 1/3] touch and pen support - proof of concept --- input.c | 340 ++++++++++++++++++++++++++++++++++++++++++++++++---- input.h | 9 ++ meson.build | 1 + somewm.c | 28 ++++- somewm.h | 4 +- 5 files changed, 357 insertions(+), 25 deletions(-) diff --git a/input.c b/input.c index 64faa2e4..b01ee278 100644 --- a/input.c +++ b/input.c @@ -28,6 +28,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -91,7 +94,7 @@ void gesturepinchend(struct wl_listener *listener, void *data); void gestureholdbegin(struct wl_listener *listener, void *data); void gestureholdend(struct wl_listener *listener, void *data); void destroypointerconstraint(struct wl_listener *listener, void *data); -static void destroytrackedpointer(struct wl_listener *listener, void *data); +static void destroytrackeddevice(struct wl_listener *listener, void *data); void motionnotify(uint32_t time, struct wlr_input_device *device, double dx, double dy, double dx_unaccel, double dy_unaccel); void pointerfocus(Client *c, struct wlr_surface *surface, double sx, double sy, @@ -99,7 +102,7 @@ void pointerfocus(Client *c, struct wlr_surface *surface, double sx, double sy, int keybinding(uint32_t mods, uint32_t keycode, xkb_keysym_t sym, xkb_keysym_t base_sym); void createkeyboard(struct wlr_keyboard *keyboard); KeyboardGroup *createkeyboardgroup(void); -void createpointer(struct wlr_pointer *pointer); +void createtrackeddevice(struct wlr_input_device *base_device); void cursorconstrain(struct wlr_pointer_constraint_v1 *constraint); void cursorwarptohint(void); void apply_input_settings_to_device(struct libinput_device *device); @@ -116,18 +119,27 @@ void setpsel(struct wl_listener *listener, void *data); void setsel(struct wl_listener *listener, void *data); void requeststartdrag(struct wl_listener *listener, void *data); void startdrag(struct wl_listener *listener, void *data); +void touchdown(struct wl_listener *listener, void *data); +void touchmotion(struct wl_listener *listener, void *data); +void touchup(struct wl_listener *listener, void *data); +void touchcancel(struct wl_listener *listener, void *data); +void touchframe(struct wl_listener *listener, void *data); +void tabletaxis(struct wl_listener *listener, void *data); +void tablettip(struct wl_listener *listener, void *data); +void tabletproximity(struct wl_listener *listener, void *data); +void tabletbutton(struct wl_listener *listener, void *data); void destroydrag(struct wl_listener *listener, void *data); void destroydragicon(struct wl_listener *listener, void *data); void xytonode(double x, double y, struct wlr_surface **psurface, Client **pc, LayerSurface **pl, drawin_t **pd, drawable_t **pdrawable, double *nx, double *ny); -/* Tracked pointer device for runtime libinput configuration */ +/* Tracked Input Device for runtime libinput configuration */ typedef struct { struct libinput_device *libinput_dev; struct wl_listener destroy; struct wl_list link; -} TrackedPointer; +} TrackedInputDevice; /* Listener structs */ struct wl_listener cursor_axis = {.notify = axisnotify}; @@ -153,6 +165,15 @@ struct wl_listener request_set_sel = {.notify = setsel}; struct wl_listener request_set_cursor_shape = {.notify = setcursorshape}; struct wl_listener request_start_drag = {.notify = requeststartdrag}; struct wl_listener start_drag = {.notify = startdrag}; +struct wl_listener touch_down = {.notify = touchdown}; +struct wl_listener touch_motion = {.notify = touchmotion}; +struct wl_listener touch_up = {.notify = touchup}; +struct wl_listener touch_cancel = {.notify = touchcancel}; +struct wl_listener touch_frame = {.notify = touchframe}; +struct wl_listener tablet_axis = {.notify = tabletaxis}; +struct wl_listener tablet_tip = {.notify = tablettip}; +struct wl_listener tablet_proximity = {.notify = tabletproximity}; +struct wl_listener tablet_button = {.notify = tabletbutton}; void gestureswipebegin(struct wl_listener *listener, void *data) @@ -686,6 +707,277 @@ motionabsolute(struct wl_listener *listener, void *data) motionnotify(event->time_msec, &event->pointer->base, dx, dy, dx, dy); } +void +touchdown(struct wl_listener *listener, void *data) +{ + struct wlr_touch_down_event *event = data; + wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); + some_notify_activity(); + + double lx, ly, sx, sy; + struct wlr_surface *surface = NULL; + Client *c = NULL; + + wlr_cursor_absolute_to_layout_coords(cursor, &event->touch->base, + event->x, event->y, &lx, &ly); + + wlr_cursor_warp_closest(cursor, &event->touch->base, lx, ly); + + xytonode(lx, ly, &surface, &c, NULL, NULL, NULL, &sx, &sy); + + if (c && !surface) { + struct wlr_pointer_button_event button_event = { + .time_msec = event->time_msec, + .button = BTN_LEFT, + .state = WL_POINTER_BUTTON_STATE_PRESSED + }; + buttonpress(NULL, &button_event); + return; + } + + if (surface) { + wlr_seat_touch_notify_down(seat, surface, event->time_msec, + event->touch_id, sx, sy); + } +} + +void +touchmotion(struct wl_listener *listener, void *data) +{ + struct wlr_touch_motion_event *event = data; + wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); + some_notify_activity(); + + double lx, ly, sx, sy; + struct wlr_surface *surface = NULL; + Client *c = NULL; + + wlr_cursor_absolute_to_layout_coords(cursor, &event->touch->base, + event->x, event->y, &lx, &ly); + + double old_x = cursor->x; + double old_y = cursor->y; + wlr_cursor_warp_closest(cursor, &event->touch->base, lx, ly); + + xytonode(lx, ly, &surface, &c, NULL, NULL, NULL, &sx, &sy); + + if (cursor_mode == CurPressed || (c && !surface)) { + double dx = cursor->x - old_x; + double dy = cursor->y - old_y; + motionnotify(event->time_msec, &event->touch->base, dx, dy, dx, dy); + return; + } + + if (surface) { + wlr_seat_touch_notify_motion(seat, event->time_msec, event->touch_id, sx, sy); + } +} + +void +touchup(struct wl_listener *listener, void *data) +{ + struct wlr_touch_up_event *event = data; + wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); + some_notify_activity(); + + if (cursor_mode == CurPressed) { + struct wlr_pointer_button_event button_event = { + .time_msec = event->time_msec, + .button = BTN_LEFT, + .state = WL_POINTER_BUTTON_STATE_RELEASED + }; + buttonpress(NULL, &button_event); + } + + wlr_seat_touch_notify_up(seat, event->time_msec, event->touch_id); +} + +void +touchcancel(struct wl_listener *listener, void *data) { + + struct wlr_touch_cancel_event *event = data; + wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); + some_notify_activity(); + + struct wlr_touch_point *point; + struct wlr_seat_client *seat_client = NULL; + + wl_list_for_each(point, &seat->touch_state.touch_points, link) { + if (point->touch_id == event->touch_id) { + seat_client = point->focus_client; + break; + } + } + + if (seat_client != NULL) { + wlr_seat_touch_notify_cancel(seat, seat_client); + } +} + + +void +touchframe(struct wl_listener *listener, void *data) +{ + wlr_seat_touch_notify_frame(seat); +} + +/* ############### TABLET ############### */ + +bool tabletisokay(struct wlr_tablet *tablet, struct wlr_tablet_tool *tool) { + + if (!tablet_mgr) return false; + + if (!tablet->data) { + tablet->data = wlr_tablet_create(tablet_mgr, seat, &tablet->base); + } + + if (!tool->data) { + tool->data = wlr_tablet_tool_create(tablet_mgr, seat, tool); + } + + return true; +} + +void +tabletaxis(struct wl_listener *listener, void *data) +{ + struct wlr_tablet_tool_axis_event *event = data; + wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); + some_notify_activity(); + + if (!tabletisokay(event->tablet, event->tool)) return; + + struct wlr_tablet_v2_tablet_tool *v2_tool = event->tool->data; + + if (event->updated_axes & (WLR_TABLET_TOOL_AXIS_X | WLR_TABLET_TOOL_AXIS_Y)) { + double lx, ly, sx, sy; + struct wlr_surface *surface = NULL; + Client *c = NULL; + + wlr_cursor_absolute_to_layout_coords(cursor, &event->tablet->base, + event->x, event->y, &lx, &ly); + + lx = (event->updated_axes & WLR_TABLET_TOOL_AXIS_X) ? lx : cursor->x; + ly = (event->updated_axes & WLR_TABLET_TOOL_AXIS_Y) ? ly : cursor->y; + + wlr_cursor_warp_closest(cursor, &event->tablet->base, lx, ly); + + xytonode(lx, ly, &surface, &c, NULL, NULL, NULL, &sx, &sy); + motionnotify(event->time_msec, &event->tablet->base, 0, 0, 0, 0); + + if (surface) { + wlr_tablet_v2_tablet_tool_notify_motion(v2_tool, sx, sy); + } + } + + if (event->updated_axes & WLR_TABLET_TOOL_AXIS_PRESSURE) { + wlr_tablet_v2_tablet_tool_notify_pressure(v2_tool, event->pressure); + } + if (event->updated_axes & (WLR_TABLET_TOOL_AXIS_TILT_X | WLR_TABLET_TOOL_AXIS_TILT_Y)) { + wlr_tablet_v2_tablet_tool_notify_tilt(v2_tool, event->tilt_x, event->tilt_y); + } + if (event->updated_axes & WLR_TABLET_TOOL_AXIS_DISTANCE) { + wlr_tablet_v2_tablet_tool_notify_distance(v2_tool, event->distance); + } +} + +void +tabletproximity(struct wl_listener *listener, void *data) +{ + struct wlr_tablet_tool_proximity_event *event = data; + wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); + some_notify_activity(); + + + if (!tabletisokay(event->tablet, event->tool)) return; + + struct wlr_tablet_v2_tablet_tool *v2_tool = event->tool->data; + + double lx, ly, sx, sy; + struct wlr_surface *surface = NULL; + Client *c = NULL; + + wlr_cursor_absolute_to_layout_coords(cursor, &event->tablet->base, + event->x, event->y, &lx, &ly); + + struct wlr_tablet_v2_tablet *v2_tablet = event->tablet->data; + + if (event->state == WLR_TABLET_TOOL_PROXIMITY_IN) { + wlr_cursor_warp_closest(cursor, &event->tablet->base, lx, ly); + xytonode(lx, ly, &surface, &c, NULL, NULL, NULL, &sx, &sy); + motionnotify(event->time_msec, &event->tablet->base, 0, 0, 0, 0); + + if (surface) { + wlr_tablet_v2_tablet_tool_notify_proximity_in(v2_tool, v2_tablet, surface); + wlr_tablet_v2_tablet_tool_notify_motion(v2_tool, sx, sy); + } + } else { + wlr_tablet_v2_tablet_tool_notify_proximity_out(v2_tool); + } +} + +void +tablettip(struct wl_listener *listener, void *data) +{ + struct wlr_tablet_tool_tip_event *event = data; + wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); + some_notify_activity(); + + if (!tabletisokay(event->tablet, event->tool)) return; + + struct wlr_tablet_v2_tablet_tool *v2_tool = event->tool->data; + + double lx, ly, sx, sy; + struct wlr_surface *surface = NULL; + Client *c = NULL; + + wlr_cursor_absolute_to_layout_coords(cursor, &event->tablet->base, + event->x, event->y, &lx, &ly); + wlr_cursor_warp_closest(cursor, &event->tablet->base, lx, ly); + xytonode(lx, ly, &surface, &c, NULL, NULL, NULL, &sx, &sy); + + if (surface) { + wlr_tablet_v2_tablet_tool_notify_motion(v2_tool, sx, sy); + } + + switch (event->state) { + case WLR_TABLET_TOOL_TIP_DOWN: + if (surface && !wlr_surface_accepts_tablet_v2(surface, event->tablet->data)) { + wlr_seat_pointer_notify_button(seat, event->time_msec, BTN_LEFT, WL_POINTER_BUTTON_STATE_PRESSED); + } else { + wlr_tablet_v2_tablet_tool_notify_down(v2_tool); + } + break; + case WLR_TABLET_TOOL_TIP_UP: + if (surface && !wlr_surface_accepts_tablet_v2(surface, event->tablet->data)) { + wlr_seat_pointer_notify_button(seat, event->time_msec, BTN_LEFT, WL_POINTER_BUTTON_STATE_RELEASED); + } else { + wlr_tablet_v2_tablet_tool_notify_up(v2_tool); + } + break; + default: + // just in case + break; + } +} + +void +tabletbutton(struct wl_listener *listener, void *data) +{ + struct wlr_tablet_tool_button_event *event = data; + wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); + some_notify_activity(); + + if (!tabletisokay(event->tablet, event->tool)) return; + + struct wlr_tablet_v2_tablet_tool *v2_tool = event->tool->data; + + // this is nonsense. might be an issue in wlroots? + // event->state type might be wrong.. + wlr_tablet_v2_tablet_tool_notify_button(v2_tool, event->button, + (enum zwp_tablet_pad_v2_button_state)event->state); +} + /* Helper function to emit mouse::leave on the object that previously had the mouse. * Also clears drawable_under_mouse tracking to emit leave on the drawable. */ void @@ -1341,7 +1633,9 @@ inputdevice(struct wl_listener *listener, void *data) createkeyboard(wlr_keyboard_from_input_device(device)); break; case WLR_INPUT_DEVICE_POINTER: - createpointer(wlr_pointer_from_input_device(device)); + case WLR_INPUT_DEVICE_TOUCH: + case WLR_INPUT_DEVICE_TABLET: + createtrackeddevice(device); break; default: /* TODO handle other input device types */ @@ -1355,6 +1649,8 @@ inputdevice(struct wl_listener *listener, void *data) caps = WL_SEAT_CAPABILITY_POINTER; if (!wl_list_empty(&kb_group->wlr_group->devices)) caps |= WL_SEAT_CAPABILITY_KEYBOARD; + caps |= WL_SEAT_CAPABILITY_TOUCH; + wlr_seat_set_capabilities(seat, caps); } @@ -1612,41 +1908,41 @@ apply_input_settings_to_device(struct libinput_device *device) void apply_input_settings_to_all_devices(void) { - TrackedPointer *tp; - wl_list_for_each(tp, &tracked_pointers, link) { - apply_input_settings_to_device(tp->libinput_dev); + TrackedInputDevice *td; + wl_list_for_each(td, &tracked_input_devices, link) { + apply_input_settings_to_device(td->libinput_dev); } } void -createpointer(struct wlr_pointer *pointer) +createtrackeddevice(struct wlr_input_device *base_device) { struct libinput_device *device; - TrackedPointer *tp; + TrackedInputDevice *td; - if (wlr_input_device_is_libinput(&pointer->base) - && (device = wlr_libinput_get_device_handle(&pointer->base))) { + if (wlr_input_device_is_libinput(base_device) + && (device = wlr_libinput_get_device_handle(base_device))) { /* Apply settings from globalconf */ apply_input_settings_to_device(device); /* Track this device for runtime reconfiguration */ - tp = ecalloc(1, sizeof(*tp)); - tp->libinput_dev = device; - wl_list_insert(&tracked_pointers, &tp->link); - LISTEN(&pointer->base.events.destroy, &tp->destroy, destroytrackedpointer); + td = ecalloc(1, sizeof(*td)); + td->libinput_dev = device; + wl_list_insert(&tracked_input_devices, &td->link); + LISTEN(&base_device->events.destroy, &td->destroy, destroytrackeddevice); } - wlr_cursor_attach_input_device(cursor, &pointer->base); + wlr_cursor_attach_input_device(cursor, base_device); } static void -destroytrackedpointer(struct wl_listener *listener, void *data) +destroytrackeddevice(struct wl_listener *listener, void *data) { - TrackedPointer *tp = wl_container_of(listener, tp, destroy); - wl_list_remove(&tp->destroy.link); - wl_list_remove(&tp->link); - free(tp); + TrackedInputDevice *td = wl_container_of(listener, td, destroy); + wl_list_remove(&td->destroy.link); + wl_list_remove(&td->link); + free(td); } void diff --git a/input.h b/input.h index f00410a6..66281b47 100644 --- a/input.h +++ b/input.h @@ -85,6 +85,15 @@ extern struct wl_listener gesture_pinch_update; extern struct wl_listener gesture_pinch_end; extern struct wl_listener gesture_hold_begin; extern struct wl_listener gesture_hold_end; +extern struct wl_listener touch_down; +extern struct wl_listener touch_motion; +extern struct wl_listener touch_up; +extern struct wl_listener touch_cancel; +extern struct wl_listener touch_frame; +extern struct wl_listener tablet_axis; +extern struct wl_listener tablet_tip; +extern struct wl_listener tablet_proximity; +extern struct wl_listener tablet_button; /* Request listeners (registered in setup()) */ extern struct wl_listener request_cursor; diff --git a/meson.build b/meson.build index b806fb07..1b4ce0ac 100644 --- a/meson.build +++ b/meson.build @@ -154,6 +154,7 @@ protocols = [ [wayland_protocols_dir / 'staging/cursor-shape/cursor-shape-v1.xml', 'enum-header'], [wayland_protocols_dir / 'unstable/pointer-constraints/pointer-constraints-unstable-v1.xml', 'enum-header'], [wayland_protocols_dir / 'stable/xdg-shell/xdg-shell.xml', 'server-header'], + [wayland_protocols_dir / 'stable/tablet/tablet-v2.xml', 'server-header'], # Local protocols ['protocols/wlr-layer-shell-unstable-v1.xml', 'enum-header'], ['protocols/wlr-output-power-management-unstable-v1.xml', 'server-header'], diff --git a/somewm.c b/somewm.c index 0313b891..2b15d524 100644 --- a/somewm.c +++ b/somewm.c @@ -54,6 +54,7 @@ #include #include #include +#include #include #include #include @@ -156,6 +157,7 @@ struct wlr_pointer_constraint_v1 *active_constraint; struct wlr_cursor *cursor; struct wlr_xcursor_manager *cursor_mgr; +struct wlr_tablet_manager_v2 *tablet_mgr; char* selected_root_cursor; struct wlr_scene_rect *root_bg; @@ -171,7 +173,7 @@ int new_client_placement = 0; /* 0 = master (default), 1 = slave */ struct wlr_output_layout *output_layout; struct wlr_box sgeom; struct wl_list mons; -struct wl_list tracked_pointers; /* For runtime libinput config */ +struct wl_list tracked_input_devices; /* For runtime libinput config */ Monitor *selmon; /* in_updatemons, updatemons_pending: owned by monitor.c */ @@ -342,6 +344,15 @@ cleanuplisteners(void) wl_list_remove(&gesture_pinch_end.link); wl_list_remove(&gesture_hold_begin.link); wl_list_remove(&gesture_hold_end.link); + wl_list_remove(&touch_down.link); + wl_list_remove(&touch_motion.link); + wl_list_remove(&touch_up.link); + wl_list_remove(&touch_cancel.link); + wl_list_remove(&touch_frame.link); + wl_list_remove(&tablet_axis.link); + wl_list_remove(&tablet_proximity.link); + wl_list_remove(&tablet_tip.link); + wl_list_remove(&tablet_button.link); wl_list_remove(&gpu_reset.link); wl_list_remove(&new_idle_inhibitor.link); wl_list_remove(&layout_change.link); @@ -1113,7 +1124,7 @@ setup(void) /* Configure a listener to be notified when new outputs are available on the * backend. */ wl_list_init(&mons); - wl_list_init(&tracked_pointers); + wl_list_init(&tracked_input_devices); wl_signal_add(&backend->events.new_output, &new_output); /* Set up our client lists, the xdg-shell and the layer-shell. The xdg-shell is a @@ -1179,6 +1190,8 @@ setup(void) } cursor_mgr = wlr_xcursor_manager_create(cursor_theme, cursor_size); + tablet_mgr = wlr_tablet_v2_create(dpy); + /* * wlr_cursor *only* displays an image on screen. It does not move around * when the pointer moves. However, we can attach input devices to it, and @@ -1204,6 +1217,17 @@ setup(void) wl_signal_add(&cursor->events.hold_begin, &gesture_hold_begin); wl_signal_add(&cursor->events.hold_end, &gesture_hold_end); + wl_signal_add(&cursor->events.touch_down, &touch_down); + wl_signal_add(&cursor->events.touch_motion, &touch_motion); + wl_signal_add(&cursor->events.touch_up, &touch_up); + wl_signal_add(&cursor->events.touch_cancel, &touch_cancel); + wl_signal_add(&cursor->events.touch_frame, &touch_frame); + + wl_signal_add(&cursor->events.tablet_tool_axis, &tablet_axis); + wl_signal_add(&cursor->events.tablet_tool_proximity, &tablet_proximity); + wl_signal_add(&cursor->events.tablet_tool_tip, &tablet_tip); + wl_signal_add(&cursor->events.tablet_tool_button, &tablet_button); + cursor_shape_mgr = wlr_cursor_shape_manager_v1_create(dpy, 1); wl_signal_add(&cursor_shape_mgr->events.request_set_shape, &request_set_cursor_shape); diff --git a/somewm.h b/somewm.h index d261d3c2..45193dbd 100644 --- a/somewm.h +++ b/somewm.h @@ -46,6 +46,7 @@ struct wlr_seat; struct wlr_session; struct wlr_session_lock_manager_v1; struct wlr_session_lock_v1; +struct wlr_tablet_manager_v2; struct wlr_virtual_keyboard_manager_v1; struct wlr_virtual_pointer_manager_v1; struct wlr_xcursor_manager; @@ -88,6 +89,7 @@ extern struct wlr_cursor_shape_manager_v1 *cursor_shape_mgr; /* Input state */ extern struct wlr_cursor *cursor; extern struct wlr_xcursor_manager *cursor_mgr; +extern struct wlr_tablet_manager_v2 *tablet_mgr; extern struct wlr_seat *seat; extern KeyboardGroup *kb_group; extern void *exclusive_focus; @@ -106,7 +108,7 @@ extern struct wlr_output_layout *output_layout; extern struct wl_list mons; extern Monitor *selmon; extern struct wlr_box sgeom; -extern struct wl_list tracked_pointers; +extern struct wl_list tracked_input_devices; /* Scene elements */ extern struct wlr_scene_tree *drag_icon; From f3761b68387b27813d349838f2e561abc580080b Mon Sep 17 00:00:00 2001 From: Zsolt Harsing Date: Thu, 11 Jun 2026 23:43:50 +0100 Subject: [PATCH 2/3] input to output mapping, cleanup --- globalconf.h | 1 + input.c | 253 +++++++++++++++++--------------------------- lua/awful/input.lua | 3 + luaa.c | 7 ++ somewm.c | 1 + 5 files changed, 107 insertions(+), 158 deletions(-) diff --git a/globalconf.h b/globalconf.h index ada2b2d8..f1778894 100644 --- a/globalconf.h +++ b/globalconf.h @@ -121,6 +121,7 @@ typedef struct InputSettings { char *tap_button_map; /* "lrm", "lmr" */ char *clickfinger_button_map; /* "lrm", "lmr" */ bool accel_speed_set; /* true if accel_speed was explicitly set */ + char *output; /* string name of associated output */ } InputSettings; /** Per-device input rule (evaluated in order, last match wins per property) */ diff --git a/input.c b/input.c index b01ee278..e261cd86 100644 --- a/input.c +++ b/input.c @@ -105,7 +105,6 @@ KeyboardGroup *createkeyboardgroup(void); void createtrackeddevice(struct wlr_input_device *base_device); void cursorconstrain(struct wlr_pointer_constraint_v1 *constraint); void cursorwarptohint(void); -void apply_input_settings_to_device(struct libinput_device *device); void mouse_emit_leave(lua_State *L); void mouse_emit_client_enter(lua_State *L, client_t *c); void mouse_emit_drawin_enter(lua_State *L, drawin_t *d); @@ -137,10 +136,13 @@ void xytonode(double x, double y, struct wlr_surface **psurface, /* Tracked Input Device for runtime libinput configuration */ typedef struct { struct libinput_device *libinput_dev; + struct wlr_input_device *base_device; struct wl_listener destroy; struct wl_list link; } TrackedInputDevice; +void apply_input_settings_to_device(TrackedInputDevice *td); + /* Listener structs */ struct wl_listener cursor_axis = {.notify = axisnotify}; struct wl_listener cursor_button = {.notify = buttonpress}; @@ -707,37 +709,35 @@ motionabsolute(struct wl_listener *listener, void *data) motionnotify(event->time_msec, &event->pointer->base, dx, dy, dx, dy); } -void -touchdown(struct wl_listener *listener, void *data) +struct event_context { + struct wlr_surface *surface; + Client *client; + double lx, ly; // layout + double sx, sy; // surface +}; + +static struct event_context +get_event_context(struct wlr_input_device *device, double x, double y) { - struct wlr_touch_down_event *event = data; - wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); - some_notify_activity(); + struct event_context ctx = {0}; - double lx, ly, sx, sy; - struct wlr_surface *surface = NULL; - Client *c = NULL; + wlr_cursor_absolute_to_layout_coords(cursor, device, x, y, &ctx.lx, &ctx.ly); - wlr_cursor_absolute_to_layout_coords(cursor, &event->touch->base, - event->x, event->y, &lx, &ly); + xytonode(ctx.lx, ctx.ly, &ctx.surface, &ctx.client, NULL, NULL, NULL, &ctx.sx, &ctx.sy); - wlr_cursor_warp_closest(cursor, &event->touch->base, lx, ly); + return ctx; +} - xytonode(lx, ly, &surface, &c, NULL, NULL, NULL, &sx, &sy); +void +touchdown(struct wl_listener *listener, void *data) +{ + struct wlr_touch_down_event *event = data; - if (c && !surface) { - struct wlr_pointer_button_event button_event = { - .time_msec = event->time_msec, - .button = BTN_LEFT, - .state = WL_POINTER_BUTTON_STATE_PRESSED - }; - buttonpress(NULL, &button_event); - return; - } + struct event_context ctx = get_event_context(&event->touch->base, event->x, event->y); - if (surface) { - wlr_seat_touch_notify_down(seat, surface, event->time_msec, - event->touch_id, sx, sy); + if (ctx.surface) { + wlr_seat_touch_notify_down(seat, ctx.surface, event->time_msec, + event->touch_id, ctx.sx, ctx.sy); } } @@ -745,31 +745,11 @@ void touchmotion(struct wl_listener *listener, void *data) { struct wlr_touch_motion_event *event = data; - wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); - some_notify_activity(); - - double lx, ly, sx, sy; - struct wlr_surface *surface = NULL; - Client *c = NULL; - - wlr_cursor_absolute_to_layout_coords(cursor, &event->touch->base, - event->x, event->y, &lx, &ly); - double old_x = cursor->x; - double old_y = cursor->y; - wlr_cursor_warp_closest(cursor, &event->touch->base, lx, ly); + struct event_context ctx = get_event_context(&event->touch->base, event->x, event->y); - xytonode(lx, ly, &surface, &c, NULL, NULL, NULL, &sx, &sy); - - if (cursor_mode == CurPressed || (c && !surface)) { - double dx = cursor->x - old_x; - double dy = cursor->y - old_y; - motionnotify(event->time_msec, &event->touch->base, dx, dy, dx, dy); - return; - } - - if (surface) { - wlr_seat_touch_notify_motion(seat, event->time_msec, event->touch_id, sx, sy); + if (ctx.surface) { + wlr_seat_touch_notify_motion(seat, event->time_msec, event->touch_id, ctx.sx, ctx.sy); } } @@ -777,53 +757,35 @@ void touchup(struct wl_listener *listener, void *data) { struct wlr_touch_up_event *event = data; - wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); - some_notify_activity(); - - if (cursor_mode == CurPressed) { - struct wlr_pointer_button_event button_event = { - .time_msec = event->time_msec, - .button = BTN_LEFT, - .state = WL_POINTER_BUTTON_STATE_RELEASED - }; - buttonpress(NULL, &button_event); - } wlr_seat_touch_notify_up(seat, event->time_msec, event->touch_id); } void -touchcancel(struct wl_listener *listener, void *data) { - - struct wlr_touch_cancel_event *event = data; - wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); - some_notify_activity(); - - struct wlr_touch_point *point; - struct wlr_seat_client *seat_client = NULL; - - wl_list_for_each(point, &seat->touch_state.touch_points, link) { - if (point->touch_id == event->touch_id) { - seat_client = point->focus_client; - break; - } - } +touchcancel(struct wl_listener *listener, void *data) +{ + struct wlr_touch_cancel_event *event = data; - if (seat_client != NULL) { - wlr_seat_touch_notify_cancel(seat, seat_client); - } + struct wlr_touch_point *point; + wl_list_for_each(point, &seat->touch_state.touch_points, link) { + if (point->touch_id == event->touch_id) { + if (point->focus_client) + wlr_seat_touch_notify_cancel(seat, point->focus_client); + break; + } + } } - void touchframe(struct wl_listener *listener, void *data) { wlr_seat_touch_notify_frame(seat); } -/* ############### TABLET ############### */ -bool tabletisokay(struct wlr_tablet *tablet, struct wlr_tablet_tool *tool) { +bool +tablet_ensure_ready(struct wlr_tablet *tablet, struct wlr_tablet_tool *tool) +{ if (!tablet_mgr) return false; @@ -842,31 +804,16 @@ void tabletaxis(struct wl_listener *listener, void *data) { struct wlr_tablet_tool_axis_event *event = data; - wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); - some_notify_activity(); - if (!tabletisokay(event->tablet, event->tool)) return; + if (!tablet_ensure_ready(event->tablet, event->tool)) return; struct wlr_tablet_v2_tablet_tool *v2_tool = event->tool->data; if (event->updated_axes & (WLR_TABLET_TOOL_AXIS_X | WLR_TABLET_TOOL_AXIS_Y)) { - double lx, ly, sx, sy; - struct wlr_surface *surface = NULL; - Client *c = NULL; + struct event_context ctx = get_event_context(&event->tablet->base, event->x, event->y); - wlr_cursor_absolute_to_layout_coords(cursor, &event->tablet->base, - event->x, event->y, &lx, &ly); - - lx = (event->updated_axes & WLR_TABLET_TOOL_AXIS_X) ? lx : cursor->x; - ly = (event->updated_axes & WLR_TABLET_TOOL_AXIS_Y) ? ly : cursor->y; - - wlr_cursor_warp_closest(cursor, &event->tablet->base, lx, ly); - - xytonode(lx, ly, &surface, &c, NULL, NULL, NULL, &sx, &sy); - motionnotify(event->time_msec, &event->tablet->base, 0, 0, 0, 0); - - if (surface) { - wlr_tablet_v2_tablet_tool_notify_motion(v2_tool, sx, sy); + if (ctx.surface) { + wlr_tablet_v2_tablet_tool_notify_motion(v2_tool, ctx.sx, ctx.sy); } } @@ -885,34 +832,17 @@ void tabletproximity(struct wl_listener *listener, void *data) { struct wlr_tablet_tool_proximity_event *event = data; - wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); - some_notify_activity(); + if (!tablet_ensure_ready(event->tablet, event->tool)) return; - if (!tabletisokay(event->tablet, event->tool)) return; - - struct wlr_tablet_v2_tablet_tool *v2_tool = event->tool->data; - - double lx, ly, sx, sy; - struct wlr_surface *surface = NULL; - Client *c = NULL; - - wlr_cursor_absolute_to_layout_coords(cursor, &event->tablet->base, - event->x, event->y, &lx, &ly); - - struct wlr_tablet_v2_tablet *v2_tablet = event->tablet->data; + struct event_context ctx = get_event_context(&event->tablet->base, event->x, event->y); if (event->state == WLR_TABLET_TOOL_PROXIMITY_IN) { - wlr_cursor_warp_closest(cursor, &event->tablet->base, lx, ly); - xytonode(lx, ly, &surface, &c, NULL, NULL, NULL, &sx, &sy); - motionnotify(event->time_msec, &event->tablet->base, 0, 0, 0, 0); - - if (surface) { - wlr_tablet_v2_tablet_tool_notify_proximity_in(v2_tool, v2_tablet, surface); - wlr_tablet_v2_tablet_tool_notify_motion(v2_tool, sx, sy); + if (ctx.surface) { + wlr_tablet_v2_tablet_tool_notify_proximity_in(event->tool->data, event->tablet->data, ctx.surface); } } else { - wlr_tablet_v2_tablet_tool_notify_proximity_out(v2_tool); + wlr_tablet_v2_tablet_tool_notify_proximity_out(event->tool->data); } } @@ -920,39 +850,24 @@ void tablettip(struct wl_listener *listener, void *data) { struct wlr_tablet_tool_tip_event *event = data; - wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); - some_notify_activity(); - - if (!tabletisokay(event->tablet, event->tool)) return; - struct wlr_tablet_v2_tablet_tool *v2_tool = event->tool->data; + if (!tablet_ensure_ready(event->tablet, event->tool)) return; - double lx, ly, sx, sy; - struct wlr_surface *surface = NULL; - Client *c = NULL; - - wlr_cursor_absolute_to_layout_coords(cursor, &event->tablet->base, - event->x, event->y, &lx, &ly); - wlr_cursor_warp_closest(cursor, &event->tablet->base, lx, ly); - xytonode(lx, ly, &surface, &c, NULL, NULL, NULL, &sx, &sy); - - if (surface) { - wlr_tablet_v2_tablet_tool_notify_motion(v2_tool, sx, sy); - } + struct event_context ctx = get_event_context(&event->tablet->base, event->x, event->y); switch (event->state) { case WLR_TABLET_TOOL_TIP_DOWN: - if (surface && !wlr_surface_accepts_tablet_v2(surface, event->tablet->data)) { + if (ctx.surface && !wlr_surface_accepts_tablet_v2(ctx.surface, event->tablet->data)) { wlr_seat_pointer_notify_button(seat, event->time_msec, BTN_LEFT, WL_POINTER_BUTTON_STATE_PRESSED); } else { - wlr_tablet_v2_tablet_tool_notify_down(v2_tool); + wlr_tablet_v2_tablet_tool_notify_down(event->tool->data); } break; case WLR_TABLET_TOOL_TIP_UP: - if (surface && !wlr_surface_accepts_tablet_v2(surface, event->tablet->data)) { + if (ctx.surface && !wlr_surface_accepts_tablet_v2(ctx.surface, event->tablet->data)) { wlr_seat_pointer_notify_button(seat, event->time_msec, BTN_LEFT, WL_POINTER_BUTTON_STATE_RELEASED); } else { - wlr_tablet_v2_tablet_tool_notify_up(v2_tool); + wlr_tablet_v2_tablet_tool_notify_up(event->tool->data); } break; default: @@ -965,17 +880,15 @@ void tabletbutton(struct wl_listener *listener, void *data) { struct wlr_tablet_tool_button_event *event = data; - wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); - some_notify_activity(); - - if (!tabletisokay(event->tablet, event->tool)) return; - struct wlr_tablet_v2_tablet_tool *v2_tool = event->tool->data; + if (!tablet_ensure_ready(event->tablet, event->tool)) return; // this is nonsense. might be an issue in wlroots? - // event->state type might be wrong.. - wlr_tablet_v2_tablet_tool_notify_button(v2_tool, event->button, - (enum zwp_tablet_pad_v2_button_state)event->state); + // event->state is of the wrong enum type.. + wlr_tablet_v2_tablet_tool_notify_button(event->tool->data, event->button, + event->state == WLR_BUTTON_PRESSED + ? ZWP_TABLET_PAD_V2_BUTTON_STATE_PRESSED + : ZWP_TABLET_PAD_V2_BUTTON_STATE_RELEASED); } /* Helper function to emit mouse::leave on the object that previously had the mouse. @@ -1649,7 +1562,14 @@ inputdevice(struct wl_listener *listener, void *data) caps = WL_SEAT_CAPABILITY_POINTER; if (!wl_list_empty(&kb_group->wlr_group->devices)) caps |= WL_SEAT_CAPABILITY_KEYBOARD; - caps |= WL_SEAT_CAPABILITY_TOUCH; + + TrackedInputDevice *td; + wl_list_for_each(td, &tracked_input_devices, link) { + if (td->base_device->type == WLR_INPUT_DEVICE_TOUCH) { + caps |= WL_SEAT_CAPABILITY_TOUCH; + break; + } + } wlr_seat_set_capabilities(seat, caps); } @@ -1777,13 +1697,16 @@ resolve_input_settings(struct libinput_device *device, struct InputSettings *out out->accel_speed = p->accel_speed; out->accel_speed_set = true; } + if (p->output) out->output = p->output; } } /* Apply resolved input settings to a single libinput device */ void -apply_input_settings_to_device(struct libinput_device *device) +apply_input_settings_to_device(TrackedInputDevice *td) { + struct libinput_device *device = td->libinput_dev; + struct InputSettings s; resolve_input_settings(device, &s); @@ -1902,6 +1825,19 @@ apply_input_settings_to_device(struct libinput_device *device) if (s.accel_speed_set) libinput_device_config_accel_set_speed(device, s.accel_speed); } + + // associate (touch) device with output + struct wlr_output *wlr_output = NULL; + if (s.output) { + struct Monitor *m; + wl_list_for_each(m, &mons, link) { + if (m->wlr_output && strcmp(m->wlr_output->name, s.output) == 0) { + wlr_output = m->wlr_output; + break; + } + } + } + wlr_cursor_map_input_to_output(cursor, td->base_device, wlr_output); } /* Apply input settings to all tracked pointer devices */ @@ -1910,30 +1846,31 @@ apply_input_settings_to_all_devices(void) { TrackedInputDevice *td; wl_list_for_each(td, &tracked_input_devices, link) { - apply_input_settings_to_device(td->libinput_dev); + apply_input_settings_to_device(td); } } void createtrackeddevice(struct wlr_input_device *base_device) { + wlr_cursor_attach_input_device(cursor, base_device); + struct libinput_device *device; TrackedInputDevice *td; if (wlr_input_device_is_libinput(base_device) && (device = wlr_libinput_get_device_handle(base_device))) { - /* Apply settings from globalconf */ - apply_input_settings_to_device(device); - /* Track this device for runtime reconfiguration */ td = ecalloc(1, sizeof(*td)); td->libinput_dev = device; + td->base_device = base_device; wl_list_insert(&tracked_input_devices, &td->link); LISTEN(&base_device->events.destroy, &td->destroy, destroytrackeddevice); - } - wlr_cursor_attach_input_device(cursor, base_device); + /* Apply settings from globalconf */ + apply_input_settings_to_device(td); + } } static void diff --git a/lua/awful/input.lua b/lua/awful/input.lua index cc851bfd..3a874784 100644 --- a/lua/awful/input.lua +++ b/lua/awful/input.lua @@ -37,6 +37,7 @@ local state = { accel_profile = nil, -- "flat", "adaptive" accel_speed = 0.0, -- -1.0 to 1.0 tap_button_map = nil, -- "lrm", "lmr" + output = nil, -- "lrm", "lmr" -- Keyboard settings keyboard_repeat_rate = 25, -- repeats per second @@ -67,6 +68,7 @@ local property_types = { send_events_mode = "string", accel_profile = "string", accel_speed = "number", + output = "string", tap_button_map = "string", keyboard_repeat_rate = "int", keyboard_repeat_delay = "int", @@ -97,6 +99,7 @@ local pointer_settings = { accel_profile = true, accel_speed = true, tap_button_map = true, + output = true, } -- Per-device input rules (evaluated in order, last match wins per property) diff --git a/luaa.c b/luaa.c index 8ec49302..5b7c1052 100644 --- a/luaa.c +++ b/luaa.c @@ -1004,6 +1004,10 @@ luaA_awesome_set_input_setting(lua_State *L) const char *val = lua_isnil(L, 2) ? NULL : luaL_checkstring(L, 2); free(globalconf.input.clickfinger_button_map); globalconf.input.clickfinger_button_map = val ? strdup(val) : NULL; + } else if (strcmp(key, "output") == 0) { + const char *val = lua_isnil(L, 2) ? NULL : luaL_checkstring(L, 2); + free(globalconf.input.output); + globalconf.input.output = val ? strdup(val) : NULL; } else { return luaL_error(L, "Unknown input setting: %s", key); } @@ -1026,6 +1030,7 @@ input_rules_free(void) free(r->properties.send_events_mode); free(r->properties.accel_profile); free(r->properties.tap_button_map); + free(r->properties.output); } free(globalconf.input_rules); globalconf.input_rules = NULL; @@ -1055,6 +1060,7 @@ input_settings_init_unset(InputSettings *s) s->accel_speed = 0.0; s->tap_button_map = NULL; s->accel_speed_set = false; + s->output = NULL; } /** Read an optional int field from a Lua table on the stack. @@ -1147,6 +1153,7 @@ luaA_awesome_set_input_rules(lua_State *L) p->send_events_mode = input_rule_get_string(L, pidx, "send_events_mode"); p->accel_profile = input_rule_get_string(L, pidx, "accel_profile"); p->tap_button_map = input_rule_get_string(L, pidx, "tap_button_map"); + p->output = input_rule_get_string(L, pidx, "output"); lua_getfield(L, pidx, "accel_speed"); if (!lua_isnil(L, -1)) { diff --git a/somewm.c b/somewm.c index 2b15d524..7a0c4f73 100644 --- a/somewm.c +++ b/somewm.c @@ -1318,6 +1318,7 @@ setup(void) globalconf.input.accel_profile = NULL; /* String property - set via Lua */ globalconf.input.accel_speed = 0.0; globalconf.input.tap_button_map = NULL; /* String property - set via Lua */ + globalconf.input.output = NULL; /* String property - set via Lua */ /* Logging defaults (only set if not already set by -d flag) */ if (globalconf.log_level == 0) From 2b7b880a629016ee11d5c98fd277c59e1c09530f Mon Sep 17 00:00:00 2001 From: Zsolt Date: Sat, 13 Jun 2026 20:21:14 +0100 Subject: [PATCH 3/3] Update lua/awful/input.lua correction of typo Co-authored-by: Sicelo --- lua/awful/input.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua/awful/input.lua b/lua/awful/input.lua index 3a874784..93966796 100644 --- a/lua/awful/input.lua +++ b/lua/awful/input.lua @@ -37,7 +37,7 @@ local state = { accel_profile = nil, -- "flat", "adaptive" accel_speed = 0.0, -- -1.0 to 1.0 tap_button_map = nil, -- "lrm", "lmr" - output = nil, -- "lrm", "lmr" + output = nil, -- "VGA-1" -- Keyboard settings keyboard_repeat_rate = 25, -- repeats per second