forked from
mitchellh.com/go-libghostty
Go bindings for libghostty-vt.
1package libghostty
2
3// System-level configuration wrapping ghostty_sys_set().
4// These are process-global settings that must be configured at startup.
5
6/*
7#include <ghostty/vt.h>
8#include <ghostty/vt/sys.h>
9
10// Forward declaration for the Go log trampoline so we can take its
11// address on the C side. Uses compatible types (no const, int for enum)
12// to match what cgo generates for the //export function.
13extern void goSysLogTrampoline(
14 void* userdata,
15 int level,
16 uint8_t* scope,
17 size_t scope_len,
18 uint8_t* message,
19 size_t message_len);
20
21// Forward declaration for the Go decode-PNG trampoline.
22// Uses compatible types (no const) to match what cgo generates for
23// the //export function.
24extern _Bool goSysDecodePngTrampoline(
25 void* userdata,
26 GhosttyAllocator* allocator,
27 uint8_t* data,
28 size_t data_len,
29 GhosttySysImage* out);
30
31// Helper to install the Go log trampoline via ghostty_sys_set.
32// We need this because cgo cannot take the address of a Go-exported
33// function directly as a C function pointer.
34static inline GhosttyResult sys_set_log_go(void) {
35 return ghostty_sys_set(GHOSTTY_SYS_OPT_LOG, (const void*)goSysLogTrampoline);
36}
37
38// Helper to install the built-in stderr log callback.
39static inline GhosttyResult sys_set_log_stderr(void) {
40 return ghostty_sys_set(GHOSTTY_SYS_OPT_LOG, (const void*)ghostty_sys_log_stderr);
41}
42
43// Helper to clear the log callback.
44static inline GhosttyResult sys_clear_log(void) {
45 return ghostty_sys_set(GHOSTTY_SYS_OPT_LOG, NULL);
46}
47
48// Helper to install the Go decode-PNG trampoline via ghostty_sys_set.
49static inline GhosttyResult sys_set_decode_png_go(void) {
50 return ghostty_sys_set(GHOSTTY_SYS_OPT_DECODE_PNG, (const void*)goSysDecodePngTrampoline);
51}
52
53// Helper to clear the decode-PNG callback.
54static inline GhosttyResult sys_clear_decode_png(void) {
55 return ghostty_sys_set(GHOSTTY_SYS_OPT_DECODE_PNG, NULL);
56}
57*/
58import "C"
59
60import "unsafe"
61
62// SysImage holds the result of decoding an image (e.g. PNG) into raw
63// RGBA pixel data. Returned by the user-supplied decode callback.
64// C: GhosttySysImage
65type SysImage struct {
66 // Width of the decoded image in pixels.
67 Width uint32
68
69 // Height of the decoded image in pixels.
70 Height uint32
71
72 // Data is the decoded RGBA pixel data (4 bytes per pixel).
73 Data []byte
74}
75
76// SysDecodePngFn is the Go callback type for PNG decoding. It receives
77// raw PNG data and must return a decoded SysImage. The returned pixel
78// data will be copied into library-managed memory; the caller does not
79// need to keep the slice alive after returning.
80//
81// Return a non-nil error to indicate decode failure.
82// C: GhosttySysDecodePngFn
83type SysDecodePngFn func(data []byte) (*SysImage, error)
84
85// sysDecodePngFn is the currently installed Go decode-PNG callback.
86var sysDecodePngFn SysDecodePngFn
87
88// SysSetDecodePng installs a Go callback that decodes PNG image data
89// into RGBA pixels. This enables PNG support in the Kitty Graphics
90// Protocol. Pass nil to clear the callback and disable PNG decoding.
91//
92// This function is not safe for concurrent use. Callers must ensure
93// that decode configuration is not modified while terminals may
94// process image data (e.g. configure at startup before creating
95// terminals).
96func SysSetDecodePng(fn SysDecodePngFn) error {
97 sysDecodePngFn = fn
98 if fn == nil {
99 return resultError(C.sys_clear_decode_png())
100 }
101 return resultError(C.sys_set_decode_png_go())
102}
103
104// SysLogLevel represents the severity level of a log message from the
105// library. Maps directly to the C enum values.
106// C: GhosttySysLogLevel
107type SysLogLevel int
108
109const (
110 // SysLogLevelError is the error log level.
111 SysLogLevelError SysLogLevel = C.GHOSTTY_SYS_LOG_LEVEL_ERROR
112
113 // SysLogLevelWarning is the warning log level.
114 SysLogLevelWarning SysLogLevel = C.GHOSTTY_SYS_LOG_LEVEL_WARNING
115
116 // SysLogLevelInfo is the info log level.
117 SysLogLevelInfo SysLogLevel = C.GHOSTTY_SYS_LOG_LEVEL_INFO
118
119 // SysLogLevelDebug is the debug log level.
120 SysLogLevelDebug SysLogLevel = C.GHOSTTY_SYS_LOG_LEVEL_DEBUG
121)
122
123// String returns a human-readable name for the log level.
124func (l SysLogLevel) String() string {
125 switch l {
126 case SysLogLevelError:
127 return "error"
128 case SysLogLevelWarning:
129 return "warning"
130 case SysLogLevelInfo:
131 return "info"
132 case SysLogLevelDebug:
133 return "debug"
134 default:
135 return "unknown"
136 }
137}
138
139// SysLogFn is the Go callback type for log messages from the library.
140// The scope identifies the subsystem (e.g. "osc", "kitty"); it is
141// empty for unscoped (default) log messages. The message and scope
142// are only valid for the duration of the call.
143// C: GhosttySysLogFn
144type SysLogFn func(level SysLogLevel, scope string, message string)
145
146// sysLogFn is the currently installed Go log callback.
147var sysLogFn SysLogFn
148
149// SysSetLog installs a Go callback that receives internal library log
150// messages. Pass nil to clear the callback and discard log messages.
151//
152// Which log levels are emitted depends on the build mode of the
153// library. Debug builds emit all levels; release builds emit info
154// and above.
155//
156// This function is not safe for concurrent use. Callers must ensure
157// that log configuration is not modified while log messages may be
158// delivered (e.g. configure at startup before creating terminals).
159func SysSetLog(fn SysLogFn) error {
160 sysLogFn = fn
161 if fn == nil {
162 return resultError(C.sys_clear_log())
163 }
164 return resultError(C.sys_set_log_go())
165}
166
167// SysSetLogStderr installs the built-in stderr log callback provided
168// by libghostty. Each message is formatted as "[level](scope): message\n"
169// and written to stderr in a thread-safe manner.
170//
171// This function is not safe for concurrent use. See [SysSetLog].
172func SysSetLogStderr() error {
173 sysLogFn = nil
174 return resultError(C.sys_set_log_stderr())
175}
176
177//export goSysLogTrampoline
178func goSysLogTrampoline(
179 _ unsafe.Pointer,
180 level C.int,
181 scopePtr *C.uint8_t,
182 scopeLen C.size_t,
183 messagePtr *C.uint8_t,
184 messageLen C.size_t,
185) {
186 fn := sysLogFn
187 if fn == nil {
188 return
189 }
190
191 var scope string
192 if scopeLen > 0 {
193 scope = C.GoStringN((*C.char)(unsafe.Pointer(scopePtr)), C.int(scopeLen))
194 }
195
196 var message string
197 if messageLen > 0 {
198 message = C.GoStringN((*C.char)(unsafe.Pointer(messagePtr)), C.int(messageLen))
199 }
200
201 fn(SysLogLevel(level), scope, message)
202}
203
204//export goSysDecodePngTrampoline
205func goSysDecodePngTrampoline(
206 _ unsafe.Pointer,
207 allocator *C.GhosttyAllocator,
208 dataPtr *C.uint8_t,
209 dataLen C.size_t,
210 out *C.GhosttySysImage,
211) C.bool {
212 fn := sysDecodePngFn
213 if fn == nil {
214 return false
215 }
216
217 // Build a Go slice over the input PNG data without copying.
218 data := unsafe.Slice((*byte)(unsafe.Pointer(dataPtr)), int(dataLen))
219
220 img, err := fn(data)
221 if err != nil || img == nil {
222 return false
223 }
224
225 // Allocate output pixel buffer through the library's allocator so
226 // the library can free it later.
227 pixelLen := C.size_t(len(img.Data))
228 buf := C.ghostty_alloc(allocator, pixelLen)
229 if buf == nil {
230 return false
231 }
232
233 // Copy decoded pixels into the library-owned buffer.
234 copy(unsafe.Slice((*byte)(unsafe.Pointer(buf)), int(pixelLen)), img.Data)
235
236 out.width = C.uint32_t(img.Width)
237 out.height = C.uint32_t(img.Height)
238 out.data = buf
239 out.data_len = pixelLen
240
241 return true
242}