apprt/embedded: support asking for selection text, existence

This commit is contained in:
Mitchell Hashimoto
2024-01-21 17:22:44 -08:00
parent 4c9fc452b6
commit 4dbd10c913
4 changed files with 55 additions and 4 deletions

View File

@ -489,6 +489,8 @@ void ghostty_surface_split_equalize(ghostty_surface_t);
bool ghostty_surface_binding_action(ghostty_surface_t, const char *, uintptr_t);
void ghostty_surface_complete_clipboard_request(ghostty_surface_t, const char *, void *, bool);
uintptr_t ghostty_surface_pwd(ghostty_surface_t, char *, uintptr_t);
bool ghostty_surface_has_selection(ghostty_surface_t);
uintptr_t ghostty_surface_selection(ghostty_surface_t, char *, uintptr_t);
ghostty_inspector_t ghostty_surface_inspector(ghostty_surface_t);
void ghostty_inspector_free(ghostty_surface_t);

View File

@ -971,8 +971,9 @@ extension Ghostty.SurfaceView: NSServicesMenuRequestor {
}
// If we have a selection we can send the accepted types too
// TODO selection
if (sendType == nil || accepted.contains(sendType!)) {
if ((self.surface != nil && ghostty_surface_has_selection(self.surface)) &&
(sendType == nil || accepted.contains(sendType!))
) {
return self
}
@ -983,8 +984,17 @@ extension Ghostty.SurfaceView: NSServicesMenuRequestor {
to pboard: NSPasteboard,
types: [NSPasteboard.PasteboardType]
) -> Bool {
// TODO
return false
guard let surface = self.surface else { return false }
// We currently cap the maximum copy size to 1MB. iTerm2 I believe
// caps theirs at 0.1MB (configurable) so this is probably reasonable.
let v = String(unsafeUninitializedCapacity: 1000000) {
Int(ghostty_surface_selection(surface, $0.baseAddress, UInt($0.count)))
}
pboard.declareTypes([.string], owner: nil)
pboard.setString(v, forType: .string)
return true
}
func readSelection(from pboard: NSPasteboard) -> Bool {

View File

@ -865,6 +865,21 @@ fn changeConfig(self: *Surface, config: *const configpkg.Config) !void {
};
}
/// Returns true if the terminal has a selection.
pub fn hasSelection(self: *const Surface) bool {
self.renderer_state.mutex.lock();
defer self.renderer_state.mutex.unlock();
return self.io.terminal.screen.selection != null;
}
/// Returns the selected text. This is allocated.
pub fn selectionString(self: *Surface, alloc: Allocator) !?[]const u8 {
self.renderer_state.mutex.lock();
defer self.renderer_state.mutex.unlock();
const sel = self.io.terminal.screen.selection orelse return null;
return try self.io.terminal.screen.selectionString(alloc, sel, false);
}
/// Returns the pwd of the terminal, if any. This is always copied because
/// the pwd can change at any point from termio. If we are calling from the IO
/// thread you should just check the terminal directly.

View File

@ -1438,6 +1438,30 @@ pub const CAPI = struct {
return surface.core_surface.needsConfirmQuit();
}
/// Returns true if the surface has a selection.
export fn ghostty_surface_has_selection(surface: *Surface) bool {
return surface.core_surface.hasSelection();
}
/// Copies the surface selection text into the provided buffer and
/// returns the copied size. If the buffer is too small, there is no
/// selection, or there is an error, then 0 is returned.
export fn ghostty_surface_selection(surface: *Surface, buf: [*]u8, cap: usize) usize {
const selection_ = surface.core_surface.selectionString(global.alloc) catch |err| {
log.warn("error getting selection err={}", .{err});
return 0;
};
const selection = selection_ orelse return 0;
defer global.alloc.free(selection);
// If the buffer is too small, return no selection.
if (selection.len > cap) return 0;
// Copy into the buffer and return the length
@memcpy(buf[0..selection.len], selection);
return selection.len;
}
/// Copies the surface working directory into the provided buffer and
/// returns the copied size. If the buffer is too small, there is no pwd,
/// or there is an error, then 0 is returned.