ghostty/macos/Sources/Ghostty/Package.swift
Mitchell Hashimoto 4ae20212bf libghostty: unified action dispatch
First, this commit modifies libghostty to use a single unified action
dispatch system based on a tagged union versus the one-off callback
system that was previously in place. This change simplifies the code on
both the core and consumer sides of the library. Importantly, as we
introduce new actions, we can now maintain ABI compatibility so long as
our union size does not change (something I don't promise yet).

Second, this moves a lot more of the functions call on a surface into
the action system. This affects all apprts and continues the previous
work of introducing a more unified API for optional surface features.
2024-09-26 22:00:11 -07:00

266 lines
9.0 KiB
Swift

import os
import SwiftUI
import GhosttyKit
struct Ghostty {
// The primary logger used by the GhosttyKit libraries.
static let logger = Logger(
subsystem: Bundle.main.bundleIdentifier!,
category: "ghostty"
)
// All the notifications that will be emitted will be put here.
struct Notification {}
// The user notification category identifier
static let userNotificationCategory = "com.mitchellh.ghostty.userNotification"
// The user notification "Show" action
static let userNotificationActionShow = "com.mitchellh.ghostty.userNotification.Show"
}
// MARK: Build Info
extension Ghostty {
struct Info {
var mode: ghostty_build_mode_e
var version: String
}
static var info: Info {
let raw = ghostty_info()
let version = NSString(
bytes: raw.version,
length: Int(raw.version_len),
encoding: NSUTF8StringEncoding
) ?? "unknown"
return Info(mode: raw.build_mode, version: String(version))
}
}
// MARK: Swift Types for C Types
extension Ghostty {
enum SetSecureInput {
case on
case off
case toggle
static func from(_ c: ghostty_action_secure_input_e) -> Self? {
switch (c) {
case GHOSTTY_SECURE_INPUT_ON:
return .on
case GHOSTTY_SECURE_INPUT_OFF:
return .off
case GHOSTTY_SECURE_INPUT_TOGGLE:
return .toggle
default:
return nil
}
}
}
/// An enum that is used for the directions that a split focus event can change.
enum SplitFocusDirection {
case previous, next, top, bottom, left, right
/// Initialize from a Ghostty API enum.
static func from(direction: ghostty_action_goto_split_e) -> Self? {
switch (direction) {
case GHOSTTY_GOTO_SPLIT_PREVIOUS:
return .previous
case GHOSTTY_GOTO_SPLIT_NEXT:
return .next
case GHOSTTY_GOTO_SPLIT_TOP:
return .top
case GHOSTTY_GOTO_SPLIT_BOTTOM:
return .bottom
case GHOSTTY_GOTO_SPLIT_LEFT:
return .left
case GHOSTTY_GOTO_SPLIT_RIGHT:
return .right
default:
return nil
}
}
func toNative() -> ghostty_action_goto_split_e {
switch (self) {
case .previous:
return GHOSTTY_GOTO_SPLIT_PREVIOUS
case .next:
return GHOSTTY_GOTO_SPLIT_NEXT
case .top:
return GHOSTTY_GOTO_SPLIT_TOP
case .bottom:
return GHOSTTY_GOTO_SPLIT_BOTTOM
case .left:
return GHOSTTY_GOTO_SPLIT_LEFT
case .right:
return GHOSTTY_GOTO_SPLIT_RIGHT
}
}
}
/// Enum used for resizing splits. This is the direction the split divider will move.
enum SplitResizeDirection {
case up, down, left, right
static func from(direction: ghostty_action_resize_split_direction_e) -> Self? {
switch (direction) {
case GHOSTTY_RESIZE_SPLIT_UP:
return .up;
case GHOSTTY_RESIZE_SPLIT_DOWN:
return .down;
case GHOSTTY_RESIZE_SPLIT_LEFT:
return .left;
case GHOSTTY_RESIZE_SPLIT_RIGHT:
return .right;
default:
return nil
}
}
func toNative() -> ghostty_action_resize_split_direction_e {
switch (self) {
case .up:
return GHOSTTY_RESIZE_SPLIT_UP;
case .down:
return GHOSTTY_RESIZE_SPLIT_DOWN;
case .left:
return GHOSTTY_RESIZE_SPLIT_LEFT;
case .right:
return GHOSTTY_RESIZE_SPLIT_RIGHT;
}
}
}
/// The type of a clipboard request
enum ClipboardRequest {
/// A direct paste of clipboard contents
case paste
/// An application is attempting to read from the clipboard using OSC 52
case osc_52_read
/// An application is attempting to write to the clipboard using OSC 52
case osc_52_write
/// The text to show in the clipboard confirmation prompt for a given request type
func text() -> String {
switch (self) {
case .paste:
return """
Pasting this text to the terminal may be dangerous as it looks like some commands may be executed.
"""
case .osc_52_read:
return """
An application is attempting to read from the clipboard.
The current clipboard contents are shown below.
"""
case .osc_52_write:
return """
An application is attempting to write to the clipboard.
The content to write is shown below.
"""
}
}
static func from(request: ghostty_clipboard_request_e) -> ClipboardRequest? {
switch (request) {
case GHOSTTY_CLIPBOARD_REQUEST_PASTE:
return .paste
case GHOSTTY_CLIPBOARD_REQUEST_OSC_52_READ:
return .osc_52_read
case GHOSTTY_CLIPBOARD_REQUEST_OSC_52_WRITE:
return .osc_52_write
default:
return nil
}
}
}
}
// MARK: Surface Notifications
extension Ghostty.Notification {
/// Used to pass a configuration along when creating a new tab/window/split.
static let NewSurfaceConfigKey = "com.mitchellh.ghostty.newSurfaceConfig"
/// Posted when a new split is requested. The sending object will be the surface that had focus. The
/// userdata has one key "direction" with the direction to split to.
static let ghosttyNewSplit = Notification.Name("com.mitchellh.ghostty.newSplit")
/// Close the calling surface.
static let ghosttyCloseSurface = Notification.Name("com.mitchellh.ghostty.closeSurface")
/// Focus previous/next split. Has a SplitFocusDirection in the userinfo.
static let ghosttyFocusSplit = Notification.Name("com.mitchellh.ghostty.focusSplit")
static let SplitDirectionKey = ghosttyFocusSplit.rawValue
/// Goto tab. Has tab index in the userinfo.
static let ghosttyGotoTab = Notification.Name("com.mitchellh.ghostty.gotoTab")
static let GotoTabKey = ghosttyGotoTab.rawValue
/// New tab. Has base surface config requested in userinfo.
static let ghosttyNewTab = Notification.Name("com.mitchellh.ghostty.newTab")
/// New window. Has base surface config requested in userinfo.
static let ghosttyNewWindow = Notification.Name("com.mitchellh.ghostty.newWindow")
/// Toggle fullscreen of current window
static let ghosttyToggleFullscreen = Notification.Name("com.mitchellh.ghostty.toggleFullscreen")
static let FullscreenModeKey = ghosttyToggleFullscreen.rawValue
/// Notification that a surface is becoming focused. This is only sent on macOS 12 to
/// work around bugs. macOS 13+ should use the ".focused()" attribute.
static let didBecomeFocusedSurface = Notification.Name("com.mitchellh.ghostty.didBecomeFocusedSurface")
/// Notification sent to toggle split maximize/unmaximize.
static let didToggleSplitZoom = Notification.Name("com.mitchellh.ghostty.didToggleSplitZoom")
/// Notification
static let didReceiveInitialWindowFrame = Notification.Name("com.mitchellh.ghostty.didReceiveInitialWindowFrame")
static let FrameKey = "com.mitchellh.ghostty.frame"
/// Notification to render the inspector for a surface
static let inspectorNeedsDisplay = Notification.Name("com.mitchellh.ghostty.inspectorNeedsDisplay")
/// Notification to show/hide the inspector
static let didControlInspector = Notification.Name("com.mitchellh.ghostty.didControlInspector")
static let confirmClipboard = Notification.Name("com.mitchellh.ghostty.confirmClipboard")
static let ConfirmClipboardStrKey = confirmClipboard.rawValue + ".str"
static let ConfirmClipboardStateKey = confirmClipboard.rawValue + ".state"
static let ConfirmClipboardRequestKey = confirmClipboard.rawValue + ".request"
/// Notification sent to the active split view to resize the split.
static let didResizeSplit = Notification.Name("com.mitchellh.ghostty.didResizeSplit")
static let ResizeSplitDirectionKey = didResizeSplit.rawValue + ".direction"
static let ResizeSplitAmountKey = didResizeSplit.rawValue + ".amount"
/// Notification sent to the split root to equalize split sizes
static let didEqualizeSplits = Notification.Name("com.mitchellh.ghostty.didEqualizeSplits")
/// Notification that renderer health changed
static let didUpdateRendererHealth = Notification.Name("com.mitchellh.ghostty.didUpdateRendererHealth")
}
// Make the input enum hashable.
extension ghostty_input_key_e : Hashable {}