- commit
- 3dcce3d
- parent
- f15dee4
- author
- Eric Bower
- date
- 2025-12-19 14:09:29 -0500 EST
refactor: input shortcut handling Another code clarity change that abstracts handling the shortcuts for detach
1 files changed,
+97,
-57
+97,
-57
1@@ -672,74 +672,67 @@ fn clientLoop(_: *Cfg, client_sock_fd: i32) !void {
2
3 if (n_opt) |n| {
4 if (n > 0) {
5- // Check for Kitty keyboard protocol escape sequences
6- if (isKittyCtrlBackslash(buf[0..n])) {
7- ipc.send(client_sock_fd, .Detach, "") catch |err| switch (err) {
8- error.BrokenPipe, error.ConnectionResetByPeer => return,
9- else => return err,
10- };
11- prefix_active = false;
12- continue;
13- }
14-
15- if (isKittyCtrlB(buf[0..n])) {
16- prefix_active = true;
17- continue;
18- }
19-
20- // Handle prefix mode for Kitty 'd' key
21- if (prefix_active and isKittyKey(buf[0..n], 'd')) {
22- ipc.send(client_sock_fd, .Detach, "") catch |err| switch (err) {
23- error.BrokenPipe, error.ConnectionResetByPeer => return,
24- else => return err,
25- };
26- prefix_active = false;
27- continue;
28- }
29-
30- var i: usize = 0;
31- while (i < n) : (i += 1) {
32- if (buf[i] == 0x1C) { // Ctrl+\ (File Separator)
33+ // Check for Kitty keyboard protocol sequences first
34+ switch (parseStdinInput(buf[0..n], &prefix_active)) {
35+ .detach => {
36 ipc.send(client_sock_fd, .Detach, "") catch |err| switch (err) {
37 error.BrokenPipe, error.ConnectionResetByPeer => return,
38 else => return err,
39 };
40- prefix_active = false;
41- } else if (buf[i] == 0x02) { // Ctrl+B
42- if (prefix_active) {
43- // Double ctrl+b sends literal ctrl+b
44- ipc.send(client_sock_fd, .Input, &[_]u8{0x02}) catch |err| switch (err) {
45- error.BrokenPipe, error.ConnectionResetByPeer => return,
46- else => return err,
47- };
48- prefix_active = false;
49- } else {
50- prefix_active = true;
51- }
52- } else if (prefix_active) {
53- if (buf[i] == 'd') {
54+ continue;
55+ },
56+ .send => |data| {
57+ ipc.send(client_sock_fd, .Input, data) catch |err| switch (err) {
58+ error.BrokenPipe, error.ConnectionResetByPeer => return,
59+ else => return err,
60+ };
61+ continue;
62+ },
63+ .activate_prefix => continue,
64+ .none => {},
65+ }
66+
67+ // Process byte-by-byte for non-Kitty input
68+ var i: usize = 0;
69+ while (i < n) : (i += 1) {
70+ const action = parseStdinByte(buf[i], prefix_active);
71+ switch (action) {
72+ .detach => {
73 ipc.send(client_sock_fd, .Detach, "") catch |err| switch (err) {
74 error.BrokenPipe, error.ConnectionResetByPeer => return,
75 else => return err,
76 };
77- } else {
78- // Unknown prefix command, forward both ctrl+b and this key
79- ipc.send(client_sock_fd, .Input, &[_]u8{0x02}) catch |err| switch (err) {
80- error.BrokenPipe, error.ConnectionResetByPeer => return,
81- else => return err,
82- };
83- ipc.send(client_sock_fd, .Input, buf[i .. i + 1]) catch |err| switch (err) {
84+ prefix_active = false;
85+ },
86+ .send => |data| {
87+ ipc.send(client_sock_fd, .Input, data) catch |err| switch (err) {
88 error.BrokenPipe, error.ConnectionResetByPeer => return,
89 else => return err,
90 };
91- }
92- prefix_active = false;
93- } else {
94- const payload = buf[i .. i + 1];
95- ipc.send(client_sock_fd, .Input, payload) catch |err| switch (err) {
96- error.BrokenPipe, error.ConnectionResetByPeer => return,
97- else => return err,
98- };
99+ prefix_active = false;
100+ },
101+ .activate_prefix => {
102+ prefix_active = true;
103+ },
104+ .none => {
105+ if (prefix_active) {
106+ // Unknown prefix command, forward both ctrl+b and this key
107+ ipc.send(client_sock_fd, .Input, &[_]u8{0x02}) catch |err| switch (err) {
108+ error.BrokenPipe, error.ConnectionResetByPeer => return,
109+ else => return err,
110+ };
111+ ipc.send(client_sock_fd, .Input, buf[i .. i + 1]) catch |err| switch (err) {
112+ error.BrokenPipe, error.ConnectionResetByPeer => return,
113+ else => return err,
114+ };
115+ prefix_active = false;
116+ } else {
117+ ipc.send(client_sock_fd, .Input, buf[i .. i + 1]) catch |err| switch (err) {
118+ error.BrokenPipe, error.ConnectionResetByPeer => return,
119+ else => return err,
120+ };
121+ }
122+ },
123 }
124 }
125 } else {
126@@ -1071,6 +1064,53 @@ fn probeSession(alloc: std.mem.Allocator, socket_path: []const u8) SessionProbeE
127 return error.Unexpected;
128 }
129
130+const InputAction = union(enum) {
131+ send: []const u8,
132+ detach,
133+ activate_prefix,
134+ none,
135+};
136+
137+fn parseStdinByte(byte: u8, prefix_active: bool) InputAction {
138+ if (byte == 0x1C) { // Ctrl+\ (File Separator)
139+ return .detach;
140+ } else if (byte == 0x02) { // Ctrl+B
141+ if (prefix_active) {
142+ return .{ .send = &[_]u8{0x02} };
143+ } else {
144+ return .activate_prefix;
145+ }
146+ } else if (prefix_active) {
147+ if (byte == 'd') {
148+ return .detach;
149+ } else {
150+ return .none; // Unknown prefix command - caller handles forwarding
151+ }
152+ }
153+ return .none; // Regular byte - caller handles forwarding
154+}
155+
156+fn parseStdinInput(buf: []const u8, prefix_active: *bool) InputAction {
157+ // Check for Kitty keyboard protocol escape sequences first
158+ if (isKittyCtrlBackslash(buf)) {
159+ prefix_active.* = false;
160+ return .detach;
161+ }
162+
163+ if (isKittyCtrlB(buf)) {
164+ prefix_active.* = true;
165+ return .activate_prefix;
166+ }
167+
168+ // Handle prefix mode for Kitty 'd' key
169+ if (prefix_active.* and isKittyKey(buf, 'd')) {
170+ prefix_active.* = false;
171+ return .detach;
172+ }
173+
174+ return .none; // Not a special sequence
175+}
176+
177 fn cleanupStaleSocket(dir: std.fs.Dir, session_name: []const u8) void {
178 std.log.warn("stale socket found, cleaning up session={s}", .{session_name});
179 dir.deleteFile(session_name) catch |err| {