Make Keyboard a toplevel struct
This commit is contained in:
parent
97d395dbfc
commit
ed98e8fe1a
275
src/keyboard.zig
275
src/keyboard.zig
@ -1,164 +1,163 @@
|
|||||||
|
const Self = @This();
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
const c = @import("c.zig");
|
const c = @import("c.zig");
|
||||||
|
|
||||||
const Log = @import("log.zig").Log;
|
const Log = @import("log.zig").Log;
|
||||||
const Seat = @import("seat.zig");
|
const Seat = @import("seat.zig");
|
||||||
|
|
||||||
pub const Keyboard = struct {
|
seat: *Seat,
|
||||||
const Self = @This();
|
wlr_input_device: *c.wlr_input_device,
|
||||||
|
wlr_keyboard: *c.wlr_keyboard,
|
||||||
|
|
||||||
seat: *Seat,
|
listen_key: c.wl_listener,
|
||||||
wlr_input_device: *c.wlr_input_device,
|
listen_modifiers: c.wl_listener,
|
||||||
wlr_keyboard: *c.wlr_keyboard,
|
|
||||||
|
|
||||||
listen_key: c.wl_listener,
|
pub fn init(self: *Self, seat: *Seat, wlr_input_device: *c.wlr_input_device) !void {
|
||||||
listen_modifiers: c.wl_listener,
|
self.seat = seat;
|
||||||
|
self.wlr_input_device = wlr_input_device;
|
||||||
|
self.wlr_keyboard = @field(wlr_input_device, c.wlr_input_device_union).keyboard;
|
||||||
|
|
||||||
pub fn init(self: *Self, seat: *Seat, wlr_input_device: *c.wlr_input_device) !void {
|
// We need to prepare an XKB keymap and assign it to the keyboard. This
|
||||||
self.seat = seat;
|
// assumes the defaults (e.g. layout = "us").
|
||||||
self.wlr_input_device = wlr_input_device;
|
const rules = c.xkb_rule_names{
|
||||||
self.wlr_keyboard = @field(wlr_input_device, c.wlr_input_device_union).keyboard;
|
.rules = null,
|
||||||
|
.model = null,
|
||||||
|
.layout = null,
|
||||||
|
.variant = null,
|
||||||
|
.options = null,
|
||||||
|
};
|
||||||
|
const context = c.xkb_context_new(.XKB_CONTEXT_NO_FLAGS) orelse
|
||||||
|
return error.CantCreateXkbContext;
|
||||||
|
defer c.xkb_context_unref(context);
|
||||||
|
|
||||||
// We need to prepare an XKB keymap and assign it to the keyboard. This
|
const keymap = c.xkb_keymap_new_from_names(
|
||||||
// assumes the defaults (e.g. layout = "us").
|
context,
|
||||||
const rules = c.xkb_rule_names{
|
&rules,
|
||||||
.rules = null,
|
.XKB_KEYMAP_COMPILE_NO_FLAGS,
|
||||||
.model = null,
|
) orelse
|
||||||
.layout = null,
|
return error.CantCreateXkbKeymap;
|
||||||
.variant = null,
|
defer c.xkb_keymap_unref(keymap);
|
||||||
.options = null,
|
|
||||||
};
|
|
||||||
const context = c.xkb_context_new(.XKB_CONTEXT_NO_FLAGS) orelse
|
|
||||||
return error.CantCreateXkbContext;
|
|
||||||
defer c.xkb_context_unref(context);
|
|
||||||
|
|
||||||
const keymap = c.xkb_keymap_new_from_names(
|
// TODO: handle failure after https://github.com/swaywm/wlroots/pull/2081
|
||||||
context,
|
c.wlr_keyboard_set_keymap(self.wlr_keyboard, keymap);
|
||||||
&rules,
|
c.wlr_keyboard_set_repeat_info(self.wlr_keyboard, 25, 600);
|
||||||
.XKB_KEYMAP_COMPILE_NO_FLAGS,
|
|
||||||
) orelse
|
|
||||||
return error.CantCreateXkbKeymap;
|
|
||||||
defer c.xkb_keymap_unref(keymap);
|
|
||||||
|
|
||||||
// TODO: handle failure after https://github.com/swaywm/wlroots/pull/2081
|
// Setup listeners for keyboard events
|
||||||
c.wlr_keyboard_set_keymap(self.wlr_keyboard, keymap);
|
self.listen_key.notify = handleKey;
|
||||||
c.wlr_keyboard_set_repeat_info(self.wlr_keyboard, 25, 600);
|
c.wl_signal_add(&self.wlr_keyboard.events.key, &self.listen_key);
|
||||||
|
|
||||||
// Setup listeners for keyboard events
|
self.listen_modifiers.notify = handleModifiers;
|
||||||
self.listen_key.notify = handleKey;
|
c.wl_signal_add(&self.wlr_keyboard.events.modifiers, &self.listen_modifiers);
|
||||||
c.wl_signal_add(&self.wlr_keyboard.events.key, &self.listen_key);
|
}
|
||||||
|
|
||||||
self.listen_modifiers.notify = handleModifiers;
|
fn handleKey(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
|
||||||
c.wl_signal_add(&self.wlr_keyboard.events.modifiers, &self.listen_modifiers);
|
// This event is raised when a key is pressed or released.
|
||||||
}
|
const self = @fieldParentPtr(Self, "listen_key", listener.?);
|
||||||
|
const event = @ptrCast(
|
||||||
|
*c.wlr_event_keyboard_key,
|
||||||
|
@alignCast(@alignOf(*c.wlr_event_keyboard_key), data),
|
||||||
|
);
|
||||||
|
|
||||||
fn handleKey(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
|
const wlr_keyboard = self.wlr_keyboard;
|
||||||
// This event is raised when a key is pressed or released.
|
|
||||||
const self = @fieldParentPtr(Self, "listen_key", listener.?);
|
|
||||||
const event = @ptrCast(
|
|
||||||
*c.wlr_event_keyboard_key,
|
|
||||||
@alignCast(@alignOf(*c.wlr_event_keyboard_key), data),
|
|
||||||
);
|
|
||||||
|
|
||||||
const wlr_keyboard = self.wlr_keyboard;
|
// Translate libinput keycode -> xkbcommon
|
||||||
|
const keycode = event.keycode + 8;
|
||||||
|
|
||||||
// Translate libinput keycode -> xkbcommon
|
// Get a list of keysyms as xkb reports them
|
||||||
const keycode = event.keycode + 8;
|
var translated_keysyms: ?[*]c.xkb_keysym_t = undefined;
|
||||||
|
const translated_keysyms_len = c.xkb_state_key_get_syms(
|
||||||
|
wlr_keyboard.xkb_state,
|
||||||
|
keycode,
|
||||||
|
&translated_keysyms,
|
||||||
|
);
|
||||||
|
|
||||||
// Get a list of keysyms as xkb reports them
|
// Get a list of keysyms ignoring modifiers (e.g. 1 instead of !)
|
||||||
var translated_keysyms: ?[*]c.xkb_keysym_t = undefined;
|
// Important for bindings like Mod+Shift+1
|
||||||
const translated_keysyms_len = c.xkb_state_key_get_syms(
|
var raw_keysyms: ?[*]c.xkb_keysym_t = undefined;
|
||||||
wlr_keyboard.xkb_state,
|
const layout_index = c.xkb_state_key_get_layout(wlr_keyboard.xkb_state, keycode);
|
||||||
keycode,
|
const raw_keysyms_len = c.xkb_keymap_key_get_syms_by_level(
|
||||||
&translated_keysyms,
|
wlr_keyboard.keymap,
|
||||||
);
|
keycode,
|
||||||
|
layout_index,
|
||||||
|
0,
|
||||||
|
&raw_keysyms,
|
||||||
|
);
|
||||||
|
|
||||||
// Get a list of keysyms ignoring modifiers (e.g. 1 instead of !)
|
var handled = false;
|
||||||
// Important for bindings like Mod+Shift+1
|
// TODO: These modifiers aren't properly handled, see sway's code
|
||||||
var raw_keysyms: ?[*]c.xkb_keysym_t = undefined;
|
const modifiers = c.wlr_keyboard_get_modifiers(wlr_keyboard);
|
||||||
const layout_index = c.xkb_state_key_get_layout(wlr_keyboard.xkb_state, keycode);
|
if (event.state == .WLR_KEY_PRESSED) {
|
||||||
const raw_keysyms_len = c.xkb_keymap_key_get_syms_by_level(
|
var i: usize = 0;
|
||||||
wlr_keyboard.keymap,
|
while (i < translated_keysyms_len) : (i += 1) {
|
||||||
keycode,
|
if (self.handleBuiltinKeybind(translated_keysyms.?[i])) {
|
||||||
layout_index,
|
handled = true;
|
||||||
0,
|
break;
|
||||||
&raw_keysyms,
|
} else if (self.seat.handleKeybinding(translated_keysyms.?[i], modifiers)) {
|
||||||
);
|
handled = true;
|
||||||
|
break;
|
||||||
var handled = false;
|
|
||||||
// TODO: These modifiers aren't properly handled, see sway's code
|
|
||||||
const modifiers = c.wlr_keyboard_get_modifiers(wlr_keyboard);
|
|
||||||
if (event.state == .WLR_KEY_PRESSED) {
|
|
||||||
var i: usize = 0;
|
|
||||||
while (i < translated_keysyms_len) : (i += 1) {
|
|
||||||
if (self.handleBuiltinKeybind(translated_keysyms.?[i])) {
|
|
||||||
handled = true;
|
|
||||||
break;
|
|
||||||
} else if (self.seat.handleKeybinding(translated_keysyms.?[i], modifiers)) {
|
|
||||||
handled = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!handled) {
|
|
||||||
i = 0;
|
|
||||||
while (i < raw_keysyms_len) : (i += 1) {
|
|
||||||
if (self.handleBuiltinKeybind(raw_keysyms.?[i])) {
|
|
||||||
handled = true;
|
|
||||||
break;
|
|
||||||
} else if (self.seat.handleKeybinding(raw_keysyms.?[i], modifiers)) {
|
|
||||||
handled = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!handled) {
|
if (!handled) {
|
||||||
// Otherwise, we pass it along to the client.
|
i = 0;
|
||||||
const wlr_seat = self.seat.wlr_seat;
|
while (i < raw_keysyms_len) : (i += 1) {
|
||||||
c.wlr_seat_set_keyboard(wlr_seat, self.wlr_input_device);
|
if (self.handleBuiltinKeybind(raw_keysyms.?[i])) {
|
||||||
c.wlr_seat_keyboard_notify_key(
|
handled = true;
|
||||||
wlr_seat,
|
break;
|
||||||
event.time_msec,
|
} else if (self.seat.handleKeybinding(raw_keysyms.?[i], modifiers)) {
|
||||||
event.keycode,
|
handled = true;
|
||||||
@intCast(u32, @enumToInt(event.state)),
|
break;
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handleModifiers(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
|
|
||||||
// This event is raised when a modifier key, such as shift or alt, is
|
|
||||||
// pressed. We simply communicate this to the client. */
|
|
||||||
const self = @fieldParentPtr(Self, "listen_modifiers", listener.?);
|
|
||||||
|
|
||||||
// A seat can only have one keyboard, but this is a limitation of the
|
|
||||||
// Wayland protocol - not wlroots. We assign all connected keyboards to the
|
|
||||||
// same seat. You can swap out the underlying wlr_keyboard like this and
|
|
||||||
// wlr_seat handles this transparently.
|
|
||||||
c.wlr_seat_set_keyboard(self.seat.wlr_seat, self.wlr_input_device);
|
|
||||||
|
|
||||||
// Send modifiers to the client.
|
|
||||||
c.wlr_seat_keyboard_notify_modifiers(
|
|
||||||
self.seat.wlr_seat,
|
|
||||||
&self.wlr_keyboard.modifiers,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Handle any builtin, harcoded compsitor bindings such as VT switching.
|
|
||||||
/// Returns true if the keysym was handled.
|
|
||||||
fn handleBuiltinKeybind(self: Self, keysym: c.xkb_keysym_t) bool {
|
|
||||||
if (keysym >= c.XKB_KEY_XF86Switch_VT_1 and keysym <= c.XKB_KEY_XF86Switch_VT_12) {
|
|
||||||
Log.Debug.log("Switch VT keysym received", .{});
|
|
||||||
const wlr_backend = self.seat.input_manager.server.wlr_backend;
|
|
||||||
if (c.river_wlr_backend_is_multi(wlr_backend)) {
|
|
||||||
if (c.river_wlr_backend_get_session(wlr_backend)) |session| {
|
|
||||||
const vt = keysym - c.XKB_KEY_XF86Switch_VT_1 + 1;
|
|
||||||
Log.Debug.log("Switching to VT {}", .{vt});
|
|
||||||
_ = c.wlr_session_change_vt(session, vt);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
if (!handled) {
|
||||||
|
// Otherwise, we pass it along to the client.
|
||||||
|
const wlr_seat = self.seat.wlr_seat;
|
||||||
|
c.wlr_seat_set_keyboard(wlr_seat, self.wlr_input_device);
|
||||||
|
c.wlr_seat_keyboard_notify_key(
|
||||||
|
wlr_seat,
|
||||||
|
event.time_msec,
|
||||||
|
event.keycode,
|
||||||
|
@intCast(u32, @enumToInt(event.state)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handleModifiers(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
|
||||||
|
// This event is raised when a modifier key, such as shift or alt, is
|
||||||
|
// pressed. We simply communicate this to the client. */
|
||||||
|
const self = @fieldParentPtr(Self, "listen_modifiers", listener.?);
|
||||||
|
|
||||||
|
// A seat can only have one keyboard, but this is a limitation of the
|
||||||
|
// Wayland protocol - not wlroots. We assign all connected keyboards to the
|
||||||
|
// same seat. You can swap out the underlying wlr_keyboard like this and
|
||||||
|
// wlr_seat handles this transparently.
|
||||||
|
c.wlr_seat_set_keyboard(self.seat.wlr_seat, self.wlr_input_device);
|
||||||
|
|
||||||
|
// Send modifiers to the client.
|
||||||
|
c.wlr_seat_keyboard_notify_modifiers(
|
||||||
|
self.seat.wlr_seat,
|
||||||
|
&self.wlr_keyboard.modifiers,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Handle any builtin, harcoded compsitor bindings such as VT switching.
|
||||||
|
/// Returns true if the keysym was handled.
|
||||||
|
fn handleBuiltinKeybind(self: Self, keysym: c.xkb_keysym_t) bool {
|
||||||
|
if (keysym >= c.XKB_KEY_XF86Switch_VT_1 and keysym <= c.XKB_KEY_XF86Switch_VT_12) {
|
||||||
|
Log.Debug.log("Switch VT keysym received", .{});
|
||||||
|
const wlr_backend = self.seat.input_manager.server.wlr_backend;
|
||||||
|
if (c.river_wlr_backend_is_multi(wlr_backend)) {
|
||||||
|
if (c.river_wlr_backend_get_session(wlr_backend)) |session| {
|
||||||
|
const vt = keysym - c.XKB_KEY_XF86Switch_VT_1 + 1;
|
||||||
|
Log.Debug.log("Switching to VT {}", .{vt});
|
||||||
|
_ = c.wlr_session_change_vt(session, vt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
@ -5,7 +5,7 @@ const std = @import("std");
|
|||||||
|
|
||||||
const Cursor = @import("cursor.zig");
|
const Cursor = @import("cursor.zig");
|
||||||
const InputManager = @import("input_manager.zig");
|
const InputManager = @import("input_manager.zig");
|
||||||
const Keyboard = @import("keyboard.zig").Keyboard;
|
const Keyboard = @import("keyboard.zig");
|
||||||
const LayerSurface = @import("layer_surface.zig");
|
const LayerSurface = @import("layer_surface.zig");
|
||||||
const Output = @import("output.zig");
|
const Output = @import("output.zig");
|
||||||
const View = @import("view.zig");
|
const View = @import("view.zig");
|
||||||
|
Loading…
Reference in New Issue
Block a user