macos: add pid and tty properties to AppleScript terminal class
Expose the foreground process PID and TTY device path as read-only properties on the AppleScript terminal class and App Intents TerminalEntity. This enables reliable process-to-terminal mapping for automation tools when multiple terminals share the same CWD. Closes #11592 Closes #10756 Session: 019d341c-a165-7843-a2f7-2f426114cf17pull/11922/head
parent
dcc39dcd40
commit
9a9002202b
|
|
@ -1115,6 +1115,8 @@ GHOSTTY_API void ghostty_surface_set_focus(ghostty_surface_t, bool);
|
||||||
GHOSTTY_API void ghostty_surface_set_occlusion(ghostty_surface_t, bool);
|
GHOSTTY_API void ghostty_surface_set_occlusion(ghostty_surface_t, bool);
|
||||||
GHOSTTY_API void ghostty_surface_set_size(ghostty_surface_t, uint32_t, uint32_t);
|
GHOSTTY_API void ghostty_surface_set_size(ghostty_surface_t, uint32_t, uint32_t);
|
||||||
GHOSTTY_API ghostty_surface_size_s ghostty_surface_size(ghostty_surface_t);
|
GHOSTTY_API ghostty_surface_size_s ghostty_surface_size(ghostty_surface_t);
|
||||||
|
GHOSTTY_API uint64_t ghostty_surface_foreground_pid(ghostty_surface_t);
|
||||||
|
GHOSTTY_API ghostty_string_s ghostty_surface_tty_name(ghostty_surface_t);
|
||||||
GHOSTTY_API void ghostty_surface_set_color_scheme(ghostty_surface_t,
|
GHOSTTY_API void ghostty_surface_set_color_scheme(ghostty_surface_t,
|
||||||
ghostty_color_scheme_e);
|
ghostty_color_scheme_e);
|
||||||
GHOSTTY_API ghostty_input_mods_e ghostty_surface_key_translation_mods(ghostty_surface_t,
|
GHOSTTY_API ghostty_input_mods_e ghostty_surface_key_translation_mods(ghostty_surface_t,
|
||||||
|
|
|
||||||
|
|
@ -90,6 +90,8 @@
|
||||||
<cocoa key="title"/>
|
<cocoa key="title"/>
|
||||||
</property>
|
</property>
|
||||||
<property name="working directory" code="Gwdr" type="text" access="r" description="Current working directory for the terminal process."/>
|
<property name="working directory" code="Gwdr" type="text" access="r" description="Current working directory for the terminal process."/>
|
||||||
|
<property name="pid" code="Gpid" type="integer" access="r" description="PID of the foreground process in this terminal."/>
|
||||||
|
<property name="tty" code="Gtty" type="text" access="r" description="TTY device path for this terminal (e.g. /dev/ttys016)."/>
|
||||||
<responds-to command="split">
|
<responds-to command="split">
|
||||||
<cocoa method="handleSplitCommand:"/>
|
<cocoa method="handleSplitCommand:"/>
|
||||||
</responds-to>
|
</responds-to>
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,12 @@ struct TerminalEntity: AppEntity {
|
||||||
@Property(title: "Working Directory")
|
@Property(title: "Working Directory")
|
||||||
var workingDirectory: String?
|
var workingDirectory: String?
|
||||||
|
|
||||||
|
@Property(title: "PID")
|
||||||
|
var pid: Int?
|
||||||
|
|
||||||
|
@Property(title: "TTY")
|
||||||
|
var tty: String?
|
||||||
|
|
||||||
@Property(title: "Kind")
|
@Property(title: "Kind")
|
||||||
var kind: Kind
|
var kind: Kind
|
||||||
|
|
||||||
|
|
@ -49,6 +55,8 @@ struct TerminalEntity: AppEntity {
|
||||||
self.id = view.id
|
self.id = view.id
|
||||||
self.title = view.title
|
self.title = view.title
|
||||||
self.workingDirectory = view.pwd
|
self.workingDirectory = view.pwd
|
||||||
|
self.pid = view.surfaceModel?.foregroundPID
|
||||||
|
self.tty = view.surfaceModel?.ttyName
|
||||||
if let nsImage = ImageRenderer(content: view.screenshot()).nsImage {
|
if let nsImage = ImageRenderer(content: view.screenshot()).nsImage {
|
||||||
self.screenshot = nsImage
|
self.screenshot = nsImage
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,8 @@ import AppKit
|
||||||
/// - `property id` -> `@objc(id)` getter below.
|
/// - `property id` -> `@objc(id)` getter below.
|
||||||
/// - `property title` -> `@objc(title)` getter below.
|
/// - `property title` -> `@objc(title)` getter below.
|
||||||
/// - `property working directory` -> `@objc(workingDirectory)` getter below.
|
/// - `property working directory` -> `@objc(workingDirectory)` getter below.
|
||||||
|
/// - `property pid` -> `@objc(pid)` getter below.
|
||||||
|
/// - `property tty` -> `@objc(tty)` getter below.
|
||||||
///
|
///
|
||||||
/// We keep only a weak reference to the underlying `SurfaceView` so this
|
/// We keep only a weak reference to the underlying `SurfaceView` so this
|
||||||
/// wrapper never extends the terminal's lifetime.
|
/// wrapper never extends the terminal's lifetime.
|
||||||
|
|
@ -53,6 +55,20 @@ final class ScriptTerminal: NSObject {
|
||||||
return surfaceView?.pwd ?? ""
|
return surfaceView?.pwd ?? ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Exposed as the AppleScript `pid` property.
|
||||||
|
@objc(pid)
|
||||||
|
var pid: Int {
|
||||||
|
guard NSApp.isAppleScriptEnabled else { return 0 }
|
||||||
|
return surfaceView?.surfaceModel?.foregroundPID ?? 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Exposed as the AppleScript `tty` property.
|
||||||
|
@objc(tty)
|
||||||
|
var tty: String {
|
||||||
|
guard NSApp.isAppleScriptEnabled else { return "" }
|
||||||
|
return surfaceView?.surfaceModel?.ttyName ?? ""
|
||||||
|
}
|
||||||
|
|
||||||
/// Used by command handling (`perform action ... on <terminal>`).
|
/// Used by command handling (`perform action ... on <terminal>`).
|
||||||
func perform(action: String) -> Bool {
|
func perform(action: String) -> Bool {
|
||||||
guard NSApp.isAppleScriptEnabled else { return false }
|
guard NSApp.isAppleScriptEnabled else { return false }
|
||||||
|
|
|
||||||
|
|
@ -92,6 +92,21 @@ extension Ghostty {
|
||||||
ghostty_surface_mouse_captured(surface)
|
ghostty_surface_mouse_captured(surface)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The PID of the foreground process group attached to the PTY.
|
||||||
|
@MainActor
|
||||||
|
var foregroundPID: Int? {
|
||||||
|
let pid = ghostty_surface_foreground_pid(surface)
|
||||||
|
guard pid != 0 else { return nil }
|
||||||
|
return Int(exactly: pid)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The PTY device name for this surface.
|
||||||
|
@MainActor
|
||||||
|
var ttyName: String? {
|
||||||
|
let ttyName = AllocatedString(ghostty_surface_tty_name(surface)).string
|
||||||
|
return ttyName.isEmpty ? nil : ttyName
|
||||||
|
}
|
||||||
|
|
||||||
/// Send a mouse button event to the terminal.
|
/// Send a mouse button event to the terminal.
|
||||||
///
|
///
|
||||||
/// This sends a complete mouse button event including the button state (press/release),
|
/// This sends a complete mouse button event including the button state (press/release),
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ const CoreInspector = @import("../inspector/main.zig").Inspector;
|
||||||
const CoreSurface = @import("../Surface.zig");
|
const CoreSurface = @import("../Surface.zig");
|
||||||
const configpkg = @import("../config.zig");
|
const configpkg = @import("../config.zig");
|
||||||
const Config = configpkg.Config;
|
const Config = configpkg.Config;
|
||||||
|
const String = @import("../main_c.zig").String;
|
||||||
|
|
||||||
const log = std.log.scoped(.embedded_window);
|
const log = std.log.scoped(.embedded_window);
|
||||||
|
|
||||||
|
|
@ -1709,6 +1710,23 @@ pub const CAPI = struct {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the PID of the foreground process for the surface PTY.
|
||||||
|
export fn ghostty_surface_foreground_pid(surface: *Surface) u64 {
|
||||||
|
return surface.core_surface.getProcessInfo(.foreground_pid) orelse 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the PTY name for the surface. The returned string must be
|
||||||
|
/// freed by the caller via ghostty_string_free.
|
||||||
|
export fn ghostty_surface_tty_name(surface: *Surface) String {
|
||||||
|
const tty_name = surface.core_surface.getProcessInfo(.tty_name) orelse return .empty;
|
||||||
|
const copy = surface.app.core_app.alloc.dupeZ(u8, tty_name) catch |err| {
|
||||||
|
log.err("error allocating tty name err={}", .{err});
|
||||||
|
return .empty;
|
||||||
|
};
|
||||||
|
|
||||||
|
return .fromSlice(copy);
|
||||||
|
}
|
||||||
|
|
||||||
/// Update the color scheme of the surface.
|
/// Update the color scheme of the surface.
|
||||||
export fn ghostty_surface_set_color_scheme(surface: *Surface, scheme_raw: c_int) void {
|
export fn ghostty_surface_set_color_scheme(surface: *Surface, scheme_raw: c_int) void {
|
||||||
const scheme = std.meta.intToEnum(apprt.ColorScheme, scheme_raw) catch {
|
const scheme = std.meta.intToEnum(apprt.ColorScheme, scheme_raw) catch {
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ const builtin = @import("builtin");
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const inputpkg = @import("../input.zig");
|
const inputpkg = @import("../input.zig");
|
||||||
const state = &@import("../global.zig").state;
|
const state = &@import("../global.zig").state;
|
||||||
const c = @import("../main_c.zig");
|
const String = @import("../main_c.zig").String;
|
||||||
|
|
||||||
const Config = @import("Config.zig");
|
const Config = @import("Config.zig");
|
||||||
const c_get = @import("c_get.zig");
|
const c_get = @import("c_get.zig");
|
||||||
|
|
@ -132,7 +132,7 @@ export fn ghostty_config_get_diagnostic(self: *Config, idx: u32) Diagnostic {
|
||||||
return .{ .message = message.ptr };
|
return .{ .message = message.ptr };
|
||||||
}
|
}
|
||||||
|
|
||||||
export fn ghostty_config_open_path() c.String {
|
export fn ghostty_config_open_path() String {
|
||||||
const path = edit.openPath(state.alloc) catch |err| {
|
const path = edit.openPath(state.alloc) catch |err| {
|
||||||
log.err("error opening config in editor err={}", .{err});
|
log.err("error opening config in editor err={}", .{err});
|
||||||
return .empty;
|
return .empty;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue