- commit
- 24420e4
- parent
- a166953
- author
- Eric Bower
- date
- 2025-12-19 13:47:25 -0500 EST
feat: ctrl+b + d to detach With support to add more commands
1 files changed,
+77,
-3
+77,
-3
1@@ -532,6 +532,9 @@ fn clientLoop(_: *Cfg, client_sock_fd: i32) !void {
2
3 const stdin_fd = posix.STDIN_FILENO;
4
5+ // Prefix key state for ctrl+b + <key> bindings
6+ var prefix_active = false;
7+
8 // Make stdin non-blocking
9 const flags = try posix.fcntl(stdin_fd, posix.F.GETFL, 0);
10 _ = try posix.fcntl(stdin_fd, posix.F.SETFL, flags | posix.SOCK.NONBLOCK);
11@@ -583,14 +586,28 @@ fn clientLoop(_: *Cfg, client_sock_fd: i32) !void {
12
13 if (n_opt) |n| {
14 if (n > 0) {
15- // Check for Kitty keyboard protocol escape sequence for Ctrl+\
16- // Format: CSI 92 ; <modifiers> u where modifiers has Ctrl bit (bit 2) set
17- // Examples: \e[92;5u (basic), \e[92;133u (with event flags)
18+ // Check for Kitty keyboard protocol escape sequences
19 if (isKittyCtrlBackslash(buf[0..n])) {
20 ipc.send(client_sock_fd, .Detach, "") catch |err| switch (err) {
21 error.BrokenPipe, error.ConnectionResetByPeer => return,
22 else => return err,
23 };
24+ prefix_active = false;
25+ continue;
26+ }
27+
28+ if (isKittyCtrlB(buf[0..n])) {
29+ prefix_active = true;
30+ continue;
31+ }
32+
33+ // Handle prefix mode for Kitty 'd' key
34+ if (prefix_active and isKittyKey(buf[0..n], 'd')) {
35+ ipc.send(client_sock_fd, .Detach, "") catch |err| switch (err) {
36+ error.BrokenPipe, error.ConnectionResetByPeer => return,
37+ else => return err,
38+ };
39+ prefix_active = false;
40 continue;
41 }
42
43@@ -601,6 +618,36 @@ fn clientLoop(_: *Cfg, client_sock_fd: i32) !void {
44 error.BrokenPipe, error.ConnectionResetByPeer => return,
45 else => return err,
46 };
47+ prefix_active = false;
48+ } else if (buf[i] == 0x02) { // Ctrl+B
49+ if (prefix_active) {
50+ // Double ctrl+b sends literal ctrl+b
51+ ipc.send(client_sock_fd, .Input, &[_]u8{0x02}) catch |err| switch (err) {
52+ error.BrokenPipe, error.ConnectionResetByPeer => return,
53+ else => return err,
54+ };
55+ prefix_active = false;
56+ } else {
57+ prefix_active = true;
58+ }
59+ } else if (prefix_active) {
60+ if (buf[i] == 'd') {
61+ ipc.send(client_sock_fd, .Detach, "") catch |err| switch (err) {
62+ error.BrokenPipe, error.ConnectionResetByPeer => return,
63+ else => return err,
64+ };
65+ } else {
66+ // Unknown prefix command, forward both ctrl+b and this key
67+ ipc.send(client_sock_fd, .Input, &[_]u8{0x02}) catch |err| switch (err) {
68+ error.BrokenPipe, error.ConnectionResetByPeer => return,
69+ else => return err,
70+ };
71+ ipc.send(client_sock_fd, .Input, buf[i .. i + 1]) catch |err| switch (err) {
72+ error.BrokenPipe, error.ConnectionResetByPeer => return,
73+ else => return err,
74+ };
75+ }
76+ prefix_active = false;
77 } else {
78 const payload = buf[i .. i + 1];
79 ipc.send(client_sock_fd, .Input, payload) catch |err| switch (err) {
80@@ -1090,3 +1137,30 @@ test "isKittyCtrlBackslash" {
81 try std.testing.expect(!isKittyCtrlBackslash("\x1b[93;5u"));
82 try std.testing.expect(!isKittyCtrlBackslash("garbage"));
83 }
84+
85+fn isKittyCtrlB(buf: []const u8) bool {
86+ return std.mem.indexOf(u8, buf, "\x1b[98;5u") != null or
87+ std.mem.indexOf(u8, buf, "\x1b[98;133u") != null;
88+}
89+
90+test "isKittyCtrlB" {
91+ try std.testing.expect(isKittyCtrlB("\x1b[98;5u"));
92+ try std.testing.expect(isKittyCtrlB("\x1b[98;133u"));
93+ try std.testing.expect(!isKittyCtrlB("\x1b[98;1u"));
94+ try std.testing.expect(!isKittyCtrlB("\x1b[99;5u"));
95+ try std.testing.expect(!isKittyCtrlB("garbage"));
96+}
97+
98+fn isKittyKey(buf: []const u8, key: u8) bool {
99+ var expected: [16]u8 = undefined;
100+ const seq = std.fmt.bufPrint(&expected, "\x1b[{d}u", .{key}) catch return false;
101+
102+ return std.mem.indexOf(u8, buf, seq) != null;
103+}
104+
105+test "isKittyKey" {
106+ try std.testing.expect(isKittyKey("\x1b[100u", 'd'));
107+ try std.testing.expect(!isKittyKey("\x1b[100;5u", 'd'));
108+ try std.testing.expect(!isKittyKey("\x1b[101u", 'd'));
109+ try std.testing.expect(!isKittyKey("d", 'd'));
110+}