Compare commits
6 commits
88c1c20340
...
27794ef4f2
| Author | SHA1 | Date | |
|---|---|---|---|
| 27794ef4f2 | |||
| 4b4e2465c7 | |||
| 2adad0b05b | |||
| 7b8a037627 | |||
| 1b99b89b2c | |||
| bddf06c633 |
7 changed files with 114 additions and 35 deletions
|
|
@ -439,6 +439,7 @@ fn wioLoop() void {
|
||||||
// during createWindow. This ensures dpi_scale and win_size are correct before
|
// during createWindow. This ensures dpi_scale and win_size are correct before
|
||||||
// the first reloadFont / sendResize, avoiding a brief render at the wrong scale.
|
// the first reloadFont / sendResize, avoiding a brief render at the wrong scale.
|
||||||
while (window.getEvent()) |event| {
|
while (window.getEvent()) |event| {
|
||||||
|
thespian.trace(thespian.channel.event, .{ "wio", "init", event });
|
||||||
switch (event) {
|
switch (event) {
|
||||||
.scale => |s| dpi_scale = s,
|
.scale => |s| dpi_scale = s,
|
||||||
.size_physical => |sz| win_size = sz,
|
.size_physical => |sz| win_size = sz,
|
||||||
|
|
@ -447,6 +448,7 @@ fn wioLoop() void {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notify the tui that the window is ready
|
// Notify the tui that the window is ready
|
||||||
|
thespian.trace(thespian.channel.event, .{ "wio", "WindowCreated", win_size.width, win_size.height });
|
||||||
reloadFont();
|
reloadFont();
|
||||||
sendResize(win_size, &state, &cell_width, &cell_height);
|
sendResize(win_size, &state, &cell_width, &cell_height);
|
||||||
tui_pid.send(.{ "RDR", "WindowCreated", @as(usize, 0) }) catch {};
|
tui_pid.send(.{ "RDR", "WindowCreated", @as(usize, 0) }) catch {};
|
||||||
|
|
@ -463,6 +465,7 @@ fn wioLoop() void {
|
||||||
maybeReloadFont(win_size, &state, &cell_width, &cell_height);
|
maybeReloadFont(win_size, &state, &cell_width, &cell_height);
|
||||||
|
|
||||||
while (window.getEvent()) |event| {
|
while (window.getEvent()) |event| {
|
||||||
|
thespian.trace(thespian.channel.event, .{ "wio", "event", event });
|
||||||
switch (event) {
|
switch (event) {
|
||||||
.close => {
|
.close => {
|
||||||
running = false;
|
running = false;
|
||||||
|
|
@ -490,17 +493,22 @@ fn wioLoop() void {
|
||||||
cp.yoff,
|
cp.yoff,
|
||||||
}) catch {};
|
}) catch {};
|
||||||
} else {
|
} else {
|
||||||
const base_cp = input_translate.codepointFromButton(btn, .{});
|
if (input_translate.codepointFromButton(btn, .{})) |base_cp| {
|
||||||
const shifted_cp = if (mods.shift) input_translate.codepointFromButton(btn, .{ .shift = true }) else base_cp;
|
const shifted_cp = if (mods.shift) input_translate.codepointFromButton(btn, .{ .shift = true }) else base_cp;
|
||||||
if (base_cp != 0) sendKey(1, base_cp, shifted_cp, mods);
|
sendKey(1, base_cp, shifted_cp orelse base_cp, mods);
|
||||||
|
} else {
|
||||||
|
if (input_translate.modifierCodepoint(btn)) |mod_cp|
|
||||||
|
sendKey(1, mod_cp, mod_cp, mods);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.button_repeat => |btn| {
|
.button_repeat => |btn| {
|
||||||
const mods = input_translate.Mods.fromButtons(held_buttons);
|
const mods = input_translate.Mods.fromButtons(held_buttons);
|
||||||
if (input_translate.mouseButtonId(btn) == null) {
|
if (input_translate.mouseButtonId(btn) == null) {
|
||||||
const base_cp = input_translate.codepointFromButton(btn, .{});
|
if (input_translate.codepointFromButton(btn, .{})) |base_cp| {
|
||||||
const shifted_cp = if (mods.shift) input_translate.codepointFromButton(btn, .{ .shift = true }) else base_cp;
|
const shifted_cp = if (mods.shift) input_translate.codepointFromButton(btn, .{ .shift = true }) else base_cp;
|
||||||
if (base_cp != 0) sendKey(2, base_cp, shifted_cp, mods);
|
sendKey(2, base_cp, shifted_cp orelse base_cp, mods);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.button_release => |btn| {
|
.button_release => |btn| {
|
||||||
|
|
@ -518,9 +526,12 @@ fn wioLoop() void {
|
||||||
cp.yoff,
|
cp.yoff,
|
||||||
}) catch {};
|
}) catch {};
|
||||||
} else {
|
} else {
|
||||||
const base_cp = input_translate.codepointFromButton(btn, .{});
|
if (input_translate.codepointFromButton(btn, .{})) |base_cp| {
|
||||||
const shifted_cp = if (mods.shift) input_translate.codepointFromButton(btn, .{ .shift = true }) else base_cp;
|
const shifted_cp = if (mods.shift) input_translate.codepointFromButton(btn, .{ .shift = true }) else base_cp;
|
||||||
if (base_cp != 0) sendKey(3, base_cp, shifted_cp, mods);
|
sendKey(3, base_cp, shifted_cp orelse base_cp, mods);
|
||||||
|
} else if (input_translate.modifierCodepoint(btn)) |mod_cp| {
|
||||||
|
sendKey(3, mod_cp, mod_cp, mods);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.char => |cp| {
|
.char => |cp| {
|
||||||
|
|
@ -557,9 +568,23 @@ fn wioLoop() void {
|
||||||
},
|
},
|
||||||
.unfocused => {
|
.unfocused => {
|
||||||
window.disableTextInput();
|
window.disableTextInput();
|
||||||
|
// Synthesize release events for any modifier keys still held so the TUI
|
||||||
|
// doesn't see them as stuck. Release in order so mods reflects reality after
|
||||||
|
// each release.
|
||||||
|
for (input_translate.modifier_buttons) |mod_btn| {
|
||||||
|
if (held_buttons.has(mod_btn)) {
|
||||||
|
held_buttons.release(mod_btn);
|
||||||
|
const mods = input_translate.Mods.fromButtons(held_buttons);
|
||||||
|
if (input_translate.modifierCodepoint(mod_btn)) |mod_cp|
|
||||||
|
sendKey(3, mod_cp, mod_cp, mods);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
held_buttons = .{};
|
||||||
tui_pid.send(.{"focus_out"}) catch {};
|
tui_pid.send(.{"focus_out"}) catch {};
|
||||||
},
|
},
|
||||||
else => {},
|
else => {
|
||||||
|
std.log.debug("wio unhandled event: {}", .{event});
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,7 @@ pub const KeyEvent = struct {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Map a wio.Button to the primary codepoint for that key
|
// Map a wio.Button to the primary codepoint for that key
|
||||||
pub fn codepointFromButton(b: wio.Button, mods: Mods) u21 {
|
pub fn codepointFromButton(b: wio.Button, mods: Mods) ?u21 {
|
||||||
return switch (b) {
|
return switch (b) {
|
||||||
.a => if (mods.shiftOnly()) 'A' else 'a',
|
.a => if (mods.shiftOnly()) 'A' else 'a',
|
||||||
.b => if (mods.shiftOnly()) 'B' else 'b',
|
.b => if (mods.shiftOnly()) 'B' else 'b',
|
||||||
|
|
@ -153,7 +153,7 @@ pub fn codepointFromButton(b: wio.Button, mods: Mods) u21 {
|
||||||
.kp_plus => vaxis.Key.kp_add,
|
.kp_plus => vaxis.Key.kp_add,
|
||||||
.kp_enter => vaxis.Key.kp_enter,
|
.kp_enter => vaxis.Key.kp_enter,
|
||||||
.kp_equals => vaxis.Key.kp_equal,
|
.kp_equals => vaxis.Key.kp_equal,
|
||||||
else => 0,
|
else => null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -161,6 +161,32 @@ pub const mouse_button_left: u8 = 0;
|
||||||
pub const mouse_button_middle: u8 = 1;
|
pub const mouse_button_middle: u8 = 1;
|
||||||
pub const mouse_button_right: u8 = 2;
|
pub const mouse_button_right: u8 = 2;
|
||||||
|
|
||||||
|
// Map modifier wio.Button values to kitty protocol codepoints (vaxis.Key.*).
|
||||||
|
// Returns 0 for non-modifier buttons.
|
||||||
|
pub fn modifierCodepoint(b: wio.Button) ?u21 {
|
||||||
|
return switch (b) {
|
||||||
|
.left_shift => vaxis.Key.left_shift,
|
||||||
|
.left_control => vaxis.Key.left_control,
|
||||||
|
.left_alt => vaxis.Key.left_alt,
|
||||||
|
.left_gui => vaxis.Key.left_super,
|
||||||
|
.right_shift => vaxis.Key.right_shift,
|
||||||
|
.right_control => vaxis.Key.right_control,
|
||||||
|
.right_alt => vaxis.Key.right_alt,
|
||||||
|
.right_gui => vaxis.Key.right_super,
|
||||||
|
.caps_lock => vaxis.Key.caps_lock,
|
||||||
|
.num_lock => vaxis.Key.num_lock,
|
||||||
|
.scroll_lock => vaxis.Key.scroll_lock,
|
||||||
|
else => null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// All buttons that contribute to modifier state, for unfocus cleanup.
|
||||||
|
pub const modifier_buttons = [_]wio.Button{
|
||||||
|
.left_shift, .left_control, .left_alt, .left_gui,
|
||||||
|
.right_shift, .right_control, .right_alt, .right_gui,
|
||||||
|
.caps_lock, .num_lock, .scroll_lock,
|
||||||
|
};
|
||||||
|
|
||||||
pub fn mouseButtonId(b: wio.Button) ?u8 {
|
pub fn mouseButtonId(b: wio.Button) ?u8 {
|
||||||
return switch (b) {
|
return switch (b) {
|
||||||
.mouse_left => mouse_button_left,
|
.mouse_left => mouse_button_left,
|
||||||
|
|
|
||||||
|
|
@ -137,8 +137,8 @@ fn fmtmsg(self: *Self, value: anytype) std.Io.Writer.Error![]const u8 {
|
||||||
return self.event_buffer.written();
|
return self.event_buffer.written();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render(self: *Self) error{}!bool {
|
pub fn render(self: *Self) error{}!?i64 {
|
||||||
if (!self.window_ready) return false;
|
if (!self.window_ready) return null;
|
||||||
|
|
||||||
var cursor = self.cursor_info;
|
var cursor = self.cursor_info;
|
||||||
|
|
||||||
|
|
@ -175,9 +175,12 @@ pub fn render(self: *Self) error{}!bool {
|
||||||
|
|
||||||
app.updateScreen(&self.vx.screen, cursor, self.secondary_cursors.items);
|
app.updateScreen(&self.vx.screen, cursor, self.secondary_cursors.items);
|
||||||
|
|
||||||
if (!self.cursor_info.vis or !self.cursor_blink) return false;
|
if (!self.cursor_info.vis or !self.cursor_blink) return null;
|
||||||
const idle = std.time.microTimestamp() - self.blink_last_change;
|
const now_check = std.time.microTimestamp();
|
||||||
return idle < self.blink_idle_us;
|
if (now_check - self.blink_last_change >= self.blink_idle_us) return null;
|
||||||
|
const elapsed = @mod(now_check - self.blink_epoch, self.blink_period_us * 2);
|
||||||
|
const deadline = now_check + (self.blink_period_us - @mod(elapsed, self.blink_period_us));
|
||||||
|
return deadline;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sigwinch(self: *Self) !void {
|
pub fn sigwinch(self: *Self) !void {
|
||||||
|
|
|
||||||
|
|
@ -219,11 +219,11 @@ pub fn run(self: *Self) Error!void {
|
||||||
try self.loop.start();
|
try self.loop.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render(self: *Self) !bool {
|
pub fn render(self: *Self) !?i64 {
|
||||||
if (in_panic.load(.acquire)) return false;
|
if (in_panic.load(.acquire)) return null;
|
||||||
try self.vx.render(self.tty.writer());
|
try self.vx.render(self.tty.writer());
|
||||||
try self.tty.writer().flush();
|
try self.tty.writer().flush();
|
||||||
return false;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sigwinch(self: *Self) !void {
|
pub fn sigwinch(self: *Self) !void {
|
||||||
|
|
|
||||||
|
|
@ -165,10 +165,10 @@ fn fmtmsg(self: *Self, value: anytype) std.Io.Writer.Error![]const u8 {
|
||||||
return self.event_buffer.written();
|
return self.event_buffer.written();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render(self: *Self) error{}!bool {
|
pub fn render(self: *Self) error{}!?i64 {
|
||||||
const hwnd = self.hwnd orelse return false;
|
const hwnd = self.hwnd orelse return null;
|
||||||
_ = gui.updateScreen(hwnd, &self.vx.screen);
|
_ = gui.updateScreen(hwnd, &self.vx.screen);
|
||||||
return false;
|
return null;
|
||||||
}
|
}
|
||||||
pub fn stop(self: *Self) void {
|
pub fn stop(self: *Self) void {
|
||||||
// this is guaranteed because stop won't be called until after
|
// this is guaranteed because stop won't be called until after
|
||||||
|
|
|
||||||
|
|
@ -401,10 +401,9 @@ pub fn render(self: *Self, theme: *const Widget.Theme) bool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!software_cursor and self.focused and tui.terminal_has_focus()) {
|
if (!software_cursor and self.focused and tui.terminal_has_focus() and self.vt.vt.mode.cursor) {
|
||||||
const scr = &tui.rdr().vx.screen;
|
const scr = &tui.rdr().vx.screen;
|
||||||
if (scr.cursor_vis)
|
tui.rdr().cursor_enable(@intCast(scr.cursor.row), @intCast(scr.cursor.col), scr.cursor_shape) catch {};
|
||||||
tui.rdr().cursor_enable(@intCast(scr.cursor.row), @intCast(scr.cursor.col), scr.cursor_shape) catch {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|
|
||||||
|
|
@ -75,6 +75,7 @@ render_pending: bool = false,
|
||||||
keepalive_timer: ?tp.Cancellable = null,
|
keepalive_timer: ?tp.Cancellable = null,
|
||||||
input_idle_timer: ?tp.Cancellable = null,
|
input_idle_timer: ?tp.Cancellable = null,
|
||||||
mouse_idle_timer: ?tp.Cancellable = null,
|
mouse_idle_timer: ?tp.Cancellable = null,
|
||||||
|
render_deadline_timer: ?tp.Cancellable = null,
|
||||||
fontface_: []const u8 = "",
|
fontface_: []const u8 = "",
|
||||||
fontfaces_: std.ArrayListUnmanaged([]const u8) = .{},
|
fontfaces_: std.ArrayListUnmanaged([]const u8) = .{},
|
||||||
input_is_idle: bool = false,
|
input_is_idle: bool = false,
|
||||||
|
|
@ -113,7 +114,7 @@ pub const ClipboardEntry = struct {
|
||||||
};
|
};
|
||||||
|
|
||||||
const keepalive = std.time.us_per_day * 365; // one year
|
const keepalive = std.time.us_per_day * 365; // one year
|
||||||
const idle_frames = 0;
|
const idle_frames = 2;
|
||||||
const mouse_idle_time_milliseconds = 3000;
|
const mouse_idle_time_milliseconds = 3000;
|
||||||
|
|
||||||
const init_delay = 1; // ms
|
const init_delay = 1; // ms
|
||||||
|
|
@ -293,6 +294,11 @@ fn deinit(self: *Self) void {
|
||||||
t.deinit();
|
t.deinit();
|
||||||
self.keepalive_timer = null;
|
self.keepalive_timer = null;
|
||||||
}
|
}
|
||||||
|
if (self.render_deadline_timer) |*t| {
|
||||||
|
t.cancel() catch {};
|
||||||
|
t.deinit();
|
||||||
|
self.render_deadline_timer = null;
|
||||||
|
}
|
||||||
if (self.input_mode_) |*m| {
|
if (self.input_mode_) |*m| {
|
||||||
m.deinit();
|
m.deinit();
|
||||||
self.input_mode_ = null;
|
self.input_mode_ = null;
|
||||||
|
|
@ -681,17 +687,24 @@ fn render(self: *Self) void {
|
||||||
top_layer_.draw(self.rdr_.stdplane());
|
top_layer_.draw(self.rdr_.stdplane());
|
||||||
}
|
}
|
||||||
|
|
||||||
const renderer_more = ret: {
|
if (self.render_deadline_timer) |*t| {
|
||||||
|
t.cancel() catch {};
|
||||||
|
t.deinit();
|
||||||
|
self.render_deadline_timer = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const render_deadline: ?i64 = ret: {
|
||||||
const frame = tracy.initZone(@src(), .{ .name = renderer.log_name ++ " render" });
|
const frame = tracy.initZone(@src(), .{ .name = renderer.log_name ++ " render" });
|
||||||
defer frame.deinit();
|
defer frame.deinit();
|
||||||
const m = self.rdr_.render() catch |e| blk: {
|
const dl = self.rdr_.render() catch |e| blk: {
|
||||||
self.logger.err("render", e);
|
self.logger.err("render", e);
|
||||||
break :blk false;
|
break :blk @as(?i64, null);
|
||||||
};
|
};
|
||||||
tracy.frameMark();
|
tracy.frameMark();
|
||||||
self.unrendered_input_events_count = 0;
|
self.unrendered_input_events_count = 0;
|
||||||
break :ret m;
|
break :ret dl;
|
||||||
};
|
};
|
||||||
|
|
||||||
self.top_layer_reset();
|
self.top_layer_reset();
|
||||||
|
|
||||||
self.idle_frame_count = if (self.unrendered_input_events_count > 0)
|
self.idle_frame_count = if (self.unrendered_input_events_count > 0)
|
||||||
|
|
@ -699,13 +712,28 @@ fn render(self: *Self) void {
|
||||||
else
|
else
|
||||||
self.idle_frame_count + 1;
|
self.idle_frame_count + 1;
|
||||||
|
|
||||||
if (more or renderer_more or self.idle_frame_count < idle_frames or self.no_sleep) {
|
const deadline_within_frame = if (render_deadline) |dl|
|
||||||
|
dl - std.time.microTimestamp() < @as(i64, @intCast(self.frame_time))
|
||||||
|
else
|
||||||
|
false;
|
||||||
|
|
||||||
|
if (more or deadline_within_frame or self.idle_frame_count < idle_frames or self.no_sleep) {
|
||||||
if (!self.frame_clock_running) {
|
if (!self.frame_clock_running) {
|
||||||
self.frame_clock.start() catch {};
|
self.frame_clock.start() catch {};
|
||||||
self.frame_clock_running = true;
|
self.frame_clock_running = true;
|
||||||
tp.trace(tp.channel.widget, .{ "frame_clock_running", "started", more });
|
tp.trace(tp.channel.widget, .{ "frame_clock_running", "started", more });
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
if (render_deadline) |deadline| {
|
||||||
|
const delay_us: u64 = @intCast(@max(0, deadline - std.time.microTimestamp()));
|
||||||
|
self.render_deadline_timer = tp.self_pid().delay_send_cancellable(
|
||||||
|
self.allocator,
|
||||||
|
"tui.render_deadline",
|
||||||
|
delay_us,
|
||||||
|
.{"render"},
|
||||||
|
) catch null;
|
||||||
|
}
|
||||||
|
|
||||||
if (self.frame_clock_running) {
|
if (self.frame_clock_running) {
|
||||||
self.frame_clock.stop() catch {};
|
self.frame_clock.stop() catch {};
|
||||||
self.frame_clock_running = false;
|
self.frame_clock_running = false;
|
||||||
|
|
@ -2174,9 +2202,7 @@ pub fn get_cursor_shape() renderer.CursorShape {
|
||||||
default_cursor
|
default_cursor
|
||||||
else
|
else
|
||||||
default_cursor;
|
default_cursor;
|
||||||
const shape = if (build_options.gui and shape_ == .default)
|
const shape = if (self.rdr_.vx.caps.multi_cursor and shape_ == .default)
|
||||||
.beam
|
|
||||||
else if (self.rdr_.vx.caps.multi_cursor and shape_ == .default)
|
|
||||||
.beam_blink
|
.beam_blink
|
||||||
else
|
else
|
||||||
shape_;
|
shape_;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue