core: add 64 bit unique ID to every core surface (#12027)

- Expose that ID as the environment variable GHOSTTY_SURFACE_ID to
processes running in Ghostty surfaces.
- Add a function to the core app to search for surfaces by ID.
- ID is randomly generated, it has no other meaning other than as a
unique identifier for the surface. The ID also cannot be zero as that is
used to indicate a null ID in some situations.
This commit is contained in:
Jeffrey C. Ollie
2026-03-31 14:50:00 -05:00
committed by GitHub
2 changed files with 30 additions and 0 deletions

View File

@@ -524,6 +524,16 @@ fn hasSurface(self: *const App, surface: *const Surface) bool {
return false;
}
/// Search for a surface by a 64 bit unique ID.
pub fn findSurfaceByID(self: *const App, id: u64) ?*Surface {
for (self.surfaces.items) |v| {
const surface: *Surface = v.core();
if (surface.id == id) return surface;
}
return null;
}
fn hasRtSurface(self: *const App, surface: *apprt.Surface) bool {
for (self.surfaces.items) |v| {
if (v == surface) return true;

View File

@@ -54,6 +54,13 @@ pub const min_window_height_cells: u32 = 4;
/// given time. `activate_key_table` calls after this are ignored.
const max_active_key_tables = 8;
/// Unique ID used to identify this surface for IPC purposes. It is
/// exposed to the commands running in surfaces as the environment variable
/// GHOSTTY_SURFACE_ID. It must not be zero as zero is used to incicate a null
/// value when communicating an ID over DBus as DBus does not allow null/maybe
/// values.
id: u64,
/// Allocator
alloc: Allocator,
@@ -579,6 +586,13 @@ pub fn init(
errdefer io_thread.deinit();
self.* = .{
.id = id: {
while (true) {
const candidate = std.crypto.random.int(u64);
if (candidate == 0) continue;
break :id candidate;
}
},
.alloc = alloc,
.app = app,
.rt_app = rt_app,
@@ -632,6 +646,12 @@ pub fn init(
// don't leak GHOSTTY_LOG to any subprocesses
env.remove("GHOSTTY_LOG");
var buf: [18]u8 = undefined;
try env.put(
"GHOSTTY_SURFACE_ID",
std.fmt.bufPrint(&buf, "0x{x:0>16}", .{self.id}) catch unreachable,
);
// Initialize our IO backend
var io_exec = try termio.Exec.init(alloc, .{
.command = command,