mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-08-02 14:57:31 +03:00
core: report cursor postion to the app runtimes
This commit is contained in:
@ -597,6 +597,7 @@ typedef enum {
|
||||
GHOSTTY_ACTION_COLOR_CHANGE,
|
||||
GHOSTTY_ACTION_RELOAD_CONFIG,
|
||||
GHOSTTY_ACTION_CONFIG_CHANGE,
|
||||
GHOSTTY_ACTION_REPORT_CURSOR_POSITION,
|
||||
} ghostty_action_tag_e;
|
||||
|
||||
typedef union {
|
||||
|
@ -71,6 +71,10 @@ renderer_thread: renderer.Thread,
|
||||
/// The actual thread
|
||||
renderer_thr: std.Thread,
|
||||
|
||||
/// Cursor state. Updated at most once per frame so it may not be accurate 100%
|
||||
/// of the time.
|
||||
cursor: Cursor,
|
||||
|
||||
/// Mouse state.
|
||||
mouse: Mouse,
|
||||
|
||||
@ -150,6 +154,37 @@ pub const InputEffect = enum {
|
||||
closed,
|
||||
};
|
||||
|
||||
/// Cursor state for the surface.
|
||||
const Cursor = struct {
|
||||
x: terminal.size.CellCountInt = 0,
|
||||
y: terminal.size.CellCountInt = 0,
|
||||
|
||||
pub fn reportCursorPosition(self: *Cursor, x: terminal.size.CellCountInt, y: terminal.size.CellCountInt) void {
|
||||
const surface: *Surface = @alignCast(@fieldParentPtr("cursor", self));
|
||||
|
||||
// Defer updating the stored cursor position until after the apprt has been
|
||||
// informed of the new position.
|
||||
defer {
|
||||
self.x = x;
|
||||
self.y = y;
|
||||
}
|
||||
|
||||
surface.rt_app.performAction(
|
||||
.{ .surface = surface },
|
||||
.report_cursor_position,
|
||||
.{
|
||||
.x = x,
|
||||
.y = y,
|
||||
},
|
||||
) catch |err| {
|
||||
log.warn(
|
||||
"failed to notify surface of cursor position err={}",
|
||||
.{err},
|
||||
);
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/// Mouse state for the surface.
|
||||
const Mouse = struct {
|
||||
/// The last tracked mouse button state by button.
|
||||
@ -496,6 +531,7 @@ pub fn init(
|
||||
.terminal = &self.io.terminal,
|
||||
},
|
||||
.renderer_thr = undefined,
|
||||
.cursor = .{},
|
||||
.mouse = .{},
|
||||
.keyboard = .{},
|
||||
.io = undefined,
|
||||
@ -943,6 +979,8 @@ pub fn handleMessage(self: *Surface, msg: Message) !void {
|
||||
.present_surface => try self.presentSurface(),
|
||||
|
||||
.password_input => |v| try self.passwordInput(v),
|
||||
|
||||
.report_cursor_position => |v| self.cursor.reportCursorPosition(v.x, v.y),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -226,6 +226,10 @@ pub const Action = union(Key) {
|
||||
/// for changes.
|
||||
config_change: ConfigChange,
|
||||
|
||||
/// Report the location of the cursor. This will happen at most once per
|
||||
/// frame, and only if the cursor has moved since the last update.
|
||||
report_cursor_position: CursorPositionReport,
|
||||
|
||||
/// Sync with: ghostty_action_tag_e
|
||||
pub const Key = enum(c_int) {
|
||||
quit,
|
||||
@ -266,6 +270,7 @@ pub const Action = union(Key) {
|
||||
color_change,
|
||||
reload_config,
|
||||
config_change,
|
||||
report_cursor_position,
|
||||
};
|
||||
|
||||
/// Sync with: ghostty_action_u
|
||||
@ -549,3 +554,9 @@ pub const ConfigChange = struct {
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/// Used to report the location of the cursor.
|
||||
pub const CursorPositionReport = extern struct {
|
||||
x: terminal.size.CellCountInt,
|
||||
y: terminal.size.CellCountInt,
|
||||
};
|
||||
|
@ -238,6 +238,7 @@ pub const App = struct {
|
||||
.pwd,
|
||||
.config_change,
|
||||
.toggle_maximize,
|
||||
.report_cursor_position,
|
||||
=> log.info("unimplemented action={}", .{action}),
|
||||
}
|
||||
}
|
||||
|
@ -535,6 +535,7 @@ pub fn performAction(
|
||||
.toggle_split_zoom => self.toggleSplitZoom(target),
|
||||
.toggle_window_decorations => self.toggleWindowDecorations(target),
|
||||
.quit_timer => self.quitTimer(value),
|
||||
.report_cursor_position => self.reportCursorPosition(target, value),
|
||||
|
||||
// Unimplemented
|
||||
.close_all_windows,
|
||||
@ -1898,6 +1899,17 @@ pub fn refreshContextMenu(_: *App, window: ?*c.GtkWindow, has_selection: bool) v
|
||||
c.g_simple_action_set_enabled(action, if (has_selection) 1 else 0);
|
||||
}
|
||||
|
||||
fn reportCursorPosition(
|
||||
_: *const App,
|
||||
target: apprt.Target,
|
||||
value: apprt.action.CursorPositionReport,
|
||||
) void {
|
||||
switch (target) {
|
||||
.app => {},
|
||||
.surface => |v| v.rt_surface.reportCursorPosition(value),
|
||||
}
|
||||
}
|
||||
|
||||
fn isValidAppId(app_id: [:0]const u8) bool {
|
||||
if (app_id.len > 255 or app_id.len == 0) return false;
|
||||
if (app_id[0] == '.') return false;
|
||||
|
@ -2189,3 +2189,8 @@ fn g_value_holds(value_: ?*c.GValue, g_type: c.GType) bool {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
pub fn reportCursorPosition(self: *Surface, position: apprt.action.CursorPositionReport) void {
|
||||
_ = self;
|
||||
log.debug("cursor position: {d}×{d}", .{ position.x, position.y });
|
||||
}
|
||||
|
@ -81,6 +81,12 @@ pub const Message = union(enum) {
|
||||
/// The terminal has reported a change in the working directory.
|
||||
pwd_change: WriteReq,
|
||||
|
||||
/// Report cursor position
|
||||
report_cursor_position: struct {
|
||||
x: terminal.size.CellCountInt,
|
||||
y: terminal.size.CellCountInt,
|
||||
},
|
||||
|
||||
pub const ReportTitleStyle = enum {
|
||||
csi_21_t,
|
||||
|
||||
|
@ -25,6 +25,7 @@ const graphics = macos.graphics;
|
||||
const fgMode = @import("cell.zig").fgMode;
|
||||
const isCovering = @import("cell.zig").isCovering;
|
||||
const shadertoy = @import("shadertoy.zig");
|
||||
const CursorPosition = @import("cursor_position.zig").CursorPosition;
|
||||
const assert = std.debug.assert;
|
||||
const Allocator = std.mem.Allocator;
|
||||
const ArenaAllocator = std.heap.ArenaAllocator;
|
||||
@ -161,6 +162,9 @@ health: std.atomic.Value(Health) = .{ .raw = .healthy },
|
||||
/// Our GPU state
|
||||
gpu_state: GPUState,
|
||||
|
||||
/// The cursor position as of the last frame update
|
||||
cursor_position: CursorPosition(Metal) = .{},
|
||||
|
||||
/// State we need for the GPU that is shared between all frames.
|
||||
pub const GPUState = struct {
|
||||
// The count of buffers we use for double/triple buffering. If
|
||||
@ -991,6 +995,10 @@ pub fn updateFrame(
|
||||
cursor_style: ?renderer.CursorStyle,
|
||||
color_palette: terminal.color.Palette,
|
||||
viewport_pin: terminal.Pin,
|
||||
cursor_position: struct {
|
||||
x: terminal.size.CellCountInt,
|
||||
y: terminal.size.CellCountInt,
|
||||
},
|
||||
|
||||
/// If true, rebuild the full screen.
|
||||
full_rebuild: bool,
|
||||
@ -1154,6 +1162,10 @@ pub fn updateFrame(
|
||||
.color_palette = state.terminal.color_palette.colors,
|
||||
.viewport_pin = viewport_pin,
|
||||
.full_rebuild = full_rebuild,
|
||||
.cursor_position = .{
|
||||
.x = state.terminal.screen.cursor.x,
|
||||
.y = state.terminal.screen.cursor.y,
|
||||
},
|
||||
};
|
||||
};
|
||||
defer {
|
||||
@ -1240,6 +1252,8 @@ pub fn updateFrame(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.cursor_position.update(critical.cursor_position.x, critical.cursor_position.y);
|
||||
}
|
||||
|
||||
/// Draw the frame to the screen.
|
||||
|
@ -30,6 +30,7 @@ const custom = @import("opengl/custom.zig");
|
||||
const Image = gl_image.Image;
|
||||
const ImageMap = gl_image.ImageMap;
|
||||
const ImagePlacementList = std.ArrayListUnmanaged(gl_image.Placement);
|
||||
const CursorPosition = @import("cursor_position.zig").CursorPosition;
|
||||
|
||||
const log = std.log.scoped(.grid);
|
||||
|
||||
@ -146,6 +147,9 @@ image_bg_end: u32 = 0,
|
||||
image_text_end: u32 = 0,
|
||||
image_virtual: bool = false,
|
||||
|
||||
/// The cursor position as of the last frame update.
|
||||
cursor_position: CursorPosition(OpenGL) = .{},
|
||||
|
||||
/// Deferred OpenGL operation to update the screen size.
|
||||
const SetScreenSize = struct {
|
||||
size: renderer.Size,
|
||||
@ -700,6 +704,10 @@ pub fn updateFrame(
|
||||
screen_type: terminal.ScreenType,
|
||||
mouse: renderer.State.Mouse,
|
||||
preedit: ?renderer.State.Preedit,
|
||||
cursor_position: struct {
|
||||
x: terminal.size.CellCountInt,
|
||||
y: terminal.size.CellCountInt,
|
||||
},
|
||||
cursor_style: ?renderer.CursorStyle,
|
||||
color_palette: terminal.color.Palette,
|
||||
};
|
||||
@ -861,6 +869,10 @@ pub fn updateFrame(
|
||||
.screen_type = state.terminal.active_screen,
|
||||
.mouse = state.mouse,
|
||||
.preedit = preedit,
|
||||
.cursor_position = .{
|
||||
.x = state.terminal.screen.cursor.x,
|
||||
.y = state.terminal.screen.cursor.y,
|
||||
},
|
||||
.cursor_style = cursor_style,
|
||||
.color_palette = state.terminal.color_palette.colors,
|
||||
};
|
||||
@ -893,6 +905,8 @@ pub fn updateFrame(
|
||||
// CoreText this triggers off-thread cleanup logic.
|
||||
self.font_shaper.endFrame();
|
||||
}
|
||||
|
||||
self.cursor_position.update(critical.cursor_position.x, critical.cursor_position.y);
|
||||
}
|
||||
|
||||
/// This goes through the Kitty graphic placements and accumulates the
|
||||
|
31
src/renderer/cursor_position.zig
Normal file
31
src/renderer/cursor_position.zig
Normal file
@ -0,0 +1,31 @@
|
||||
const terminal = @import("../terminal/main.zig");
|
||||
|
||||
pub fn CursorPosition(comptime T: type) type {
|
||||
return struct {
|
||||
x: terminal.size.CellCountInt = 0,
|
||||
y: terminal.size.CellCountInt = 0,
|
||||
|
||||
pub fn update(
|
||||
self: *CursorPosition(T),
|
||||
x: terminal.size.CellCountInt,
|
||||
y: terminal.size.CellCountInt,
|
||||
) void {
|
||||
const renderer: *T = @alignCast(@fieldParentPtr("cursor_position", self));
|
||||
|
||||
if (self.x != x or self.y != y) {
|
||||
_ = renderer.surface_mailbox.push(
|
||||
.{
|
||||
.report_cursor_position = .{
|
||||
.x = x,
|
||||
.y = y,
|
||||
},
|
||||
},
|
||||
.{ .instant = {} },
|
||||
);
|
||||
}
|
||||
|
||||
self.x = x;
|
||||
self.y = y;
|
||||
}
|
||||
};
|
||||
}
|
Reference in New Issue
Block a user