libghostty: starting the SelectionGesture API, just init/get
parent
3103ae8838
commit
2f61ba036e
|
|
@ -39,6 +39,17 @@ extern "C" {
|
||||||
* @{
|
* @{
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opaque handle to state for interpreting terminal selection gestures.
|
||||||
|
*
|
||||||
|
* The gesture owns only the state required to interpret pointer events. Calls
|
||||||
|
* that use a gesture are not concurrency-safe and must be serialized with
|
||||||
|
* terminal mutations.
|
||||||
|
*
|
||||||
|
* @ingroup selection
|
||||||
|
*/
|
||||||
|
typedef struct GhosttySelectionGestureImpl* GhosttySelectionGesture;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A snapshot selection range defined by two grid references.
|
* A snapshot selection range defined by two grid references.
|
||||||
*
|
*
|
||||||
|
|
@ -283,6 +294,189 @@ typedef enum GHOSTTY_ENUM_TYPED {
|
||||||
GHOSTTY_SELECTION_ADJUST_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE,
|
GHOSTTY_SELECTION_ADJUST_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE,
|
||||||
} GhosttySelectionAdjust;
|
} GhosttySelectionAdjust;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Selection behavior chosen for a gesture's click sequence.
|
||||||
|
*
|
||||||
|
* @ingroup selection
|
||||||
|
*/
|
||||||
|
typedef enum GHOSTTY_ENUM_TYPED {
|
||||||
|
/** Cell-granular drag selection. */
|
||||||
|
GHOSTTY_SELECTION_GESTURE_BEHAVIOR_CELL = 0,
|
||||||
|
|
||||||
|
/** Word selection on press and word-granular drag selection. */
|
||||||
|
GHOSTTY_SELECTION_GESTURE_BEHAVIOR_WORD = 1,
|
||||||
|
|
||||||
|
/** Line selection on press and line-granular drag selection. */
|
||||||
|
GHOSTTY_SELECTION_GESTURE_BEHAVIOR_LINE = 2,
|
||||||
|
|
||||||
|
/** Semantic command output selection on press and drag. */
|
||||||
|
GHOSTTY_SELECTION_GESTURE_BEHAVIOR_OUTPUT = 3,
|
||||||
|
|
||||||
|
GHOSTTY_SELECTION_GESTURE_BEHAVIOR_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE,
|
||||||
|
} GhosttySelectionGestureBehavior;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Current autoscroll direction for an active selection drag gesture.
|
||||||
|
*
|
||||||
|
* @ingroup selection
|
||||||
|
*/
|
||||||
|
typedef enum GHOSTTY_ENUM_TYPED {
|
||||||
|
/** No selection autoscroll is requested. */
|
||||||
|
GHOSTTY_SELECTION_GESTURE_AUTOSCROLL_NONE = 0,
|
||||||
|
|
||||||
|
/** Selection dragging should autoscroll the viewport upward. */
|
||||||
|
GHOSTTY_SELECTION_GESTURE_AUTOSCROLL_UP = 1,
|
||||||
|
|
||||||
|
/** Selection dragging should autoscroll the viewport downward. */
|
||||||
|
GHOSTTY_SELECTION_GESTURE_AUTOSCROLL_DOWN = 2,
|
||||||
|
|
||||||
|
GHOSTTY_SELECTION_GESTURE_AUTOSCROLL_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE,
|
||||||
|
} GhosttySelectionGestureAutoscroll;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data fields readable from a selection gesture with
|
||||||
|
* ghostty_selection_gesture_get().
|
||||||
|
*
|
||||||
|
* @ingroup selection
|
||||||
|
*/
|
||||||
|
typedef enum GHOSTTY_ENUM_TYPED {
|
||||||
|
/** Current click count: uint8_t*. 0 means inactive. */
|
||||||
|
GHOSTTY_SELECTION_GESTURE_DATA_CLICK_COUNT = 0,
|
||||||
|
|
||||||
|
/** Whether the current/last left-click gesture has dragged: bool*. */
|
||||||
|
GHOSTTY_SELECTION_GESTURE_DATA_DRAGGED = 1,
|
||||||
|
|
||||||
|
/** Current autoscroll request: GhosttySelectionGestureAutoscroll*. */
|
||||||
|
GHOSTTY_SELECTION_GESTURE_DATA_AUTOSCROLL = 2,
|
||||||
|
|
||||||
|
/** Current gesture behavior: GhosttySelectionGestureBehavior*. */
|
||||||
|
GHOSTTY_SELECTION_GESTURE_DATA_BEHAVIOR = 3,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Current left-click anchor: GhosttyGridRef*.
|
||||||
|
*
|
||||||
|
* Returns GHOSTTY_NO_VALUE if there is no valid active anchor. On success,
|
||||||
|
* writes an untracked GhosttyGridRef snapshot with normal GhosttyGridRef
|
||||||
|
* lifetime rules.
|
||||||
|
*/
|
||||||
|
GHOSTTY_SELECTION_GESTURE_DATA_ANCHOR = 4,
|
||||||
|
|
||||||
|
GHOSTTY_SELECTION_GESTURE_DATA_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE,
|
||||||
|
} GhosttySelectionGestureData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a selection gesture object.
|
||||||
|
*
|
||||||
|
* The gesture stores mutable state for terminal text selection gestures. The
|
||||||
|
* gesture is not bound to a terminal at creation time; terminal-dependent APIs
|
||||||
|
* take the terminal explicitly.
|
||||||
|
*
|
||||||
|
* @param allocator Allocator, or NULL for the default allocator
|
||||||
|
* @param out_gesture Receives the created gesture handle
|
||||||
|
* @return GHOSTTY_SUCCESS on success, GHOSTTY_INVALID_VALUE if out_gesture is
|
||||||
|
* NULL, or GHOSTTY_OUT_OF_MEMORY if allocation fails
|
||||||
|
*
|
||||||
|
* @ingroup selection
|
||||||
|
*/
|
||||||
|
GHOSTTY_API GhosttyResult ghostty_selection_gesture_new(
|
||||||
|
const GhosttyAllocator* allocator,
|
||||||
|
GhosttySelectionGesture* out_gesture);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Free a selection gesture object.
|
||||||
|
*
|
||||||
|
* This releases any tracked terminal references owned by the gesture using the
|
||||||
|
* provided terminal, then frees the gesture object. Passing NULL for gesture is
|
||||||
|
* allowed and is a no-op.
|
||||||
|
*
|
||||||
|
* If the terminal is still alive, pass the terminal most recently used with the
|
||||||
|
* gesture so any tracked terminal references can be released correctly. If the
|
||||||
|
* terminal has already been freed, pass NULL for terminal; the terminal's page
|
||||||
|
* storage has already released the underlying tracked references, so the
|
||||||
|
* gesture wrapper can be safely discarded without touching the stale terminal
|
||||||
|
* state.
|
||||||
|
*
|
||||||
|
* @param gesture Selection gesture handle to free
|
||||||
|
* @param terminal Terminal used to release tracked gesture state, or NULL if
|
||||||
|
* the terminal has already been freed
|
||||||
|
*
|
||||||
|
* @ingroup selection
|
||||||
|
*/
|
||||||
|
GHOSTTY_API void ghostty_selection_gesture_free(
|
||||||
|
GhosttySelectionGesture gesture,
|
||||||
|
GhosttyTerminal terminal);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset any active selection gesture state.
|
||||||
|
*
|
||||||
|
* This cancels the active click sequence and releases any tracked terminal
|
||||||
|
* references owned by the gesture without freeing the gesture object.
|
||||||
|
* Passing NULL is allowed and is a no-op.
|
||||||
|
*
|
||||||
|
* @param gesture Selection gesture handle to reset
|
||||||
|
* @param terminal Terminal used to release tracked gesture state
|
||||||
|
*
|
||||||
|
* @ingroup selection
|
||||||
|
*/
|
||||||
|
GHOSTTY_API void ghostty_selection_gesture_reset(
|
||||||
|
GhosttySelectionGesture gesture,
|
||||||
|
GhosttyTerminal terminal);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read data from a selection gesture.
|
||||||
|
*
|
||||||
|
* The type of value depends on data and is documented by
|
||||||
|
* GhosttySelectionGestureData. For GHOSTTY_SELECTION_GESTURE_DATA_ANCHOR,
|
||||||
|
* the returned GhosttyGridRef is an untracked snapshot with normal grid-ref
|
||||||
|
* lifetime rules.
|
||||||
|
*
|
||||||
|
* @param gesture Selection gesture handle (NULL returns GHOSTTY_INVALID_VALUE)
|
||||||
|
* @param terminal Terminal used to validate terminal-backed gesture state
|
||||||
|
* @param data Data field to read
|
||||||
|
* @param value Output pointer whose type depends on data
|
||||||
|
* @return GHOSTTY_SUCCESS on success, GHOSTTY_NO_VALUE if the requested data
|
||||||
|
* has no value, or GHOSTTY_INVALID_VALUE if gesture, terminal, data, or
|
||||||
|
* value is invalid
|
||||||
|
*
|
||||||
|
* @ingroup selection
|
||||||
|
*/
|
||||||
|
GHOSTTY_API GhosttyResult ghostty_selection_gesture_get(
|
||||||
|
GhosttySelectionGesture gesture,
|
||||||
|
GhosttyTerminal terminal,
|
||||||
|
GhosttySelectionGestureData data,
|
||||||
|
void* value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read multiple data fields from a selection gesture in a single call.
|
||||||
|
*
|
||||||
|
* This is an optimization over calling ghostty_selection_gesture_get() multiple
|
||||||
|
* times. Each entry in values must point to storage of the type documented by
|
||||||
|
* the corresponding GhosttySelectionGestureData key.
|
||||||
|
*
|
||||||
|
* If any individual read fails, the function returns that error and writes the
|
||||||
|
* index of the failing key to out_written when out_written is non-NULL. On
|
||||||
|
* success, out_written receives count when non-NULL.
|
||||||
|
*
|
||||||
|
* @param gesture Selection gesture handle (NULL returns GHOSTTY_INVALID_VALUE)
|
||||||
|
* @param terminal Terminal used to validate terminal-backed gesture state
|
||||||
|
* @param count Number of data fields to read
|
||||||
|
* @param keys Data fields to read (must not be NULL)
|
||||||
|
* @param values Output pointers corresponding to keys (must not be NULL)
|
||||||
|
* @param out_written Optional number of fields read, or failing index on error
|
||||||
|
* @return GHOSTTY_SUCCESS on success, GHOSTTY_NO_VALUE if a requested data
|
||||||
|
* field has no value, or GHOSTTY_INVALID_VALUE if gesture, terminal,
|
||||||
|
* keys, values, or a value pointer is invalid
|
||||||
|
*
|
||||||
|
* @ingroup selection
|
||||||
|
*/
|
||||||
|
GHOSTTY_API GhosttyResult ghostty_selection_gesture_get_multi(
|
||||||
|
GhosttySelectionGesture gesture,
|
||||||
|
GhosttyTerminal terminal,
|
||||||
|
size_t count,
|
||||||
|
const GhosttySelectionGestureData* keys,
|
||||||
|
void** values,
|
||||||
|
size_t* out_written);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Derive a word selection snapshot from a terminal grid reference.
|
* Derive a word selection snapshot from a terminal grid reference.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -251,6 +251,11 @@ comptime {
|
||||||
@export(&c.terminal_selection_ordered, .{ .name = "ghostty_terminal_selection_ordered" });
|
@export(&c.terminal_selection_ordered, .{ .name = "ghostty_terminal_selection_ordered" });
|
||||||
@export(&c.terminal_selection_contains, .{ .name = "ghostty_terminal_selection_contains" });
|
@export(&c.terminal_selection_contains, .{ .name = "ghostty_terminal_selection_contains" });
|
||||||
@export(&c.terminal_selection_equal, .{ .name = "ghostty_terminal_selection_equal" });
|
@export(&c.terminal_selection_equal, .{ .name = "ghostty_terminal_selection_equal" });
|
||||||
|
@export(&c.selection_gesture_new, .{ .name = "ghostty_selection_gesture_new" });
|
||||||
|
@export(&c.selection_gesture_free, .{ .name = "ghostty_selection_gesture_free" });
|
||||||
|
@export(&c.selection_gesture_reset, .{ .name = "ghostty_selection_gesture_reset" });
|
||||||
|
@export(&c.selection_gesture_get, .{ .name = "ghostty_selection_gesture_get" });
|
||||||
|
@export(&c.selection_gesture_get_multi, .{ .name = "ghostty_selection_gesture_get_multi" });
|
||||||
@export(&c.terminal_grid_ref, .{ .name = "ghostty_terminal_grid_ref" });
|
@export(&c.terminal_grid_ref, .{ .name = "ghostty_terminal_grid_ref" });
|
||||||
@export(&c.terminal_grid_ref_track, .{ .name = "ghostty_terminal_grid_ref_track" });
|
@export(&c.terminal_grid_ref_track, .{ .name = "ghostty_terminal_grid_ref_track" });
|
||||||
@export(&c.terminal_point_from_grid_ref, .{ .name = "ghostty_terminal_point_from_grid_ref" });
|
@export(&c.terminal_point_from_grid_ref, .{ .name = "ghostty_terminal_point_from_grid_ref" });
|
||||||
|
|
|
||||||
|
|
@ -70,6 +70,7 @@ const std = @import("std");
|
||||||
const assert = std.debug.assert;
|
const assert = std.debug.assert;
|
||||||
const testing = std.testing;
|
const testing = std.testing;
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
|
const lib = @import("lib.zig");
|
||||||
const PageList = @import("PageList.zig");
|
const PageList = @import("PageList.zig");
|
||||||
const Pin = PageList.Pin;
|
const Pin = PageList.Pin;
|
||||||
const Screen = @import("Screen.zig");
|
const Screen = @import("Screen.zig");
|
||||||
|
|
@ -118,22 +119,26 @@ left_drag_autoscroll: Autoscroll,
|
||||||
///
|
///
|
||||||
/// This is used to implement selection above/below the viewport that
|
/// This is used to implement selection above/below the viewport that
|
||||||
/// wants to drag the viewport.
|
/// wants to drag the viewport.
|
||||||
pub const Autoscroll = enum { none, up, down };
|
pub const Autoscroll = lib.Enum(lib.target, &.{
|
||||||
|
"none",
|
||||||
|
"up",
|
||||||
|
"down",
|
||||||
|
});
|
||||||
|
|
||||||
/// The selection behavior for a click and subsequent drag.
|
/// The selection behavior for a click and subsequent drag.
|
||||||
pub const Behavior = enum {
|
pub const Behavior = lib.Enum(lib.target, &.{
|
||||||
/// Cell-granular drag selection. Press returns null to clear selection.
|
// Cell-granular drag selection. Press returns null to clear selection.
|
||||||
cell,
|
"cell",
|
||||||
|
|
||||||
/// Word selection on press and word-granular drag selection.
|
// Word selection on press and word-granular drag selection.
|
||||||
word,
|
"word",
|
||||||
|
|
||||||
/// Line selection on press and line-granular drag selection.
|
// Line selection on press and line-granular drag selection.
|
||||||
line,
|
"line",
|
||||||
|
|
||||||
/// Semantic command output selection on press and drag.
|
// Semantic command output selection on press and drag.
|
||||||
output,
|
"output",
|
||||||
};
|
});
|
||||||
|
|
||||||
/// Standard terminal selection behavior for single-, double-, and triple-clicks.
|
/// Standard terminal selection behavior for single-, double-, and triple-clicks.
|
||||||
///
|
///
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ pub const modes = @import("modes.zig");
|
||||||
pub const osc = @import("osc.zig");
|
pub const osc = @import("osc.zig");
|
||||||
pub const render = @import("render.zig");
|
pub const render = @import("render.zig");
|
||||||
pub const selection = @import("selection.zig");
|
pub const selection = @import("selection.zig");
|
||||||
|
pub const selection_gesture = @import("selection_gesture.zig");
|
||||||
pub const key_event = @import("key_event.zig");
|
pub const key_event = @import("key_event.zig");
|
||||||
pub const key_encode = @import("key_encode.zig");
|
pub const key_encode = @import("key_encode.zig");
|
||||||
pub const mouse_event = @import("mouse_event.zig");
|
pub const mouse_event = @import("mouse_event.zig");
|
||||||
|
|
@ -182,6 +183,11 @@ pub const terminal_selection_order = selection.order;
|
||||||
pub const terminal_selection_ordered = selection.ordered;
|
pub const terminal_selection_ordered = selection.ordered;
|
||||||
pub const terminal_selection_contains = selection.contains;
|
pub const terminal_selection_contains = selection.contains;
|
||||||
pub const terminal_selection_equal = selection.equal;
|
pub const terminal_selection_equal = selection.equal;
|
||||||
|
pub const selection_gesture_new = selection_gesture.new;
|
||||||
|
pub const selection_gesture_free = selection_gesture.free;
|
||||||
|
pub const selection_gesture_reset = selection_gesture.reset;
|
||||||
|
pub const selection_gesture_get = selection_gesture.get;
|
||||||
|
pub const selection_gesture_get_multi = selection_gesture.get_multi;
|
||||||
pub const terminal_grid_ref = terminal.grid_ref;
|
pub const terminal_grid_ref = terminal.grid_ref;
|
||||||
pub const terminal_grid_ref_track = terminal.grid_ref_track;
|
pub const terminal_grid_ref_track = terminal.grid_ref_track;
|
||||||
pub const terminal_point_from_grid_ref = terminal.point_from_grid_ref;
|
pub const terminal_point_from_grid_ref = terminal.point_from_grid_ref;
|
||||||
|
|
@ -214,6 +220,7 @@ test {
|
||||||
_ = osc;
|
_ = osc;
|
||||||
_ = render;
|
_ = render;
|
||||||
_ = selection;
|
_ = selection;
|
||||||
|
_ = selection_gesture;
|
||||||
_ = key_event;
|
_ = key_event;
|
||||||
_ = key_encode;
|
_ = key_encode;
|
||||||
_ = mouse_event;
|
_ = mouse_event;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,272 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const testing = std.testing;
|
||||||
|
const lib = @import("../lib.zig");
|
||||||
|
const CAllocator = lib.alloc.Allocator;
|
||||||
|
const SelectionGesture = @import("../SelectionGesture.zig");
|
||||||
|
const grid_ref = @import("grid_ref.zig");
|
||||||
|
const terminal_c = @import("terminal.zig");
|
||||||
|
const Result = @import("result.zig").Result;
|
||||||
|
|
||||||
|
const log = std.log.scoped(.selection_gesture_c);
|
||||||
|
|
||||||
|
/// C: GhosttySelectionGesture
|
||||||
|
pub const Gesture = ?*GestureWrapper;
|
||||||
|
|
||||||
|
const GestureWrapper = struct {
|
||||||
|
alloc: std.mem.Allocator,
|
||||||
|
gesture: SelectionGesture = .init,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// C: GhosttySelectionGestureBehavior
|
||||||
|
pub const Behavior = SelectionGesture.Behavior;
|
||||||
|
|
||||||
|
/// C: GhosttySelectionGestureAutoscroll
|
||||||
|
pub const Autoscroll = SelectionGesture.Autoscroll;
|
||||||
|
|
||||||
|
/// C: GhosttySelectionGestureData
|
||||||
|
pub const Data = enum(c_int) {
|
||||||
|
click_count = 0,
|
||||||
|
dragged = 1,
|
||||||
|
autoscroll = 2,
|
||||||
|
behavior = 3,
|
||||||
|
anchor = 4,
|
||||||
|
|
||||||
|
pub fn OutType(comptime self: Data) type {
|
||||||
|
return switch (self) {
|
||||||
|
.click_count => u8,
|
||||||
|
.dragged => bool,
|
||||||
|
.autoscroll => Autoscroll,
|
||||||
|
.behavior => Behavior,
|
||||||
|
.anchor => grid_ref.CGridRef,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn new(
|
||||||
|
alloc_: ?*const CAllocator,
|
||||||
|
out_gesture: ?*Gesture,
|
||||||
|
) callconv(lib.calling_conv) Result {
|
||||||
|
const out = out_gesture orelse return .invalid_value;
|
||||||
|
|
||||||
|
const alloc = lib.alloc.default(alloc_);
|
||||||
|
const gesture = alloc.create(GestureWrapper) catch {
|
||||||
|
out.* = null;
|
||||||
|
return .out_of_memory;
|
||||||
|
};
|
||||||
|
gesture.* = .{
|
||||||
|
.alloc = alloc,
|
||||||
|
};
|
||||||
|
out.* = gesture;
|
||||||
|
return .success;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn free(
|
||||||
|
gesture_: Gesture,
|
||||||
|
terminal: terminal_c.Terminal,
|
||||||
|
) callconv(lib.calling_conv) void {
|
||||||
|
const wrapper = gesture_ orelse return;
|
||||||
|
if (terminal_c.zigTerminal(terminal)) |t| {
|
||||||
|
wrapper.gesture.deinit(t);
|
||||||
|
}
|
||||||
|
const alloc = wrapper.alloc;
|
||||||
|
alloc.destroy(wrapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reset(
|
||||||
|
gesture_: Gesture,
|
||||||
|
terminal: terminal_c.Terminal,
|
||||||
|
) callconv(lib.calling_conv) void {
|
||||||
|
const wrapper = gesture_ orelse return;
|
||||||
|
const t = terminal_c.zigTerminal(terminal) orelse return;
|
||||||
|
wrapper.gesture.reset(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(
|
||||||
|
gesture_: Gesture,
|
||||||
|
terminal: terminal_c.Terminal,
|
||||||
|
data: Data,
|
||||||
|
out: ?*anyopaque,
|
||||||
|
) callconv(lib.calling_conv) Result {
|
||||||
|
if (comptime std.debug.runtime_safety) {
|
||||||
|
_ = std.meta.intToEnum(Data, @intFromEnum(data)) catch {
|
||||||
|
log.warn("selection_gesture_get invalid data value={d}", .{@intFromEnum(data)});
|
||||||
|
return .invalid_value;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const out_ptr = out orelse return .invalid_value;
|
||||||
|
return switch (data) {
|
||||||
|
inline else => |comptime_data| getTyped(
|
||||||
|
gesture_,
|
||||||
|
terminal,
|
||||||
|
comptime_data,
|
||||||
|
@ptrCast(@alignCast(out_ptr)),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_multi(
|
||||||
|
gesture_: Gesture,
|
||||||
|
terminal: terminal_c.Terminal,
|
||||||
|
count: usize,
|
||||||
|
keys: ?[*]const Data,
|
||||||
|
values: ?[*]?*anyopaque,
|
||||||
|
out_written: ?*usize,
|
||||||
|
) callconv(lib.calling_conv) Result {
|
||||||
|
const k = keys orelse return .invalid_value;
|
||||||
|
const v = values orelse return .invalid_value;
|
||||||
|
|
||||||
|
for (0..count) |i| {
|
||||||
|
const result = get(gesture_, terminal, k[i], v[i]);
|
||||||
|
if (result != .success) {
|
||||||
|
if (out_written) |w| w.* = i;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (out_written) |w| w.* = count;
|
||||||
|
return .success;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn getTyped(
|
||||||
|
gesture_: Gesture,
|
||||||
|
terminal: terminal_c.Terminal,
|
||||||
|
comptime data: Data,
|
||||||
|
out: *data.OutType(),
|
||||||
|
) Result {
|
||||||
|
const wrapper = gesture_ orelse return .invalid_value;
|
||||||
|
const t = terminal_c.zigTerminal(terminal) orelse return .invalid_value;
|
||||||
|
|
||||||
|
switch (data) {
|
||||||
|
.click_count => out.* = wrapper.gesture.left_click_count,
|
||||||
|
.dragged => out.* = wrapper.gesture.left_click_dragged,
|
||||||
|
.autoscroll => out.* = wrapper.gesture.left_drag_autoscroll,
|
||||||
|
.behavior => out.* = wrapper.gesture.left_click_behavior,
|
||||||
|
.anchor => {
|
||||||
|
const pin = wrapper.gesture.validatedLeftClickPin(&t.screens) orelse
|
||||||
|
return .no_value;
|
||||||
|
out.* = .fromPin(pin.*);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return .success;
|
||||||
|
}
|
||||||
|
|
||||||
|
test "selection gesture lifecycle and get" {
|
||||||
|
var terminal: terminal_c.Terminal = null;
|
||||||
|
try testing.expectEqual(Result.success, terminal_c.new(
|
||||||
|
&lib.alloc.test_allocator,
|
||||||
|
&terminal,
|
||||||
|
.{ .cols = 5, .rows = 2, .max_scrollback = 10_000 },
|
||||||
|
));
|
||||||
|
defer terminal_c.free(terminal);
|
||||||
|
|
||||||
|
var gesture: Gesture = null;
|
||||||
|
try testing.expectEqual(Result.success, new(
|
||||||
|
&lib.alloc.test_allocator,
|
||||||
|
&gesture,
|
||||||
|
));
|
||||||
|
defer free(gesture, terminal);
|
||||||
|
|
||||||
|
var click_count: u8 = 255;
|
||||||
|
try testing.expectEqual(Result.success, get(gesture, terminal, .click_count, &click_count));
|
||||||
|
try testing.expectEqual(@as(u8, 0), click_count);
|
||||||
|
|
||||||
|
var dragged = true;
|
||||||
|
try testing.expectEqual(Result.success, get(gesture, terminal, .dragged, &dragged));
|
||||||
|
try testing.expect(!dragged);
|
||||||
|
|
||||||
|
var autoscroll: Autoscroll = .up;
|
||||||
|
try testing.expectEqual(Result.success, get(gesture, terminal, .autoscroll, &autoscroll));
|
||||||
|
try testing.expectEqual(Autoscroll.none, autoscroll);
|
||||||
|
|
||||||
|
var behavior: Behavior = .word;
|
||||||
|
try testing.expectEqual(Result.success, get(gesture, terminal, .behavior, &behavior));
|
||||||
|
try testing.expectEqual(Behavior.cell, behavior);
|
||||||
|
|
||||||
|
var anchor: grid_ref.CGridRef = undefined;
|
||||||
|
try testing.expectEqual(Result.no_value, get(gesture, terminal, .anchor, &anchor));
|
||||||
|
}
|
||||||
|
|
||||||
|
test "selection gesture get_multi" {
|
||||||
|
var terminal: terminal_c.Terminal = null;
|
||||||
|
try testing.expectEqual(Result.success, terminal_c.new(
|
||||||
|
&lib.alloc.test_allocator,
|
||||||
|
&terminal,
|
||||||
|
.{ .cols = 5, .rows = 2, .max_scrollback = 10_000 },
|
||||||
|
));
|
||||||
|
defer terminal_c.free(terminal);
|
||||||
|
|
||||||
|
var gesture: Gesture = null;
|
||||||
|
try testing.expectEqual(Result.success, new(
|
||||||
|
&lib.alloc.test_allocator,
|
||||||
|
&gesture,
|
||||||
|
));
|
||||||
|
defer free(gesture, terminal);
|
||||||
|
|
||||||
|
const keys = [_]Data{ .click_count, .dragged, .autoscroll, .behavior };
|
||||||
|
var click_count: u8 = 255;
|
||||||
|
var dragged = true;
|
||||||
|
var autoscroll: Autoscroll = .up;
|
||||||
|
var behavior: Behavior = .word;
|
||||||
|
var values = [_]?*anyopaque{
|
||||||
|
&click_count,
|
||||||
|
&dragged,
|
||||||
|
&autoscroll,
|
||||||
|
&behavior,
|
||||||
|
};
|
||||||
|
var written: usize = 0;
|
||||||
|
|
||||||
|
try testing.expectEqual(Result.success, get_multi(
|
||||||
|
gesture,
|
||||||
|
terminal,
|
||||||
|
keys.len,
|
||||||
|
&keys,
|
||||||
|
&values,
|
||||||
|
&written,
|
||||||
|
));
|
||||||
|
try testing.expectEqual(keys.len, written);
|
||||||
|
try testing.expectEqual(@as(u8, 0), click_count);
|
||||||
|
try testing.expect(!dragged);
|
||||||
|
try testing.expectEqual(Autoscroll.none, autoscroll);
|
||||||
|
try testing.expectEqual(Behavior.cell, behavior);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "selection gesture get_multi returns first failing index" {
|
||||||
|
var terminal: terminal_c.Terminal = null;
|
||||||
|
try testing.expectEqual(Result.success, terminal_c.new(
|
||||||
|
&lib.alloc.test_allocator,
|
||||||
|
&terminal,
|
||||||
|
.{ .cols = 5, .rows = 2, .max_scrollback = 10_000 },
|
||||||
|
));
|
||||||
|
defer terminal_c.free(terminal);
|
||||||
|
|
||||||
|
var gesture: Gesture = null;
|
||||||
|
try testing.expectEqual(Result.success, new(
|
||||||
|
&lib.alloc.test_allocator,
|
||||||
|
&gesture,
|
||||||
|
));
|
||||||
|
defer free(gesture, terminal);
|
||||||
|
|
||||||
|
const keys = [_]Data{ .click_count, .anchor, .dragged };
|
||||||
|
var click_count: u8 = 255;
|
||||||
|
var anchor: grid_ref.CGridRef = undefined;
|
||||||
|
var dragged = true;
|
||||||
|
var values = [_]?*anyopaque{ &click_count, &anchor, &dragged };
|
||||||
|
var written: usize = 0;
|
||||||
|
|
||||||
|
try testing.expectEqual(Result.no_value, get_multi(
|
||||||
|
gesture,
|
||||||
|
terminal,
|
||||||
|
keys.len,
|
||||||
|
&keys,
|
||||||
|
&values,
|
||||||
|
&written,
|
||||||
|
));
|
||||||
|
try testing.expectEqual(@as(usize, 1), written);
|
||||||
|
try testing.expectEqual(@as(u8, 0), click_count);
|
||||||
|
try testing.expect(dragged);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "selection gesture free null" {
|
||||||
|
free(null, null);
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue