Make Keyboard a toplevel struct

This commit is contained in:
Isaac Freund 2020-05-02 16:48:09 +02:00
parent 97d395dbfc
commit ed98e8fe1a
No known key found for this signature in database
GPG key ID: 86DED400DDFD7A11
2 changed files with 138 additions and 139 deletions

View file

@ -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;
}

View file

@ -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");