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 "term/term.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 term *tt_new_opts(int cols, int rows, const struct term_callbacks *cb)
{
struct term_options opt = {
.size = { .x = cols, .y = rows },
.scrollback = TEST_SCROLLBACK,
};
if (cb)
opt.callbacks = *cb;
return term_new(&opt);
}
static inline struct term *tt_new(int cols, int rows)
{
return tt_new_opts(cols, rows, NULL);
}
static inline void tt_write(struct term *t, const char *s, size_t len)
{
term_feed(t, s, len, 0);
}
static inline void tt_puts(struct term *t, const char *s)
{
term_feed(t, s, strlen(s), 0);
}
static inline int tt_cols(const struct term *t) { return term_size(t).x; }
static inline int tt_rows(const struct term *t) { return term_size(t).y; }
static inline int tt_cursor_x(const struct term *t) { return term_cursor(t).pos.x; }
static inline int tt_cursor_y(const struct term *t) { return term_cursor(t).pos.y; }
static inline int tt_history(const struct term *t) { return term_scrollback(t).total; }
static inline int tt_view_offset(const struct term *t) { return term_scrollback(t).offset; }
/* One snapshot serves every read. It follows whichever terminal a test
* reads last. */
static struct term_snapshot *tt_snapshot_for(struct term *t)
{
static struct term_snapshot *snap;
if (!snap)
snap = term_snapshot_new();
term_snapshot_update(snap, t);
return snap;
}
static const struct term_cell tt_blank_cell = {
.text = ' ', .fg = TERM_COLOR_FG, .bg = TERM_COLOR_BG, .width = 1,
};
/* A cell of the viewport. Out-of-range columns read as a blank cell. */
static inline const struct term_cell *tt_view_cell(struct term *t, int col, int row)
{
struct term_snapshot *s = tt_snapshot_for(t);
const struct term_cell *cells = term_snapshot_row(s, row);
if (!cells)
return NULL;
if (col < 0 || col >= term_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 term_cell *tt_cell(struct term *t, int col, int row)
{
int offset = tt_view_offset(t);
if (offset)
term_scroll_to(t, 0);
const struct term_cell *cell = tt_view_cell(t, col, row);
if (offset)
term_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 term *t, const struct term_cell *cell,
uint32_t *cps, int cap)
{
return term_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 term *t, const struct term_cell *cell)
{
uint32_t cp = 0;
if (!(cell->text & TERM_TEXT_CLUSTER))
return cell->text;
tt_cell_cps(t, cell, &cp, 1);
return cp;
}
static inline bool tt_is_cluster(const struct term_cell *cell)
{
return (cell->text & TERM_TEXT_CLUSTER) != 0;
}
/* The selected columns [begin, end) of a viewport row. */
static inline struct term_span tt_row_selection(struct term *t, int row)
{
return term_snapshot_row_selection(tt_snapshot_for(t), row);
}
static inline bool tt_selected(struct term *t, int col, int row)
{
struct term_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 term *t, int x1, int y1, int x2, int y2)
{
bool forward = y1 < y2 || (y1 == y2 && x1 <= x2);
struct term_vector2i a = { .x = x1, .y = y1 }, b = { .x = x2, .y = y2 };
term_select_start(t, forward ? a : b, false, TERM_SELECT_CELL);
term_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 term *t)
{
return term_selection_text(t);
}
static inline char *tt_history_text(struct term *t, int max_rows)
{
return term_history_text(t, max_rows);
}
/* NULL when there is no link. */
static inline char *tt_link_at(struct term *t, int col, int row)
{
return term_link_at(t, (struct term_vector2i){ .x = col, .y = row });
}
static inline bool tt_resize(struct term *t, int cols, int rows)
{
return term_resize(t, (struct term_vector2i){ .x = cols, .y = rows });
}
/* The ARGB value of a color in the terminal's current palette. */
static inline uint32_t tt_color(struct term *t, term_color c)
{
return term_view_color(term_snapshot_view(tt_snapshot_for(t)), c);
}
#endif
|