diff --git a/globalconf.h b/globalconf.h index ef74ff34..1df2599f 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 627b1201..312be06f 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,10 +102,9 @@ 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); 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); @@ -116,18 +118,30 @@ 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 wlr_input_device *base_device; struct wl_listener destroy; struct wl_list link; -} TrackedPointer; +} TrackedInputDevice; + +void apply_input_settings_to_device(TrackedInputDevice *td); /* Listener structs */ struct wl_listener cursor_axis = {.notify = axisnotify}; @@ -153,6 +167,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 +709,188 @@ motionabsolute(struct wl_listener *listener, void *data) motionnotify(event->time_msec, &event->pointer->base, dx, dy, dx, dy); } +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 event_context ctx = {0}; + + wlr_cursor_absolute_to_layout_coords(cursor, device, x, y, &ctx.lx, &ctx.ly); + + xytonode(ctx.lx, ctx.ly, &ctx.surface, &ctx.client, NULL, NULL, NULL, &ctx.sx, &ctx.sy); + + return ctx; +} + +void +touchdown(struct wl_listener *listener, void *data) +{ + struct wlr_touch_down_event *event = data; + + struct event_context ctx = get_event_context(&event->touch->base, event->x, event->y); + + if (ctx.surface) { + wlr_seat_touch_notify_down(seat, ctx.surface, event->time_msec, + event->touch_id, ctx.sx, ctx.sy); + } +} + +void +touchmotion(struct wl_listener *listener, void *data) +{ + struct wlr_touch_motion_event *event = data; + + struct event_context ctx = get_event_context(&event->touch->base, event->x, event->y); + + if (ctx.surface) { + wlr_seat_touch_notify_motion(seat, event->time_msec, event->touch_id, ctx.sx, ctx.sy); + } +} + +void +touchup(struct wl_listener *listener, void *data) +{ + struct wlr_touch_up_event *event = data; + + 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; + + 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); +} + + +bool +tablet_ensure_ready(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; + + 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)) { + struct event_context ctx = get_event_context(&event->tablet->base, event->x, event->y); + + if (ctx.surface) { + wlr_tablet_v2_tablet_tool_notify_motion(v2_tool, ctx.sx, ctx.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; + + if (!tablet_ensure_ready(event->tablet, event->tool)) return; + + struct event_context ctx = get_event_context(&event->tablet->base, event->x, event->y); + + if (event->state == WLR_TABLET_TOOL_PROXIMITY_IN) { + 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(event->tool->data); + } +} + +void +tablettip(struct wl_listener *listener, void *data) +{ + struct wlr_tablet_tool_tip_event *event = data; + + if (!tablet_ensure_ready(event->tablet, event->tool)) return; + + struct event_context ctx = get_event_context(&event->tablet->base, event->x, event->y); + + switch (event->state) { + case WLR_TABLET_TOOL_TIP_DOWN: + 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(event->tool->data); + } + break; + case WLR_TABLET_TOOL_TIP_UP: + 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(event->tool->data); + } + break; + default: + // just in case + break; + } +} + +void +tabletbutton(struct wl_listener *listener, void *data) +{ + struct wlr_tablet_tool_button_event *event = data; + + if (!tablet_ensure_ready(event->tablet, event->tool)) return; + + // this is nonsense. might be an issue in wlroots? + // 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. * Also clears drawable_under_mouse tracking to emit leave on the drawable. */ void @@ -1339,7 +1544,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 */ @@ -1353,6 +1560,15 @@ 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; + + 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); } @@ -1479,13 +1695,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); @@ -1604,47 +1823,61 @@ 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 */ 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); } } void -createpointer(struct wlr_pointer *pointer) +createtrackeddevice(struct wlr_input_device *base_device) { - struct libinput_device *device; - TrackedPointer *tp; + wlr_cursor_attach_input_device(cursor, base_device); - if (wlr_input_device_is_libinput(&pointer->base) - && (device = wlr_libinput_get_device_handle(&pointer->base))) { + struct libinput_device *device; + TrackedInputDevice *td; - /* Apply settings from globalconf */ - apply_input_settings_to_device(device); + if (wlr_input_device_is_libinput(base_device) + && (device = wlr_libinput_get_device_handle(base_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; + 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, &pointer->base); + /* Apply settings from globalconf */ + apply_input_settings_to_device(td); + } } 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/lua/awful/input.lua b/lua/awful/input.lua index cc851bfd..93966796 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, -- "VGA-1" -- 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 fe7f66bf..408d34bd 100644 --- a/luaa.c +++ b/luaa.c @@ -1015,6 +1015,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); } @@ -1037,6 +1041,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; @@ -1066,6 +1071,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. @@ -1158,6 +1164,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/meson.build b/meson.build index 003e3b32..cacf7657 100644 --- a/meson.build +++ b/meson.build @@ -151,6 +151,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 326ccb80..8ff81139 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); @@ -1139,7 +1150,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 @@ -1205,6 +1216,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 @@ -1230,6 +1243,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); @@ -1320,6 +1344,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) 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;