macos: making the surface state get passed down...

This commit is contained in:
Mitchell Hashimoto
2023-03-05 13:37:01 -08:00
parent b1d57cd500
commit 1fbbdd3fc7
2 changed files with 141 additions and 137 deletions

View File

@ -15,6 +15,9 @@ struct TerminalSurface: NSViewRepresentable {
category: String(describing: TerminalSurfaceView.self)
)
/// The view to render for the terminal surface.
let view: TerminalSurfaceView
/// This should be set to true wen the surface has focus. This is up to the parent because
/// focus is also defined by window focus. It is important this is set correctly since if it is
/// false then the surface will idle at almost 0% CPU.
@ -34,27 +37,18 @@ struct TerminalSurface: NSViewRepresentable {
/// set the appropriate title of the window/tab/split/etc. if they care.
@Binding var title: String
@StateObject private var state: TerminalSurfaceView
init(_ app: ghostty_app_t, hasFocus: Bool, size: CGSize, title: Binding<String>) {
self._state = StateObject(wrappedValue: TerminalSurfaceView(app))
self._title = title
self.hasFocus = hasFocus
self.size = size
}
func makeNSView(context: Context) -> TerminalSurfaceView {
// We need the view as part of the state to be created previously because
// the view is sent to the Ghostty API so that it can manipulate it
// directly since we draw on a render thread.
state.delegate = context.coordinator
return state;
view.delegate = context.coordinator
return view;
}
func updateNSView(_ view: TerminalSurfaceView, context: Context) {
state.delegate = context.coordinator
state.focusDidChange(hasFocus)
state.sizeDidChange(size)
view.delegate = context.coordinator
view.focusDidChange(hasFocus)
view.sizeDidChange(size)
}
func makeCoordinator() -> Coordinator {
@ -81,11 +75,13 @@ protocol TerminalSurfaceDelegate: AnyObject {
/// The actual NSView implementation for the terminal surface.
class TerminalSurfaceView: NSView, NSTextInputClient, ObservableObject {
// This can be set to receive event callbacks.
weak var delegate: TerminalSurfaceDelegate?
// The current title of the surface as defined by the pty. This can be
// changed with escape codes.
var title: String = "" {
// changed with escape codes. This is public because the callbacks go
// to the app level and it is set from there.
@Published var title: String = "" {
didSet {
if let delegate = self.delegate {
delegate.titleDidChange(to: title)
@ -93,8 +89,9 @@ class TerminalSurfaceView: NSView, NSTextInputClient, ObservableObject {
}
}
var surface: ghostty_surface_t? = nil
private(set) var surface: ghostty_surface_t?
var error: Error? = nil
private var markedText: NSMutableAttributedString;
// We need to support being a first responder so that we can get input events
@ -103,123 +100,6 @@ class TerminalSurfaceView: NSView, NSTextInputClient, ObservableObject {
// I don't thikn we need this but this lets us know we should redraw our layer
// so we'll use that to tell ghostty to refresh.
override var wantsUpdateLayer: Bool { return true }
// Mapping of event keyCode to ghostty input key values. This is cribbed from
// glfw mostly since we started as a glfw-based app way back in the day!
static let keycodes: [UInt16 : ghostty_input_key_e] = [
0x1D: GHOSTTY_KEY_ZERO,
0x12: GHOSTTY_KEY_ONE,
0x13: GHOSTTY_KEY_TWO,
0x14: GHOSTTY_KEY_THREE,
0x15: GHOSTTY_KEY_FOUR,
0x17: GHOSTTY_KEY_FIVE,
0x16: GHOSTTY_KEY_SIX,
0x1A: GHOSTTY_KEY_SEVEN,
0x1C: GHOSTTY_KEY_EIGHT,
0x19: GHOSTTY_KEY_NINE,
0x00: GHOSTTY_KEY_A,
0x0B: GHOSTTY_KEY_B,
0x08: GHOSTTY_KEY_C,
0x02: GHOSTTY_KEY_D,
0x0E: GHOSTTY_KEY_E,
0x03: GHOSTTY_KEY_F,
0x05: GHOSTTY_KEY_G,
0x04: GHOSTTY_KEY_H,
0x22: GHOSTTY_KEY_I,
0x26: GHOSTTY_KEY_J,
0x28: GHOSTTY_KEY_K,
0x25: GHOSTTY_KEY_L,
0x2E: GHOSTTY_KEY_M,
0x2D: GHOSTTY_KEY_N,
0x1F: GHOSTTY_KEY_O,
0x23: GHOSTTY_KEY_P,
0x0C: GHOSTTY_KEY_Q,
0x0F: GHOSTTY_KEY_R,
0x01: GHOSTTY_KEY_S,
0x11: GHOSTTY_KEY_T,
0x20: GHOSTTY_KEY_U,
0x09: GHOSTTY_KEY_V,
0x0D: GHOSTTY_KEY_W,
0x07: GHOSTTY_KEY_X,
0x10: GHOSTTY_KEY_Y,
0x06: GHOSTTY_KEY_Z,
0x27: GHOSTTY_KEY_APOSTROPHE,
0x2A: GHOSTTY_KEY_BACKSLASH,
0x2B: GHOSTTY_KEY_COMMA,
0x18: GHOSTTY_KEY_EQUAL,
0x32: GHOSTTY_KEY_GRAVE_ACCENT,
0x21: GHOSTTY_KEY_LEFT_BRACKET,
0x1B: GHOSTTY_KEY_MINUS,
0x2F: GHOSTTY_KEY_PERIOD,
0x1E: GHOSTTY_KEY_RIGHT_BRACKET,
0x29: GHOSTTY_KEY_SEMICOLON,
0x2C: GHOSTTY_KEY_SLASH,
0x33: GHOSTTY_KEY_BACKSPACE,
0x39: GHOSTTY_KEY_CAPS_LOCK,
0x75: GHOSTTY_KEY_DELETE,
0x7D: GHOSTTY_KEY_DOWN,
0x77: GHOSTTY_KEY_END,
0x24: GHOSTTY_KEY_ENTER,
0x35: GHOSTTY_KEY_ESCAPE,
0x7A: GHOSTTY_KEY_F1,
0x78: GHOSTTY_KEY_F2,
0x63: GHOSTTY_KEY_F3,
0x76: GHOSTTY_KEY_F4,
0x60: GHOSTTY_KEY_F5,
0x61: GHOSTTY_KEY_F6,
0x62: GHOSTTY_KEY_F7,
0x64: GHOSTTY_KEY_F8,
0x65: GHOSTTY_KEY_F9,
0x6D: GHOSTTY_KEY_F10,
0x67: GHOSTTY_KEY_F11,
0x6F: GHOSTTY_KEY_F12,
0x69: GHOSTTY_KEY_PRINT_SCREEN,
0x6B: GHOSTTY_KEY_F14,
0x71: GHOSTTY_KEY_F15,
0x6A: GHOSTTY_KEY_F16,
0x40: GHOSTTY_KEY_F17,
0x4F: GHOSTTY_KEY_F18,
0x50: GHOSTTY_KEY_F19,
0x5A: GHOSTTY_KEY_F20,
0x73: GHOSTTY_KEY_HOME,
0x72: GHOSTTY_KEY_INSERT,
0x7B: GHOSTTY_KEY_LEFT,
0x3A: GHOSTTY_KEY_LEFT_ALT,
0x3B: GHOSTTY_KEY_LEFT_CONTROL,
0x38: GHOSTTY_KEY_LEFT_SHIFT,
0x37: GHOSTTY_KEY_LEFT_SUPER,
0x47: GHOSTTY_KEY_NUM_LOCK,
0x79: GHOSTTY_KEY_PAGE_DOWN,
0x74: GHOSTTY_KEY_PAGE_UP,
0x7C: GHOSTTY_KEY_RIGHT,
0x3D: GHOSTTY_KEY_RIGHT_ALT,
0x3E: GHOSTTY_KEY_RIGHT_CONTROL,
0x3C: GHOSTTY_KEY_RIGHT_SHIFT,
0x36: GHOSTTY_KEY_RIGHT_SUPER,
0x31: GHOSTTY_KEY_SPACE,
0x30: GHOSTTY_KEY_TAB,
0x7E: GHOSTTY_KEY_UP,
0x52: GHOSTTY_KEY_KP_0,
0x53: GHOSTTY_KEY_KP_1,
0x54: GHOSTTY_KEY_KP_2,
0x55: GHOSTTY_KEY_KP_3,
0x56: GHOSTTY_KEY_KP_4,
0x57: GHOSTTY_KEY_KP_5,
0x58: GHOSTTY_KEY_KP_6,
0x59: GHOSTTY_KEY_KP_7,
0x5B: GHOSTTY_KEY_KP_8,
0x5C: GHOSTTY_KEY_KP_9,
0x45: GHOSTTY_KEY_KP_ADD,
0x41: GHOSTTY_KEY_KP_DECIMAL,
0x4B: GHOSTTY_KEY_KP_DIVIDE,
0x4C: GHOSTTY_KEY_KP_ENTER,
0x51: GHOSTTY_KEY_KP_EQUAL,
0x43: GHOSTTY_KEY_KP_MULTIPLY,
0x4E: GHOSTTY_KEY_KP_SUBTRACT,
];
init(_ app: ghostty_app_t) {
self.markedText = NSMutableAttributedString()
@ -483,4 +363,121 @@ class TerminalSurfaceView: NSView, NSTextInputClient, ObservableObject {
return ghostty_input_mods_e(mods)
}
// Mapping of event keyCode to ghostty input key values. This is cribbed from
// glfw mostly since we started as a glfw-based app way back in the day!
static let keycodes: [UInt16 : ghostty_input_key_e] = [
0x1D: GHOSTTY_KEY_ZERO,
0x12: GHOSTTY_KEY_ONE,
0x13: GHOSTTY_KEY_TWO,
0x14: GHOSTTY_KEY_THREE,
0x15: GHOSTTY_KEY_FOUR,
0x17: GHOSTTY_KEY_FIVE,
0x16: GHOSTTY_KEY_SIX,
0x1A: GHOSTTY_KEY_SEVEN,
0x1C: GHOSTTY_KEY_EIGHT,
0x19: GHOSTTY_KEY_NINE,
0x00: GHOSTTY_KEY_A,
0x0B: GHOSTTY_KEY_B,
0x08: GHOSTTY_KEY_C,
0x02: GHOSTTY_KEY_D,
0x0E: GHOSTTY_KEY_E,
0x03: GHOSTTY_KEY_F,
0x05: GHOSTTY_KEY_G,
0x04: GHOSTTY_KEY_H,
0x22: GHOSTTY_KEY_I,
0x26: GHOSTTY_KEY_J,
0x28: GHOSTTY_KEY_K,
0x25: GHOSTTY_KEY_L,
0x2E: GHOSTTY_KEY_M,
0x2D: GHOSTTY_KEY_N,
0x1F: GHOSTTY_KEY_O,
0x23: GHOSTTY_KEY_P,
0x0C: GHOSTTY_KEY_Q,
0x0F: GHOSTTY_KEY_R,
0x01: GHOSTTY_KEY_S,
0x11: GHOSTTY_KEY_T,
0x20: GHOSTTY_KEY_U,
0x09: GHOSTTY_KEY_V,
0x0D: GHOSTTY_KEY_W,
0x07: GHOSTTY_KEY_X,
0x10: GHOSTTY_KEY_Y,
0x06: GHOSTTY_KEY_Z,
0x27: GHOSTTY_KEY_APOSTROPHE,
0x2A: GHOSTTY_KEY_BACKSLASH,
0x2B: GHOSTTY_KEY_COMMA,
0x18: GHOSTTY_KEY_EQUAL,
0x32: GHOSTTY_KEY_GRAVE_ACCENT,
0x21: GHOSTTY_KEY_LEFT_BRACKET,
0x1B: GHOSTTY_KEY_MINUS,
0x2F: GHOSTTY_KEY_PERIOD,
0x1E: GHOSTTY_KEY_RIGHT_BRACKET,
0x29: GHOSTTY_KEY_SEMICOLON,
0x2C: GHOSTTY_KEY_SLASH,
0x33: GHOSTTY_KEY_BACKSPACE,
0x39: GHOSTTY_KEY_CAPS_LOCK,
0x75: GHOSTTY_KEY_DELETE,
0x7D: GHOSTTY_KEY_DOWN,
0x77: GHOSTTY_KEY_END,
0x24: GHOSTTY_KEY_ENTER,
0x35: GHOSTTY_KEY_ESCAPE,
0x7A: GHOSTTY_KEY_F1,
0x78: GHOSTTY_KEY_F2,
0x63: GHOSTTY_KEY_F3,
0x76: GHOSTTY_KEY_F4,
0x60: GHOSTTY_KEY_F5,
0x61: GHOSTTY_KEY_F6,
0x62: GHOSTTY_KEY_F7,
0x64: GHOSTTY_KEY_F8,
0x65: GHOSTTY_KEY_F9,
0x6D: GHOSTTY_KEY_F10,
0x67: GHOSTTY_KEY_F11,
0x6F: GHOSTTY_KEY_F12,
0x69: GHOSTTY_KEY_PRINT_SCREEN,
0x6B: GHOSTTY_KEY_F14,
0x71: GHOSTTY_KEY_F15,
0x6A: GHOSTTY_KEY_F16,
0x40: GHOSTTY_KEY_F17,
0x4F: GHOSTTY_KEY_F18,
0x50: GHOSTTY_KEY_F19,
0x5A: GHOSTTY_KEY_F20,
0x73: GHOSTTY_KEY_HOME,
0x72: GHOSTTY_KEY_INSERT,
0x7B: GHOSTTY_KEY_LEFT,
0x3A: GHOSTTY_KEY_LEFT_ALT,
0x3B: GHOSTTY_KEY_LEFT_CONTROL,
0x38: GHOSTTY_KEY_LEFT_SHIFT,
0x37: GHOSTTY_KEY_LEFT_SUPER,
0x47: GHOSTTY_KEY_NUM_LOCK,
0x79: GHOSTTY_KEY_PAGE_DOWN,
0x74: GHOSTTY_KEY_PAGE_UP,
0x7C: GHOSTTY_KEY_RIGHT,
0x3D: GHOSTTY_KEY_RIGHT_ALT,
0x3E: GHOSTTY_KEY_RIGHT_CONTROL,
0x3C: GHOSTTY_KEY_RIGHT_SHIFT,
0x36: GHOSTTY_KEY_RIGHT_SUPER,
0x31: GHOSTTY_KEY_SPACE,
0x30: GHOSTTY_KEY_TAB,
0x7E: GHOSTTY_KEY_UP,
0x52: GHOSTTY_KEY_KP_0,
0x53: GHOSTTY_KEY_KP_1,
0x54: GHOSTTY_KEY_KP_2,
0x55: GHOSTTY_KEY_KP_3,
0x56: GHOSTTY_KEY_KP_4,
0x57: GHOSTTY_KEY_KP_5,
0x58: GHOSTTY_KEY_KP_6,
0x59: GHOSTTY_KEY_KP_7,
0x5B: GHOSTTY_KEY_KP_8,
0x5C: GHOSTTY_KEY_KP_9,
0x45: GHOSTTY_KEY_KP_ADD,
0x41: GHOSTTY_KEY_KP_DECIMAL,
0x4B: GHOSTTY_KEY_KP_DIVIDE,
0x4C: GHOSTTY_KEY_KP_ENTER,
0x51: GHOSTTY_KEY_KP_EQUAL,
0x43: GHOSTTY_KEY_KP_MULTIPLY,
0x4E: GHOSTTY_KEY_KP_SUBTRACT,
];
}

View File

@ -2,7 +2,9 @@ import SwiftUI
import GhosttyKit
struct TerminalView: View {
let app: ghostty_app_t
// The surface to create a view for
let surfaceView: TerminalSurfaceView
@FocusState private var surfaceFocus: Bool
@Environment(\.isKeyWindow) private var isKeyWindow: Bool
@State private var title: String = "Ghostty"
@ -11,12 +13,17 @@ struct TerminalView: View {
// it is both individually focused and the containing window is key.
private var hasFocus: Bool { surfaceFocus && isKeyWindow }
// Initialize a TerminalView with a new surface view state.
init(_ app: ghostty_app_t) {
self.surfaceView = TerminalSurfaceView(app)
}
var body: some View {
// We use a GeometryReader to get the frame bounds so that our metal surface
// is up to date. See TerminalSurfaceView for why we don't use the NSView
// resize callback.
GeometryReader { geo in
TerminalSurface(app, hasFocus: hasFocus, size: geo.size, title: $title)
TerminalSurface(view: surfaceView, hasFocus: hasFocus, size: geo.size, title: $title)
.focused($surfaceFocus)
.navigationTitle(title)
}
@ -54,7 +61,7 @@ struct TerminalSplittableView: View {
Button("Split Vertical") { splitDirection = .vertical }
}
TerminalView(app: app)
TerminalView(app)
.focused($focusedSide, equals: .TopLeft)
}
case .horizontal: