1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
|
#ifndef WSAP_TEST_TERM_API_H
#define WSAP_TEST_TERM_API_H
/*
* Short forms of the public terminal API for tests. Cell reads go through
* a snapshot, the same way a renderer reads, so tests check only what the
* terminal shows.
*/
#include "wsap_vtcore/wsap_vtcore.h"
#include <stdbool.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
/* The history size the tests assume. */
#define TEST_SCROLLBACK 4096
static inline struct wsap_vtcore *tt_new_opts(int cols, int rows, const struct wsap_vtcore_callbacks *cb)
{
struct wsap_vtcore_options opt = {
.size = { .x = cols, .y = rows },
.scrollback = TEST_SCROLLBACK,
};
if (cb)
opt.callbacks = *cb;
return wsap_vtcore_new(&opt);
}
static inline struct wsap_vtcore *tt_new(int cols, int rows)
{
return tt_new_opts(cols, rows, NULL);
}
static inline void tt_write(struct wsap_vtcore *t, const char *s, size_t len)
{
wsap_vtcore_feed(t, s, len, 0);
}
static inline void tt_puts(struct wsap_vtcore *t, const char *s)
{
wsap_vtcore_feed(t, s, strlen(s), 0);
}
static inline int tt_cols(const struct wsap_vtcore *t) { return wsap_vtcore_size(t).x; }
static inline int tt_rows(const struct wsap_vtcore *t) { return wsap_vtcore_size(t).y; }
static inline int tt_cursor_x(const struct wsap_vtcore *t) { return wsap_vtcore_cursor(t).pos.x; }
static inline int tt_cursor_y(const struct wsap_vtcore *t) { return wsap_vtcore_cursor(t).pos.y; }
static inline int tt_history(const struct wsap_vtcore *t) { return wsap_vtcore_scrollback(t).total; }
static inline int tt_view_offset(const struct wsap_vtcore *t) { return wsap_vtcore_scrollback(t).offset; }
/* One snapshot serves every read. It follows whichever terminal a test
* reads last. */
static struct wsap_vtcore_snapshot *tt_snapshot_for(struct wsap_vtcore *t)
{
static struct wsap_vtcore_snapshot *snap;
if (!snap)
snap = wsap_vtcore_snapshot_new();
wsap_vtcore_snapshot_update(snap, t);
return snap;
}
static const struct wsap_vtcore_cell tt_blank_cell = {
.text = ' ', .fg = WSAP_VTCORE_COLOR_FG, .bg = WSAP_VTCORE_COLOR_BG, .width = 1,
};
/* A cell of the viewport. Out-of-range columns read as a blank cell. */
static inline const struct wsap_vtcore_cell *tt_view_cell(struct wsap_vtcore *t, int col, int row)
{
struct wsap_vtcore_snapshot *s = tt_snapshot_for(t);
const struct wsap_vtcore_cell *cells = wsap_vtcore_snapshot_row(s, row);
if (!cells)
return NULL;
if (col < 0 || col >= wsap_vtcore_snapshot_view(s)->size.x)
return &tt_blank_cell;
return &cells[col];
}
/* A cell of the live screen, whatever the viewport shows. */
static inline const struct wsap_vtcore_cell *tt_cell(struct wsap_vtcore *t, int col, int row)
{
int offset = tt_view_offset(t);
if (offset)
wsap_vtcore_scroll_to(t, 0);
const struct wsap_vtcore_cell *cell = tt_view_cell(t, col, row);
if (offset)
wsap_vtcore_scroll_to(t, offset);
return cell;
}
/* The code points of a cell's text: one, or a whole cluster. The snapshot
* that the last read used must still describe the terminal. */
static inline int tt_cell_cps(struct wsap_vtcore *t, const struct wsap_vtcore_cell *cell,
uint32_t *cps, int cap)
{
return wsap_vtcore_snapshot_cluster(tt_snapshot_for(t), cell->text, cps, cap);
}
/* The first code point of a cell's text. */
static inline uint32_t tt_cell_cp(struct wsap_vtcore *t, const struct wsap_vtcore_cell *cell)
{
uint32_t cp = 0;
if (!(cell->text & WSAP_VTCORE_TEXT_CLUSTER))
return cell->text;
tt_cell_cps(t, cell, &cp, 1);
return cp;
}
static inline bool tt_is_cluster(const struct wsap_vtcore_cell *cell)
{
return (cell->text & WSAP_VTCORE_TEXT_CLUSTER) != 0;
}
/* The selected columns [begin, end) of a viewport row. */
static inline struct wsap_vtcore_span tt_row_selection(struct wsap_vtcore *t, int row)
{
return wsap_vtcore_snapshot_row_selection(tt_snapshot_for(t), row);
}
static inline bool tt_selected(struct wsap_vtcore *t, int col, int row)
{
struct wsap_vtcore_span span = tt_row_selection(t, row);
return col >= span.begin && col < span.end;
}
/* Select from one viewport cell to another, both inclusive. */
static inline void tt_select(struct wsap_vtcore *t, int x1, int y1, int x2, int y2)
{
bool forward = y1 < y2 || (y1 == y2 && x1 <= x2);
struct wsap_vtcore_vector2i a = { .x = x1, .y = y1 }, b = { .x = x2, .y = y2 };
wsap_vtcore_select_start(t, forward ? a : b, false, WSAP_VTCORE_SELECT_CELL);
wsap_vtcore_select_extend(t, forward ? b : a, true);
}
/* The helpers below return new strings. The caller frees them. */
/* NULL when nothing is selected. */
static inline char *tt_selection_text(const struct wsap_vtcore *t)
{
return wsap_vtcore_selection_text(t);
}
static inline char *tt_history_text(struct wsap_vtcore *t, int max_rows)
{
return wsap_vtcore_history_text(t, max_rows);
}
/* NULL when there is no link. */
static inline char *tt_link_at(struct wsap_vtcore *t, int col, int row)
{
return wsap_vtcore_link_at(t, (struct wsap_vtcore_vector2i){ .x = col, .y = row });
}
static inline bool tt_resize(struct wsap_vtcore *t, int cols, int rows)
{
return wsap_vtcore_resize(t, (struct wsap_vtcore_vector2i){ .x = cols, .y = rows });
}
/* The ARGB value of a color in the terminal's current palette. */
static inline uint32_t tt_color(struct wsap_vtcore *t, wsap_vtcore_color c)
{
return wsap_vtcore_view_color(wsap_vtcore_snapshot_view(tt_snapshot_for(t)), c);
}
#endif
|