Go bindings for libghostty-vt.
0
fork

Configure Feed

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

Merge pull request #8 from mitchellh/push-xoqvpoxzlzlm

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

authored by

Mitchell Hashimoto and committed by
GitHub
5367d47a 55c8fd16

+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 }