summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/mouse.zig17
-rw-r--r--src/ninep/events.zig50
-rw-r--r--src/ninep/pane.zig8
3 files changed, 68 insertions, 7 deletions
diff --git a/src/mouse.zig b/src/mouse.zig
index 9f054e2e..0d1bfadd 100644
--- a/src/mouse.zig
+++ b/src/mouse.zig
@@ -1676,5 +1676,22 @@ test "a chorded exec an event reader holds is told with its argument, and writte
try std.testing.expectEqual(ctlfs.Status.ok, th.call(p, .{ .tag = 3, .op = .write, .node = event, .handle = h, .data = line }).reply.status);
const pane = p.panes[0].?;
try std.testing.expectEqualStrings("arg-text", pane.msg[0..pane.msg_len]);
+ // All three records written back as read, in one write or in three:
+ // the two after the first are its argument and where it came from,
+ // never commands of their own.
+ for ([_]bool{ true, false }) |together| {
+ pane.msg_len = 0;
+ var all: [256]u8 = undefined;
+ var w = std.Io.Writer.fixed(&all);
+ for (records) |r| try w.print("M{s}", .{r[1..]});
+ if (together) {
+ _ = th.call(p, .{ .tag = 5, .op = .write, .node = event, .handle = h, .data = w.buffered() });
+ } else for (records) |r| {
+ var one: [80]u8 = undefined;
+ _ = th.call(p, .{ .tag = 6, .op = .write, .node = event, .handle = h, .data = try std.fmt.bufPrint(&one, "M{s}", .{r[1..]}) });
+ }
+ try std.testing.expectEqualStrings("arg-text", pane.msg[0..pane.msg_len]);
+ try std.testing.expect(!th.logHas(p, "run "));
+ }
_ = th.call(p, .{ .tag = 4, .op = .release, .node = event, .handle = h });
}
diff --git a/src/ninep/events.zig b/src/ninep/events.zig
index 1067b60e..7a06bd04 100644
--- a/src/ninep/events.zig
+++ b/src/ninep/events.zig
@@ -536,7 +536,7 @@ pub fn notePtyOutput(p: *Pardes, id: usize, bytes: []const u8) void {
p.fs.news = true;
}
-const EventRecord = struct { origin: u8, action: Action, q0: u32, q1: u32, text: ?[]const u8 = null };
+const EventRecord = struct { origin: u8, action: Action, q0: u32, q1: u32, flag: u32 = 0, text: ?[]const u8 = null };
const EventReader = struct {
data: []const u8,
@@ -557,8 +557,9 @@ const EventReader = struct {
// is taken too, so a click with no place in the text (one written
// to a pane's own look or exec) can be written back and done.
var text: ?[]const u8 = null;
+ var flag: u32 = 0;
if (i < er.data.len and er.data[i] != '\n') {
- _ = scanNumber(er.data, &i) orelse return null;
+ flag = scanNumber(er.data, &i) orelse return null;
const n = scanNumber(er.data, &i) orelse return null;
if (i >= er.data.len or er.data[i] != ' ' or er.data.len - (i + 1) < n) return null;
text = er.data[i + 1 ..][0..n];
@@ -566,7 +567,7 @@ const EventReader = struct {
}
if (i >= er.data.len or er.data[i] != '\n') return null;
er.i = i + 1;
- return .{ .origin = origin, .action = action, .q0 = q0, .q1 = q1, .text = text };
+ return .{ .origin = origin, .action = action, .q0 = q0, .q1 = q1, .flag = flag, .text = text };
}
};
@@ -616,17 +617,52 @@ pub fn writeEvent(p: *Pardes, req: Req, id: usize) Reply {
// never a click, and runs as the command it was.
// The chorded exec a reader was told of runs with its argument
// (mouse.reportGesture), when it is that range written back.
- .body_exec => {
+ .body_exec, .tag_exec => {
const pf = &pane.fs;
- const arg = if (pf.chord_arg) |a| (if (pf.chord_at.q0 == r.q0 and pf.chord_at.q1 == r.q1) a else null) else null;
- _ = exec.executeFrom(p, live, exec.withArg(p, text, arg), r.origin != 'F');
+ // The two records that follow a chorded exec (its argument
+ // and where it came from), written back after it, are its:
+ // consumed, never run as commands of their own.
+ if (pf.chord_follow > 0 and r.q0 == 0 and r.q1 == 0 and r.flag == 0) {
+ pf.chord_follow -= 1;
+ // The argument a command written back alone waited for.
+ if (pf.chord_pending) |command| {
+ pf.chord_pending = null;
+ defer p.gpa.free(command);
+ const line = exec.withArg(p, command, r.text orelse "");
+ if (pf.chord_pending_tag) _ = exec.execute(p, live, line) else _ = exec.executeFrom(p, live, line, true);
+ }
+ continue;
+ }
+ var arg = if (pf.chord_arg) |a| (if (pf.chord_at.q0 == r.q0 and pf.chord_at.q1 == r.q1) a else null) else null;
+ if (r.flag & flag_chorded != 0) {
+ // Its argument is the next record when it came in the
+ // same write; the rest that follow are consumed.
+ pf.chord_follow = 2;
+ const save = run.i;
+ if (run.next()) |following| if (following.q0 == 0 and following.q1 == 0 and following.flag == 0) {
+ if (following.text) |t| arg = p.scratch.allocator().dupe(u8, t) catch arg;
+ pf.chord_follow = 1;
+ } else {
+ run.i = save;
+ };
+ // Alone, with no argument kept: it waits for its own.
+ if (arg == null) {
+ if (pf.chord_pending) |old| p.gpa.free(old);
+ pf.chord_pending = p.gpa.dupe(u8, text) catch null;
+ pf.chord_pending_tag = r.action == .tag_exec;
+ continue;
+ }
+ }
+ if (r.action == .tag_exec)
+ _ = exec.execute(p, live, exec.withArg(p, text, arg))
+ else
+ _ = exec.executeFrom(p, live, exec.withArg(p, text, arg), r.origin != 'F');
if (arg != null) if (p.paneBySerial(serial)) |still| {
const spf = &p.panes[still].?.fs;
if (spf.chord_arg) |a| p.gpa.free(a);
spf.chord_arg = null;
};
},
- .tag_exec => _ = exec.execute(p, live, text),
// What it found is what the look file reads back next, as a
// look written there would leave (ctl.run).
.body_look, .tag_look => {
diff --git a/src/ninep/pane.zig b/src/ninep/pane.zig
index 4a50b9a1..899d8528 100644
--- a/src/ninep/pane.zig
+++ b/src/ninep/pane.zig
@@ -81,11 +81,19 @@ pub const State = struct {
/// for the record's write-back at `chord_at`, which runs with it.
chord_arg: ?[]u8 = null,
chord_at: Range = .{},
+ /// A chorded record written back: how many of its two follow-up records
+ /// (argument, location) are still to be consumed rather than run.
+ chord_follow: u8 = 0,
+ /// A chorded record written back alone with no argument kept: the
+ /// command, run when its argument record comes.
+ chord_pending: ?[]u8 = null,
+ chord_pending_tag: bool = false,
pub const Range = struct { q0: u32 = 0, q1: u32 = 0 };
pub fn deinit(pf: *State, gpa: std.mem.Allocator) void {
if (pf.chord_arg) |a| gpa.free(a);
+ if (pf.chord_pending) |c| gpa.free(c);
pf.events.deinit(gpa);
pf.pty_out.deinit(gpa);
pf.tag_snap.deinit(gpa);