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_COLOR_CHANGE,
|
||||||
GHOSTTY_ACTION_RELOAD_CONFIG,
|
GHOSTTY_ACTION_RELOAD_CONFIG,
|
||||||
GHOSTTY_ACTION_CONFIG_CHANGE,
|
GHOSTTY_ACTION_CONFIG_CHANGE,
|
||||||
|
GHOSTTY_ACTION_REPORT_CURSOR_POSITION,
|
||||||
} ghostty_action_tag_e;
|
} ghostty_action_tag_e;
|
||||||
|
|
||||||
typedef union {
|
typedef union {
|
||||||
|
@ -71,6 +71,10 @@ renderer_thread: renderer.Thread,
|
|||||||
/// The actual thread
|
/// The actual thread
|
||||||
renderer_thr: std.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 state.
|
||||||
mouse: Mouse,
|
mouse: Mouse,
|
||||||
|
|
||||||
@ -150,6 +154,37 @@ pub const InputEffect = enum {
|
|||||||
closed,
|
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.
|
/// Mouse state for the surface.
|
||||||
const Mouse = struct {
|
const Mouse = struct {
|
||||||
/// The last tracked mouse button state by button.
|
/// The last tracked mouse button state by button.
|
||||||
@ -496,6 +531,7 @@ pub fn init(
|
|||||||
.terminal = &self.io.terminal,
|
.terminal = &self.io.terminal,
|
||||||
},
|
},
|
||||||
.renderer_thr = undefined,
|
.renderer_thr = undefined,
|
||||||
|
.cursor = .{},
|
||||||
.mouse = .{},
|
.mouse = .{},
|
||||||
.keyboard = .{},
|
.keyboard = .{},
|
||||||
.io = undefined,
|
.io = undefined,
|
||||||
@ -943,6 +979,8 @@ pub fn handleMessage(self: *Surface, msg: Message) !void {
|
|||||||
.present_surface => try self.presentSurface(),
|
.present_surface => try self.presentSurface(),
|
||||||
|
|
||||||
.password_input => |v| try self.passwordInput(v),
|
.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.
|
/// for changes.
|
||||||
config_change: ConfigChange,
|
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
|
/// Sync with: ghostty_action_tag_e
|
||||||
pub const Key = enum(c_int) {
|
pub const Key = enum(c_int) {
|
||||||
quit,
|
quit,
|
||||||
@ -266,6 +270,7 @@ pub const Action = union(Key) {
|
|||||||
color_change,
|
color_change,
|
||||||
reload_config,
|
reload_config,
|
||||||
config_change,
|
config_change,
|
||||||
|
report_cursor_position,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Sync with: ghostty_action_u
|
/// 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,
|
.pwd,
|
||||||
.config_change,
|
.config_change,
|
||||||
.toggle_maximize,
|
.toggle_maximize,
|
||||||
|
.report_cursor_position,
|
||||||
=> log.info("unimplemented action={}", .{action}),
|
=> log.info("unimplemented action={}", .{action}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -535,6 +535,7 @@ pub fn performAction(
|
|||||||
.toggle_split_zoom => self.toggleSplitZoom(target),
|
.toggle_split_zoom => self.toggleSplitZoom(target),
|
||||||
.toggle_window_decorations => self.toggleWindowDecorations(target),
|
.toggle_window_decorations => self.toggleWindowDecorations(target),
|
||||||
.quit_timer => self.quitTimer(value),
|
.quit_timer => self.quitTimer(value),
|
||||||
|
.report_cursor_position => self.reportCursorPosition(target, value),
|
||||||
|
|
||||||
// Unimplemented
|
// Unimplemented
|
||||||
.close_all_windows,
|
.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);
|
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 {
|
fn isValidAppId(app_id: [:0]const u8) bool {
|
||||||
if (app_id.len > 255 or app_id.len == 0) return false;
|
if (app_id.len > 255 or app_id.len == 0) return false;
|
||||||
if (app_id[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;
|
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.
|
/// The terminal has reported a change in the working directory.
|
||||||
pwd_change: WriteReq,
|
pwd_change: WriteReq,
|
||||||
|
|
||||||
|
/// Report cursor position
|
||||||
|
report_cursor_position: struct {
|
||||||
|
x: terminal.size.CellCountInt,
|
||||||
|
y: terminal.size.CellCountInt,
|
||||||
|
},
|
||||||
|
|
||||||
pub const ReportTitleStyle = enum {
|
pub const ReportTitleStyle = enum {
|
||||||
csi_21_t,
|
csi_21_t,
|
||||||
|
|
||||||
|
@ -25,6 +25,7 @@ const graphics = macos.graphics;
|
|||||||
const fgMode = @import("cell.zig").fgMode;
|
const fgMode = @import("cell.zig").fgMode;
|
||||||
const isCovering = @import("cell.zig").isCovering;
|
const isCovering = @import("cell.zig").isCovering;
|
||||||
const shadertoy = @import("shadertoy.zig");
|
const shadertoy = @import("shadertoy.zig");
|
||||||
|
const CursorPosition = @import("cursor_position.zig").CursorPosition;
|
||||||
const assert = std.debug.assert;
|
const assert = std.debug.assert;
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
const ArenaAllocator = std.heap.ArenaAllocator;
|
const ArenaAllocator = std.heap.ArenaAllocator;
|
||||||
@ -161,6 +162,9 @@ health: std.atomic.Value(Health) = .{ .raw = .healthy },
|
|||||||
/// Our GPU state
|
/// Our GPU state
|
||||||
gpu_state: GPUState,
|
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.
|
/// State we need for the GPU that is shared between all frames.
|
||||||
pub const GPUState = struct {
|
pub const GPUState = struct {
|
||||||
// The count of buffers we use for double/triple buffering. If
|
// The count of buffers we use for double/triple buffering. If
|
||||||
@ -991,6 +995,10 @@ pub fn updateFrame(
|
|||||||
cursor_style: ?renderer.CursorStyle,
|
cursor_style: ?renderer.CursorStyle,
|
||||||
color_palette: terminal.color.Palette,
|
color_palette: terminal.color.Palette,
|
||||||
viewport_pin: terminal.Pin,
|
viewport_pin: terminal.Pin,
|
||||||
|
cursor_position: struct {
|
||||||
|
x: terminal.size.CellCountInt,
|
||||||
|
y: terminal.size.CellCountInt,
|
||||||
|
},
|
||||||
|
|
||||||
/// If true, rebuild the full screen.
|
/// If true, rebuild the full screen.
|
||||||
full_rebuild: bool,
|
full_rebuild: bool,
|
||||||
@ -1154,6 +1162,10 @@ pub fn updateFrame(
|
|||||||
.color_palette = state.terminal.color_palette.colors,
|
.color_palette = state.terminal.color_palette.colors,
|
||||||
.viewport_pin = viewport_pin,
|
.viewport_pin = viewport_pin,
|
||||||
.full_rebuild = full_rebuild,
|
.full_rebuild = full_rebuild,
|
||||||
|
.cursor_position = .{
|
||||||
|
.x = state.terminal.screen.cursor.x,
|
||||||
|
.y = state.terminal.screen.cursor.y,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
defer {
|
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.
|
/// Draw the frame to the screen.
|
||||||
|
@ -30,6 +30,7 @@ const custom = @import("opengl/custom.zig");
|
|||||||
const Image = gl_image.Image;
|
const Image = gl_image.Image;
|
||||||
const ImageMap = gl_image.ImageMap;
|
const ImageMap = gl_image.ImageMap;
|
||||||
const ImagePlacementList = std.ArrayListUnmanaged(gl_image.Placement);
|
const ImagePlacementList = std.ArrayListUnmanaged(gl_image.Placement);
|
||||||
|
const CursorPosition = @import("cursor_position.zig").CursorPosition;
|
||||||
|
|
||||||
const log = std.log.scoped(.grid);
|
const log = std.log.scoped(.grid);
|
||||||
|
|
||||||
@ -146,6 +147,9 @@ image_bg_end: u32 = 0,
|
|||||||
image_text_end: u32 = 0,
|
image_text_end: u32 = 0,
|
||||||
image_virtual: bool = false,
|
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.
|
/// Deferred OpenGL operation to update the screen size.
|
||||||
const SetScreenSize = struct {
|
const SetScreenSize = struct {
|
||||||
size: renderer.Size,
|
size: renderer.Size,
|
||||||
@ -700,6 +704,10 @@ pub fn updateFrame(
|
|||||||
screen_type: terminal.ScreenType,
|
screen_type: terminal.ScreenType,
|
||||||
mouse: renderer.State.Mouse,
|
mouse: renderer.State.Mouse,
|
||||||
preedit: ?renderer.State.Preedit,
|
preedit: ?renderer.State.Preedit,
|
||||||
|
cursor_position: struct {
|
||||||
|
x: terminal.size.CellCountInt,
|
||||||
|
y: terminal.size.CellCountInt,
|
||||||
|
},
|
||||||
cursor_style: ?renderer.CursorStyle,
|
cursor_style: ?renderer.CursorStyle,
|
||||||
color_palette: terminal.color.Palette,
|
color_palette: terminal.color.Palette,
|
||||||
};
|
};
|
||||||
@ -861,6 +869,10 @@ pub fn updateFrame(
|
|||||||
.screen_type = state.terminal.active_screen,
|
.screen_type = state.terminal.active_screen,
|
||||||
.mouse = state.mouse,
|
.mouse = state.mouse,
|
||||||
.preedit = preedit,
|
.preedit = preedit,
|
||||||
|
.cursor_position = .{
|
||||||
|
.x = state.terminal.screen.cursor.x,
|
||||||
|
.y = state.terminal.screen.cursor.y,
|
||||||
|
},
|
||||||
.cursor_style = cursor_style,
|
.cursor_style = cursor_style,
|
||||||
.color_palette = state.terminal.color_palette.colors,
|
.color_palette = state.terminal.color_palette.colors,
|
||||||
};
|
};
|
||||||
@ -893,6 +905,8 @@ pub fn updateFrame(
|
|||||||
// CoreText this triggers off-thread cleanup logic.
|
// CoreText this triggers off-thread cleanup logic.
|
||||||
self.font_shaper.endFrame();
|
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
|
/// 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