mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-04-14 03:25:50 +00:00
Misc inspector fixes (#10470)
Inspector is getting some love! While working on some new functionality I found a bunch of bugs. Sending the bug fixes separately. - Mode checkboxes didn't have a unique ID, causing Imgui warnings - renderer: we keep a draw timer active while the inspector is visible, allowing animations to work - GTK and macOS: we were always calculating a delta time of 0 since we converted to float after int math, making hover timers not work - macOS: precision scrolling made scrolling way too fast, slow it down No AI, just meat sticks
This commit is contained in:
@@ -269,16 +269,10 @@ extension Ghostty {
|
||||
// Builds up the "input.ScrollMods" bitmask
|
||||
var mods: Int32 = 0
|
||||
|
||||
var x = event.scrollingDeltaX
|
||||
var y = event.scrollingDeltaY
|
||||
let x = event.scrollingDeltaX
|
||||
let y = event.scrollingDeltaY
|
||||
if event.hasPreciseScrollingDeltas {
|
||||
mods = 1
|
||||
|
||||
// We do a 2x speed multiplier. This is subjective, it "feels" better to me.
|
||||
x *= 2;
|
||||
y *= 2;
|
||||
|
||||
// TODO(mitchellh): do we have to scale the x/y here by window scale factor?
|
||||
}
|
||||
|
||||
// Determine our momentum value
|
||||
|
||||
@@ -1133,15 +1133,18 @@ pub const Inspector = struct {
|
||||
yoff: f64,
|
||||
mods: input.ScrollMods,
|
||||
) void {
|
||||
_ = mods;
|
||||
|
||||
self.queueRender();
|
||||
cimgui.c.ImGui_SetCurrentContext(self.ig_ctx);
|
||||
const io: *cimgui.c.ImGuiIO = cimgui.c.ImGui_GetIO();
|
||||
|
||||
// For precision scrolling (trackpads), the values are in pixels which
|
||||
// scroll way too fast. Scale them down to approximate discrete wheel
|
||||
// notches. imgui expects 1.0 to scroll ~5 lines of text.
|
||||
const scale: f64 = if (mods.precision) 0.1 else 1.0;
|
||||
cimgui.c.ImGuiIO_AddMouseWheelEvent(
|
||||
io,
|
||||
@floatCast(xoff),
|
||||
@floatCast(yoff),
|
||||
@floatCast(xoff * scale),
|
||||
@floatCast(yoff * scale),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1202,10 +1205,11 @@ pub const Inspector = struct {
|
||||
// Determine our delta time
|
||||
const now = try std.time.Instant.now();
|
||||
io.DeltaTime = if (self.instant) |prev| delta: {
|
||||
const since_ns = now.since(prev);
|
||||
const since_s: f32 = @floatFromInt(since_ns / std.time.ns_per_s);
|
||||
const since_ns: f64 = @floatFromInt(now.since(prev));
|
||||
const ns_per_s: f64 = @floatFromInt(std.time.ns_per_s);
|
||||
const since_s: f32 = @floatCast(since_ns / ns_per_s);
|
||||
break :delta @max(0.00001, since_s);
|
||||
} else (1 / 60);
|
||||
} else (1.0 / 60.0);
|
||||
self.instant = now;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -131,21 +131,17 @@ pub const ImguiWidget = extern struct {
|
||||
|
||||
/// Initialize the frame. Expects that the context is already current.
|
||||
fn newFrame(self: *Self) void {
|
||||
// If we can't determine the time since the last frame we default to
|
||||
// 1/60th of a second.
|
||||
const default_delta_time = 1 / 60;
|
||||
|
||||
const priv = self.private();
|
||||
|
||||
const io: *cimgui.c.ImGuiIO = cimgui.c.ImGui_GetIO();
|
||||
|
||||
// Determine our delta time
|
||||
const now = std.time.Instant.now() catch unreachable;
|
||||
io.DeltaTime = if (priv.instant) |prev| delta: {
|
||||
const since_ns = now.since(prev);
|
||||
const since_s: f32 = @floatFromInt(since_ns / std.time.ns_per_s);
|
||||
const since_ns: f64 = @floatFromInt(now.since(prev));
|
||||
const ns_per_s: f64 = @floatFromInt(std.time.ns_per_s);
|
||||
const since_s: f32 = @floatCast(since_ns / ns_per_s);
|
||||
break :delta @max(0.00001, since_s);
|
||||
} else default_delta_time;
|
||||
} else (1.0 / 60.0);
|
||||
|
||||
priv.instant = now;
|
||||
}
|
||||
|
||||
@@ -583,10 +583,12 @@ fn renderModesWindow(self: *Inspector) void {
|
||||
const tag: terminal.modes.ModeTag = @bitCast(@as(terminal.modes.ModeTag.Backing, field.value));
|
||||
|
||||
cimgui.c.ImGui_TableNextRow();
|
||||
cimgui.c.ImGui_PushIDInt(@intCast(field.value));
|
||||
defer cimgui.c.ImGui_PopID();
|
||||
{
|
||||
_ = cimgui.c.ImGui_TableSetColumnIndex(0);
|
||||
var value: bool = t.modes.get(@field(terminal.Mode, field.name));
|
||||
_ = cimgui.c.ImGui_Checkbox("", &value);
|
||||
_ = cimgui.c.ImGui_Checkbox("##checkbox", &value);
|
||||
}
|
||||
{
|
||||
_ = cimgui.c.ImGui_TableSetColumnIndex(1);
|
||||
|
||||
@@ -254,7 +254,7 @@ fn threadMain_(self: *Thread) !void {
|
||||
);
|
||||
|
||||
// Start the draw timer
|
||||
self.startDrawTimer();
|
||||
self.syncDrawTimer();
|
||||
|
||||
// Run
|
||||
log.debug("starting renderer thread", .{});
|
||||
@@ -292,11 +292,33 @@ fn setQosClass(self: *const Thread) void {
|
||||
}
|
||||
}
|
||||
|
||||
fn startDrawTimer(self: *Thread) void {
|
||||
// If our renderer doesn't support animations then we never run this.
|
||||
if (!@hasDecl(rendererpkg.Renderer, "hasAnimations")) return;
|
||||
if (!self.renderer.hasAnimations()) return;
|
||||
if (self.config.custom_shader_animation == .false) return;
|
||||
fn syncDrawTimer(self: *Thread) void {
|
||||
skip: {
|
||||
// If we have an inspector, we always run the draw timer.
|
||||
if (self.flags.has_inspector) break :skip;
|
||||
|
||||
// If our renderer supports animations and has them, then we
|
||||
// always have a draw timer.
|
||||
if (@hasDecl(rendererpkg.Renderer, "hasAnimations") and
|
||||
self.renderer.hasAnimations())
|
||||
{
|
||||
break :skip;
|
||||
}
|
||||
|
||||
// If our config says to always animate, we do so.
|
||||
switch (self.config.custom_shader_animation) {
|
||||
// Always animate
|
||||
.always => break :skip,
|
||||
// Only when focused
|
||||
.true => if (self.flags.focused) break :skip,
|
||||
// Never animate
|
||||
.false => {},
|
||||
}
|
||||
|
||||
// We're skipping the draw timer. Stop it on the next iteration.
|
||||
self.draw_active = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Set our active state so it knows we're running. We set this before
|
||||
// even checking the active state in case we have a pending shutdown.
|
||||
@@ -316,11 +338,6 @@ fn startDrawTimer(self: *Thread) void {
|
||||
);
|
||||
}
|
||||
|
||||
fn stopDrawTimer(self: *Thread) void {
|
||||
// This will stop the draw on the next iteration.
|
||||
self.draw_active = false;
|
||||
}
|
||||
|
||||
/// Drain the mailbox.
|
||||
fn drainMailbox(self: *Thread) !void {
|
||||
// There's probably a more elegant way to do this...
|
||||
@@ -377,12 +394,10 @@ fn drainMailbox(self: *Thread) !void {
|
||||
// Set it on the renderer
|
||||
try self.renderer.setFocus(v);
|
||||
|
||||
if (!v) {
|
||||
if (self.config.custom_shader_animation != .always) {
|
||||
// Stop the draw timer
|
||||
self.stopDrawTimer();
|
||||
}
|
||||
// We always resync our draw timer (may disable it)
|
||||
self.syncDrawTimer();
|
||||
|
||||
if (!v) {
|
||||
// If we're not focused, then we stop the cursor blink
|
||||
if (self.cursor_c.state() == .active and
|
||||
self.cursor_c_cancel.state() == .dead)
|
||||
@@ -397,9 +412,6 @@ fn drainMailbox(self: *Thread) !void {
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// Start the draw timer
|
||||
self.startDrawTimer();
|
||||
|
||||
// If we're focused, we immediately show the cursor again
|
||||
// and then restart the timer.
|
||||
if (self.cursor_c.state() != .active) {
|
||||
@@ -446,8 +458,7 @@ fn drainMailbox(self: *Thread) !void {
|
||||
|
||||
// Stop and start the draw timer to capture the new
|
||||
// hasAnimations value.
|
||||
self.stopDrawTimer();
|
||||
self.startDrawTimer();
|
||||
self.syncDrawTimer();
|
||||
},
|
||||
|
||||
.search_viewport_matches => |v| {
|
||||
@@ -466,7 +477,12 @@ fn drainMailbox(self: *Thread) !void {
|
||||
self.renderer.search_matches_dirty = true;
|
||||
},
|
||||
|
||||
.inspector => |v| self.flags.has_inspector = v,
|
||||
.inspector => |v| {
|
||||
self.flags.has_inspector = v;
|
||||
// Reset our draw timer state, which might change due
|
||||
// to the inspector change.
|
||||
self.syncDrawTimer();
|
||||
},
|
||||
|
||||
.macos_display_id => |v| {
|
||||
if (@hasDecl(rendererpkg.Renderer, "setMacOSDisplayID")) {
|
||||
|
||||
Reference in New Issue
Block a user