mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-08-02 14:57:31 +03:00
core: add input binding to control terminal inspector
This commit is contained in:
@ -49,6 +49,12 @@ typedef enum {
|
|||||||
GHOSTTY_SPLIT_FOCUS_RIGHT,
|
GHOSTTY_SPLIT_FOCUS_RIGHT,
|
||||||
} ghostty_split_focus_direction_e;
|
} ghostty_split_focus_direction_e;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GHOSTTY_INSPECTOR_TOGGLE,
|
||||||
|
GHOSTTY_INSPECTOR_SHOW,
|
||||||
|
GHOSTTY_INSPECTOR_HIDE,
|
||||||
|
} ghostty_inspector_mode_e;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
GHOSTTY_MOUSE_RELEASE,
|
GHOSTTY_MOUSE_RELEASE,
|
||||||
GHOSTTY_MOUSE_PRESS,
|
GHOSTTY_MOUSE_PRESS,
|
||||||
@ -323,6 +329,7 @@ typedef void (*ghostty_runtime_write_clipboard_cb)(void *, const char *, ghostty
|
|||||||
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);
|
||||||
typedef void (*ghostty_runtime_new_tab_cb)(void *, ghostty_surface_config_s);
|
typedef void (*ghostty_runtime_new_tab_cb)(void *, ghostty_surface_config_s);
|
||||||
typedef void (*ghostty_runtime_new_window_cb)(void *, ghostty_surface_config_s);
|
typedef void (*ghostty_runtime_new_window_cb)(void *, ghostty_surface_config_s);
|
||||||
|
typedef void (*ghostty_runtime_control_inspector_cb)(void *, ghostty_inspector_mode_e);
|
||||||
typedef void (*ghostty_runtime_close_surface_cb)(void *, bool);
|
typedef void (*ghostty_runtime_close_surface_cb)(void *, bool);
|
||||||
typedef void (*ghostty_runtime_focus_split_cb)(void *, ghostty_split_focus_direction_e);
|
typedef void (*ghostty_runtime_focus_split_cb)(void *, ghostty_split_focus_direction_e);
|
||||||
typedef void (*ghostty_runtime_toggle_split_zoom_cb)(void *);
|
typedef void (*ghostty_runtime_toggle_split_zoom_cb)(void *);
|
||||||
@ -344,6 +351,7 @@ typedef struct {
|
|||||||
ghostty_runtime_new_split_cb new_split_cb;
|
ghostty_runtime_new_split_cb new_split_cb;
|
||||||
ghostty_runtime_new_tab_cb new_tab_cb;
|
ghostty_runtime_new_tab_cb new_tab_cb;
|
||||||
ghostty_runtime_new_window_cb new_window_cb;
|
ghostty_runtime_new_window_cb new_window_cb;
|
||||||
|
ghostty_runtime_control_inspector_cb control_inspector_cb;
|
||||||
ghostty_runtime_close_surface_cb close_surface_cb;
|
ghostty_runtime_close_surface_cb close_surface_cb;
|
||||||
ghostty_runtime_focus_split_cb focus_split_cb;
|
ghostty_runtime_focus_split_cb focus_split_cb;
|
||||||
ghostty_runtime_toggle_split_zoom_cb toggle_split_zoom_cb;
|
ghostty_runtime_toggle_split_zoom_cb toggle_split_zoom_cb;
|
||||||
|
@ -36,6 +36,7 @@ class AppDelegate: NSObject, ObservableObject, NSApplicationDelegate, GhosttyApp
|
|||||||
@IBOutlet private var menuIncreaseFontSize: NSMenuItem?
|
@IBOutlet private var menuIncreaseFontSize: NSMenuItem?
|
||||||
@IBOutlet private var menuDecreaseFontSize: NSMenuItem?
|
@IBOutlet private var menuDecreaseFontSize: NSMenuItem?
|
||||||
@IBOutlet private var menuResetFontSize: NSMenuItem?
|
@IBOutlet private var menuResetFontSize: NSMenuItem?
|
||||||
|
@IBOutlet private var menuTerminalInspector: NSMenuItem?
|
||||||
|
|
||||||
/// The dock menu
|
/// The dock menu
|
||||||
private var dockMenu: NSMenu = NSMenu()
|
private var dockMenu: NSMenu = NSMenu()
|
||||||
@ -216,6 +217,7 @@ class AppDelegate: NSObject, ObservableObject, NSApplicationDelegate, GhosttyApp
|
|||||||
syncMenuShortcut(action: "increase_font_size:1", menuItem: self.menuIncreaseFontSize)
|
syncMenuShortcut(action: "increase_font_size:1", menuItem: self.menuIncreaseFontSize)
|
||||||
syncMenuShortcut(action: "decrease_font_size:1", menuItem: self.menuDecreaseFontSize)
|
syncMenuShortcut(action: "decrease_font_size:1", menuItem: self.menuDecreaseFontSize)
|
||||||
syncMenuShortcut(action: "reset_font_size", menuItem: self.menuResetFontSize)
|
syncMenuShortcut(action: "reset_font_size", menuItem: self.menuResetFontSize)
|
||||||
|
syncMenuShortcut(action: "inspector:toggle", menuItem: self.menuTerminalInspector)
|
||||||
|
|
||||||
// Dock menu
|
// Dock menu
|
||||||
reloadDockMenu()
|
reloadDockMenu()
|
||||||
@ -394,4 +396,9 @@ class AppDelegate: NSObject, ObservableObject, NSApplicationDelegate, GhosttyApp
|
|||||||
guard let surface = focusedSurface() else { return }
|
guard let surface = focusedSurface() else { return }
|
||||||
ghostty.changeFontSize(surface: surface, .reset)
|
ghostty.changeFontSize(surface: surface, .reset)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@IBAction func toggleTerminalInspector(_ sender: Any) {
|
||||||
|
guard let surface = focusedSurface() else { return }
|
||||||
|
ghostty.toggleTerminalInspector(surface: surface)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -136,6 +136,7 @@ extension Ghostty {
|
|||||||
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) },
|
||||||
new_tab_cb: { userdata, surfaceConfig in AppState.newTab(userdata, config: surfaceConfig) },
|
new_tab_cb: { userdata, surfaceConfig in AppState.newTab(userdata, config: surfaceConfig) },
|
||||||
new_window_cb: { userdata, surfaceConfig in AppState.newWindow(userdata, config: surfaceConfig) },
|
new_window_cb: { userdata, surfaceConfig in AppState.newWindow(userdata, config: surfaceConfig) },
|
||||||
|
control_inspector_cb: { userdata, mode in AppState.controlInspector(userdata, mode: mode) },
|
||||||
close_surface_cb: { userdata, processAlive in AppState.closeSurface(userdata, processAlive: processAlive) },
|
close_surface_cb: { userdata, processAlive in AppState.closeSurface(userdata, processAlive: processAlive) },
|
||||||
focus_split_cb: { userdata, direction in AppState.focusSplit(userdata, direction: direction) },
|
focus_split_cb: { userdata, direction in AppState.focusSplit(userdata, direction: direction) },
|
||||||
toggle_split_zoom_cb: { userdata in AppState.toggleSplitZoom(userdata) },
|
toggle_split_zoom_cb: { userdata in AppState.toggleSplitZoom(userdata) },
|
||||||
@ -300,6 +301,13 @@ extension Ghostty {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func toggleTerminalInspector(surface: ghostty_surface_t) {
|
||||||
|
let action = "inspector:toggle"
|
||||||
|
if (!ghostty_surface_binding_action(surface, action, UInt(action.count))) {
|
||||||
|
AppDelegate.logger.warning("action failed action=\(action)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Called when the selected keyboard changes. We have to notify Ghostty so that
|
// Called when the selected keyboard changes. We have to notify Ghostty so that
|
||||||
// it can reload the keyboard mapping for input.
|
// it can reload the keyboard mapping for input.
|
||||||
@objc private func keyboardSelectionDidChange(notification: NSNotification) {
|
@objc private func keyboardSelectionDidChange(notification: NSNotification) {
|
||||||
@ -495,6 +503,13 @@ extension Ghostty {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static func controlInspector(_ userdata: UnsafeMutableRawPointer?, mode: ghostty_inspector_mode_e) {
|
||||||
|
guard let surface = self.surfaceUserdata(from: userdata) else { return }
|
||||||
|
NotificationCenter.default.post(name: Notification.didControlInspector, object: surface, userInfo: [
|
||||||
|
"mode": mode,
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the GhosttyState from the given userdata value.
|
/// Returns the GhosttyState from the given userdata value.
|
||||||
static private func appState(fromView view: SurfaceView) -> AppState? {
|
static private func appState(fromView view: SurfaceView) -> AppState? {
|
||||||
guard let surface = view.surface else { return nil }
|
guard let surface = view.surface else { return nil }
|
||||||
|
@ -105,6 +105,9 @@ extension Ghostty.Notification {
|
|||||||
|
|
||||||
/// Notification to render the inspector for a surface
|
/// Notification to render the inspector for a surface
|
||||||
static let inspectorNeedsDisplay = Notification.Name("com.mitchellh.ghostty.inspectorNeedsDisplay")
|
static let inspectorNeedsDisplay = Notification.Name("com.mitchellh.ghostty.inspectorNeedsDisplay")
|
||||||
|
|
||||||
|
/// Notification to show/hide the inspector
|
||||||
|
static let didControlInspector = Notification.Name("com.mitchellh.ghostty.didControlInspector")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make the input enum hashable.
|
// Make the input enum hashable.
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="22154" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="22155" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<deployment identifier="macosx"/>
|
<deployment identifier="macosx"/>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="22154"/>
|
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="22155"/>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<objects>
|
<objects>
|
||||||
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
|
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
|
||||||
@ -33,6 +33,7 @@
|
|||||||
<outlet property="menuSelectSplitRight" destination="upj-mc-L7X" id="nLY-o1-lky"/>
|
<outlet property="menuSelectSplitRight" destination="upj-mc-L7X" id="nLY-o1-lky"/>
|
||||||
<outlet property="menuSplitHorizontal" destination="VUR-Ld-nLx" id="RxO-Zw-ovb"/>
|
<outlet property="menuSplitHorizontal" destination="VUR-Ld-nLx" id="RxO-Zw-ovb"/>
|
||||||
<outlet property="menuSplitVertical" destination="UDZ-4y-6xL" id="fgZ-Wb-8OR"/>
|
<outlet property="menuSplitVertical" destination="UDZ-4y-6xL" id="fgZ-Wb-8OR"/>
|
||||||
|
<outlet property="menuTerminalInspector" destination="QwP-M5-fvh" id="wJi-Dh-S9f"/>
|
||||||
<outlet property="menuToggleFullScreen" destination="8kY-Pi-KaY" id="yQg-6V-OO6"/>
|
<outlet property="menuToggleFullScreen" destination="8kY-Pi-KaY" id="yQg-6V-OO6"/>
|
||||||
<outlet property="menuZoomSplit" destination="oPd-mn-IEH" id="wTu-jK-egI"/>
|
<outlet property="menuZoomSplit" destination="oPd-mn-IEH" id="wTu-jK-egI"/>
|
||||||
</connections>
|
</connections>
|
||||||
@ -153,6 +154,13 @@
|
|||||||
<action selector="decreaseFontSize:" target="bbz-4X-AYv" id="rlw-0o-kA2"/>
|
<action selector="decreaseFontSize:" target="bbz-4X-AYv" id="rlw-0o-kA2"/>
|
||||||
</connections>
|
</connections>
|
||||||
</menuItem>
|
</menuItem>
|
||||||
|
<menuItem isSeparatorItem="YES" id="L3L-I8-sqk"/>
|
||||||
|
<menuItem title="Terminal Inspector" id="QwP-M5-fvh">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="toggleTerminalInspector:" target="bbz-4X-AYv" id="DON-fR-wyr"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
</items>
|
</items>
|
||||||
</menu>
|
</menu>
|
||||||
</menuItem>
|
</menuItem>
|
||||||
|
@ -2278,6 +2278,12 @@ pub fn performBindingAction(self: *Surface, action: input.Binding.Action) !bool
|
|||||||
} else log.warn("runtime doesn't implement toggleFullscreen", .{});
|
} else log.warn("runtime doesn't implement toggleFullscreen", .{});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
.inspector => |mode| {
|
||||||
|
if (@hasDecl(apprt.Surface, "controlInspector")) {
|
||||||
|
self.rt_surface.controlInspector(mode);
|
||||||
|
} else log.warn("runtime doesn't implement controlInspector", .{});
|
||||||
|
},
|
||||||
|
|
||||||
.close_surface => self.close(),
|
.close_surface => self.close(),
|
||||||
|
|
||||||
.close_window => try self.app.closeSurface(self),
|
.close_window => try self.app.closeSurface(self),
|
||||||
|
@ -73,6 +73,9 @@ pub const App = struct {
|
|||||||
/// New window with options.
|
/// New window with options.
|
||||||
new_window: ?*const fn (SurfaceUD, apprt.Surface.Options) callconv(.C) void = null,
|
new_window: ?*const fn (SurfaceUD, apprt.Surface.Options) callconv(.C) void = null,
|
||||||
|
|
||||||
|
/// Control the inspector visibility
|
||||||
|
control_inspector: ?*const fn (SurfaceUD, input.InspectorMode) callconv(.C) void = null,
|
||||||
|
|
||||||
/// Close the current surface given by this function.
|
/// Close the current surface given by this function.
|
||||||
close_surface: ?*const fn (SurfaceUD, bool) callconv(.C) void = null,
|
close_surface: ?*const fn (SurfaceUD, bool) callconv(.C) void = null,
|
||||||
|
|
||||||
@ -326,6 +329,15 @@ pub const Surface = struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn controlInspector(self: *const Surface, mode: input.InspectorMode) void {
|
||||||
|
const func = self.app.opts.control_inspector orelse {
|
||||||
|
log.info("runtime embedder does not support the terminal inspector", .{});
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
func(self.opts.userdata, mode);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn newSplit(self: *const Surface, direction: input.SplitDirection) !void {
|
pub fn newSplit(self: *const Surface, direction: input.SplitDirection) !void {
|
||||||
const func = self.app.opts.new_split orelse {
|
const func = self.app.opts.new_split orelse {
|
||||||
log.info("runtime embedder does not support splits", .{});
|
log.info("runtime embedder does not support splits", .{});
|
||||||
|
@ -8,6 +8,7 @@ pub const keycodes = @import("input/keycodes.zig");
|
|||||||
pub const kitty = @import("input/kitty.zig");
|
pub const kitty = @import("input/kitty.zig");
|
||||||
pub const Binding = @import("input/Binding.zig");
|
pub const Binding = @import("input/Binding.zig");
|
||||||
pub const KeyEncoder = @import("input/KeyEncoder.zig");
|
pub const KeyEncoder = @import("input/KeyEncoder.zig");
|
||||||
|
pub const InspectorMode = Binding.Action.InspectorMode;
|
||||||
pub const SplitDirection = Binding.Action.SplitDirection;
|
pub const SplitDirection = Binding.Action.SplitDirection;
|
||||||
pub const SplitFocusDirection = Binding.Action.SplitFocusDirection;
|
pub const SplitFocusDirection = Binding.Action.SplitFocusDirection;
|
||||||
|
|
||||||
|
@ -176,6 +176,10 @@ pub const Action = union(enum) {
|
|||||||
/// zoom/unzoom the current split.
|
/// zoom/unzoom the current split.
|
||||||
toggle_split_zoom: void,
|
toggle_split_zoom: void,
|
||||||
|
|
||||||
|
/// Show, hide, or toggle the terminal inspector for the currently
|
||||||
|
/// focused terminal.
|
||||||
|
inspector: InspectorMode,
|
||||||
|
|
||||||
/// Reload the configuration. The exact meaning depends on the app runtime
|
/// Reload the configuration. The exact meaning depends on the app runtime
|
||||||
/// in use but this usually involves re-reading the configuration file
|
/// in use but this usually involves re-reading the configuration file
|
||||||
/// and applying any changes. Note that not all changes can be applied at
|
/// and applying any changes. Note that not all changes can be applied at
|
||||||
@ -220,6 +224,13 @@ pub const Action = union(enum) {
|
|||||||
right,
|
right,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Extern because it is used in the embedded runtime ABI.
|
||||||
|
pub const InspectorMode = enum(c_int) {
|
||||||
|
toggle,
|
||||||
|
show,
|
||||||
|
hide,
|
||||||
|
};
|
||||||
|
|
||||||
/// Parse an action in the format of "key=value" where key is the
|
/// Parse an action in the format of "key=value" where key is the
|
||||||
/// action name and value is the action parameter. The parameter
|
/// action name and value is the action parameter. The parameter
|
||||||
/// is optional depending on the action.
|
/// is optional depending on the action.
|
||||||
|
Reference in New Issue
Block a user