forked from ghostty-org/ghostty
-
Notifications
You must be signed in to change notification settings - Fork 92
Expand file tree
/
Copy pathGhostty.Surface.swift
More file actions
158 lines (144 loc) 路 6.4 KB
/
Copy pathGhostty.Surface.swift
File metadata and controls
158 lines (144 loc) 路 6.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
import GhosttyKit
extension Ghostty {
/// Represents a single surface within Ghostty.
///
/// NOTE(mitchellh): This is a work-in-progress class as part of a general refactor
/// of our Ghostty data model. At the time of writing there's still a ton of surface
/// functionality that is not encapsulated in this class. It is planned to migrate that
/// all over.
///
/// Wraps a `ghostty_surface_t`
final class Surface: Sendable {
private let surface: ghostty_surface_t
/// Read the underlying C value for this surface. This is unsafe because the value will be
/// freed when the Surface class is deinitialized.
var unsafeCValue: ghostty_surface_t {
surface
}
/// Initialize from the C structure.
init(cSurface: ghostty_surface_t) {
self.surface = cSurface
}
deinit {
// deinit is not guaranteed to happen on the main actor and our API
// calls into libghostty must happen there so we capture the surface
// value so we don't capture `self` and then we detach it in a task.
// We can't wait for the task to succeed so this will happen sometime
// but that's okay.
let surface = self.surface
Task.detached { @MainActor in
ghostty_surface_free(surface)
}
}
/// Send text to the terminal as if it was typed. This doesn't send the key events so keyboard
/// shortcuts and other encodings do not take effect.
@MainActor
func sendText(_ text: String) {
let len = text.utf8CString.count
if len == 0 { return }
text.withCString { ptr in
// len includes the null terminator so we do len - 1
ghostty_surface_text_input(surface, ptr, UInt(len - 1))
}
}
/// Send a key event to the terminal.
///
/// This sends the full key event including modifiers, action type, and text to the terminal.
/// Unlike `sendText`, this method processes keyboard shortcuts, key bindings, and terminal
/// encoding based on the complete key event information.
///
/// - Parameter event: The key event to send to the terminal
@MainActor
func sendKeyEvent(_ event: Input.KeyEvent) {
event.withCValue { cEvent in
ghostty_surface_key(surface, cEvent)
}
}
/// Check if a key event matches a keybinding.
///
/// This checks whether the given key event would trigger a keybinding in the terminal.
/// If it matches, returns the binding flags indicating properties of the matched binding.
///
/// - Parameter event: The key event to check
/// - Returns: The binding flags if a binding matches, or nil if no binding matches
@MainActor
func keyIsBinding(_ event: ghostty_input_key_s) -> Input.BindingFlags? {
var flags = ghostty_binding_flags_e(0)
guard ghostty_surface_key_is_binding(surface, event, &flags) else { return nil }
return Input.BindingFlags(cFlags: flags)
}
/// See `keyIsBinding(_ event: ghostty_input_key_s)`.
@MainActor
func keyIsBinding(_ event: Input.KeyEvent) -> Input.BindingFlags? {
event.withCValue { keyIsBinding($0) }
}
/// Whether the terminal has captured mouse input.
///
/// When the mouse is captured, the terminal application is receiving mouse events
/// directly rather than the host system handling them. This typically occurs when
/// a terminal application enables mouse reporting mode.
@MainActor
var mouseCaptured: Bool {
ghostty_surface_mouse_captured(surface)
}
/// Send a mouse button event to the terminal.
///
/// This sends a complete mouse button event including the button state (press/release),
/// which button was pressed, and any modifier keys that were held during the event.
/// The terminal processes this event according to its mouse handling configuration.
///
/// - Parameter event: The mouse button event to send to the terminal
@MainActor
func sendMouseButton(_ event: Input.MouseButtonEvent) {
ghostty_surface_mouse_button(
surface,
event.action.cMouseState,
event.button.cMouseButton,
event.mods.cMods)
}
/// Send a mouse position event to the terminal.
///
/// This reports the current mouse position to the terminal, which may be used
/// for mouse tracking, hover effects, or other position-dependent features.
/// The terminal will only receive these events if mouse reporting is enabled.
///
/// - Parameter event: The mouse position event to send to the terminal
@MainActor
func sendMousePos(_ event: Input.MousePosEvent) {
ghostty_surface_mouse_pos(
surface,
event.x,
event.y,
event.mods.cMods)
}
/// Send a mouse scroll event to the terminal.
///
/// This sends scroll wheel input to the terminal with delta values for both
/// horizontal and vertical scrolling, along with precision and momentum information.
/// The terminal processes this according to its scroll handling configuration.
///
/// - Parameter event: The mouse scroll event to send to the terminal
@MainActor
func sendMouseScroll(_ event: Input.MouseScrollEvent) {
ghostty_surface_mouse_scroll(
surface,
event.x,
event.y,
event.mods.cScrollMods)
}
/// Perform a keybinding action.
///
/// The action can be any valid keybind parameter. e.g. `keybind = goto_tab:4`
/// you can perform `goto_tab:4` with this.
///
/// Returns true if the action was performed. Invalid actions return false.
@MainActor
func perform(action: String) -> Bool {
let len = action.utf8CString.count
if len == 0 { return false }
return action.withCString { cString in
ghostty_surface_binding_action(surface, cString, UInt(len - 1))
}
}
}
}