summaryrefslogtreecommitdiffhomepage
path: root/packages/kernel/src/input.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'packages/kernel/src/input.cpp')
-rw-r--r--packages/kernel/src/input.cpp241
1 files changed, 75 insertions, 166 deletions
diff --git a/packages/kernel/src/input.cpp b/packages/kernel/src/input.cpp
index 7095154..b6c4c0c 100644
--- a/packages/kernel/src/input.cpp
+++ b/packages/kernel/src/input.cpp
@@ -4,6 +4,14 @@
namespace unbox::kernel {
+// Slice 4: the kernel owns generic input PLUMBING only. It moves the cursor,
+// tracks seat capabilities, handles the seat's own protocol requests, runs the
+// key_filter (consume-or-pass), and EMITS typed events. It routes NOTHING to
+// client surfaces and makes NO focus decision — ext-xdg-shell (and others) do
+// that from the bus + borrows. The only client forward the kernel still does
+// is keyboard key passthrough AFTER the filter, because the seat already holds
+// the focus an extension set via wlr_seat_keyboard_notify_enter.
+
// ---- Device hotplug -----------------------------------------------------------
void Server::Impl::handle_new_input(wlr_input_device* device) {
@@ -44,7 +52,7 @@ void Server::Impl::new_keyboard(wlr_input_device* device) {
keyboard->keyboard = wlr_kb;
keyboards.push_back(std::move(owned));
- // Default XKB keymap (layout "us" etc.); unbox.toml takes over later.
+ // Default XKB keymap; per-config keymap is a later slice.
xkb_context* context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
xkb_keymap* keymap = xkb_keymap_new_from_names(context, nullptr, XKB_KEYMAP_COMPILE_NO_FLAGS);
wlr_keyboard_set_keymap(wlr_kb, keymap);
@@ -53,7 +61,6 @@ void Server::Impl::new_keyboard(wlr_input_device* device) {
wlr_keyboard_set_repeat_info(wlr_kb, 25, 600);
keyboard->modifiers.connect(wlr_kb->events.modifiers, [this, keyboard](void*) {
- // The seat exposes one logical keyboard; swap the active device in.
wlr_seat_set_keyboard(seat, keyboard->keyboard);
wlr_seat_keyboard_notify_modifiers(seat, &keyboard->keyboard->modifiers);
});
@@ -65,16 +72,39 @@ void Server::Impl::new_keyboard(wlr_input_device* device) {
const xkb_keysym_t* syms = nullptr;
const int nsyms =
xkb_state_key_get_syms(keyboard->keyboard->xkb_state, keycode, &syms);
-
- bool handled = false;
const std::uint32_t modifiers = wlr_keyboard_get_modifiers(keyboard->keyboard);
- if ((modifiers & WLR_MODIFIER_ALT) != 0 &&
- event->state == WL_KEYBOARD_KEY_STATE_PRESSED) {
- for (int i = 0; i < nsyms; i++) {
- handled = handle_keybinding(syms[i]);
- }
- }
- if (!handled) {
+ const bool pressed = event->state == WL_KEYBOARD_KEY_STATE_PRESSED;
+
+ // Thread each resolved keysym through the key_filter; a filter link may
+ // CONSUME the key (set handled=true) — that is how extensions implement
+ // compositor shortcuts. If any resolution is consumed, suppress the
+ // client forward for this key.
+ bool consumed = false;
+ for (int i = 0; i < nsyms; ++i) {
+ KeyEvent ke{};
+ ke.keysym = syms[i];
+ ke.keycode = event->keycode;
+ ke.modifiers = modifiers;
+ ke.pressed = pressed;
+ ke.time_msec = event->time_msec;
+ ke.handled = false;
+ ke = key_filter.apply(ke);
+ consumed = consumed || ke.handled;
+ }
+ // With no resolved syms (e.g. modifier-only) the filter still runs once
+ // so extensions can observe raw modifier keys if they wish.
+ if (nsyms == 0) {
+ KeyEvent ke{};
+ ke.keysym = XKB_KEY_NoSymbol;
+ ke.keycode = event->keycode;
+ ke.modifiers = modifiers;
+ ke.pressed = pressed;
+ ke.time_msec = event->time_msec;
+ ke = key_filter.apply(ke);
+ consumed = consumed || ke.handled;
+ }
+
+ if (!consumed) {
wlr_seat_set_keyboard(seat, keyboard->keyboard);
wlr_seat_keyboard_notify_key(seat, event->time_msec, event->keycode, event->state);
}
@@ -89,8 +119,8 @@ void Server::Impl::new_keyboard(wlr_input_device* device) {
}
void Server::Impl::new_pointer(wlr_input_device* device) {
- // All pointer handling is proxied through wlr_cursor; per-device
- // libinput config (acceleration, tap…) is a later slice.
+ // All pointer handling is proxied through wlr_cursor; per-device libinput
+ // config is a later slice.
wlr_cursor_attach_input_device(cursor, device);
}
@@ -107,225 +137,104 @@ void Server::Impl::new_touch(wlr_input_device* device) {
touch_devices.remove_if([touch](const auto& owned) { return owned.get() == touch; });
});
- // wlr_cursor aggregates touch devices too and emits layout-mapped
- // touch_* events (handled below).
wlr_cursor_attach_input_device(cursor, device);
}
-// ---- Compositor keybindings ------------------------------------------------------
-
-auto Server::Impl::handle_keybinding(std::uint32_t keysym) -> bool {
- // Slice-2 placeholder bindings (Alt held), replaced by the keybinding
- // filter chain in slice 5.
- switch (keysym) {
- case XKB_KEY_Escape:
- wl_display_terminate(display);
- return true;
- case XKB_KEY_F1:
- if (mapped_toplevels.size() >= 2) {
- focus_toplevel(mapped_toplevels.back());
- }
- return true;
- default:
- return false;
- }
-}
+// ---- Pointer (via wlr_cursor): move cursor + emit, route nothing ------------
-// ---- Pointer (via wlr_cursor) ------------------------------------------------------
-
-void Server::Impl::process_cursor_move() {
- wlr_scene_node_set_position(&grabbed_toplevel->scene_tree->node,
- static_cast<int>(cursor->x - grab_x),
- static_cast<int>(cursor->y - grab_y));
-}
-
-void Server::Impl::process_cursor_resize() {
- // Resizing moves the node when dragging top/left edges; the client is
- // asked for the new size (it commits a matching buffer later).
- Toplevel* toplevel = grabbed_toplevel;
- const double border_x = cursor->x - grab_x;
- const double border_y = cursor->y - grab_y;
- int new_left = grab_geobox.x;
- int new_right = grab_geobox.x + grab_geobox.width;
- int new_top = grab_geobox.y;
- int new_bottom = grab_geobox.y + grab_geobox.height;
-
- if ((resize_edges & WLR_EDGE_TOP) != 0) {
- new_top = static_cast<int>(border_y);
- if (new_top >= new_bottom) {
- new_top = new_bottom - 1;
- }
- } else if ((resize_edges & WLR_EDGE_BOTTOM) != 0) {
- new_bottom = static_cast<int>(border_y);
- if (new_bottom <= new_top) {
- new_bottom = new_top + 1;
- }
- }
- if ((resize_edges & WLR_EDGE_LEFT) != 0) {
- new_left = static_cast<int>(border_x);
- if (new_left >= new_right) {
- new_left = new_right - 1;
- }
- } else if ((resize_edges & WLR_EDGE_RIGHT) != 0) {
- new_right = static_cast<int>(border_x);
- if (new_right <= new_left) {
- new_right = new_left + 1;
- }
- }
-
- wlr_box* geo_box = &toplevel->xdg_toplevel->base->geometry;
- wlr_scene_node_set_position(&toplevel->scene_tree->node, new_left - geo_box->x,
- new_top - geo_box->y);
- wlr_xdg_toplevel_set_size(toplevel->xdg_toplevel, new_right - new_left,
- new_bottom - new_top);
-}
-
-void Server::Impl::process_cursor_motion(std::uint32_t time_msec) {
- if (cursor_mode == CursorMode::Move) {
- process_cursor_move();
- return;
- }
- if (cursor_mode == CursorMode::Resize) {
- process_cursor_resize();
- return;
- }
-
- // Slice-3 spike input proof (NOT the slice-5 routing contract): if the
- // cursor is over the spike node, forward surface-local coords to RmlUi so
- // the document's button reacts to hover. Crude and private.
+void Server::Impl::emit_pointer_motion(std::uint32_t time_msec) {
+ // Slice-3 spike input proof (kernel-internal; NOT a contract): forward
+ // surface-local coords over the spike node so its button hovers.
if (ui_spike != nullptr) {
if (wlr_scene_node* spike = ui_spike->node()) {
int nx = 0;
int ny = 0;
wlr_scene_node_coords(spike, &nx, &ny);
- const double sx = cursor->x - nx;
- const double sy = cursor->y - ny;
- ui_spike->on_pointer_motion(sx, sy);
+ ui_spike->on_pointer_motion(cursor->x - nx, cursor->y - ny);
}
}
- double sx = 0;
- double sy = 0;
- wlr_surface* surface = nullptr;
- Toplevel* toplevel = toplevel_at(cursor->x, cursor->y, &surface, &sx, &sy);
- if (toplevel == nullptr) {
- // Over no toplevel: the compositor draws its own default cursor.
- wlr_cursor_set_xcursor(cursor, cursor_mgr, "default");
- }
- if (surface != nullptr) {
- // Enter gives the surface pointer focus; wlroots dedupes repeats.
- wlr_seat_pointer_notify_enter(seat, surface, sx, sy);
- wlr_seat_pointer_notify_motion(seat, time_msec, sx, sy);
- } else {
- wlr_seat_pointer_clear_focus(seat);
- }
+ const PointerMotionEvent ev{cursor->x, cursor->y, time_msec};
+ ev_pointer_motion.emit(ev);
}
void Server::Impl::attach_cursor_handlers() {
cursor_motion.connect(cursor->events.motion, [this](void* data) {
const auto* event = static_cast<wlr_pointer_motion_event*>(data);
wlr_cursor_move(cursor, &event->pointer->base, event->delta_x, event->delta_y);
- process_cursor_motion(event->time_msec);
+ emit_pointer_motion(event->time_msec);
});
cursor_motion_absolute.connect(cursor->events.motion_absolute, [this](void* data) {
const auto* event = static_cast<wlr_pointer_motion_absolute_event*>(data);
wlr_cursor_warp_absolute(cursor, &event->pointer->base, event->x, event->y);
- process_cursor_motion(event->time_msec);
+ emit_pointer_motion(event->time_msec);
});
cursor_button.connect(cursor->events.button, [this](void* data) {
const auto* event = static_cast<wlr_pointer_button_event*>(data);
- wlr_seat_pointer_notify_button(seat, event->time_msec, event->button, event->state);
+ const bool pressed = event->state == WL_POINTER_BUTTON_STATE_PRESSED;
- // Slice-3 spike input proof: forward clicks over the spike node to
- // RmlUi so its button reacts to press/release. Crude and private.
+ // Slice-3 spike input proof (kernel-internal): forward clicks over the
+ // spike node so its button reacts.
if (ui_spike != nullptr) {
if (wlr_scene_node* spike = ui_spike->node()) {
- int nx = 0;
- int ny = 0;
- wlr_scene_node_coords(spike, &nx, &ny);
if (wlr_scene_node_at(spike, cursor->x, cursor->y, nullptr, nullptr) != nullptr) {
- ui_spike->on_pointer_button(event->state ==
- WL_POINTER_BUTTON_STATE_PRESSED);
+ ui_spike->on_pointer_button(pressed);
}
}
}
- if (event->state == WL_POINTER_BUTTON_STATE_RELEASED) {
- reset_cursor_mode();
- } else {
- // Click-to-focus.
- double sx = 0;
- double sy = 0;
- wlr_surface* surface = nullptr;
- focus_toplevel(toplevel_at(cursor->x, cursor->y, &surface, &sx, &sy));
- }
+ const PointerButtonEvent ev{event->button, pressed, cursor->x, cursor->y,
+ event->time_msec};
+ ev_pointer_button.emit(ev);
});
cursor_axis.connect(cursor->events.axis, [this](void* data) {
const auto* event = static_cast<wlr_pointer_axis_event*>(data);
- wlr_seat_pointer_notify_axis(seat, event->time_msec, event->orientation, event->delta,
- event->delta_discrete, event->source,
- event->relative_direction);
- });
- cursor_frame.connect(cursor->events.frame, [this](void*) {
- wlr_seat_pointer_notify_frame(seat);
+ const PointerAxisEvent ev{event->orientation, event->delta, event->delta_discrete,
+ event->source, event->time_msec};
+ ev_pointer_axis.emit(ev);
});
+ cursor_frame.connect(cursor->events.frame, [this](void*) { ev_pointer_frame.emit(); });
- // ---- Touch (tinywl doesn't have this; the CF-AX3 does) ----
+ // ---- Touch (tinywl lacks this; the CF-AX3 has it). Convert to layout
+ // coords + emit; extensions route to surfaces. ----
cursor_touch_down.connect(cursor->events.touch_down, [this](void* data) {
const auto* event = static_cast<wlr_touch_down_event*>(data);
double lx = 0;
double ly = 0;
wlr_cursor_absolute_to_layout_coords(cursor, &event->touch->base, event->x, event->y,
&lx, &ly);
- double sx = 0;
- double sy = 0;
- wlr_surface* surface = nullptr;
- Toplevel* toplevel = toplevel_at(lx, ly, &surface, &sx, &sy);
- if (toplevel != nullptr) {
- focus_toplevel(toplevel); // tap raises + focuses
- }
- if (surface != nullptr) {
- touch_points.insert_or_assign(event->touch_id,
- TouchPoint{surface, lx - sx, ly - sy});
- wlr_seat_touch_notify_down(seat, surface, event->time_msec, event->touch_id, sx, sy);
- }
+ const TouchDownEvent ev{event->touch_id, lx, ly, event->time_msec};
+ ev_touch_down.emit(ev);
});
cursor_touch_motion.connect(cursor->events.touch_motion, [this](void* data) {
const auto* event = static_cast<wlr_touch_motion_event*>(data);
- auto it = touch_points.find(event->touch_id);
- if (it == touch_points.end()) {
- return; // down landed on no surface; nothing is grabbed
- }
double lx = 0;
double ly = 0;
wlr_cursor_absolute_to_layout_coords(cursor, &event->touch->base, event->x, event->y,
&lx, &ly);
- wlr_seat_touch_notify_motion(seat, event->time_msec, event->touch_id,
- lx - it->second.origin_x, ly - it->second.origin_y);
+ const TouchMotionEvent ev{event->touch_id, lx, ly, event->time_msec};
+ ev_touch_motion.emit(ev);
});
cursor_touch_up.connect(cursor->events.touch_up, [this](void* data) {
const auto* event = static_cast<wlr_touch_up_event*>(data);
- touch_points.erase(event->touch_id);
- wlr_seat_touch_notify_up(seat, event->time_msec, event->touch_id);
+ const TouchUpEvent ev{event->touch_id, event->time_msec};
+ ev_touch_up.emit(ev);
});
cursor_touch_cancel.connect(cursor->events.touch_cancel, [this](void* data) {
const auto* event = static_cast<wlr_touch_cancel_event*>(data);
- if (wlr_touch_point* point = wlr_seat_touch_get_point(seat, event->touch_id)) {
- wlr_seat_touch_notify_cancel(seat, point->client);
- }
- touch_points.erase(event->touch_id);
+ const TouchCancelEvent ev{event->touch_id};
+ ev_touch_cancel.emit(ev);
});
cursor_touch_frame.connect(cursor->events.touch_frame, [this](void*) {
- wlr_seat_touch_notify_frame(seat);
+ ev_touch_frame.emit();
});
}
-// ---- Seat requests -------------------------------------------------------------
+// ---- Seat requests (generic protocol glue) ----------------------------------
void Server::Impl::attach_seat_handlers() {
seat_request_cursor.connect(seat->events.request_set_cursor, [this](void* data) {
const auto* event = static_cast<wlr_seat_pointer_request_set_cursor_event*>(data);
- // Any client may send this; honor only the pointer-focused one.
if (seat->pointer_state.focused_client == event->seat_client) {
wlr_cursor_set_surface(cursor, event->surface, event->hotspot_x, event->hotspot_y);
}