#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 #include #include #include /* 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) { size_t n = term_selection_text(t, NULL, 0); if (!n) return NULL; char *text = malloc(n + 1); if (text) term_selection_text(t, text, n + 1); return text; } static inline char *tt_history_text(struct term *t, int max_rows) { size_t n = term_history_text(t, max_rows, NULL, 0); char *text = malloc(n + 1); if (text) term_history_text(t, max_rows, text, n + 1); return text; } /* NULL when there is no link. */ static inline char *tt_link_at(struct term *t, int col, int row) { struct term_vector2i cell = { .x = col, .y = row }; size_t n = term_link_at(t, cell, NULL, 0); if (!n) return NULL; char *url = malloc(n + 1); if (url) term_link_at(t, cell, url, n + 1); return url; } 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