118 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			Zig
		
	
	
	
	
	
			
		
		
	
	
			118 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			Zig
		
	
	
	
	
	
| const std = @import("std");
 | |
| const c = @import("c.zig").c;
 | |
| 
 | |
| const Keyboard = struct {
 | |
|     seat: *Seat,
 | |
|     device: *c.wlr_input_device,
 | |
| 
 | |
|     listen_modifiers: c.wl_listener,
 | |
|     listen_key: c.wl_listener,
 | |
| 
 | |
|     pub fn init(seat: *Seat, device: *c.wlr_input_device) @This() {
 | |
|         var keyboard = @This(){
 | |
|             .seat = seat,
 | |
|             .device = device,
 | |
| 
 | |
|             .listen_modifiers = c.wl_listener{
 | |
|                 .link = undefined,
 | |
|                 .notify = handle_modifiers,
 | |
|             },
 | |
|             .listen_key = c.wl_listener{
 | |
|                 .link = undefined,
 | |
|                 .notify = handle_key,
 | |
|             },
 | |
|         };
 | |
| 
 | |
|         // 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);
 | |
|         defer c.xkb_context_unref(context);
 | |
| 
 | |
|         const keymap = man_c.xkb_map_new_from_names(
 | |
|             context,
 | |
|             &rules,
 | |
|             c.enum_xkb_keymap_compile_flags.XKB_KEYMAP_COMPILE_NO_FLAGS,
 | |
|         );
 | |
|         defer c.xkb_keymap_unref(keymap);
 | |
| 
 | |
|         var keyboard_device = device.*.unnamed_37.keyboard;
 | |
|         c.wlr_keyboard_set_keymap(keyboard_device, keymap);
 | |
|         c.wlr_keyboard_set_repeat_info(keyboard_device, 25, 600);
 | |
| 
 | |
|         // Setup listeners for keyboard events
 | |
|         c.wl_signal_add(&keyboard_device.*.events.modifiers, &keyboard.*.listen_modifiers);
 | |
|         c.wl_signal_add(&keyboard_device.*.events.key, &keyboard.*.listen_key);
 | |
| 
 | |
|         return keyboard;
 | |
|     }
 | |
| 
 | |
|     fn handle_modifiers(listener: [*c]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. */
 | |
|         var 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.*.server.*.seat, keyboard.*.device);
 | |
| 
 | |
|         // Send modifiers to the client.
 | |
|         c.wlr_seat_keyboard_notify_modifiers(keyboard.*.server.*.seat, &keyboard.*.device.*.unnamed_37.keyboard.*.modifiers);
 | |
|     }
 | |
| 
 | |
|     fn handle_key(listener: [*c]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 server = keyboard.*.server;
 | |
|         const seat = server.*.seat;
 | |
|         const keyboard_device = keyboard.*.device.*.unnamed_37.keyboard;
 | |
| 
 | |
|         // Translate libinput keycode -> xkbcommon
 | |
|         const keycode = event.*.keycode + 8;
 | |
|         // Get a list of keysyms based on the keymap for this keyboard
 | |
|         var syms: *c.xkb_keysym_t = undefined;
 | |
|         const nsyms = c.xkb_state_key_get_syms(keyboard_device.*.xkb_state, keycode, &syms);
 | |
| 
 | |
|         var handled = false;
 | |
|         const modifiers = c.wlr_keyboard_get_modifiers(keyboard_device);
 | |
|         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 < nsyms) {
 | |
|                 handled = keyboard.seat.server.handle_keybinding(syms[i]);
 | |
|                 if (handled) {
 | |
|                     break;
 | |
|                 }
 | |
|                 i += 1;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         if (!handled) {
 | |
|             // Otherwise, we pass it along to the client.
 | |
|             c.wlr_seat_set_keyboard(seat, keyboard.*.device);
 | |
|             c.wlr_seat_keyboard_notify_key(
 | |
|                 seat,
 | |
|                 event.*.time_msec,
 | |
|                 event.*.keycode,
 | |
|                 @intCast(u32, @enumToInt(event.*.state)),
 | |
|             );
 | |
|         }
 | |
|     }
 | |
| };
 |