From 6f311af3b3b571acf32bc390d892a6adcc8ed354 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Mon, 1 Jan 2024 00:37:16 -0600 Subject: [PATCH] TextInput/InputRelay: style nits There should be no functional changes in this commit --- river/InputManager.zig | 44 +++------------- river/InputRelay.zig | 115 +++++++++++++++++++++++++---------------- river/Seat.zig | 11 ++-- river/TextInput.zig | 74 +++++++++++++++----------- 4 files changed, 126 insertions(+), 118 deletions(-) diff --git a/river/InputManager.zig b/river/InputManager.zig index 1e4df90..4ce937a 100644 --- a/river/InputManager.zig +++ b/river/InputManager.zig @@ -173,48 +173,18 @@ fn handleNewConstraint( }; } -fn handleNewInputMethod( - _: *wl.Listener(*wlr.InputMethodV2), - input_method: *wlr.InputMethodV2, -) void { - //const self = @fieldParentPtr(Self, "new_input_method", listener); - const seat = @as(*Seat, @ptrFromInt(input_method.seat.data)); - const relay = &seat.relay; +fn handleNewInputMethod(_: *wl.Listener(*wlr.InputMethodV2), input_method: *wlr.InputMethodV2) void { + const seat: *Seat = @ptrFromInt(input_method.seat.data); - // Only one input_method can be bound to a seat. - if (relay.input_method != null) { - log.debug("attempted to connect second input method to a seat", .{}); - input_method.sendUnavailable(); - return; - } + log.debug("new input method on seat {s}", .{seat.wlr_seat.name}); - relay.input_method = input_method; - - input_method.events.commit.add(&relay.input_method_commit); - input_method.events.grab_keyboard.add(&relay.grab_keyboard); - input_method.events.destroy.add(&relay.input_method_destroy); - - log.debug("new input method on seat {s}", .{relay.seat.wlr_seat.name}); - - if (seat.focused.surface()) |surface| { - relay.focus(surface); - } + seat.relay.newInputMethod(input_method); } -fn handleNewTextInput( - _: *wl.Listener(*wlr.TextInputV3), - wlr_text_input: *wlr.TextInputV3, -) void { - //const self = @fieldParentPtr(Self, "new_text_input", listener); - const seat = @as(*Seat, @ptrFromInt(wlr_text_input.seat.data)); - const relay = &seat.relay; - - const text_input_node = util.gpa.create(std.TailQueue(TextInput).Node) catch { +fn handleNewTextInput(_: *wl.Listener(*wlr.TextInputV3), wlr_text_input: *wlr.TextInputV3) void { + TextInput.create(wlr_text_input) catch { + log.err("out of memory", .{}); wlr_text_input.resource.postNoMemory(); return; }; - text_input_node.data.init(relay, wlr_text_input); - relay.text_inputs.append(text_input_node); - - log.debug("new text input on seat {s}", .{relay.seat.wlr_seat.name}); } diff --git a/river/InputRelay.zig b/river/InputRelay.zig index 99d4f34..d70d447 100644 --- a/river/InputRelay.zig +++ b/river/InputRelay.zig @@ -15,7 +15,7 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -const Self = @This(); +const InputRelay = @This(); const std = @import("std"); const assert = std.debug.assert; @@ -30,17 +30,18 @@ const Seat = @import("Seat.zig"); const log = std.log.scoped(.input_relay); -/// The Relay structure manages the communication between text_inputs -/// and input_method on a given seat. -seat: *Seat, - -/// List of all TextInput bound to the relay. -/// Multiple wlr_text_input interfaces can be bound to a relay, -/// but only one at a time can receive events. -text_inputs: std.TailQueue(TextInput) = .{}, +/// List of all text input objects for the seat. +/// Multiple text input objects may be created per seat, even multiple from the same client. +/// However, only one text input per seat may be enabled at a time. +text_inputs: wl.list.Head(TextInput, .link), +/// The input method currently in use for this seat. +/// Only one input method per seat may be used at a time and if one is +/// already in use new input methods are ignored. +/// If this is null, no text input enter events will be sent. input_method: ?*wlr.InputMethodV2 = null, /// The currently enabled text input for the currently focused surface. +/// Always null if there is no input method. text_input: ?*TextInput = null, input_method_commit: wl.Listener(*wlr.InputMethodV2) = @@ -53,19 +54,44 @@ input_method_destroy: wl.Listener(*wlr.InputMethodV2) = grab_keyboard_destroy: wl.Listener(*wlr.InputMethodV2.KeyboardGrab) = wl.Listener(*wlr.InputMethodV2.KeyboardGrab).init(handleInputMethodGrabKeyboardDestroy), -pub fn init(self: *Self, seat: *Seat) void { - self.* = .{ .seat = seat }; +pub fn init(relay: *InputRelay) void { + relay.* = .{ .text_inputs = undefined }; + + relay.text_inputs.init(); +} + +pub fn newInputMethod(relay: *InputRelay, input_method: *wlr.InputMethodV2) void { + const seat = @fieldParentPtr(Seat, "relay", relay); + + log.debug("new input method on seat {s}", .{seat.wlr_seat.name}); + + // Only one input_method can be bound to a seat. + if (relay.input_method != null) { + log.info("seat {s} already has an input method", .{seat.wlr_seat.name}); + input_method.sendUnavailable(); + return; + } + + relay.input_method = input_method; + + input_method.events.commit.add(&relay.input_method_commit); + input_method.events.grab_keyboard.add(&relay.grab_keyboard); + input_method.events.destroy.add(&relay.input_method_destroy); + + if (seat.focused.surface()) |surface| { + relay.focus(surface); + } } fn handleInputMethodCommit( listener: *wl.Listener(*wlr.InputMethodV2), input_method: *wlr.InputMethodV2, ) void { - const self = @fieldParentPtr(Self, "input_method_commit", listener); - assert(input_method == self.input_method); + const relay = @fieldParentPtr(InputRelay, "input_method_commit", listener); + assert(input_method == relay.input_method); if (!input_method.client_active) return; - const text_input = self.text_input orelse return; + const text_input = relay.text_input orelse return; if (input_method.current.preedit.text) |preedit_text| { text_input.wlr_text_input.sendPreeditString( @@ -95,56 +121,59 @@ fn handleInputMethodDestroy( listener: *wl.Listener(*wlr.InputMethodV2), input_method: *wlr.InputMethodV2, ) void { - const self = @fieldParentPtr(Self, "input_method_destroy", listener); - assert(input_method == self.input_method); + const relay = @fieldParentPtr(InputRelay, "input_method_destroy", listener); + assert(input_method == relay.input_method); - self.input_method_commit.link.remove(); - self.grab_keyboard.link.remove(); - self.input_method_destroy.link.remove(); + relay.input_method_commit.link.remove(); + relay.grab_keyboard.link.remove(); + relay.input_method_destroy.link.remove(); - self.input_method = null; + relay.input_method = null; - self.focus(null); + relay.focus(null); + + assert(relay.text_input == null); } fn handleInputMethodGrabKeyboard( listener: *wl.Listener(*wlr.InputMethodV2.KeyboardGrab), keyboard_grab: *wlr.InputMethodV2.KeyboardGrab, ) void { - const self = @fieldParentPtr(Self, "grab_keyboard", listener); + const relay = @fieldParentPtr(InputRelay, "grab_keyboard", listener); + const seat = @fieldParentPtr(Seat, "relay", relay); - const active_keyboard = self.seat.wlr_seat.getKeyboard(); + const active_keyboard = seat.wlr_seat.getKeyboard(); keyboard_grab.setKeyboard(active_keyboard); - keyboard_grab.events.destroy.add(&self.grab_keyboard_destroy); + keyboard_grab.events.destroy.add(&relay.grab_keyboard_destroy); } fn handleInputMethodGrabKeyboardDestroy( listener: *wl.Listener(*wlr.InputMethodV2.KeyboardGrab), keyboard_grab: *wlr.InputMethodV2.KeyboardGrab, ) void { - const self = @fieldParentPtr(Self, "grab_keyboard_destroy", listener); - self.grab_keyboard_destroy.link.remove(); + const relay = @fieldParentPtr(InputRelay, "grab_keyboard_destroy", listener); + relay.grab_keyboard_destroy.link.remove(); if (keyboard_grab.keyboard) |keyboard| { keyboard_grab.input_method.seat.keyboardNotifyModifiers(&keyboard.modifiers); } } -pub fn disableTextInput(self: *Self) void { - assert(self.text_input != null); +pub fn disableTextInput(relay: *InputRelay) void { + assert(relay.text_input != null); - if (self.input_method) |input_method| { + if (relay.input_method) |input_method| { input_method.sendDeactivate(); input_method.sendDone(); } - self.text_input = null; + relay.text_input = null; } -pub fn sendInputMethodState(self: *Self) void { - const input_method = self.input_method.?; - const wlr_text_input = self.text_input.?.wlr_text_input; +pub fn sendInputMethodState(relay: *InputRelay) void { + const input_method = relay.input_method.?; + const wlr_text_input = relay.text_input.?.wlr_text_input; // TODO Send these events only if something changed. // On activation all events must be sent for all active features. @@ -171,13 +200,11 @@ pub fn sendInputMethodState(self: *Self) void { input_method.sendDone(); } -pub fn focus(self: *Self, new_focus: ?*wlr.Surface) void { +pub fn focus(relay: *InputRelay, new_focus: ?*wlr.Surface) void { // Send leave events { - var it = self.text_inputs.first; - while (it) |node| : (it = node.next) { - const text_input = &node.data; - + var it = relay.text_inputs.iterator(.forward); + while (it.next()) |text_input| { if (text_input.wlr_text_input.focused_surface) |surface| { // This function should not be called unless focus changes assert(surface != new_focus); @@ -187,19 +214,17 @@ pub fn focus(self: *Self, new_focus: ?*wlr.Surface) void { } // Clear currently enabled text input - if (self.text_input != null) { - self.disableTextInput(); + if (relay.text_input != null) { + relay.disableTextInput(); } // Send enter events if we have an input method. // No text input for the new surface should be enabled yet as the client // should wait until it receives an enter event. if (new_focus) |surface| { - if (self.input_method != null) { - var it = self.text_inputs.first; - while (it) |node| : (it = node.next) { - const text_input = &node.data; - + if (relay.input_method != null) { + var it = relay.text_inputs.iterator(.forward); + while (it.next()) |text_input| { if (text_input.wlr_text_input.resource.getClient() == surface.resource.getClient()) { text_input.wlr_text_input.sendEnter(surface); } diff --git a/river/Seat.zig b/river/Seat.zig index 6058dd1..dc28b2f 100644 --- a/river/Seat.zig +++ b/river/Seat.zig @@ -68,7 +68,9 @@ pub const FocusTarget = union(enum) { wlr_seat: *wlr.Seat, /// Multiple mice are handled by the same Cursor -cursor: Cursor = undefined, +cursor: Cursor, +/// Input Method handling +relay: InputRelay, /// ID of the current keymap mode mode_id: u32 = 0, @@ -99,9 +101,6 @@ drag: enum { touch, } = .none, -/// Relay for communication between text_input and input_method. -relay: InputRelay = undefined, - request_set_selection: wl.Listener(*wlr.Seat.event.RequestSetSelection) = wl.Listener(*wlr.Seat.event.RequestSetSelection).init(handleRequestSetSelection), request_start_drag: wl.Listener(*wlr.Seat.event.RequestStartDrag) = @@ -119,12 +118,14 @@ pub fn init(self: *Self, name: [*:0]const u8) !void { self.* = .{ // This will be automatically destroyed when the display is destroyed .wlr_seat = try wlr.Seat.create(server.wl_server, name), + .cursor = undefined, + .relay = undefined, .mapping_repeat_timer = mapping_repeat_timer, }; self.wlr_seat.data = @intFromPtr(self); try self.cursor.init(self); - self.relay.init(self); + self.relay.init(); self.wlr_seat.events.request_set_selection.add(&self.request_set_selection); self.wlr_seat.events.request_start_drag.add(&self.request_start_drag); diff --git a/river/TextInput.zig b/river/TextInput.zig index 3375500..56661b2 100644 --- a/river/TextInput.zig +++ b/river/TextInput.zig @@ -15,7 +15,7 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -const Self = @This(); +const TextInput = @This(); const std = @import("std"); const assert = std.debug.assert; @@ -29,7 +29,8 @@ const Seat = @import("Seat.zig"); const log = std.log.scoped(.text_input); -relay: *InputRelay, +link: wl.list.Link, + wlr_text_input: *wlr.TextInputV3, enable: wl.Listener(*wlr.TextInputV3) = @@ -41,68 +42,79 @@ disable: wl.Listener(*wlr.TextInputV3) = destroy: wl.Listener(*wlr.TextInputV3) = wl.Listener(*wlr.TextInputV3).init(handleDestroy), -pub fn init(self: *Self, relay: *InputRelay, wlr_text_input: *wlr.TextInputV3) void { - self.* = .{ - .relay = relay, +pub fn create(wlr_text_input: *wlr.TextInputV3) !void { + const seat: *Seat = @ptrFromInt(wlr_text_input.seat.data); + + const text_input = try util.gpa.create(TextInput); + + log.debug("new text input on seat {s}", .{seat.wlr_seat.name}); + + text_input.* = .{ + .link = undefined, .wlr_text_input = wlr_text_input, }; - wlr_text_input.events.enable.add(&self.enable); - wlr_text_input.events.commit.add(&self.commit); - wlr_text_input.events.disable.add(&self.disable); - wlr_text_input.events.destroy.add(&self.destroy); + seat.relay.text_inputs.append(text_input); + + wlr_text_input.events.enable.add(&text_input.enable); + wlr_text_input.events.commit.add(&text_input.commit); + wlr_text_input.events.disable.add(&text_input.disable); + wlr_text_input.events.destroy.add(&text_input.destroy); } fn handleEnable(listener: *wl.Listener(*wlr.TextInputV3), _: *wlr.TextInputV3) void { - const self = @fieldParentPtr(Self, "enable", listener); + const text_input = @fieldParentPtr(TextInput, "enable", listener); + const seat: *Seat = @ptrFromInt(text_input.wlr_text_input.seat.data); - if (self.relay.text_input != null) { + if (seat.relay.text_input != null) { log.err("client requested to enable more than one text input on a single seat, ignoring request", .{}); return; } - self.relay.text_input = self; + seat.relay.text_input = text_input; - if (self.relay.input_method) |input_method| { + if (seat.relay.input_method) |input_method| { input_method.sendActivate(); - self.relay.sendInputMethodState(); + seat.relay.sendInputMethodState(); } } fn handleCommit(listener: *wl.Listener(*wlr.TextInputV3), _: *wlr.TextInputV3) void { - const self = @fieldParentPtr(Self, "commit", listener); + const text_input = @fieldParentPtr(TextInput, "commit", listener); + const seat: *Seat = @ptrFromInt(text_input.wlr_text_input.seat.data); - if (self.relay.text_input != self) { + if (seat.relay.text_input != text_input) { log.err("inactive text input tried to commit an update, client bug?", .{}); return; } - if (self.relay.input_method != null) { - self.relay.sendInputMethodState(); + if (seat.relay.input_method != null) { + seat.relay.sendInputMethodState(); } } fn handleDisable(listener: *wl.Listener(*wlr.TextInputV3), _: *wlr.TextInputV3) void { - const self = @fieldParentPtr(Self, "disable", listener); + const text_input = @fieldParentPtr(TextInput, "disable", listener); + const seat: *Seat = @ptrFromInt(text_input.wlr_text_input.seat.data); - if (self.relay.text_input == self) { - self.relay.disableTextInput(); + if (seat.relay.text_input == text_input) { + seat.relay.disableTextInput(); } } fn handleDestroy(listener: *wl.Listener(*wlr.TextInputV3), _: *wlr.TextInputV3) void { - const self = @fieldParentPtr(Self, "destroy", listener); - const node = @fieldParentPtr(std.TailQueue(Self).Node, "data", self); + const text_input = @fieldParentPtr(TextInput, "destroy", listener); + const seat: *Seat = @ptrFromInt(text_input.wlr_text_input.seat.data); - if (self.relay.text_input == self) { - self.relay.disableTextInput(); + if (seat.relay.text_input == text_input) { + seat.relay.disableTextInput(); } - self.enable.link.remove(); - self.commit.link.remove(); - self.disable.link.remove(); - self.destroy.link.remove(); + text_input.enable.link.remove(); + text_input.commit.link.remove(); + text_input.disable.link.remove(); + text_input.destroy.link.remove(); - self.relay.text_inputs.remove(node); - util.gpa.destroy(node); + text_input.link.remove(); + util.gpa.destroy(text_input); }