Go bindings for libghostty-vt.
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

bind ghostty_alloc/ghostty_free and replace C.malloc/C.free

Add alloc.go with exported Alloc and Free functions wrapping the
upstream ghostty_alloc() and ghostty_free() from allocator.h. These
use the default (NULL) allocator, matching how the rest of the
bindings pass nil for the allocator parameter.

This is important because it lets us not explicitly depend on any libc
functionality.

+83 -50
+1 -1
TODO.md
··· 9 9 - [ ] Paste utilities (`paste.h`) 10 10 - [ ] Focus encoding (`focus.h`) 11 11 - [x] Kitty graphics (`kitty_graphics.h`) 12 - - [ ] Allocator (`allocator.h` — `ghostty_alloc`, `ghostty_free`) 12 + - [x] Allocator (`allocator.h` — `ghostty_alloc`, `ghostty_free`) 13 13 - [ ] Selection type (`selection.h`) 14 14 15 15 ## Partially Bound
+36
alloc.go
··· 1 + package libghostty 2 + 3 + // Memory allocation helpers wrapping the upstream ghostty_alloc() and 4 + // ghostty_free() functions from allocator.h. 5 + // 6 + // These replace direct C.malloc/C.free calls so that all memory is 7 + // allocated and freed through libghostty's allocator. This is critical 8 + // on platforms where the library's internal allocator differs from the 9 + // consumer's C runtime (e.g. Windows, where Zig's libc and MSVC's CRT 10 + // maintain separate heaps). 11 + 12 + /* 13 + #include <ghostty/vt.h> 14 + */ 15 + import "C" 16 + 17 + import "unsafe" 18 + 19 + // Alloc allocates len bytes through the default libghostty allocator 20 + // (NULL allocator). Returns a pointer to the allocated memory or nil 21 + // if the allocation failed. 22 + // 23 + // The returned memory must be freed with Free using the same length. 24 + // C: ghostty_alloc 25 + func Alloc(len uintptr) unsafe.Pointer { 26 + return unsafe.Pointer(C.ghostty_alloc(nil, C.size_t(len))) 27 + } 28 + 29 + // Free frees memory allocated by Alloc (or returned by a libghostty 30 + // function) using the default libghostty allocator (NULL allocator). 31 + // The len must match the original allocation size. It is safe to pass 32 + // nil. 33 + // C: ghostty_free 34 + func Free(ptr unsafe.Pointer, len uintptr) { 35 + C.ghostty_free(nil, (*C.uint8_t)(ptr), C.size_t(len)) 36 + }
+11 -13
get_multi.go
··· 3 3 // Shared helpers for the get_multi pattern used by multiple types. 4 4 // These helpers solve the cgo pointer-passing rule: Go cannot pass 5 5 // a Go-allocated void** (array of pointers to Go memory) directly 6 - // to C. Instead, we allocate the void** array in C heap memory, 7 - // copy the Go pointer values in, call the C function, then free. 8 - 9 - /* 10 - #include <stdlib.h> 11 - */ 12 - import "C" 6 + // to C. Instead, we allocate the void** array via libghostty's 7 + // allocator, copy the Go pointer values in, call the C function, 8 + // then free. 13 9 14 10 import "unsafe" 15 11 16 - // cValuesArray allocates a C-heap array of void* pointers, copies the 17 - // Go unsafe.Pointer values into it, and returns the C array pointer. 18 - // The caller must free the returned pointer with C.free when done. 19 - func cValuesArray(values []unsafe.Pointer) *unsafe.Pointer { 12 + // cValuesArray allocates a C-heap array of void* pointers via the 13 + // libghostty allocator, copies the Go unsafe.Pointer values into it, 14 + // and returns the C array pointer and allocation size. The caller must 15 + // free the returned pointer with Free(ptr, size) when done. 16 + func cValuesArray(values []unsafe.Pointer) (*unsafe.Pointer, uintptr) { 20 17 n := len(values) 21 - cArr := (*unsafe.Pointer)(C.malloc(C.size_t(n) * C.size_t(unsafe.Sizeof(unsafe.Pointer(nil))))) 18 + size := uintptr(n) * unsafe.Sizeof(unsafe.Pointer(nil)) 19 + cArr := (*unsafe.Pointer)(Alloc(size)) 22 20 dst := unsafe.Slice(cArr, n) 23 21 copy(dst, values) 24 - return cArr 22 + return cArr, size 25 23 }
+8 -9
kitty_graphics.go
··· 5 5 // protocol. 6 6 7 7 /* 8 - #include <stdlib.h> 9 8 #include <ghostty/vt.h> 10 9 11 10 // Helper to create a properly initialized GhosttySelection (sized struct). ··· 446 445 return nil 447 446 } 448 447 // Allocate the void** array in C memory to satisfy cgo pointer-passing rules. 449 - cVals := cValuesArray(values) 450 - defer C.free(unsafe.Pointer(cVals)) 448 + cVals, cValsSize := cValuesArray(values) 449 + defer Free(unsafe.Pointer(cVals), cValsSize) 451 450 return resultError(C.ghostty_kitty_graphics_image_get_multi( 452 451 img.ptr, 453 452 C.size_t(len(keys)), ··· 526 525 unsafe.Pointer(&dataPtr), 527 526 unsafe.Pointer(&dataLen), 528 527 } 529 - cVals := cValuesArray(values[:]) 530 - defer C.free(unsafe.Pointer(cVals)) 528 + cVals, cValsSize := cValuesArray(values[:]) 529 + defer Free(unsafe.Pointer(cVals), cValsSize) 531 530 532 531 if err := resultError(C.ghostty_kitty_graphics_image_get_multi( 533 532 img.ptr, ··· 649 648 return nil 650 649 } 651 650 // Allocate the void** array in C memory to satisfy cgo pointer-passing rules. 652 - cVals := cValuesArray(values) 653 - defer C.free(unsafe.Pointer(cVals)) 651 + cVals, cValsSize := cValuesArray(values) 652 + defer Free(unsafe.Pointer(cVals), cValsSize) 654 653 return resultError(C.ghostty_kitty_graphics_placement_get_multi( 655 654 it.ptr, 656 655 C.size_t(len(keys)), ··· 873 872 unsafe.Pointer(&rows), 874 873 unsafe.Pointer(&z), 875 874 } 876 - cVals := cValuesArray(values[:]) 877 - defer C.free(unsafe.Pointer(cVals)) 875 + cVals, cValsSize := cValuesArray(values[:]) 876 + defer Free(unsafe.Pointer(cVals), cValsSize) 878 877 879 878 if err := resultError(C.ghostty_kitty_graphics_placement_get_multi( 880 879 it.ptr,
+2 -3
render_state_cell.go
··· 4 4 // GhosttyRenderStateRowCells C APIs. 5 5 6 6 /* 7 - #include <stdlib.h> 8 7 #include <ghostty/vt.h> 9 8 */ 10 9 import "C" ··· 119 118 return nil 120 119 } 121 120 // Allocate the void** array in C memory to satisfy cgo pointer-passing rules. 122 - cVals := cValuesArray(values) 123 - defer C.free(unsafe.Pointer(cVals)) 121 + cVals, cValsSize := cValuesArray(values) 122 + defer Free(unsafe.Pointer(cVals), cValsSize) 124 123 return resultError(C.ghostty_render_state_row_cells_get_multi( 125 124 rc.ptr, 126 125 C.size_t(len(keys)),
+2 -3
render_state_data.go
··· 5 5 // Functions are ordered alphabetically. 6 6 7 7 /* 8 - #include <stdlib.h> 9 8 #include <ghostty/vt.h> 10 9 11 10 // Helper to create a properly initialized GhosttyRenderStateColors (sized struct). ··· 209 208 return nil 210 209 } 211 210 // Allocate the void** array in C memory to satisfy cgo pointer-passing rules. 212 - cVals := cValuesArray(values) 213 - defer C.free(unsafe.Pointer(cVals)) 211 + cVals, cValsSize := cValuesArray(values) 212 + defer Free(unsafe.Pointer(cVals), cValsSize) 214 213 return resultError(C.ghostty_render_state_get_multi( 215 214 rs.ptr, 216 215 C.size_t(len(keys)),
+2 -3
render_state_row.go
··· 4 4 // GhosttyRenderStateRowIterator C APIs. 5 5 6 6 /* 7 - #include <stdlib.h> 8 7 #include <ghostty/vt.h> 9 8 */ 10 9 import "C" ··· 99 98 return nil 100 99 } 101 100 // Allocate the void** array in C memory to satisfy cgo pointer-passing rules. 102 - cVals := cValuesArray(values) 103 - defer C.free(unsafe.Pointer(cVals)) 101 + cVals, cValsSize := cValuesArray(values) 102 + defer Free(unsafe.Pointer(cVals), cValsSize) 104 103 return resultError(C.ghostty_render_state_row_get_multi( 105 104 ri.ptr, 106 105 C.size_t(len(keys)),
+4 -5
screen.go
··· 1 1 package libghostty 2 2 3 3 /* 4 - #include <stdlib.h> 5 4 #include <ghostty/vt.h> 6 5 */ 7 6 import "C" ··· 213 212 return nil 214 213 } 215 214 // Allocate the void** array in C memory to satisfy cgo pointer-passing rules. 216 - cVals := cValuesArray(values) 217 - defer C.free(unsafe.Pointer(cVals)) 215 + cVals, cValsSize := cValuesArray(values) 216 + defer Free(unsafe.Pointer(cVals), cValsSize) 218 217 return resultError(C.ghostty_cell_get_multi( 219 218 c.c, 220 219 C.size_t(len(keys)), ··· 354 353 return nil 355 354 } 356 355 // Allocate the void** array in C memory to satisfy cgo pointer-passing rules. 357 - cVals := cValuesArray(values) 358 - defer C.free(unsafe.Pointer(cVals)) 356 + cVals, cValsSize := cValuesArray(values) 357 + defer Free(unsafe.Pointer(cVals), cValsSize) 359 358 return resultError(C.ghostty_row_get_multi( 360 359 r.c, 361 360 C.size_t(len(keys)),
+3 -3
terminal.go
··· 1 1 package libghostty 2 2 3 3 /* 4 - #include <stdlib.h> 5 4 #include <ghostty/vt.h> 6 5 */ 7 6 import "C" ··· 35 34 // returned by an effect trampoline (e.g. enquiry, xtversion). 36 35 // libghostty copies the data immediately, so a single buffer 37 36 // shared across effects is sufficient. 38 - effectBuf unsafe.Pointer 37 + effectBuf unsafe.Pointer 38 + effectBufLen uintptr 39 39 } 40 40 41 41 // TerminalOption is a functional option for configuring a Terminal. ··· 246 246 t.handle.Delete() 247 247 C.ghostty_terminal_free(t.ptr) 248 248 if t.effectBuf != nil { 249 - C.free(t.effectBuf) 249 + Free(t.effectBuf, t.effectBufLen) 250 250 } 251 251 } 252 252
+2 -3
terminal_data.go
··· 4 4 // Functions are ordered alphabetically. 5 5 6 6 /* 7 - #include <stdlib.h> 8 7 #include <ghostty/vt.h> 9 8 */ 10 9 import "C" ··· 230 229 return nil 231 230 } 232 231 // Allocate the void** array in C memory to satisfy cgo pointer-passing rules. 233 - cVals := cValuesArray(values) 234 - defer C.free(unsafe.Pointer(cVals)) 232 + cVals, cValsSize := cValuesArray(values) 233 + defer Free(unsafe.Pointer(cVals), cValsSize) 235 234 return resultError(C.ghostty_terminal_get_multi( 236 235 t.ptr, 237 236 C.size_t(len(keys)),
+12 -7
terminal_effect.go
··· 8 8 // recovers the Terminal and dispatches to the user-supplied Go effect handler. 9 9 10 10 /* 11 - #include <stdlib.h> 12 11 #include <ghostty/vt.h> 13 12 14 13 // Forward declarations for the Go trampolines so we can take their ··· 214 213 return C.bool(true) 215 214 } 216 215 217 - // effectString copies data into C memory, updates effectBuf, and 218 - // returns a GhosttyString pointing to it. The previous effectBuf 219 - // is freed. Returns a zero-length GhosttyString if data is empty. 216 + // effectString copies data into C memory allocated via the libghostty 217 + // allocator, updates effectBuf/effectBufLen, and returns a 218 + // GhosttyString pointing to it. The previous effectBuf is freed. 219 + // Returns a zero-length GhosttyString if data is empty. 220 220 func (t *Terminal) effectString(data []byte) C.GhosttyString { 221 221 if t.effectBuf != nil { 222 - C.free(t.effectBuf) 222 + Free(t.effectBuf, t.effectBufLen) 223 + t.effectBuf = nil 224 + t.effectBufLen = 0 223 225 } 224 226 225 227 if len(data) == 0 { 226 228 return C.GhosttyString{} 227 229 } 228 230 229 - cmem := C.CBytes(data) 231 + n := uintptr(len(data)) 232 + cmem := Alloc(n) 233 + copy(unsafe.Slice((*byte)(cmem), n), data) 230 234 t.effectBuf = cmem 235 + t.effectBufLen = n 231 236 return C.GhosttyString{ 232 237 ptr: (*C.uint8_t)(cmem), 233 - len: C.size_t(len(data)), 238 + len: C.size_t(n), 234 239 } 235 240 }