1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
|
const std = @import("std");
const c = @import("c.zig");
const Seat = @import("seat.zig").Seat;
pub const Keyboard = struct {
const Self = @This();
seat: *Seat,
device: *c.wlr_input_device,
wlr_keyboard: *c.wlr_keyboard,
listen_modifiers: c.wl_listener,
listen_key: c.wl_listener,
pub fn init(self: *Self, seat: *Seat, device: *c.wlr_input_device) !void {
self.seat = seat;
self.device = device;
self.wlr_keyboard = device.unnamed_133.keyboard;
// We need to prepare an XKB keymap and assign it to the keyboard. This
// assumes the defaults (e.g. layout = "us").
const rules = c.xkb_rule_names{
.rules = null,
.model = null,
.layout = null,
.variant = null,
.options = null,
};
const context = c.xkb_context_new(c.enum_xkb_context_flags.XKB_CONTEXT_NO_FLAGS) orelse
return error.CantCreateXkbContext;
defer c.xkb_context_unref(context);
const keymap = c.xkb_keymap_new_from_names(
context,
&rules,
c.enum_xkb_keymap_compile_flags.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
c.wlr_keyboard_set_keymap(self.wlr_keyboard, keymap);
c.wlr_keyboard_set_repeat_info(self.wlr_keyboard, 25, 600);
// Setup listeners for keyboard events
self.listen_modifiers.notify = handleModifiers;
c.wl_signal_add(&self.wlr_keyboard.events.modifiers, &self.listen_modifiers);
self.listen_key.notify = handleKey;
c.wl_signal_add(&self.wlr_keyboard.events.key, &self.listen_key);
}
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 keyboard = @fieldParentPtr(Keyboard, "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(keyboard.seat.wlr_seat, keyboard.device);
// Send modifiers to the client.
c.wlr_seat_keyboard_notify_modifiers(
keyboard.seat.wlr_seat,
&keyboard.wlr_keyboard.modifiers,
);
}
fn handleKey(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
// This event is raised when a key is pressed or released.
const keyboard = @fieldParentPtr(Keyboard, "listen_key", listener.?);
const event = @ptrCast(
*c.wlr_event_keyboard_key,
@alignCast(@alignOf(*c.wlr_event_keyboard_key), data),
);
const wlr_keyboard: *c.wlr_keyboard = keyboard.device.unnamed_133.keyboard;
// Translate libinput keycode -> xkbcommon
const keycode = event.keycode + 8;
// Get a list of keysyms as xkb reports them
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 ignoring modifiers (e.g. 1 instead of !)
// Important for bindings like Mod+Shift+1
var raw_keysyms: ?[*]c.xkb_keysym_t = undefined;
const layout_index = c.xkb_state_key_get_layout(wlr_keyboard.xkb_state, keycode);
const raw_keysyms_len = c.xkb_keymap_key_get_syms_by_level(
wlr_keyboard.keymap,
keycode,
layout_index,
0,
&raw_keysyms,
);
var handled = false;
// TODO: These modifiers aren't properly handled, see sway's code
const modifiers = c.wlr_keyboard_get_modifiers(wlr_keyboard);
if (modifiers & @intCast(u32, c.WLR_MODIFIER_LOGO) != 0 and
event.state == c.enum_wlr_key_state.WLR_KEY_PRESSED)
{
// If mod is held down and this button was _pressed_, we attempt to
// process it as a compositor keybinding.
var i: usize = 0;
while (i < translated_keysyms_len) : (i += 1) {
if (keyboard.seat.server.handleKeybinding(translated_keysyms.?[i], modifiers)) {
handled = true;
break;
}
}
if (!handled) {
i = 0;
while (i < raw_keysyms_len) : (i += 1) {
if (keyboard.seat.server.handleKeybinding(raw_keysyms.?[i], modifiers)) {
handled = true;
break;
}
}
}
}
if (!handled) {
// Otherwise, we pass it along to the client.
const wlr_seat = keyboard.seat.wlr_seat;
c.wlr_seat_set_keyboard(wlr_seat, keyboard.device);
c.wlr_seat_keyboard_notify_key(
wlr_seat,
event.time_msec,
event.keycode,
@intCast(u32, @enumToInt(event.state)),
);
}
}
};
|