package libghostty // System-level configuration wrapping ghostty_sys_set(). // These are process-global settings that must be configured at startup. /* #include #include // Forward declaration for the Go log trampoline so we can take its // address on the C side. Uses compatible types (no const, int for enum) // to match what cgo generates for the //export function. extern void goSysLogTrampoline( void* userdata, int level, uint8_t* scope, size_t scope_len, uint8_t* message, size_t message_len); // Forward declaration for the Go decode-PNG trampoline. // Uses compatible types (no const) to match what cgo generates for // the //export function. extern _Bool goSysDecodePngTrampoline( void* userdata, GhosttyAllocator* allocator, uint8_t* data, size_t data_len, GhosttySysImage* out); // Forward declaration for the Go secure-random trampoline. extern _Bool goSysRandomSecureTrampoline( void* userdata, uint8_t* buf, size_t len); // Helper to install the Go log trampoline via ghostty_sys_set. // We need this because cgo cannot take the address of a Go-exported // function directly as a C function pointer. static inline GhosttyResult sys_set_log_go(void) { return ghostty_sys_set(GHOSTTY_SYS_OPT_LOG, (const void*)goSysLogTrampoline); } // Helper to install the built-in stderr log callback. static inline GhosttyResult sys_set_log_stderr(void) { return ghostty_sys_set(GHOSTTY_SYS_OPT_LOG, (const void*)ghostty_sys_log_stderr); } // Helper to clear the log callback. static inline GhosttyResult sys_clear_log(void) { return ghostty_sys_set(GHOSTTY_SYS_OPT_LOG, NULL); } // Helper to install the Go decode-PNG trampoline via ghostty_sys_set. static inline GhosttyResult sys_set_decode_png_go(void) { return ghostty_sys_set(GHOSTTY_SYS_OPT_DECODE_PNG, (const void*)goSysDecodePngTrampoline); } // Helper to clear the decode-PNG callback. static inline GhosttyResult sys_clear_decode_png(void) { return ghostty_sys_set(GHOSTTY_SYS_OPT_DECODE_PNG, NULL); } // Helper to install the Go secure-random trampoline via ghostty_sys_set. static inline GhosttyResult sys_set_random_secure_go(void) { return ghostty_sys_set(GHOSTTY_SYS_OPT_RANDOM_SECURE, (const void*)goSysRandomSecureTrampoline); } // Helper to restore the platform secure-random implementation. static inline GhosttyResult sys_clear_random_secure(void) { return ghostty_sys_set(GHOSTTY_SYS_OPT_RANDOM_SECURE, NULL); } */ import "C" import "unsafe" // SysImage is a decoded image, returned by a [SysDecodePngFn]. // // C: GhosttySysImage type SysImage struct { // Width is the image width in pixels. Width uint32 // Height is the image height in pixels. Height uint32 // Data holds the pixels as 8-bit RGBA, four bytes per pixel. Rows // are stored in order starting from the top-left corner, with no // padding between them, so a complete image is Width*Height*4 bytes. Data []byte } // SysDecodePngFn decodes the PNG image in data and returns its pixels. // Return a non-nil error if the image cannot be decoded. The image is then // rejected. // // data is only valid until the function returns, so copy it if you need // it later. The pixels in the returned [SysImage] are copied into memory // owned by libghostty, so the function does not need to keep them alive. // // C: GhosttySysDecodePngFn type SysDecodePngFn func(data []byte) (*SysImage, error) // sysDecodePngFn is the currently installed Go decode-PNG callback. var sysDecodePngFn SysDecodePngFn // SysRandomSecureFn fills data with cryptographically secure random bytes. // Implementations must use a real CSPRNG because libghostty uses this output // for secrets such as Kitty clipboard protocol one-time passwords. Return a // non-nil error when secure entropy is unavailable. // // C: GhosttySysRandomSecureFn type SysRandomSecureFn func(data []byte) error // sysRandomSecureFn is the currently installed Go secure-random callback. var sysRandomSecureFn SysRandomSecureFn // SysSetDecodePng installs a Go callback that decodes PNG image data // into RGBA pixels. This enables PNG support in the Kitty Graphics // Protocol. Pass nil to clear the callback and disable PNG decoding. // // This function is not safe for concurrent use. Callers must ensure // that decode configuration is not modified while terminals may // process image data (e.g. configure at startup before creating // terminals). func SysSetDecodePng(fn SysDecodePngFn) error { sysDecodePngFn = fn if fn == nil { return resultError(C.sys_clear_decode_png()) } return resultError(C.sys_set_decode_png_go()) } // SysSetRandomSecure overrides libghostty's platform secure-random source. // Pass nil to restore the platform default. Targets without a platform source, // such as wasm32-freestanding, require an override for operations that mint // secrets. // // This function is not safe for concurrent use. Configure it at startup before // terminals can perform operations that require secure entropy. func SysSetRandomSecure(fn SysRandomSecureFn) error { sysRandomSecureFn = fn if fn == nil { return resultError(C.sys_clear_random_secure()) } return resultError(C.sys_set_random_secure_go()) } // SysLogLevel represents the severity level of a log message from the // library. Maps directly to the C enum values. // C: GhosttySysLogLevel type SysLogLevel int const ( // SysLogLevelError is the error log level. SysLogLevelError SysLogLevel = C.GHOSTTY_SYS_LOG_LEVEL_ERROR // SysLogLevelWarning is the warning log level. SysLogLevelWarning SysLogLevel = C.GHOSTTY_SYS_LOG_LEVEL_WARNING // SysLogLevelInfo is the info log level. SysLogLevelInfo SysLogLevel = C.GHOSTTY_SYS_LOG_LEVEL_INFO // SysLogLevelDebug is the debug log level. SysLogLevelDebug SysLogLevel = C.GHOSTTY_SYS_LOG_LEVEL_DEBUG ) // String returns a human-readable name for the log level. func (l SysLogLevel) String() string { switch l { case SysLogLevelError: return "error" case SysLogLevelWarning: return "warning" case SysLogLevelInfo: return "info" case SysLogLevelDebug: return "debug" default: return "unknown" } } // SysLogFn is the Go callback type for log messages from the library. // The scope identifies the subsystem (e.g. "osc", "kitty"); it is // empty for unscoped (default) log messages. The message and scope // are only valid for the duration of the call. // C: GhosttySysLogFn type SysLogFn func(level SysLogLevel, scope string, message string) // sysLogFn is the currently installed Go log callback. var sysLogFn SysLogFn // SysSetLog installs a Go callback that receives internal library log // messages. Pass nil to clear the callback and discard log messages. // // Which log levels are emitted depends on the build mode of the // library. Debug builds emit all levels; release builds emit info // and above. // // This function is not safe for concurrent use. Callers must ensure // that log configuration is not modified while log messages may be // delivered (e.g. configure at startup before creating terminals). func SysSetLog(fn SysLogFn) error { sysLogFn = fn if fn == nil { return resultError(C.sys_clear_log()) } return resultError(C.sys_set_log_go()) } // SysSetLogStderr installs the built-in stderr log callback provided // by libghostty. Each message is formatted as "[level](scope): message\n" // and written to stderr in a thread-safe manner. // // This function is not safe for concurrent use. See [SysSetLog]. func SysSetLogStderr() error { sysLogFn = nil return resultError(C.sys_set_log_stderr()) } //export goSysLogTrampoline func goSysLogTrampoline( _ unsafe.Pointer, level C.int, scopePtr *C.uint8_t, scopeLen C.size_t, messagePtr *C.uint8_t, messageLen C.size_t, ) { fn := sysLogFn if fn == nil { return } var scope string if scopeLen > 0 { scope = C.GoStringN((*C.char)(unsafe.Pointer(scopePtr)), C.int(scopeLen)) } var message string if messageLen > 0 { message = C.GoStringN((*C.char)(unsafe.Pointer(messagePtr)), C.int(messageLen)) } fn(SysLogLevel(level), scope, message) } //export goSysDecodePngTrampoline func goSysDecodePngTrampoline( _ unsafe.Pointer, allocator *C.GhosttyAllocator, dataPtr *C.uint8_t, dataLen C.size_t, out *C.GhosttySysImage, ) C.bool { fn := sysDecodePngFn if fn == nil { return false } // Build a Go slice over the input PNG data without copying. data := unsafe.Slice((*byte)(unsafe.Pointer(dataPtr)), int(dataLen)) img, err := fn(data) if err != nil || img == nil { return false } // Allocate output pixel buffer through the library's allocator so // the library can free it later. pixelLen := C.size_t(len(img.Data)) buf := C.ghostty_alloc(allocator, pixelLen) if buf == nil { return false } // Copy decoded pixels into the library-owned buffer. copy(unsafe.Slice((*byte)(unsafe.Pointer(buf)), int(pixelLen)), img.Data) // out.data is a pointer, and Go may only store a pointer into C // memory that is already zeroed (see allocZeroed). libghostty zeroes // out before calling us, so the fields can be set directly. out.width = C.uint32_t(img.Width) out.height = C.uint32_t(img.Height) out.data = buf out.data_len = pixelLen return true } //export goSysRandomSecureTrampoline func goSysRandomSecureTrampoline( _ unsafe.Pointer, buf *C.uint8_t, length C.size_t, ) (ok C.bool) { fn := sysRandomSecureFn if fn == nil { return C.bool(false) } // A panic must never unwind across the C boundary. A failed random source // is represented by false, which makes the originating operation fail with // GHOSTTY_IO_ERROR. defer func() { if recover() != nil { ok = C.bool(false) } }() count, valid := ghosttySizeToInt(length) if !valid || (count > 0 && buf == nil) { return C.bool(false) } data := unsafe.Slice((*byte)(unsafe.Pointer(buf)), count) if err := fn(data); err != nil { return C.bool(false) } return C.bool(true) }