From 6c857877e8f164b03cedceb0981689cb6863d9b4 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Wed, 8 Mar 2023 15:05:15 -0800 Subject: [PATCH] apprt/embedded: close surface callback --- include/ghostty.h | 2 ++ macos/Sources/Ghostty/AppState.swift | 8 +++++++- macos/Sources/Ghostty/Ghostty.SplitView.swift | 8 ++++++++ macos/Sources/Ghostty/SurfaceView.swift | 3 +++ src/Surface.zig | 6 ++++++ src/apprt/embedded.zig | 14 +++++++++++++- src/config.zig | 4 ++-- src/input/Binding.zig | 6 +++++- 8 files changed, 46 insertions(+), 5 deletions(-) diff --git a/include/ghostty.h b/include/ghostty.h index b93f44ea4..206e123dc 100644 --- a/include/ghostty.h +++ b/include/ghostty.h @@ -31,6 +31,7 @@ typedef void (*ghostty_runtime_set_title_cb)(void *, const char *); typedef const char* (*ghostty_runtime_read_clipboard_cb)(void *); typedef void (*ghostty_runtime_write_clipboard_cb)(void *, const char *); typedef void (*ghostty_runtime_new_split_cb)(void *, ghostty_split_direction_e); +typedef void (*ghostty_runtime_close_surface_cb)(void *); typedef struct { void *userdata; @@ -39,6 +40,7 @@ typedef struct { ghostty_runtime_read_clipboard_cb read_clipboard_cb; ghostty_runtime_write_clipboard_cb write_clipboard_cb; ghostty_runtime_new_split_cb new_split_cb; + ghostty_runtime_close_surface_cb close_surface_cb; } ghostty_runtime_config_s; typedef struct { diff --git a/macos/Sources/Ghostty/AppState.swift b/macos/Sources/Ghostty/AppState.swift index 2e40e3aef..b6b4dcb5e 100644 --- a/macos/Sources/Ghostty/AppState.swift +++ b/macos/Sources/Ghostty/AppState.swift @@ -57,7 +57,8 @@ extension Ghostty { set_title_cb: { userdata, title in AppState.setTitle(userdata, title: title) }, read_clipboard_cb: { userdata in AppState.readClipboard(userdata) }, write_clipboard_cb: { userdata, str in AppState.writeClipboard(userdata, string: str) }, - new_split_cb: { userdata, direction in AppState.newSplit(userdata, direction: ghostty_split_direction_e(UInt32(direction))) } + new_split_cb: { userdata, direction in AppState.newSplit(userdata, direction: ghostty_split_direction_e(UInt32(direction))) }, + close_surface_cb: { userdata in AppState.closeSurface(userdata) } ) // Create the ghostty app. @@ -90,6 +91,11 @@ extension Ghostty { ]) } + static func closeSurface(_ userdata: UnsafeMutableRawPointer?) { + guard let surface = self.surfaceUserdata(from: userdata) else { return } + NotificationCenter.default.post(name: Notification.ghosttyCloseSurface, object: surface) + } + static func readClipboard(_ userdata: UnsafeMutableRawPointer?) -> UnsafePointer? { guard let appState = self.appState(fromSurface: userdata) else { return nil } guard let str = NSPasteboard.general.string(forType: .string) else { return nil } diff --git a/macos/Sources/Ghostty/Ghostty.SplitView.swift b/macos/Sources/Ghostty/Ghostty.SplitView.swift index edc580ba4..d33aebd14 100644 --- a/macos/Sources/Ghostty/Ghostty.SplitView.swift +++ b/macos/Sources/Ghostty/Ghostty.SplitView.swift @@ -143,15 +143,23 @@ extension Ghostty { .onReceive(pub) { onNewSplit(notification: $0) } case .horizontal: SplitView(.horizontal, left: { + let pub = NotificationCenter.default.publisher(for: Ghostty.Notification.ghosttyCloseSurface, object: state.topLeft) TerminalSplitChild(app, topLeft: state.topLeft) + .onReceive(pub) { _ in closeTopLeft() } }, right: { + let pub = NotificationCenter.default.publisher(for: Ghostty.Notification.ghosttyCloseSurface, object: state.bottomRight!) TerminalSplitChild(app, topLeft: state.bottomRight!) + .onReceive(pub) { _ in closeBottomRight() } }) case .vertical: SplitView(.vertical, left: { + let pub = NotificationCenter.default.publisher(for: Ghostty.Notification.ghosttyCloseSurface, object: state.topLeft) TerminalSplitChild(app, topLeft: state.topLeft) + .onReceive(pub) { _ in closeTopLeft() } }, right: { + let pub = NotificationCenter.default.publisher(for: Ghostty.Notification.ghosttyCloseSurface, object: state.bottomRight!) TerminalSplitChild(app, topLeft: state.bottomRight!) + .onReceive(pub) { _ in closeBottomRight() } }) } } diff --git a/macos/Sources/Ghostty/SurfaceView.swift b/macos/Sources/Ghostty/SurfaceView.swift index 2d7584d69..3c1079885 100644 --- a/macos/Sources/Ghostty/SurfaceView.swift +++ b/macos/Sources/Ghostty/SurfaceView.swift @@ -510,6 +510,9 @@ extension Ghostty.Notification { /// Posted when a new split is requested. The sending object will be the surface that had focus. The /// userdata has one key "direction" with the direction to split to. static let ghosttyNewSplit = Notification.Name("com.mitchellh.ghostty.newSplit") + + /// Close the calling surface. + static let ghosttyCloseSurface = Notification.Name("com.mitchellh.ghostty.closeSurface") } // MARK: Surface Environment Keys diff --git a/src/Surface.zig b/src/Surface.zig index b2a7a0998..b3e7693cd 100644 --- a/src/Surface.zig +++ b/src/Surface.zig @@ -946,6 +946,12 @@ pub fn keyCallback( } else log.warn("runtime doesn't implement newSplit", .{}); }, + .close_surface => { + if (@hasDecl(apprt.Surface, "closeSurface")) { + try self.rt_surface.closeSurface(); + } else log.warn("runtime doesn't implement closeSurface", .{}); + }, + .close_window => { _ = self.app_mailbox.push(.{ .close = self }, .{ .instant = {} }); }, diff --git a/src/apprt/embedded.zig b/src/apprt/embedded.zig index 946fad24f..8e739a2cc 100644 --- a/src/apprt/embedded.zig +++ b/src/apprt/embedded.zig @@ -47,7 +47,10 @@ pub const App = struct { /// Create a new split view. If the embedder doesn't support split /// views then this can be null. - new_split: ?*const fn (SurfaceUD, input.Binding.Action.SplitDirection) callconv(.C) void = null, + new_split: ?*const fn (SurfaceUD, input.SplitDirection) callconv(.C) void = null, + + /// Close the current surface given by this function. + close_surface: ?*const fn (SurfaceUD) callconv(.C) void = null, }; core_app: *CoreApp, @@ -161,6 +164,15 @@ pub const Surface = struct { func(self.opts.userdata, direction); } + pub fn closeSurface(self: *const Surface) !void { + const func = self.app.opts.close_surface orelse { + log.info("runtime embedder does not closing a surface", .{}); + return; + }; + + func(self.opts.userdata); + } + pub fn getContentScale(self: *const Surface) !apprt.ContentScale { return self.content_scale; } diff --git a/src/config.zig b/src/config.zig index 0e8762bad..a9d74c27c 100644 --- a/src/config.zig +++ b/src/config.zig @@ -278,8 +278,8 @@ pub const Config = struct { ); try result.keybind.set.put( alloc, - .{ .key = .w, .mods = .{ .super = true } }, - .{ .close_window = {} }, + .{ .key = .w, .mods = .{ .super = true, .shift = true } }, + .{ .close_surface = {} }, ); try result.keybind.set.put( alloc, diff --git a/src/input/Binding.zig b/src/input/Binding.zig index ecae7495b..3eeb941a6 100644 --- a/src/input/Binding.zig +++ b/src/input/Binding.zig @@ -184,7 +184,11 @@ pub const Action = union(enum) { /// in the direction given. new_split: SplitDirection, - /// Close the current window or tab + /// Close the current "surface", whether that is a window, tab, split, + /// etc. This only closes ONE surface. + close_surface: void, + + /// Close the window, regardless of how many tabs or splits there may be. close_window: void, /// Quit ghostty