mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-16 16:56:09 +03:00
Merge pull request #462 from mitchellh/hide-mouse
Option to hide mouse cursor while typing
This commit is contained in:
@ -302,6 +302,7 @@ typedef void (*ghostty_runtime_wakeup_cb)(void *);
|
|||||||
typedef const ghostty_config_t (*ghostty_runtime_reload_config_cb)(void *);
|
typedef const ghostty_config_t (*ghostty_runtime_reload_config_cb)(void *);
|
||||||
typedef void (*ghostty_runtime_set_title_cb)(void *, const char *);
|
typedef void (*ghostty_runtime_set_title_cb)(void *, const char *);
|
||||||
typedef void (*ghostty_runtime_set_mouse_shape_cb)(void *, ghostty_mouse_shape_e);
|
typedef void (*ghostty_runtime_set_mouse_shape_cb)(void *, ghostty_mouse_shape_e);
|
||||||
|
typedef void (*ghostty_runtime_set_mouse_visibility_cb)(void *, bool);
|
||||||
typedef const char* (*ghostty_runtime_read_clipboard_cb)(void *, ghostty_clipboard_e);
|
typedef const char* (*ghostty_runtime_read_clipboard_cb)(void *, ghostty_clipboard_e);
|
||||||
typedef void (*ghostty_runtime_write_clipboard_cb)(void *, const char *, ghostty_clipboard_e);
|
typedef void (*ghostty_runtime_write_clipboard_cb)(void *, const char *, ghostty_clipboard_e);
|
||||||
typedef void (*ghostty_runtime_new_split_cb)(void *, ghostty_split_direction_e, ghostty_surface_config_s);
|
typedef void (*ghostty_runtime_new_split_cb)(void *, ghostty_split_direction_e, ghostty_surface_config_s);
|
||||||
@ -320,6 +321,7 @@ typedef struct {
|
|||||||
ghostty_runtime_reload_config_cb reload_config_cb;
|
ghostty_runtime_reload_config_cb reload_config_cb;
|
||||||
ghostty_runtime_set_title_cb set_title_cb;
|
ghostty_runtime_set_title_cb set_title_cb;
|
||||||
ghostty_runtime_set_mouse_shape_cb set_mouse_shape_cb;
|
ghostty_runtime_set_mouse_shape_cb set_mouse_shape_cb;
|
||||||
|
ghostty_runtime_set_mouse_visibility_cb set_mouse_visibility_cb;
|
||||||
ghostty_runtime_read_clipboard_cb read_clipboard_cb;
|
ghostty_runtime_read_clipboard_cb read_clipboard_cb;
|
||||||
ghostty_runtime_write_clipboard_cb write_clipboard_cb;
|
ghostty_runtime_write_clipboard_cb write_clipboard_cb;
|
||||||
ghostty_runtime_new_split_cb new_split_cb;
|
ghostty_runtime_new_split_cb new_split_cb;
|
||||||
|
@ -73,6 +73,7 @@ extension Ghostty {
|
|||||||
reload_config_cb: { userdata in AppState.reloadConfig(userdata) },
|
reload_config_cb: { userdata in AppState.reloadConfig(userdata) },
|
||||||
set_title_cb: { userdata, title in AppState.setTitle(userdata, title: title) },
|
set_title_cb: { userdata, title in AppState.setTitle(userdata, title: title) },
|
||||||
set_mouse_shape_cb: { userdata, shape in AppState.setMouseShape(userdata, shape: shape) },
|
set_mouse_shape_cb: { userdata, shape in AppState.setMouseShape(userdata, shape: shape) },
|
||||||
|
set_mouse_visibility_cb: { userdata, visible in AppState.setMouseVisibility(userdata, visible: visible) },
|
||||||
read_clipboard_cb: { userdata, loc in AppState.readClipboard(userdata, location: loc) },
|
read_clipboard_cb: { userdata, loc in AppState.readClipboard(userdata, location: loc) },
|
||||||
write_clipboard_cb: { userdata, str, loc in AppState.writeClipboard(userdata, string: str, location: loc) },
|
write_clipboard_cb: { userdata, str, loc in AppState.writeClipboard(userdata, string: str, location: loc) },
|
||||||
new_split_cb: { userdata, direction, surfaceConfig in AppState.newSplit(userdata, direction: direction, config: surfaceConfig) },
|
new_split_cb: { userdata, direction, surfaceConfig in AppState.newSplit(userdata, direction: direction, config: surfaceConfig) },
|
||||||
@ -338,6 +339,11 @@ extension Ghostty {
|
|||||||
let surfaceView = Unmanaged<SurfaceView>.fromOpaque(userdata!).takeUnretainedValue()
|
let surfaceView = Unmanaged<SurfaceView>.fromOpaque(userdata!).takeUnretainedValue()
|
||||||
surfaceView.setCursorShape(shape)
|
surfaceView.setCursorShape(shape)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static func setMouseVisibility(_ userdata: UnsafeMutableRawPointer?, visible: Bool) {
|
||||||
|
let surfaceView = Unmanaged<SurfaceView>.fromOpaque(userdata!).takeUnretainedValue()
|
||||||
|
surfaceView.setCursorVisibility(visible)
|
||||||
|
}
|
||||||
|
|
||||||
static func toggleFullscreen(_ userdata: UnsafeMutableRawPointer?, nonNativeFullscreen: ghostty_non_native_fullscreen_e) {
|
static func toggleFullscreen(_ userdata: UnsafeMutableRawPointer?, nonNativeFullscreen: ghostty_non_native_fullscreen_e) {
|
||||||
guard let surface = self.surfaceUserdata(from: userdata) else { return }
|
guard let surface = self.surfaceUserdata(from: userdata) else { return }
|
||||||
|
@ -197,7 +197,9 @@ extension Ghostty {
|
|||||||
|
|
||||||
private var markedText: NSMutableAttributedString
|
private var markedText: NSMutableAttributedString
|
||||||
private var mouseEntered: Bool = false
|
private var mouseEntered: Bool = false
|
||||||
private var cursor: NSCursor = .arrow
|
private var focused: Bool = true
|
||||||
|
private var cursor: NSCursor = .iBeam
|
||||||
|
private var cursorVisible: CursorVisibility = .visible
|
||||||
|
|
||||||
// We need to support being a first responder so that we can get input events
|
// We need to support being a first responder so that we can get input events
|
||||||
override var acceptsFirstResponder: Bool { return true }
|
override var acceptsFirstResponder: Bool { return true }
|
||||||
@ -206,6 +208,15 @@ extension Ghostty {
|
|||||||
// so we'll use that to tell ghostty to refresh.
|
// so we'll use that to tell ghostty to refresh.
|
||||||
override var wantsUpdateLayer: Bool { return true }
|
override var wantsUpdateLayer: Bool { return true }
|
||||||
|
|
||||||
|
// State machine for mouse cursor visibility because every call to
|
||||||
|
// NSCursor.hide/unhide must be balanced.
|
||||||
|
enum CursorVisibility {
|
||||||
|
case visible
|
||||||
|
case hidden
|
||||||
|
case pendingVisible
|
||||||
|
case pendingHidden
|
||||||
|
}
|
||||||
|
|
||||||
init(_ app: ghostty_app_t, _ baseConfig: ghostty_surface_config_s?) {
|
init(_ app: ghostty_app_t, _ baseConfig: ghostty_surface_config_s?) {
|
||||||
self.markedText = NSMutableAttributedString()
|
self.markedText = NSMutableAttributedString()
|
||||||
|
|
||||||
@ -236,7 +247,14 @@ extension Ghostty {
|
|||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
trackingAreas.forEach { removeTrackingArea($0) }
|
trackingAreas.forEach { removeTrackingArea($0) }
|
||||||
|
|
||||||
|
// mouseExited is not called by AppKit one last time when the view
|
||||||
|
// closes so we do it manually to ensure our NSCursor state remains
|
||||||
|
// accurate.
|
||||||
|
if (mouseEntered) {
|
||||||
|
mouseExited(with: NSEvent())
|
||||||
|
}
|
||||||
|
|
||||||
guard let surface = self.surface else { return }
|
guard let surface = self.surface else { return }
|
||||||
ghostty_surface_free(surface)
|
ghostty_surface_free(surface)
|
||||||
}
|
}
|
||||||
@ -320,8 +338,41 @@ extension Ghostty {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set our cursor immediately if our mouse is over our window
|
// Set our cursor immediately if our mouse is over our window
|
||||||
|
if (mouseEntered) { cursorUpdate(with: NSEvent()) }
|
||||||
|
if let window = self.window {
|
||||||
|
window.invalidateCursorRects(for: self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func setCursorVisibility(_ visible: Bool) {
|
||||||
|
switch (cursorVisible) {
|
||||||
|
case .visible:
|
||||||
|
// If we want to be visible, do nothing. If we want to be hidden
|
||||||
|
// enter the pending state.
|
||||||
|
if (visible) { return }
|
||||||
|
cursorVisible = .pendingHidden
|
||||||
|
|
||||||
|
case .hidden:
|
||||||
|
// If we want to be hidden, do nothing. If we want to be visible
|
||||||
|
// enter the pending state.
|
||||||
|
if (!visible) { return }
|
||||||
|
cursorVisible = .pendingVisible
|
||||||
|
|
||||||
|
case .pendingVisible:
|
||||||
|
// If we want to be visible, do nothing because we're already pending.
|
||||||
|
// If we want to be hidden, we're already hidden so reset state.
|
||||||
|
if (visible) { return }
|
||||||
|
cursorVisible = .hidden
|
||||||
|
|
||||||
|
case .pendingHidden:
|
||||||
|
// If we want to be hidden, do nothing because we're pending that switch.
|
||||||
|
// If we want to be visible, we're already visible so reset state.
|
||||||
|
if (!visible) { return }
|
||||||
|
cursorVisible = .visible
|
||||||
|
}
|
||||||
|
|
||||||
if (mouseEntered) {
|
if (mouseEntered) {
|
||||||
cursor.set()
|
cursorUpdate(with: NSEvent())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -338,13 +389,22 @@ extension Ghostty {
|
|||||||
// If we have a blur, set the blur
|
// If we have a blur, set the blur
|
||||||
ghostty_set_window_background_blur(surface, Unmanaged.passUnretained(window).toOpaque())
|
ghostty_set_window_background_blur(surface, Unmanaged.passUnretained(window).toOpaque())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override func becomeFirstResponder() -> Bool {
|
||||||
|
let result = super.becomeFirstResponder()
|
||||||
|
if (result) { focused = true }
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
override func resignFirstResponder() -> Bool {
|
override func resignFirstResponder() -> Bool {
|
||||||
let result = super.resignFirstResponder()
|
let result = super.resignFirstResponder()
|
||||||
|
|
||||||
// We sometimes call this manually (see SplitView) as a way to force us to
|
// We sometimes call this manually (see SplitView) as a way to force us to
|
||||||
// yield our focus state.
|
// yield our focus state.
|
||||||
if (result) { focusDidChange(false) }
|
if (result) {
|
||||||
|
focusDidChange(false)
|
||||||
|
focused = false
|
||||||
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
@ -372,7 +432,7 @@ extension Ghostty {
|
|||||||
|
|
||||||
override func resetCursorRects() {
|
override func resetCursorRects() {
|
||||||
discardCursorRects()
|
discardCursorRects()
|
||||||
addCursorRect(frame, cursor: .iBeam)
|
addCursorRect(frame, cursor: self.cursor)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func viewDidChangeBackingProperties() {
|
override func viewDidChangeBackingProperties() {
|
||||||
@ -419,7 +479,7 @@ extension Ghostty {
|
|||||||
|
|
||||||
override func mouseMoved(with event: NSEvent) {
|
override func mouseMoved(with event: NSEvent) {
|
||||||
guard let surface = self.surface else { return }
|
guard let surface = self.surface else { return }
|
||||||
|
|
||||||
// Convert window position to view position. Note (0, 0) is bottom left.
|
// Convert window position to view position. Note (0, 0) is bottom left.
|
||||||
let pos = self.convert(event.locationInWindow, from: nil)
|
let pos = self.convert(event.locationInWindow, from: nil)
|
||||||
ghostty_surface_mouse_pos(surface, pos.x, frame.height - pos.y)
|
ghostty_surface_mouse_pos(surface, pos.x, frame.height - pos.y)
|
||||||
@ -432,10 +492,20 @@ extension Ghostty {
|
|||||||
|
|
||||||
override func mouseEntered(with event: NSEvent) {
|
override func mouseEntered(with event: NSEvent) {
|
||||||
mouseEntered = true
|
mouseEntered = true
|
||||||
|
|
||||||
|
// If our cursor is hidden, we hide it on upon entry and we unhide
|
||||||
|
// it on exit (mouseExited)
|
||||||
|
if (cursorVisible == .hidden) {
|
||||||
|
NSCursor.hide()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override func mouseExited(with event: NSEvent) {
|
override func mouseExited(with event: NSEvent) {
|
||||||
mouseEntered = false
|
mouseEntered = false
|
||||||
|
|
||||||
|
if (cursorVisible == .hidden) {
|
||||||
|
NSCursor.unhide()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override func scrollWheel(with event: NSEvent) {
|
override func scrollWheel(with event: NSEvent) {
|
||||||
@ -482,6 +552,22 @@ extension Ghostty {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override func cursorUpdate(with event: NSEvent) {
|
override func cursorUpdate(with event: NSEvent) {
|
||||||
|
if (focused) {
|
||||||
|
switch (cursorVisible) {
|
||||||
|
case .visible, .hidden:
|
||||||
|
// Do nothing, stable state
|
||||||
|
break
|
||||||
|
|
||||||
|
case .pendingHidden:
|
||||||
|
NSCursor.hide()
|
||||||
|
cursorVisible = .hidden
|
||||||
|
|
||||||
|
case .pendingVisible:
|
||||||
|
NSCursor.unhide()
|
||||||
|
cursorVisible = .visible
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
cursor.set()
|
cursor.set()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,6 +122,9 @@ const Mouse = struct {
|
|||||||
/// Pending scroll amounts for high-precision scrolls
|
/// Pending scroll amounts for high-precision scrolls
|
||||||
pending_scroll_x: f64 = 0,
|
pending_scroll_x: f64 = 0,
|
||||||
pending_scroll_y: f64 = 0,
|
pending_scroll_y: f64 = 0,
|
||||||
|
|
||||||
|
/// True if the mouse is hidden
|
||||||
|
hidden: bool = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// The configuration that a surface has, this is copied from the main
|
/// The configuration that a surface has, this is copied from the main
|
||||||
@ -138,6 +141,7 @@ const DerivedConfig = struct {
|
|||||||
copy_on_select: configpkg.CopyOnSelect,
|
copy_on_select: configpkg.CopyOnSelect,
|
||||||
confirm_close_surface: bool,
|
confirm_close_surface: bool,
|
||||||
mouse_interval: u64,
|
mouse_interval: u64,
|
||||||
|
mouse_hide_while_typing: bool,
|
||||||
macos_non_native_fullscreen: configpkg.NonNativeFullscreen,
|
macos_non_native_fullscreen: configpkg.NonNativeFullscreen,
|
||||||
macos_option_as_alt: configpkg.OptionAsAlt,
|
macos_option_as_alt: configpkg.OptionAsAlt,
|
||||||
window_padding_x: u32,
|
window_padding_x: u32,
|
||||||
@ -157,6 +161,7 @@ const DerivedConfig = struct {
|
|||||||
.copy_on_select = config.@"copy-on-select",
|
.copy_on_select = config.@"copy-on-select",
|
||||||
.confirm_close_surface = config.@"confirm-close-surface",
|
.confirm_close_surface = config.@"confirm-close-surface",
|
||||||
.mouse_interval = config.@"click-repeat-interval" * 1_000_000, // 500ms
|
.mouse_interval = config.@"click-repeat-interval" * 1_000_000, // 500ms
|
||||||
|
.mouse_hide_while_typing = config.@"mouse-hide-while-typing",
|
||||||
.macos_non_native_fullscreen = config.@"macos-non-native-fullscreen",
|
.macos_non_native_fullscreen = config.@"macos-non-native-fullscreen",
|
||||||
.macos_option_as_alt = config.@"macos-option-as-alt",
|
.macos_option_as_alt = config.@"macos-option-as-alt",
|
||||||
.window_padding_x = config.@"window-padding-x",
|
.window_padding_x = config.@"window-padding-x",
|
||||||
@ -566,6 +571,11 @@ fn changeConfig(self: *Surface, config: *const configpkg.Config) !void {
|
|||||||
self.config.deinit();
|
self.config.deinit();
|
||||||
self.config = derived;
|
self.config = derived;
|
||||||
|
|
||||||
|
// If our mouse is hidden but we disabled mouse hiding, then show it again.
|
||||||
|
if (!self.config.mouse_hide_while_typing and self.mouse.hidden) {
|
||||||
|
self.showMouse();
|
||||||
|
}
|
||||||
|
|
||||||
// We need to store our configs in a heap-allocated pointer so that
|
// We need to store our configs in a heap-allocated pointer so that
|
||||||
// our messages aren't huge.
|
// our messages aren't huge.
|
||||||
var renderer_config_ptr = try self.alloc.create(Renderer.DerivedConfig);
|
var renderer_config_ptr = try self.alloc.create(Renderer.DerivedConfig);
|
||||||
@ -961,6 +971,14 @@ pub fn keyCallback(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If this input event has text, then we hide the mouse if configured.
|
||||||
|
if (self.config.mouse_hide_while_typing and
|
||||||
|
!self.mouse.hidden and
|
||||||
|
event.utf8.len > 0)
|
||||||
|
{
|
||||||
|
self.hideMouse();
|
||||||
|
}
|
||||||
|
|
||||||
// No binding, so we have to perform an encoding task. This
|
// No binding, so we have to perform an encoding task. This
|
||||||
// may still result in no encoding. Under different modes and
|
// may still result in no encoding. Under different modes and
|
||||||
// inputs there are many keybindings that result in no encoding
|
// inputs there are many keybindings that result in no encoding
|
||||||
@ -1049,6 +1067,9 @@ pub fn scrollCallback(
|
|||||||
|
|
||||||
// log.info("SCROLL: xoff={} yoff={} mods={}", .{ xoff, yoff, scroll_mods });
|
// log.info("SCROLL: xoff={} yoff={} mods={}", .{ xoff, yoff, scroll_mods });
|
||||||
|
|
||||||
|
// Always show the mouse again if it is hidden
|
||||||
|
if (self.mouse.hidden) self.showMouse();
|
||||||
|
|
||||||
const ScrollAmount = struct {
|
const ScrollAmount = struct {
|
||||||
// Positive is up, right
|
// Positive is up, right
|
||||||
sign: isize = 1,
|
sign: isize = 1,
|
||||||
@ -1446,6 +1467,9 @@ pub fn mouseButtonCallback(
|
|||||||
self.mouse.click_state[@intCast(@intFromEnum(button))] = action;
|
self.mouse.click_state[@intCast(@intFromEnum(button))] = action;
|
||||||
self.mouse.mods = @bitCast(mods);
|
self.mouse.mods = @bitCast(mods);
|
||||||
|
|
||||||
|
// Always show the mouse again if it is hidden
|
||||||
|
if (self.mouse.hidden) self.showMouse();
|
||||||
|
|
||||||
// Shift-click continues the previous mouse state if we have a selection.
|
// Shift-click continues the previous mouse state if we have a selection.
|
||||||
// cursorPosCallback will also do a mouse report so we don't need to do any
|
// cursorPosCallback will also do a mouse report so we don't need to do any
|
||||||
// of the logic below.
|
// of the logic below.
|
||||||
@ -1594,6 +1618,9 @@ pub fn cursorPosCallback(
|
|||||||
const tracy = trace(@src());
|
const tracy = trace(@src());
|
||||||
defer tracy.end();
|
defer tracy.end();
|
||||||
|
|
||||||
|
// Always show the mouse again if it is hidden
|
||||||
|
if (self.mouse.hidden) self.showMouse();
|
||||||
|
|
||||||
// We are reading/writing state for the remainder
|
// We are reading/writing state for the remainder
|
||||||
self.renderer_state.mutex.lock();
|
self.renderer_state.mutex.lock();
|
||||||
defer self.renderer_state.mutex.unlock();
|
defer self.renderer_state.mutex.unlock();
|
||||||
@ -1852,6 +1879,18 @@ fn scrollToBottom(self: *Surface) !void {
|
|||||||
try self.queueRender();
|
try self.queueRender();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn hideMouse(self: *Surface) void {
|
||||||
|
if (self.mouse.hidden) return;
|
||||||
|
self.mouse.hidden = true;
|
||||||
|
self.rt_surface.setMouseVisibility(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn showMouse(self: *Surface) void {
|
||||||
|
if (!self.mouse.hidden) return;
|
||||||
|
self.mouse.hidden = false;
|
||||||
|
self.rt_surface.setMouseVisibility(true);
|
||||||
|
}
|
||||||
|
|
||||||
/// Perform a binding action. A binding is a keybinding. This function
|
/// Perform a binding action. A binding is a keybinding. This function
|
||||||
/// must be called from the GUI thread.
|
/// must be called from the GUI thread.
|
||||||
pub fn performBindingAction(self: *Surface, action: input.Binding.Action) !void {
|
pub fn performBindingAction(self: *Surface, action: input.Binding.Action) !void {
|
||||||
|
@ -52,6 +52,9 @@ pub const App = struct {
|
|||||||
/// Called to set the cursor shape.
|
/// Called to set the cursor shape.
|
||||||
set_mouse_shape: *const fn (SurfaceUD, terminal.MouseShape) callconv(.C) void,
|
set_mouse_shape: *const fn (SurfaceUD, terminal.MouseShape) callconv(.C) void,
|
||||||
|
|
||||||
|
/// Called to set the mouse visibility.
|
||||||
|
set_mouse_visibility: *const fn (SurfaceUD, bool) callconv(.C) void,
|
||||||
|
|
||||||
/// Read the clipboard value. The return value must be preserved
|
/// Read the clipboard value. The return value must be preserved
|
||||||
/// by the host until the next call. If there is no valid clipboard
|
/// by the host until the next call. If there is no valid clipboard
|
||||||
/// value then this should return null.
|
/// value then this should return null.
|
||||||
@ -321,6 +324,14 @@ pub const Surface = struct {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the visibility of the mouse cursor.
|
||||||
|
pub fn setMouseVisibility(self: *Surface, visible: bool) void {
|
||||||
|
self.app.opts.set_mouse_visibility(
|
||||||
|
self.opts.userdata,
|
||||||
|
visible,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn supportsClipboard(
|
pub fn supportsClipboard(
|
||||||
self: *const Surface,
|
self: *const Surface,
|
||||||
clipboard_type: apprt.Clipboard,
|
clipboard_type: apprt.Clipboard,
|
||||||
|
@ -542,6 +542,11 @@ pub const Surface = struct {
|
|||||||
self.cursor = new;
|
self.cursor = new;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the visibility of the mouse cursor.
|
||||||
|
pub fn setMouseVisibility(self: *Surface, visible: bool) void {
|
||||||
|
self.window.setInputModeCursor(if (visible) .normal else .hidden);
|
||||||
|
}
|
||||||
|
|
||||||
/// Read the clipboard. The windowing system is responsible for allocating
|
/// Read the clipboard. The windowing system is responsible for allocating
|
||||||
/// a buffer as necessary. This should be a stable pointer until the next
|
/// a buffer as necessary. This should be a stable pointer until the next
|
||||||
/// time getClipboardString is called.
|
/// time getClipboardString is called.
|
||||||
|
@ -45,6 +45,9 @@ pub const App = struct {
|
|||||||
app: *c.GtkApplication,
|
app: *c.GtkApplication,
|
||||||
ctx: *c.GMainContext,
|
ctx: *c.GMainContext,
|
||||||
|
|
||||||
|
/// The "none" cursor. We use one that is shared across the entire app.
|
||||||
|
cursor_none: ?*c.GdkCursor,
|
||||||
|
|
||||||
/// This is set to false when the main loop should exit.
|
/// This is set to false when the main loop should exit.
|
||||||
running: bool = true,
|
running: bool = true,
|
||||||
|
|
||||||
@ -68,6 +71,10 @@ pub const App = struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The "none" cursor is used for hiding the cursor
|
||||||
|
const cursor_none = c.gdk_cursor_new_from_name("none", null);
|
||||||
|
errdefer if (cursor_none) |cursor| c.g_object_unref(cursor);
|
||||||
|
|
||||||
// Our uniqueness ID is based on whether we're in a debug mode or not.
|
// Our uniqueness ID is based on whether we're in a debug mode or not.
|
||||||
// In debug mode we want to be separate so we can develop Ghostty in
|
// In debug mode we want to be separate so we can develop Ghostty in
|
||||||
// Ghostty.
|
// Ghostty.
|
||||||
@ -128,6 +135,7 @@ pub const App = struct {
|
|||||||
.app = app,
|
.app = app,
|
||||||
.config = config,
|
.config = config,
|
||||||
.ctx = ctx,
|
.ctx = ctx,
|
||||||
|
.cursor_none = cursor_none,
|
||||||
|
|
||||||
// If we are NOT the primary instance, then we never want to run.
|
// If we are NOT the primary instance, then we never want to run.
|
||||||
// This means that another instance of the GTK app is running and
|
// This means that another instance of the GTK app is running and
|
||||||
@ -144,6 +152,8 @@ pub const App = struct {
|
|||||||
c.g_main_context_release(self.ctx);
|
c.g_main_context_release(self.ctx);
|
||||||
c.g_object_unref(self.app);
|
c.g_object_unref(self.app);
|
||||||
|
|
||||||
|
if (self.cursor_none) |cursor| c.g_object_unref(cursor);
|
||||||
|
|
||||||
self.config.deinit();
|
self.config.deinit();
|
||||||
|
|
||||||
glfw.terminate();
|
glfw.terminate();
|
||||||
@ -1042,6 +1052,21 @@ pub const Surface = struct {
|
|||||||
self.cursor = cursor;
|
self.cursor = cursor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the visibility of the mouse cursor.
|
||||||
|
pub fn setMouseVisibility(self: *Surface, visible: bool) void {
|
||||||
|
// Note in there that self.cursor or cursor_none may be null. That's
|
||||||
|
// not a problem because NULL is a valid argument for set cursor
|
||||||
|
// which means to just use the parent value.
|
||||||
|
|
||||||
|
if (visible) {
|
||||||
|
c.gtk_widget_set_cursor(@ptrCast(self.gl_area), self.cursor);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set our new cursor to the app "none" cursor
|
||||||
|
c.gtk_widget_set_cursor(@ptrCast(self.gl_area), self.app.cursor_none);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn getClipboardString(
|
pub fn getClipboardString(
|
||||||
self: *Surface,
|
self: *Surface,
|
||||||
clipboard_type: apprt.Clipboard,
|
clipboard_type: apprt.Clipboard,
|
||||||
|
@ -119,6 +119,11 @@ palette: Palette = .{},
|
|||||||
/// will be chosen.
|
/// will be chosen.
|
||||||
@"cursor-text": ?Color = null,
|
@"cursor-text": ?Color = null,
|
||||||
|
|
||||||
|
/// Hide the mouse immediately when typing. The mouse becomes visible
|
||||||
|
/// again when the mouse is used. The mouse is only hidden if the mouse
|
||||||
|
/// cursor is over the active terminal surface.
|
||||||
|
@"mouse-hide-while-typing": bool = false,
|
||||||
|
|
||||||
/// The opacity level (opposite of transparency) of the background.
|
/// The opacity level (opposite of transparency) of the background.
|
||||||
/// A value of 1 is fully opaque and a value of 0 is fully transparent.
|
/// A value of 1 is fully opaque and a value of 0 is fully transparent.
|
||||||
/// A value less than 0 or greater than 1 will be clamped to the nearest
|
/// A value less than 0 or greater than 1 will be clamped to the nearest
|
||||||
|
Reference in New Issue
Block a user