Reduce data sent to GPU per draw by 30%
Split up the Cell structure into a CPUCell and a GPUCell. Only the GPUCell part needs to be sent to the GPU. Should make kitty use even less system resources and make a performance difference on systems where the GPU bandwidth is constrained. Also allows adding more CPU only data in the future without affecting GPU bandwidth. For example, hyperlinks or more combining characters.
This commit is contained in:
parent
c8756612f7
commit
8dea5b3e3e
@ -178,7 +178,7 @@ find_substitute_face(CFStringRef str, CTFontRef old_font) {
|
||||
}
|
||||
|
||||
PyObject*
|
||||
create_fallback_face(PyObject *base_face, Cell* cell, bool UNUSED bold, bool UNUSED italic, bool emoji_presentation, FONTS_DATA_HANDLE fg UNUSED) {
|
||||
create_fallback_face(PyObject *base_face, CPUCell* cell, bool UNUSED bold, bool UNUSED italic, bool emoji_presentation, FONTS_DATA_HANDLE fg UNUSED) {
|
||||
CTFace *self = (CTFace*)base_face;
|
||||
CTFontRef new_font;
|
||||
if (emoji_presentation) new_font = CTFontCreateWithName((CFStringRef)@"AppleColorEmoji", self->scaled_point_sz, NULL);
|
||||
|
||||
@ -136,7 +136,7 @@ END_ALLOW_CASE_RANGE
|
||||
}
|
||||
|
||||
void
|
||||
apply_sgr_to_cells(Cell *first_cell, unsigned int cell_count, unsigned int *params, unsigned int count) {
|
||||
apply_sgr_to_cells(GPUCell *first_cell, unsigned int cell_count, unsigned int *params, unsigned int count) {
|
||||
#define RANGE for(unsigned c = 0; c < cell_count; c++, cell++)
|
||||
#define SET(shift) RANGE { cell->attrs |= (1 << shift); } break;
|
||||
#define RESET(shift) RANGE { cell->attrs &= ~(1 << shift); } break;
|
||||
@ -148,7 +148,7 @@ apply_sgr_to_cells(Cell *first_cell, unsigned int cell_count, unsigned int *para
|
||||
unsigned int i = 0, attr;
|
||||
if (!count) { params[0] = 0; count = 1; }
|
||||
while (i < count) {
|
||||
Cell *cell = first_cell;
|
||||
GPUCell *cell = first_cell;
|
||||
attr = params[i++];
|
||||
switch(attr) {
|
||||
case 0:
|
||||
|
||||
@ -80,7 +80,7 @@ typedef enum MouseShapes { BEAM, HAND, ARROW } MouseShape;
|
||||
(c)->reverse = (a >> REVERSE_SHIFT) & 1; (c)->strikethrough = (a >> STRIKE_SHIFT) & 1; (c)->dim = (a >> DIM_SHIFT) & 1;
|
||||
|
||||
#define COPY_CELL(src, s, dest, d) \
|
||||
(dest)->cells[d] = (src)->cells[s];
|
||||
(dest)->cpu_cells[d] = (src)->cpu_cells[s]; (dest)->gpu_cells[d] = (src)->gpu_cells[s];
|
||||
|
||||
#define COPY_SELF_CELL(s, d) COPY_CELL(self, s, self, d)
|
||||
|
||||
@ -140,15 +140,19 @@ typedef struct {
|
||||
color_type fg, bg, decoration_fg;
|
||||
sprite_index sprite_x, sprite_y, sprite_z;
|
||||
attrs_type attrs;
|
||||
// The following are only needed on the CPU, not the GPU
|
||||
} GPUCell;
|
||||
|
||||
typedef struct {
|
||||
char_type ch;
|
||||
combining_type cc_idx[2];
|
||||
} Cell;
|
||||
} CPUCell;
|
||||
|
||||
|
||||
typedef struct {
|
||||
PyObject_HEAD
|
||||
|
||||
Cell *cells;
|
||||
GPUCell *gpu_cells;
|
||||
CPUCell *cpu_cells;
|
||||
index_type xnum, ynum;
|
||||
bool continued, needs_free, has_dirty_text;
|
||||
} Line;
|
||||
@ -157,14 +161,16 @@ typedef struct {
|
||||
typedef struct {
|
||||
PyObject_HEAD
|
||||
|
||||
Cell *buf;
|
||||
GPUCell *gpu_cell_buf;
|
||||
CPUCell *cpu_cell_buf;
|
||||
index_type xnum, ynum, *line_map, *scratch;
|
||||
line_attrs_type *line_attrs;
|
||||
Line *line;
|
||||
} LineBuf;
|
||||
|
||||
typedef struct {
|
||||
Cell *cells;
|
||||
GPUCell *gpu_cells;
|
||||
CPUCell *cpu_cells;
|
||||
line_attrs_type *line_attrs;
|
||||
} HistoryBufSegment;
|
||||
|
||||
@ -226,10 +232,10 @@ typedef struct {FONTS_DATA_HEAD} *FONTS_DATA_HANDLE;
|
||||
for(index_type __i__ = (at); __i__ < (line)->xnum - (num); __i__++) { \
|
||||
COPY_CELL(line, __i__ + (num), line, __i__) \
|
||||
} \
|
||||
if ((((line)->cells[(at)].attrs) & WIDTH_MASK) != 1) { \
|
||||
(line)->cells[(at)].ch = BLANK_CHAR; \
|
||||
(line)->cells[(at)].attrs = BLANK_CHAR ? 1 : 0; \
|
||||
clear_sprite_position((line)->cells[(at)]); \
|
||||
if ((((line)->gpu_cells[(at)].attrs) & WIDTH_MASK) != 1) { \
|
||||
(line)->cpu_cells[(at)].ch = BLANK_CHAR; \
|
||||
(line)->gpu_cells[(at)].attrs = BLANK_CHAR ? 1 : 0; \
|
||||
clear_sprite_position((line)->gpu_cells[(at)]); \
|
||||
}\
|
||||
}
|
||||
|
||||
@ -260,7 +266,7 @@ Cursor* cursor_copy(Cursor*);
|
||||
void cursor_copy_to(Cursor *src, Cursor *dest);
|
||||
void cursor_reset_display_attrs(Cursor*);
|
||||
void cursor_from_sgr(Cursor *self, unsigned int *params, unsigned int count);
|
||||
void apply_sgr_to_cells(Cell *first_cell, unsigned int cell_count, unsigned int *params, unsigned int count);
|
||||
void apply_sgr_to_cells(GPUCell *first_cell, unsigned int cell_count, unsigned int *params, unsigned int count);
|
||||
const char* cursor_as_sgr(Cursor*, Cursor*);
|
||||
|
||||
double monotonic();
|
||||
|
||||
@ -194,7 +194,7 @@ end:
|
||||
}
|
||||
|
||||
PyObject*
|
||||
create_fallback_face(PyObject UNUSED *base_face, Cell* cell, bool bold, bool italic, bool emoji_presentation, FONTS_DATA_HANDLE fg) {
|
||||
create_fallback_face(PyObject UNUSED *base_face, CPUCell* cell, bool bold, bool italic, bool emoji_presentation, FONTS_DATA_HANDLE fg) {
|
||||
PyObject *ans = NULL;
|
||||
FcPattern *pat = FcPatternCreate();
|
||||
if (pat == NULL) return PyErr_NoMemory();
|
||||
|
||||
167
kitty/fonts.c
167
kitty/fonts.c
@ -420,12 +420,12 @@ face_has_codepoint(PyObject* face, char_type cp) {
|
||||
}
|
||||
|
||||
static inline bool
|
||||
has_emoji_presentation(Cell *cell) {
|
||||
return (cell->attrs & WIDTH_MASK) == 2 && is_emoji(cell->ch) && cell->cc_idx[0] != VS15;
|
||||
has_emoji_presentation(CPUCell *cpu_cell, GPUCell *gpu_cell) {
|
||||
return (gpu_cell->attrs & WIDTH_MASK) == 2 && is_emoji(cpu_cell->ch) && cpu_cell->cc_idx[0] != VS15;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
has_cell_text(Font *self, Cell *cell) {
|
||||
has_cell_text(Font *self, CPUCell *cell) {
|
||||
if (!face_has_codepoint(self->face, cell->ch)) return false;
|
||||
for (unsigned i = 0; i < arraysz(cell->cc_idx) && cell->cc_idx[i]; i++) {
|
||||
combining_type cc_idx = cell->cc_idx[i];
|
||||
@ -436,7 +436,7 @@ has_cell_text(Font *self, Cell *cell) {
|
||||
}
|
||||
|
||||
static inline void
|
||||
output_cell_fallback_data(Cell *cell, bool bold, bool italic, bool emoji_presentation, PyObject *face, bool new_face) {
|
||||
output_cell_fallback_data(CPUCell *cell, bool bold, bool italic, bool emoji_presentation, PyObject *face, bool new_face) {
|
||||
printf("U+%x ", cell->ch);
|
||||
for (unsigned i = 0; i < arraysz(cell->cc_idx) && cell->cc_idx[i]; i++) {
|
||||
printf("U+%x ", codepoint_for_mark(cell->cc_idx[i]));
|
||||
@ -450,7 +450,7 @@ output_cell_fallback_data(Cell *cell, bool bold, bool italic, bool emoji_present
|
||||
}
|
||||
|
||||
static inline ssize_t
|
||||
load_fallback_font(FontGroup *fg, Cell *cell, bool bold, bool italic, bool emoji_presentation) {
|
||||
load_fallback_font(FontGroup *fg, CPUCell *cell, bool bold, bool italic, bool emoji_presentation) {
|
||||
if (fg->fallback_fonts_count > 100) { log_error("Too many fallback fonts"); return MISSING_FONT; }
|
||||
ssize_t f;
|
||||
|
||||
@ -475,21 +475,21 @@ load_fallback_font(FontGroup *fg, Cell *cell, bool bold, bool italic, bool emoji
|
||||
}
|
||||
|
||||
static inline ssize_t
|
||||
fallback_font(FontGroup *fg, Cell *cell) {
|
||||
bool bold = (cell->attrs >> BOLD_SHIFT) & 1;
|
||||
bool italic = (cell->attrs >> ITALIC_SHIFT) & 1;
|
||||
bool emoji_presentation = has_emoji_presentation(cell);
|
||||
fallback_font(FontGroup *fg, CPUCell *cpu_cell, GPUCell *gpu_cell) {
|
||||
bool bold = (gpu_cell->attrs >> BOLD_SHIFT) & 1;
|
||||
bool italic = (gpu_cell->attrs >> ITALIC_SHIFT) & 1;
|
||||
bool emoji_presentation = has_emoji_presentation(cpu_cell, gpu_cell);
|
||||
|
||||
// Check if one of the existing fallback fonts has this text
|
||||
for (size_t i = 0, j = fg->first_fallback_font_idx; i < fg->fallback_fonts_count; i++, j++) {
|
||||
Font *ff = fg->fonts +j;
|
||||
if (ff->bold == bold && ff->italic == italic && ff->emoji_presentation == emoji_presentation && has_cell_text(ff, cell)) {
|
||||
if (global_state.debug_font_fallback) output_cell_fallback_data(cell, bold, italic, emoji_presentation, ff->face, false);
|
||||
if (ff->bold == bold && ff->italic == italic && ff->emoji_presentation == emoji_presentation && has_cell_text(ff, cpu_cell)) {
|
||||
if (global_state.debug_font_fallback) output_cell_fallback_data(cpu_cell, bold, italic, emoji_presentation, ff->face, false);
|
||||
return j;
|
||||
}
|
||||
}
|
||||
|
||||
return load_fallback_font(fg, cell, bold, italic, emoji_presentation);
|
||||
return load_fallback_font(fg, cpu_cell, bold, italic, emoji_presentation);
|
||||
}
|
||||
|
||||
static inline ssize_t
|
||||
@ -502,10 +502,10 @@ in_symbol_maps(FontGroup *fg, char_type ch) {
|
||||
|
||||
|
||||
static ssize_t
|
||||
font_for_cell(FontGroup *fg, Cell *cell) {
|
||||
font_for_cell(FontGroup *fg, CPUCell *cpu_cell, GPUCell *gpu_cell) {
|
||||
START_ALLOW_CASE_RANGE
|
||||
ssize_t ans;
|
||||
switch(cell->ch) {
|
||||
switch(cpu_cell->ch) {
|
||||
case 0:
|
||||
case ' ':
|
||||
return BLANK_FONT;
|
||||
@ -517,9 +517,9 @@ START_ALLOW_CASE_RANGE
|
||||
case 0xe0b6:
|
||||
return BOX_FONT;
|
||||
default:
|
||||
ans = in_symbol_maps(fg, cell->ch);
|
||||
ans = in_symbol_maps(fg, cpu_cell->ch);
|
||||
if (ans > -1) return ans;
|
||||
switch(BI_VAL(cell->attrs)) {
|
||||
switch(BI_VAL(gpu_cell->attrs)) {
|
||||
case 0:
|
||||
ans = fg->medium_font_idx; break;
|
||||
case 1:
|
||||
@ -530,14 +530,14 @@ START_ALLOW_CASE_RANGE
|
||||
ans = fg->bi_font_idx; break;
|
||||
}
|
||||
if (ans < 0) ans = fg->medium_font_idx;
|
||||
if (!has_emoji_presentation(cell) && has_cell_text(fg->fonts + ans, cell)) return ans;
|
||||
return fallback_font(fg, cell);
|
||||
if (!has_emoji_presentation(cpu_cell, gpu_cell) && has_cell_text(fg->fonts + ans, cpu_cell)) return ans;
|
||||
return fallback_font(fg, cpu_cell, gpu_cell);
|
||||
}
|
||||
END_ALLOW_CASE_RANGE
|
||||
}
|
||||
|
||||
static inline void
|
||||
set_sprite(Cell *cell, sprite_index x, sprite_index y, sprite_index z) {
|
||||
set_sprite(GPUCell *cell, sprite_index x, sprite_index y, sprite_index z) {
|
||||
cell->sprite_x = x; cell->sprite_y = y; cell->sprite_z = z;
|
||||
}
|
||||
|
||||
@ -577,21 +577,21 @@ render_alpha_mask(uint8_t *alpha_mask, pixel* dest, Region *src_rect, Region *de
|
||||
}
|
||||
|
||||
static void
|
||||
render_box_cell(FontGroup *fg, Cell *cell) {
|
||||
render_box_cell(FontGroup *fg, CPUCell *cpu_cell, GPUCell *gpu_cell) {
|
||||
int error = 0;
|
||||
glyph_index glyph = box_glyph_id(cell->ch);
|
||||
glyph_index glyph = box_glyph_id(cpu_cell->ch);
|
||||
static ExtraGlyphs extra_glyphs = {{0}};
|
||||
SpritePosition *sp = sprite_position_for(fg, &fg->fonts[BOX_FONT], glyph, &extra_glyphs, false, &error);
|
||||
if (sp == NULL) {
|
||||
sprite_map_set_error(error); PyErr_Print();
|
||||
set_sprite(cell, 0, 0, 0);
|
||||
set_sprite(gpu_cell, 0, 0, 0);
|
||||
return;
|
||||
}
|
||||
set_sprite(cell, sp->x, sp->y, sp->z);
|
||||
set_sprite(gpu_cell, sp->x, sp->y, sp->z);
|
||||
if (sp->rendered) return;
|
||||
sp->rendered = true;
|
||||
sp->colored = false;
|
||||
PyObject *ret = PyObject_CallFunction(box_drawing_function, "IIId", cell->ch, fg->cell_width, fg->cell_height, (fg->logical_dpi_x + fg->logical_dpi_y) / 2.0);
|
||||
PyObject *ret = PyObject_CallFunction(box_drawing_function, "IIId", cpu_cell->ch, fg->cell_width, fg->cell_height, (fg->logical_dpi_x + fg->logical_dpi_y) / 2.0);
|
||||
if (ret == NULL) { PyErr_Print(); return; }
|
||||
uint8_t *alpha_mask = PyLong_AsVoidPtr(PyTuple_GET_ITEM(ret, 0));
|
||||
clear_canvas(fg);
|
||||
@ -602,17 +602,17 @@ render_box_cell(FontGroup *fg, Cell *cell) {
|
||||
}
|
||||
|
||||
static inline void
|
||||
load_hb_buffer(Cell *first_cell, index_type num_cells) {
|
||||
load_hb_buffer(CPUCell *first_cpu_cell, GPUCell *first_gpu_cell, index_type num_cells) {
|
||||
index_type num;
|
||||
hb_buffer_clear_contents(harfbuzz_buffer);
|
||||
while (num_cells) {
|
||||
attrs_type prev_width = 0;
|
||||
for (num = 0; num_cells && num < arraysz(shape_buffer) - 20 - arraysz(first_cell->cc_idx); first_cell++, num_cells--) {
|
||||
for (num = 0; num_cells && num < arraysz(shape_buffer) - 20 - arraysz(first_cpu_cell->cc_idx); first_cpu_cell++, first_gpu_cell++, num_cells--) {
|
||||
if (prev_width == 2) { prev_width = 0; continue; }
|
||||
shape_buffer[num++] = first_cell->ch;
|
||||
prev_width = first_cell->attrs & WIDTH_MASK;
|
||||
for (unsigned i = 0; i < arraysz(first_cell->cc_idx) && first_cell->cc_idx[i]; i++) {
|
||||
shape_buffer[num++] = codepoint_for_mark(first_cell->cc_idx[i]);
|
||||
shape_buffer[num++] = first_cpu_cell->ch;
|
||||
prev_width = first_gpu_cell->attrs & WIDTH_MASK;
|
||||
for (unsigned i = 0; i < arraysz(first_cpu_cell->cc_idx) && first_cpu_cell->cc_idx[i]; i++) {
|
||||
shape_buffer[num++] = codepoint_for_mark(first_cpu_cell->cc_idx[i]);
|
||||
}
|
||||
}
|
||||
hb_buffer_add_utf32(harfbuzz_buffer, shape_buffer, num, 0, num);
|
||||
@ -622,7 +622,7 @@ load_hb_buffer(Cell *first_cell, index_type num_cells) {
|
||||
|
||||
|
||||
static inline void
|
||||
set_cell_sprite(Cell *cell, SpritePosition *sp) {
|
||||
set_cell_sprite(GPUCell *cell, SpritePosition *sp) {
|
||||
cell->sprite_x = sp->x; cell->sprite_y = sp->y; cell->sprite_z = sp->z;
|
||||
if (sp->colored) cell->sprite_z |= 0x4000;
|
||||
}
|
||||
@ -636,7 +636,7 @@ extract_cell_from_canvas(FontGroup *fg, unsigned int i, unsigned int num_cells)
|
||||
}
|
||||
|
||||
static inline void
|
||||
render_group(FontGroup *fg, unsigned int num_cells, unsigned int num_glyphs, Cell *cells, hb_glyph_info_t *info, hb_glyph_position_t *positions, Font *font, glyph_index glyph, ExtraGlyphs *extra_glyphs) {
|
||||
render_group(FontGroup *fg, unsigned int num_cells, unsigned int num_glyphs, CPUCell *cpu_cells, GPUCell *gpu_cells, hb_glyph_info_t *info, hb_glyph_position_t *positions, Font *font, glyph_index glyph, ExtraGlyphs *extra_glyphs) {
|
||||
static SpritePosition* sprite_position[16];
|
||||
int error = 0;
|
||||
num_cells = MIN(sizeof(sprite_position)/sizeof(sprite_position[0]), num_cells);
|
||||
@ -645,19 +645,19 @@ render_group(FontGroup *fg, unsigned int num_cells, unsigned int num_glyphs, Cel
|
||||
if (error != 0) { sprite_map_set_error(error); PyErr_Print(); return; }
|
||||
}
|
||||
if (sprite_position[0]->rendered) {
|
||||
for (unsigned int i = 0; i < num_cells; i++) { set_cell_sprite(cells + i, sprite_position[i]); }
|
||||
for (unsigned int i = 0; i < num_cells; i++) { set_cell_sprite(gpu_cells + i, sprite_position[i]); }
|
||||
return;
|
||||
}
|
||||
|
||||
clear_canvas(fg);
|
||||
bool was_colored = (cells->attrs & WIDTH_MASK) == 2 && is_emoji(cells->ch);
|
||||
bool was_colored = (gpu_cells->attrs & WIDTH_MASK) == 2 && is_emoji(cpu_cells->ch);
|
||||
render_glyphs_in_cells(font->face, font->bold, font->italic, info, positions, num_glyphs, fg->canvas, fg->cell_width, fg->cell_height, num_cells, fg->baseline, &was_colored, (FONTS_DATA_HANDLE)fg);
|
||||
if (PyErr_Occurred()) PyErr_Print();
|
||||
|
||||
for (unsigned int i = 0; i < num_cells; i++) {
|
||||
sprite_position[i]->rendered = true;
|
||||
sprite_position[i]->colored = was_colored;
|
||||
set_cell_sprite(cells + i, sprite_position[i]);
|
||||
set_cell_sprite(gpu_cells + i, sprite_position[i]);
|
||||
pixel *buf = num_cells == 1 ? fg->canvas : extract_cell_from_canvas(fg, i, num_cells);
|
||||
current_send_sprite_to_gpu((FONTS_DATA_HANDLE)fg, sprite_position[i]->x, sprite_position[i]->y, sprite_position[i]->z, buf);
|
||||
}
|
||||
@ -665,7 +665,8 @@ render_group(FontGroup *fg, unsigned int num_cells, unsigned int num_glyphs, Cel
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
Cell *cell;
|
||||
CPUCell *cpu_cell;
|
||||
GPUCell *gpu_cell;
|
||||
unsigned int num_codepoints;
|
||||
unsigned int codepoints_consumed;
|
||||
char_type current_codepoint;
|
||||
@ -681,7 +682,8 @@ typedef struct {
|
||||
CellData current_cell_data;
|
||||
Group *groups;
|
||||
size_t groups_capacity, group_idx, glyph_idx, cell_idx, num_cells, num_glyphs;
|
||||
Cell *first_cell, *last_cell;
|
||||
CPUCell *first_cpu_cell, *last_cpu_cell;
|
||||
GPUCell *first_gpu_cell, *last_gpu_cell;
|
||||
hb_glyph_info_t *info;
|
||||
hb_glyph_position_t *positions;
|
||||
} GroupState;
|
||||
@ -689,14 +691,14 @@ typedef struct {
|
||||
static GroupState group_state = {0};
|
||||
|
||||
static inline unsigned int
|
||||
num_codepoints_in_cell(Cell *cell) {
|
||||
num_codepoints_in_cell(CPUCell *cell) {
|
||||
unsigned int ans = 1;
|
||||
for (unsigned i = 0; i < arraysz(cell->cc_idx) && cell->cc_idx[i]; i++) ans++;
|
||||
return ans;
|
||||
}
|
||||
|
||||
static inline void
|
||||
shape(Cell *first_cell, index_type num_cells, hb_font_t *font) {
|
||||
shape(CPUCell *first_cpu_cell, GPUCell *first_gpu_cell, index_type num_cells, hb_font_t *font) {
|
||||
if (group_state.groups_capacity <= 2 * num_cells) {
|
||||
group_state.groups_capacity = MAX(128, 2 * num_cells); // avoid unnecessary reallocs
|
||||
group_state.groups = realloc(group_state.groups, sizeof(Group) * group_state.groups_capacity);
|
||||
@ -705,15 +707,21 @@ shape(Cell *first_cell, index_type num_cells, hb_font_t *font) {
|
||||
group_state.previous_cluster = UINT32_MAX;
|
||||
group_state.prev_was_special = false;
|
||||
group_state.prev_was_empty = false;
|
||||
group_state.current_cell_data.cell = first_cell; group_state.current_cell_data.num_codepoints = num_codepoints_in_cell(first_cell); group_state.current_cell_data.codepoints_consumed = 0; group_state.current_cell_data.current_codepoint = first_cell->ch;
|
||||
group_state.current_cell_data.cpu_cell = first_cpu_cell;
|
||||
group_state.current_cell_data.gpu_cell = first_gpu_cell;
|
||||
group_state.current_cell_data.num_codepoints = num_codepoints_in_cell(first_cpu_cell);
|
||||
group_state.current_cell_data.codepoints_consumed = 0;
|
||||
group_state.current_cell_data.current_codepoint = first_cpu_cell->ch;
|
||||
memset(group_state.groups, 0, sizeof(Group) * group_state.groups_capacity);
|
||||
group_state.group_idx = 0;
|
||||
group_state.glyph_idx = 0;
|
||||
group_state.cell_idx = 0;
|
||||
group_state.num_cells = num_cells;
|
||||
group_state.first_cell = first_cell;
|
||||
group_state.last_cell = first_cell + (num_cells ? num_cells - 1 : 0);
|
||||
load_hb_buffer(first_cell, num_cells);
|
||||
group_state.first_cpu_cell = first_cpu_cell;
|
||||
group_state.first_gpu_cell = first_gpu_cell;
|
||||
group_state.last_cpu_cell = first_cpu_cell + (num_cells ? num_cells - 1 : 0);
|
||||
group_state.last_gpu_cell = first_gpu_cell + (num_cells ? num_cells - 1 : 0);
|
||||
load_hb_buffer(first_cpu_cell, first_gpu_cell, num_cells);
|
||||
hb_shape(font, harfbuzz_buffer, NULL, 0);
|
||||
unsigned int info_length, positions_length;
|
||||
group_state.info = hb_buffer_get_glyph_infos(harfbuzz_buffer, &info_length);
|
||||
@ -752,24 +760,25 @@ is_empty_glyph(glyph_index glyph_id, Font *font) {
|
||||
}
|
||||
|
||||
static inline unsigned int
|
||||
check_cell_consumed(CellData *cell_data, Cell *last_cell) {
|
||||
check_cell_consumed(CellData *cell_data, CPUCell *last_cpu_cell) {
|
||||
cell_data->codepoints_consumed++;
|
||||
if (cell_data->codepoints_consumed >= cell_data->num_codepoints) {
|
||||
attrs_type width = cell_data->cell->attrs & WIDTH_MASK;
|
||||
cell_data->cell += MAX(1, width);
|
||||
attrs_type width = cell_data->gpu_cell->attrs & WIDTH_MASK;
|
||||
cell_data->cpu_cell += MAX(1, width);
|
||||
cell_data->gpu_cell += MAX(1, width);
|
||||
cell_data->codepoints_consumed = 0;
|
||||
if (cell_data->cell <= last_cell) {
|
||||
cell_data->num_codepoints = num_codepoints_in_cell(cell_data->cell);
|
||||
cell_data->current_codepoint = cell_data->cell->ch;
|
||||
if (cell_data->cpu_cell <= last_cpu_cell) {
|
||||
cell_data->num_codepoints = num_codepoints_in_cell(cell_data->cpu_cell);
|
||||
cell_data->current_codepoint = cell_data->cpu_cell->ch;
|
||||
} else cell_data->current_codepoint = 0;
|
||||
return width;
|
||||
} else {
|
||||
switch(cell_data->codepoints_consumed) {
|
||||
case 0:
|
||||
cell_data->current_codepoint = cell_data->cell->ch;
|
||||
cell_data->current_codepoint = cell_data->cpu_cell->ch;
|
||||
break;
|
||||
default:
|
||||
cell_data->current_codepoint = codepoint_for_mark(cell_data->cell->cc_idx[cell_data->codepoints_consumed - 1]);
|
||||
cell_data->current_codepoint = codepoint_for_mark(cell_data->cpu_cell->cc_idx[cell_data->codepoints_consumed - 1]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -778,8 +787,8 @@ check_cell_consumed(CellData *cell_data, Cell *last_cell) {
|
||||
|
||||
|
||||
static inline void
|
||||
shape_run(Cell *first_cell, index_type num_cells, Font *font) {
|
||||
shape(first_cell, num_cells, harfbuzz_font_for_face(font->face));
|
||||
shape_run(CPUCell *first_cpu_cell, GPUCell *first_gpu_cell, index_type num_cells, Font *font) {
|
||||
shape(first_cpu_cell, first_gpu_cell, num_cells, harfbuzz_font_for_face(font->face));
|
||||
#if 0
|
||||
// You can also generate this easily using hb-shape --show-extents --cluster-level=1 --shapers=ot /path/to/font/file text
|
||||
hb_buffer_serialize_glyphs(harfbuzz_buffer, 0, group_state.num_glyphs, (char*)canvas, sizeof(pixel) * CELLS_IN_CANVAS * cell_width * cell_height, NULL, harfbuzz_font_for_face(font->face), HB_BUFFER_SERIALIZE_FORMAT_TEXT, HB_BUFFER_SERIALIZE_FLAG_DEFAULT | HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS);
|
||||
@ -853,7 +862,7 @@ shape_run(Cell *first_cell, index_type num_cells, Font *font) {
|
||||
} else {
|
||||
unsigned int num_cells_consumed = 0, start_cell_idx = G(cell_idx);
|
||||
while (num_codepoints_used_by_glyph && G(cell_idx) < G(num_cells)) {
|
||||
unsigned int w = check_cell_consumed(&G(current_cell_data), G(last_cell));
|
||||
unsigned int w = check_cell_consumed(&G(current_cell_data), G(last_cpu_cell));
|
||||
G(cell_idx) += w;
|
||||
num_cells_consumed += w;
|
||||
num_codepoints_used_by_glyph--;
|
||||
@ -915,7 +924,7 @@ render_groups(FontGroup *fg, Font *font) {
|
||||
int last = -1;
|
||||
for (i = 1; i < MIN(arraysz(ed.data) + 1, group->num_glyphs); i++) { last = i - 1; ed.data[last] = G(info)[group->first_glyph_idx + i].codepoint; }
|
||||
if ((size_t)(last + 1) < arraysz(ed.data)) ed.data[last + 1] = 0;
|
||||
render_group(fg, group->num_cells, group->num_glyphs, G(first_cell) + group->first_cell_idx, G(info) + group->first_glyph_idx, G(positions) + group->first_glyph_idx, font, primary, &ed);
|
||||
render_group(fg, group->num_cells, group->num_glyphs, G(first_cpu_cell) + group->first_cell_idx, G(first_gpu_cell) + group->first_cell_idx, G(info) + group->first_glyph_idx, G(positions) + group->first_glyph_idx, font, primary, &ed);
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
@ -927,7 +936,7 @@ test_shape(PyObject UNUSED *self, PyObject *args) {
|
||||
int index = 0;
|
||||
if(!PyArg_ParseTuple(args, "O!|zi", &Line_Type, &line, &path, &index)) return NULL;
|
||||
index_type num = 0;
|
||||
while(num < line->xnum && line->cells[num].ch) num += line->cells[num].attrs & WIDTH_MASK;
|
||||
while(num < line->xnum && line->cpu_cells[num].ch) num += line->gpu_cells[num].attrs & WIDTH_MASK;
|
||||
PyObject *face = NULL;
|
||||
Font *font;
|
||||
if (!num_font_groups) { PyErr_SetString(PyExc_RuntimeError, "must create at least one font group first"); return NULL; }
|
||||
@ -940,7 +949,7 @@ test_shape(PyObject UNUSED *self, PyObject *args) {
|
||||
FontGroup *fg = font_groups;
|
||||
font = fg->fonts + fg->medium_font_idx;
|
||||
}
|
||||
shape_run(line->cells, num, font);
|
||||
shape_run(line->cpu_cells, line->gpu_cells, num, font);
|
||||
|
||||
PyObject *ans = PyList_New(0);
|
||||
unsigned int idx = 0;
|
||||
@ -961,21 +970,21 @@ test_shape(PyObject UNUSED *self, PyObject *args) {
|
||||
#undef G
|
||||
|
||||
static inline void
|
||||
render_run(FontGroup *fg, Cell *first_cell, index_type num_cells, ssize_t font_idx, bool pua_space_ligature) {
|
||||
render_run(FontGroup *fg, CPUCell *first_cpu_cell, GPUCell *first_gpu_cell, index_type num_cells, ssize_t font_idx, bool pua_space_ligature) {
|
||||
switch(font_idx) {
|
||||
default:
|
||||
shape_run(first_cell, num_cells, &fg->fonts[font_idx]);
|
||||
shape_run(first_cpu_cell, first_gpu_cell, num_cells, &fg->fonts[font_idx]);
|
||||
if (pua_space_ligature) merge_groups_for_pua_space_ligature();
|
||||
render_groups(fg, &fg->fonts[font_idx]);
|
||||
break;
|
||||
case BLANK_FONT:
|
||||
while(num_cells--) set_sprite(first_cell++, 0, 0, 0);
|
||||
while(num_cells--) { set_sprite(first_gpu_cell, 0, 0, 0); first_cpu_cell++; first_gpu_cell++; }
|
||||
break;
|
||||
case BOX_FONT:
|
||||
while(num_cells--) render_box_cell(fg, first_cell++);
|
||||
while(num_cells--) { render_box_cell(fg, first_cpu_cell, first_gpu_cell); first_cpu_cell++; first_gpu_cell++; }
|
||||
break;
|
||||
case MISSING_FONT:
|
||||
while(num_cells--) set_sprite(first_cell++, MISSING_GLYPH, 0, 0);
|
||||
while(num_cells--) { set_sprite(first_gpu_cell, MISSING_GLYPH, 0, 0); first_cpu_cell++; first_gpu_cell++; }
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -987,32 +996,33 @@ is_private_use(char_type ch) {
|
||||
|
||||
void
|
||||
render_line(FONTS_DATA_HANDLE fg_, Line *line) {
|
||||
#define RENDER if (run_font_idx != NO_FONT && i > first_cell_in_run) render_run(fg, line->cells + first_cell_in_run, i - first_cell_in_run, run_font_idx, false);
|
||||
#define RENDER if (run_font_idx != NO_FONT && i > first_cell_in_run) render_run(fg, line->cpu_cells + first_cell_in_run, line->gpu_cells + first_cell_in_run, i - first_cell_in_run, run_font_idx, false);
|
||||
FontGroup *fg = (FontGroup*)fg_;
|
||||
ssize_t run_font_idx = NO_FONT;
|
||||
index_type first_cell_in_run, i;
|
||||
attrs_type prev_width = 0;
|
||||
for (i=0, first_cell_in_run=0; i < line->xnum; i++) {
|
||||
if (prev_width == 2) { prev_width = 0; continue; }
|
||||
Cell *cell = line->cells + i;
|
||||
ssize_t cell_font_idx = font_for_cell(fg, cell);
|
||||
if (is_private_use(cell->ch) && i + 1 < line->xnum && (line->cells[i+1].ch == ' ' || line->cells[i+1].ch == 0) && cell_font_idx != BOX_FONT && cell_font_idx != MISSING_FONT) {
|
||||
CPUCell *cpu_cell = line->cpu_cells + i;
|
||||
GPUCell *gpu_cell = line->gpu_cells + i;
|
||||
ssize_t cell_font_idx = font_for_cell(fg, cpu_cell, gpu_cell);
|
||||
if (is_private_use(cpu_cell->ch) && i + 1 < line->xnum && (line->cpu_cells[i+1].ch == ' ' || line->cpu_cells[i+1].ch == 0) && cell_font_idx != BOX_FONT && cell_font_idx != MISSING_FONT) {
|
||||
// We have a private use char followed by a space char, render it as a two cell ligature.
|
||||
Cell *space_cell = line->cells + i+1;
|
||||
GPUCell *space_cell = line->gpu_cells + i+1;
|
||||
// Ensure the space cell uses the foreground colors from the PUA cell
|
||||
// This is needed because there are stupid applications like
|
||||
// powerline that use PUA+space with different foreground colors
|
||||
// for the space and the PUA. See for example: https://github.com/kovidgoyal/kitty/issues/467
|
||||
space_cell->fg = cell->fg; space_cell->decoration_fg = cell->decoration_fg;
|
||||
space_cell->fg = gpu_cell->fg; space_cell->decoration_fg = gpu_cell->decoration_fg;
|
||||
RENDER;
|
||||
render_run(fg, line->cells + i, 2, cell_font_idx, true);
|
||||
render_run(fg, line->cpu_cells + i, line->gpu_cells + i, 2, cell_font_idx, true);
|
||||
run_font_idx = NO_FONT;
|
||||
first_cell_in_run = i + 2;
|
||||
prev_width = line->cells[i+1].attrs & WIDTH_MASK;
|
||||
prev_width = line->gpu_cells[i+1].attrs & WIDTH_MASK;
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
prev_width = cell->attrs & WIDTH_MASK;
|
||||
prev_width = gpu_cell->attrs & WIDTH_MASK;
|
||||
if (run_font_idx == NO_FONT) run_font_idx = cell_font_idx;
|
||||
if (run_font_idx == cell_font_idx) continue;
|
||||
RENDER;
|
||||
@ -1259,15 +1269,16 @@ get_fallback_font(PyObject UNUSED *self, PyObject *args) {
|
||||
PyObject *text;
|
||||
int bold, italic;
|
||||
if (!PyArg_ParseTuple(args, "Upp", &text, &bold, &italic)) return NULL;
|
||||
Cell cell = {0};
|
||||
static Py_UCS4 char_buf[2 + arraysz(cell.cc_idx)];
|
||||
CPUCell cpu_cell = {0};
|
||||
GPUCell gpu_cell = {0};
|
||||
static Py_UCS4 char_buf[2 + arraysz(cpu_cell.cc_idx)];
|
||||
if (!PyUnicode_AsUCS4(text, char_buf, arraysz(char_buf), 1)) return NULL;
|
||||
cell.ch = char_buf[0];
|
||||
for (unsigned i = 0; i + 1 < (unsigned) PyUnicode_GetLength(text) && i < arraysz(cell.cc_idx); i++) cell.cc_idx[i] = mark_for_codepoint(char_buf[i + 1]);
|
||||
if (bold) cell.attrs |= 1 << BOLD_SHIFT;
|
||||
if (italic) cell.attrs |= 1 << ITALIC_SHIFT;
|
||||
cpu_cell.ch = char_buf[0];
|
||||
for (unsigned i = 0; i + 1 < (unsigned) PyUnicode_GetLength(text) && i < arraysz(cpu_cell.cc_idx); i++) cpu_cell.cc_idx[i] = mark_for_codepoint(char_buf[i + 1]);
|
||||
if (bold) gpu_cell.attrs |= 1 << BOLD_SHIFT;
|
||||
if (italic) gpu_cell.attrs |= 1 << ITALIC_SHIFT;
|
||||
FontGroup *fg = font_groups;
|
||||
ssize_t ans = fallback_font(fg, &cell);
|
||||
ssize_t ans = fallback_font(fg, &cpu_cell, &gpu_cell);
|
||||
if (ans < 0) { PyErr_SetString(PyExc_ValueError, "Too many fallback fonts"); return NULL; }
|
||||
return fg->fonts[ans].face;
|
||||
}
|
||||
|
||||
@ -22,7 +22,7 @@ hb_font_t* harfbuzz_font_for_face(PyObject*);
|
||||
bool set_size_for_face(PyObject*, unsigned int, bool, FONTS_DATA_HANDLE);
|
||||
void cell_metrics(PyObject*, unsigned int*, unsigned int*, unsigned int*, unsigned int*, unsigned int*);
|
||||
bool render_glyphs_in_cells(PyObject *f, bool bold, bool italic, hb_glyph_info_t *info, hb_glyph_position_t *positions, unsigned int num_glyphs, pixel *canvas, unsigned int cell_width, unsigned int cell_height, unsigned int num_cells, unsigned int baseline, bool *was_colored, FONTS_DATA_HANDLE);
|
||||
PyObject* create_fallback_face(PyObject *base_face, Cell* cell, bool bold, bool italic, bool emoji_presentation, FONTS_DATA_HANDLE fg);
|
||||
PyObject* create_fallback_face(PyObject *base_face, CPUCell* cell, bool bold, bool italic, bool emoji_presentation, FONTS_DATA_HANDLE fg);
|
||||
PyObject* specialize_font_descriptor(PyObject *base_descriptor, FONTS_DATA_HANDLE);
|
||||
PyObject* face_from_path(const char *path, int index, FONTS_DATA_HANDLE);
|
||||
PyObject* face_from_descriptor(PyObject*, FONTS_DATA_HANDLE);
|
||||
|
||||
@ -18,9 +18,10 @@ add_segment(HistoryBuf *self) {
|
||||
self->segments = PyMem_Realloc(self->segments, sizeof(HistoryBufSegment) * self->num_segments);
|
||||
if (self->segments == NULL) fatal("Out of memory allocating new history buffer segment");
|
||||
HistoryBufSegment *s = self->segments + self->num_segments - 1;
|
||||
s->cells = PyMem_Calloc(self->xnum * SEGMENT_SIZE, sizeof(Cell));
|
||||
s->cpu_cells = PyMem_Calloc(self->xnum * SEGMENT_SIZE, sizeof(CPUCell));
|
||||
s->gpu_cells = PyMem_Calloc(self->xnum * SEGMENT_SIZE, sizeof(GPUCell));
|
||||
s->line_attrs = PyMem_Calloc(SEGMENT_SIZE, sizeof(line_attrs_type));
|
||||
if (s->cells == NULL || s->line_attrs == NULL) fatal("Out of memory allocating new history buffer segment");
|
||||
if (s->cpu_cells == NULL || s->gpu_cells == NULL || s->line_attrs == NULL) fatal("Out of memory allocating new history buffer segment");
|
||||
}
|
||||
|
||||
static inline index_type
|
||||
@ -37,9 +38,14 @@ segment_for(HistoryBuf *self, index_type y) {
|
||||
return self->segments[seg_num].which + y * stride; \
|
||||
}
|
||||
|
||||
static inline Cell*
|
||||
lineptr(HistoryBuf *self, index_type y) {
|
||||
seg_ptr(cells, self->xnum);
|
||||
static inline CPUCell*
|
||||
cpu_lineptr(HistoryBuf *self, index_type y) {
|
||||
seg_ptr(cpu_cells, self->xnum);
|
||||
}
|
||||
|
||||
static inline GPUCell*
|
||||
gpu_lineptr(HistoryBuf *self, index_type y) {
|
||||
seg_ptr(gpu_cells, self->xnum);
|
||||
}
|
||||
|
||||
|
||||
@ -64,12 +70,12 @@ new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {
|
||||
if (self != NULL) {
|
||||
self->xnum = xnum;
|
||||
self->ynum = ynum;
|
||||
self->line = alloc_line();
|
||||
self->num_segments = 0;
|
||||
add_segment(self);
|
||||
self->line = alloc_line();
|
||||
self->line->xnum = xnum;
|
||||
for(index_type y = 0; y < self->ynum; y++) {
|
||||
clear_chars_in_line(lineptr(self, y), self->xnum, BLANK_CHAR);
|
||||
clear_chars_in_line(cpu_lineptr(self, y), gpu_lineptr(self, y), self->xnum, BLANK_CHAR);
|
||||
}
|
||||
}
|
||||
|
||||
@ -80,7 +86,8 @@ static void
|
||||
dealloc(HistoryBuf* self) {
|
||||
Py_CLEAR(self->line);
|
||||
for (size_t i = 0; i < self->num_segments; i++) {
|
||||
PyMem_Free(self->segments[i].cells);
|
||||
PyMem_Free(self->segments[i].cpu_cells);
|
||||
PyMem_Free(self->segments[i].gpu_cells);
|
||||
PyMem_Free(self->segments[i].line_attrs);
|
||||
}
|
||||
PyMem_Free(self->segments);
|
||||
@ -99,7 +106,8 @@ index_of(HistoryBuf *self, index_type lnum) {
|
||||
static inline void
|
||||
init_line(HistoryBuf *self, index_type num, Line *l) {
|
||||
// Initialize the line l, setting its pointer to the offsets for the line at index (buffer position) num
|
||||
l->cells = lineptr(self, num);
|
||||
l->cpu_cells = cpu_lineptr(self, num);
|
||||
l->gpu_cells = gpu_lineptr(self, num);
|
||||
l->continued = *attrptr(self, num) & CONTINUED_MASK;
|
||||
l->has_dirty_text = *attrptr(self, num) & TEXT_DIRTY_MASK ? true : false;
|
||||
}
|
||||
@ -286,7 +294,8 @@ void historybuf_rewrap(HistoryBuf *self, HistoryBuf *other) {
|
||||
if (other->xnum == self->xnum && other->ynum == self->ynum) {
|
||||
// Fast path
|
||||
for (index_type i = 0; i < self->num_segments; i++) {
|
||||
memcpy(other->segments[i].cells, self->segments[i].cells, SEGMENT_SIZE * self->xnum * sizeof(Cell));
|
||||
memcpy(other->segments[i].cpu_cells, self->segments[i].cpu_cells, SEGMENT_SIZE * self->xnum * sizeof(CPUCell));
|
||||
memcpy(other->segments[i].gpu_cells, self->segments[i].gpu_cells, SEGMENT_SIZE * self->xnum * sizeof(GPUCell));
|
||||
memcpy(other->segments[i].line_attrs, self->segments[i].line_attrs, SEGMENT_SIZE * sizeof(line_attrs_type));
|
||||
}
|
||||
other->count = self->count; other->start_of_data = self->start_of_data;
|
||||
|
||||
@ -12,19 +12,25 @@
|
||||
extern PyTypeObject Line_Type;
|
||||
extern PyTypeObject HistoryBuf_Type;
|
||||
|
||||
static inline Cell*
|
||||
lineptr(LineBuf *linebuf, index_type y) {
|
||||
return linebuf->buf + y * linebuf->xnum;
|
||||
static inline CPUCell*
|
||||
cpu_lineptr(LineBuf *linebuf, index_type y) {
|
||||
return linebuf->cpu_cell_buf + y * linebuf->xnum;
|
||||
}
|
||||
|
||||
static inline GPUCell*
|
||||
gpu_lineptr(LineBuf *linebuf, index_type y) {
|
||||
return linebuf->gpu_cell_buf + y * linebuf->xnum;
|
||||
}
|
||||
|
||||
static inline void
|
||||
clear_chars_to(LineBuf* linebuf, index_type y, char_type ch) {
|
||||
clear_chars_in_line(lineptr(linebuf, y), linebuf->xnum, ch);
|
||||
clear_chars_in_line(cpu_lineptr(linebuf, y), gpu_lineptr(linebuf, y), linebuf->xnum, ch);
|
||||
}
|
||||
|
||||
void
|
||||
linebuf_clear(LineBuf *self, char_type ch) {
|
||||
memset(self->buf, 0, self->xnum * self->ynum * sizeof(Cell));
|
||||
memset(self->cpu_cell_buf, 0, self->xnum * self->ynum * sizeof(CPUCell));
|
||||
memset(self->gpu_cell_buf, 0, self->xnum * self->ynum * sizeof(GPUCell));
|
||||
memset(self->line_attrs, 0, self->ynum * sizeof(line_attrs_type));
|
||||
for (index_type i = 0; i < self->ynum; i++) self->line_map[i] = i;
|
||||
if (ch != 0) {
|
||||
@ -73,14 +79,15 @@ new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {
|
||||
if (self != NULL) {
|
||||
self->xnum = xnum;
|
||||
self->ynum = ynum;
|
||||
self->buf = PyMem_Calloc(xnum * ynum, sizeof(Cell));
|
||||
self->cpu_cell_buf = PyMem_Calloc(xnum * ynum, sizeof(CPUCell));
|
||||
self->gpu_cell_buf = PyMem_Calloc(xnum * ynum, sizeof(GPUCell));
|
||||
self->line_map = PyMem_Calloc(ynum, sizeof(index_type));
|
||||
self->scratch = PyMem_Calloc(ynum, sizeof(index_type));
|
||||
self->line_attrs = PyMem_Calloc(ynum, sizeof(line_attrs_type));
|
||||
self->line = alloc_line();
|
||||
if (self->buf == NULL || self->line_map == NULL || self->scratch == NULL || self->line_attrs == NULL || self->line == NULL) {
|
||||
if (self->cpu_cell_buf == NULL || self->gpu_cell_buf == NULL || self->line_map == NULL || self->scratch == NULL || self->line_attrs == NULL || self->line == NULL) {
|
||||
PyErr_NoMemory();
|
||||
PyMem_Free(self->buf); PyMem_Free(self->line_map); PyMem_Free(self->line_attrs); Py_CLEAR(self->line);
|
||||
PyMem_Free(self->cpu_cell_buf); PyMem_Free(self->gpu_cell_buf); PyMem_Free(self->line_map); PyMem_Free(self->line_attrs); Py_CLEAR(self->line);
|
||||
Py_CLEAR(self);
|
||||
} else {
|
||||
self->line->xnum = xnum;
|
||||
@ -96,7 +103,8 @@ new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {
|
||||
|
||||
static void
|
||||
dealloc(LineBuf* self) {
|
||||
PyMem_Free(self->buf);
|
||||
PyMem_Free(self->cpu_cell_buf);
|
||||
PyMem_Free(self->gpu_cell_buf);
|
||||
PyMem_Free(self->line_map);
|
||||
PyMem_Free(self->line_attrs);
|
||||
PyMem_Free(self->scratch);
|
||||
@ -106,7 +114,8 @@ dealloc(LineBuf* self) {
|
||||
|
||||
static inline void
|
||||
init_line(LineBuf *lb, Line *l, index_type ynum) {
|
||||
l->cells = lineptr(lb, ynum);
|
||||
l->cpu_cells = cpu_lineptr(lb, ynum);
|
||||
l->gpu_cells = gpu_lineptr(lb, ynum);
|
||||
}
|
||||
|
||||
void
|
||||
@ -133,13 +142,13 @@ line(LineBuf *self, PyObject *y) {
|
||||
|
||||
unsigned int
|
||||
linebuf_char_width_at(LineBuf *self, index_type x, index_type y) {
|
||||
return (lineptr(self, self->line_map[y])[x].attrs) & WIDTH_MASK;
|
||||
return (gpu_lineptr(self, self->line_map[y])[x].attrs) & WIDTH_MASK;
|
||||
}
|
||||
|
||||
void
|
||||
linebuf_set_attribute(LineBuf *self, unsigned int shift, unsigned int val) {
|
||||
for (index_type y = 0; y < self->ynum; y++) {
|
||||
set_attribute_on_line(lineptr(self, y), shift, val, self->xnum);
|
||||
set_attribute_on_line(gpu_lineptr(self, y), shift, val, self->xnum);
|
||||
self->line_attrs[y] |= TEXT_DIRTY_MASK;
|
||||
}
|
||||
}
|
||||
@ -181,12 +190,14 @@ dirty_lines(LineBuf *self, PyObject *a UNUSED) {
|
||||
static inline bool
|
||||
allocate_line_storage(Line *line, bool initialize) {
|
||||
if (initialize) {
|
||||
line->cells = PyMem_Calloc(line->xnum, sizeof(Cell));
|
||||
if (line->cells == NULL) { PyErr_NoMemory(); return false; }
|
||||
if (BLANK_CHAR != 0) clear_chars_in_line(line->cells, line->xnum, BLANK_CHAR);
|
||||
line->cpu_cells = PyMem_Calloc(line->xnum, sizeof(CPUCell));
|
||||
line->gpu_cells = PyMem_Calloc(line->xnum, sizeof(GPUCell));
|
||||
if (line->cpu_cells == NULL || line->gpu_cells) { PyErr_NoMemory(); return false; }
|
||||
if (BLANK_CHAR != 0) clear_chars_in_line(line->cpu_cells, line->gpu_cells, line->xnum, BLANK_CHAR);
|
||||
} else {
|
||||
line->cells = PyMem_Malloc(line->xnum * sizeof(Cell));
|
||||
if (line->cells == NULL) { PyErr_NoMemory(); return false; }
|
||||
line->cpu_cells = PyMem_Malloc(line->xnum * sizeof(CPUCell));
|
||||
line->gpu_cells = PyMem_Malloc(line->xnum * sizeof(GPUCell));
|
||||
if (line->cpu_cells == NULL || line->gpu_cells == NULL) { PyErr_NoMemory(); return false; }
|
||||
}
|
||||
line->needs_free = 1;
|
||||
return true;
|
||||
@ -232,8 +243,9 @@ copy_line_to(LineBuf *self, PyObject *args) {
|
||||
|
||||
static inline void
|
||||
clear_line_(Line *l, index_type xnum) {
|
||||
memset(l->cells, 0, xnum * sizeof(Cell));
|
||||
if (BLANK_CHAR != 0) clear_chars_in_line(l->cells, xnum, BLANK_CHAR);
|
||||
memset(l->cpu_cells, 0, xnum * sizeof(CPUCell));
|
||||
memset(l->gpu_cells, 0, xnum * sizeof(GPUCell));
|
||||
if (BLANK_CHAR != 0) clear_chars_in_line(l->cpu_cells, l->gpu_cells, xnum, BLANK_CHAR);
|
||||
l->has_dirty_text = false;
|
||||
}
|
||||
|
||||
@ -515,7 +527,8 @@ linebuf_rewrap(LineBuf *self, LineBuf *other, index_type *num_content_lines_befo
|
||||
if (other->xnum == self->xnum && other->ynum == self->ynum) {
|
||||
memcpy(other->line_map, self->line_map, sizeof(index_type) * self->ynum);
|
||||
memcpy(other->line_attrs, self->line_attrs, sizeof(bool) * self->ynum);
|
||||
memcpy(other->buf, self->buf, self->xnum * self->ynum * sizeof(Cell));
|
||||
memcpy(other->cpu_cell_buf, self->cpu_cell_buf, self->xnum * self->ynum * sizeof(CPUCell));
|
||||
memcpy(other->gpu_cell_buf, self->gpu_cell_buf, self->xnum * self->ynum * sizeof(GPUCell));
|
||||
*num_content_lines_before = self->ynum; *num_content_lines_after = self->ynum;
|
||||
return;
|
||||
}
|
||||
@ -524,7 +537,7 @@ linebuf_rewrap(LineBuf *self, LineBuf *other, index_type *num_content_lines_befo
|
||||
first = self->ynum;
|
||||
do {
|
||||
first--;
|
||||
Cell *cells = lineptr(self, self->line_map[first]);
|
||||
CPUCell *cells = cpu_lineptr(self, self->line_map[first]);
|
||||
for(i = 0; i < self->xnum; i++) {
|
||||
if ((cells[i].ch) != BLANK_CHAR) { is_empty = false; break; }
|
||||
}
|
||||
|
||||
114
kitty/line.c
114
kitty/line.c
@ -20,7 +20,8 @@ new(PyTypeObject UNUSED *type, PyObject UNUSED *args, PyObject UNUSED *kwds) {
|
||||
static void
|
||||
dealloc(Line* self) {
|
||||
if (self->needs_free) {
|
||||
PyMem_Free(self->cells);
|
||||
PyMem_Free(self->cpu_cells);
|
||||
PyMem_Free(self->gpu_cells);
|
||||
}
|
||||
Py_TYPE(self)->tp_free((PyObject*)self);
|
||||
}
|
||||
@ -29,13 +30,13 @@ unsigned int
|
||||
line_length(Line *self) {
|
||||
index_type last = self->xnum - 1;
|
||||
for (index_type i = 0; i < self->xnum; i++) {
|
||||
if ((self->cells[last - i].ch) != BLANK_CHAR) return self->xnum - i;
|
||||
if ((self->cpu_cells[last - i].ch) != BLANK_CHAR) return self->xnum - i;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
PyObject*
|
||||
cell_text(Cell *cell) {
|
||||
cell_text(CPUCell *cell) {
|
||||
PyObject *ans;
|
||||
unsigned num = 1;
|
||||
static Py_UCS4 buf[arraysz(cell->cc_idx) + 1];
|
||||
@ -59,7 +60,7 @@ find_colon_slash(Line *self, index_type x, index_type limit) {
|
||||
limit = MAX(2, limit);
|
||||
if (pos < limit) return 0;
|
||||
do {
|
||||
char_type ch = self->cells[pos].ch;
|
||||
char_type ch = self->cpu_cells[pos].ch;
|
||||
if (!is_url_char(ch)) return false;
|
||||
switch(state) {
|
||||
case ANY:
|
||||
@ -83,7 +84,7 @@ prefix_matches(Line *self, index_type at, const char* prefix, index_type prefix_
|
||||
if (prefix_len > at) return false;
|
||||
index_type p, i;
|
||||
for (p = at - prefix_len, i = 0; i < prefix_len && p < self->xnum; i++, p++) {
|
||||
if ((self->cells[p].ch) != (unsigned char)prefix[i]) return false;
|
||||
if ((self->cpu_cells[p].ch) != (unsigned char)prefix[i]) return false;
|
||||
}
|
||||
return i == prefix_len;
|
||||
}
|
||||
@ -108,7 +109,7 @@ static inline bool
|
||||
has_url_beyond(Line *self, index_type x) {
|
||||
if (self->xnum <= x + MIN_URL_LEN + 3) return false;
|
||||
for (index_type i = x; i < MIN(x + MIN_URL_LEN + 3, self->xnum); i++) {
|
||||
if (!is_url_char(self->cells[i].ch)) return false;
|
||||
if (!is_url_char(self->cpu_cells[i].ch)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -134,9 +135,9 @@ index_type
|
||||
line_url_end_at(Line *self, index_type x, bool check_short) {
|
||||
index_type ans = x;
|
||||
if (x >= self->xnum || (check_short && self->xnum <= MIN_URL_LEN + 3)) return 0;
|
||||
while (ans < self->xnum && is_url_char(self->cells[ans].ch)) ans++;
|
||||
while (ans < self->xnum && is_url_char(self->cpu_cells[ans].ch)) ans++;
|
||||
if (ans) ans--;
|
||||
while (ans > x && can_strip_from_end_of_url(self->cells[ans].ch)) ans--;
|
||||
while (ans > x && can_strip_from_end_of_url(self->cpu_cells[ans].ch)) ans--;
|
||||
return ans;
|
||||
}
|
||||
|
||||
@ -158,11 +159,11 @@ static PyObject*
|
||||
text_at(Line* self, Py_ssize_t xval) {
|
||||
#define text_at_doc "[x] -> Return the text in the specified cell"
|
||||
if ((unsigned)xval >= self->xnum) { PyErr_SetString(PyExc_IndexError, "Column number out of bounds"); return NULL; }
|
||||
return cell_text(self->cells + xval);
|
||||
return cell_text(self->cpu_cells + xval);
|
||||
}
|
||||
|
||||
size_t
|
||||
cell_as_unicode(Cell *cell, bool include_cc, Py_UCS4 *buf, char_type zero_char) {
|
||||
cell_as_unicode(CPUCell *cell, bool include_cc, Py_UCS4 *buf, char_type zero_char) {
|
||||
size_t n = 1;
|
||||
buf[0] = cell->ch ? cell->ch : zero_char;
|
||||
if (include_cc) {
|
||||
@ -172,7 +173,7 @@ cell_as_unicode(Cell *cell, bool include_cc, Py_UCS4 *buf, char_type zero_char)
|
||||
}
|
||||
|
||||
size_t
|
||||
cell_as_utf8(Cell *cell, bool include_cc, char *buf, char_type zero_char) {
|
||||
cell_as_utf8(CPUCell *cell, bool include_cc, char *buf, char_type zero_char) {
|
||||
size_t n = encode_utf8(cell->ch ? cell->ch : zero_char, buf);
|
||||
if (include_cc) {
|
||||
for (unsigned i = 0; i < arraysz(cell->cc_idx) && cell->cc_idx[i]; i++) n += encode_utf8(codepoint_for_mark(cell->cc_idx[i]), buf + n);
|
||||
@ -188,13 +189,13 @@ unicode_in_range(Line *self, index_type start, index_type limit, bool include_cc
|
||||
static Py_UCS4 buf[4096];
|
||||
if (leading_char) buf[n++] = leading_char;
|
||||
char_type previous_width = 0;
|
||||
for(index_type i = start; i < limit && n < arraysz(buf) - 2 - arraysz(self->cells->cc_idx); i++) {
|
||||
char_type ch = self->cells[i].ch;
|
||||
for(index_type i = start; i < limit && n < arraysz(buf) - 2 - arraysz(self->cpu_cells->cc_idx); i++) {
|
||||
char_type ch = self->cpu_cells[i].ch;
|
||||
if (ch == 0) {
|
||||
if (previous_width == 2) { previous_width = 0; continue; };
|
||||
}
|
||||
n += cell_as_unicode(self->cells + i, include_cc, buf + n, ' ');
|
||||
previous_width = self->cells[i].attrs & WIDTH_MASK;
|
||||
n += cell_as_unicode(self->cpu_cells + i, include_cc, buf + n, ' ');
|
||||
previous_width = self->gpu_cells[i].attrs & WIDTH_MASK;
|
||||
}
|
||||
return PyUnicode_FromKindAndData(PyUnicode_4BYTE_KIND, buf, n);
|
||||
}
|
||||
@ -209,7 +210,7 @@ sprite_at(Line* self, PyObject *x) {
|
||||
#define sprite_at_doc "[x] -> Return the sprite in the specified cell"
|
||||
unsigned long xval = PyLong_AsUnsignedLong(x);
|
||||
if (xval >= self->xnum) { PyErr_SetString(PyExc_IndexError, "Column number out of bounds"); return NULL; }
|
||||
Cell *c = self->cells + xval;
|
||||
GPUCell *c = self->gpu_cells + xval;
|
||||
return Py_BuildValue("HHH", c->sprite_x, c->sprite_y, c->sprite_z);
|
||||
}
|
||||
|
||||
@ -236,21 +237,21 @@ line_as_ansi(Line *self, Py_UCS4 *buf, index_type buflen) {
|
||||
Cursor *cursor = &c1, *prev_cursor = &c2, *t;
|
||||
|
||||
for (index_type pos=0; pos < limit; pos++) {
|
||||
char_type attrs = self->cells[pos].attrs, ch = self->cells[pos].ch;
|
||||
char_type attrs = self->gpu_cells[pos].attrs, ch = self->cpu_cells[pos].ch;
|
||||
if (ch == 0) {
|
||||
if (previous_width == 2) { previous_width = 0; continue; }
|
||||
ch = ' ';
|
||||
}
|
||||
ATTRS_TO_CURSOR(attrs, cursor);
|
||||
cursor->fg = self->cells[pos].fg; cursor->bg = self->cells[pos].bg;
|
||||
cursor->decoration_fg = self->cells[pos].decoration_fg & COL_MASK;
|
||||
cursor->fg = self->gpu_cells[pos].fg; cursor->bg = self->gpu_cells[pos].bg;
|
||||
cursor->decoration_fg = self->gpu_cells[pos].decoration_fg & COL_MASK;
|
||||
|
||||
const char *sgr = cursor_as_sgr(cursor, prev_cursor);
|
||||
t = prev_cursor; prev_cursor = cursor; cursor = t;
|
||||
if (*sgr) WRITE_SGR(sgr);
|
||||
WRITE_CH(ch);
|
||||
for(unsigned c = 0; c < arraysz(self->cells[pos].cc_idx) && self->cells[pos].cc_idx[c]; c++) {
|
||||
WRITE_CH(codepoint_for_mark(self->cells[pos].cc_idx[c]));
|
||||
for(unsigned c = 0; c < arraysz(self->cpu_cells[pos].cc_idx) && self->cpu_cells[pos].cc_idx[c]; c++) {
|
||||
WRITE_CH(codepoint_for_mark(self->cpu_cells[pos].cc_idx[c]));
|
||||
}
|
||||
previous_width = attrs & WIDTH_MASK;
|
||||
}
|
||||
@ -293,13 +294,13 @@ width(Line *self, PyObject *val) {
|
||||
#define width_doc "width(x) -> the width of the character at x"
|
||||
unsigned long x = PyLong_AsUnsignedLong(val);
|
||||
if (x >= self->xnum) { PyErr_SetString(PyExc_ValueError, "Out of bounds"); return NULL; }
|
||||
char_type attrs = self->cells[x].attrs;
|
||||
char_type attrs = self->gpu_cells[x].attrs;
|
||||
return PyLong_FromUnsignedLong((unsigned long) (attrs & WIDTH_MASK));
|
||||
}
|
||||
|
||||
void
|
||||
line_add_combining_char(Line *self, uint32_t ch, unsigned int x) {
|
||||
Cell *cell = self->cells + x;
|
||||
CPUCell *cell = self->cpu_cells + x;
|
||||
if (!cell->ch) return; // dont allow adding combining chars to a null cell
|
||||
for (unsigned i = 0; i < arraysz(cell->cc_idx); i++) {
|
||||
if (!cell->cc_idx[i]) { cell->cc_idx[i] = mark_for_codepoint(ch); return; }
|
||||
@ -349,12 +350,12 @@ set_text(Line* self, PyObject *args) {
|
||||
color_type dfg = cursor->decoration_fg & COL_MASK;
|
||||
|
||||
for (index_type i = cursor->x; offset < limit && i < self->xnum; i++, offset++) {
|
||||
self->cells[i].ch = (PyUnicode_READ(kind, buf, offset));
|
||||
self->cells[i].attrs = attrs;
|
||||
self->cells[i].fg = fg;
|
||||
self->cells[i].bg = bg;
|
||||
self->cells[i].decoration_fg = dfg;
|
||||
memset(self->cells[i].cc_idx, 0, sizeof(self->cells[i].cc_idx));
|
||||
self->cpu_cells[i].ch = (PyUnicode_READ(kind, buf, offset));
|
||||
self->gpu_cells[i].attrs = attrs;
|
||||
self->gpu_cells[i].fg = fg;
|
||||
self->gpu_cells[i].bg = bg;
|
||||
self->gpu_cells[i].decoration_fg = dfg;
|
||||
memset(self->cpu_cells[i].cc_idx, 0, sizeof(self->cpu_cells[i].cc_idx));
|
||||
}
|
||||
|
||||
Py_RETURN_NONE;
|
||||
@ -373,10 +374,10 @@ cursor_from(Line* self, PyObject *args) {
|
||||
ans = alloc_cursor();
|
||||
if (ans == NULL) { PyErr_NoMemory(); return NULL; }
|
||||
ans->x = x; ans->y = y;
|
||||
char_type attrs = self->cells[x].attrs;
|
||||
char_type attrs = self->gpu_cells[x].attrs;
|
||||
ATTRS_TO_CURSOR(attrs, ans);
|
||||
ans->fg = self->cells[x].fg; ans->bg = self->cells[x].bg;
|
||||
ans->decoration_fg = self->cells[x].decoration_fg & COL_MASK;
|
||||
ans->fg = self->gpu_cells[x].fg; ans->bg = self->gpu_cells[x].bg;
|
||||
ans->decoration_fg = self->gpu_cells[x].decoration_fg & COL_MASK;
|
||||
|
||||
return (PyObject*)ans;
|
||||
}
|
||||
@ -386,8 +387,8 @@ line_clear_text(Line *self, unsigned int at, unsigned int num, char_type ch) {
|
||||
attrs_type width = ch ? 1 : 0;
|
||||
#define PREFIX \
|
||||
for (index_type i = at; i < MIN(self->xnum, at + num); i++) { \
|
||||
self->cells[i].ch = ch; memset(self->cells[i].cc_idx, 0, sizeof(self->cells[i].cc_idx)); \
|
||||
self->cells[i].attrs = (self->cells[i].attrs & ATTRS_MASK_WITHOUT_WIDTH) | width; \
|
||||
self->cpu_cells[i].ch = ch; memset(self->cpu_cells[i].cc_idx, 0, sizeof(self->cpu_cells[i].cc_idx)); \
|
||||
self->gpu_cells[i].attrs = (self->gpu_cells[i].attrs & ATTRS_MASK_WITHOUT_WIDTH) | width; \
|
||||
}
|
||||
if (CHAR_IS_BLANK(ch)) {
|
||||
PREFIX
|
||||
@ -415,16 +416,16 @@ line_apply_cursor(Line *self, Cursor *cursor, unsigned int at, unsigned int num,
|
||||
|
||||
for (index_type i = at; i < self->xnum && i < at + num; i++) {
|
||||
if (clear_char) {
|
||||
self->cells[i].ch = BLANK_CHAR;
|
||||
memset(self->cells[i].cc_idx, 0, sizeof(self->cells[i].cc_idx));
|
||||
self->cells[i].attrs = attrs;
|
||||
clear_sprite_position(self->cells[i]);
|
||||
self->cpu_cells[i].ch = BLANK_CHAR;
|
||||
memset(self->cpu_cells[i].cc_idx, 0, sizeof(self->cpu_cells[i].cc_idx));
|
||||
self->gpu_cells[i].attrs = attrs;
|
||||
clear_sprite_position(self->gpu_cells[i]);
|
||||
} else {
|
||||
attrs_type w = self->cells[i].attrs & WIDTH_MASK;
|
||||
self->cells[i].attrs = attrs | w;
|
||||
attrs_type w = self->gpu_cells[i].attrs & WIDTH_MASK;
|
||||
self->gpu_cells[i].attrs = attrs | w;
|
||||
}
|
||||
self->cells[i].fg = fg; self->cells[i].bg = bg;
|
||||
self->cells[i].decoration_fg = dfg;
|
||||
self->gpu_cells[i].fg = fg; self->gpu_cells[i].bg = bg;
|
||||
self->gpu_cells[i].decoration_fg = dfg;
|
||||
}
|
||||
}
|
||||
|
||||
@ -444,11 +445,11 @@ void line_right_shift(Line *self, unsigned int at, unsigned int num) {
|
||||
COPY_SELF_CELL(i - num, i)
|
||||
}
|
||||
// Check if a wide character was split at the right edge
|
||||
char_type w = (self->cells[self->xnum - 1].attrs) & WIDTH_MASK;
|
||||
char_type w = (self->gpu_cells[self->xnum - 1].attrs) & WIDTH_MASK;
|
||||
if (w != 1) {
|
||||
self->cells[self->xnum - 1].ch = BLANK_CHAR;
|
||||
self->cells[self->xnum - 1].attrs = BLANK_CHAR ? 1 : 0;
|
||||
clear_sprite_position(self->cells[self->xnum - 1]);
|
||||
self->cpu_cells[self->xnum - 1].ch = BLANK_CHAR;
|
||||
self->gpu_cells[self->xnum - 1].attrs = BLANK_CHAR ? 1 : 0;
|
||||
clear_sprite_position(self->gpu_cells[self->xnum - 1]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -483,15 +484,15 @@ left_shift(Line *self, PyObject *args) {
|
||||
void
|
||||
line_set_char(Line *self, unsigned int at, uint32_t ch, unsigned int width, Cursor *cursor, bool UNUSED is_second) {
|
||||
if (cursor == NULL) {
|
||||
self->cells[at].attrs = (self->cells[at].attrs & ATTRS_MASK_WITHOUT_WIDTH) | width;
|
||||
self->gpu_cells[at].attrs = (self->gpu_cells[at].attrs & ATTRS_MASK_WITHOUT_WIDTH) | width;
|
||||
} else {
|
||||
self->cells[at].attrs = CURSOR_TO_ATTRS(cursor, width & WIDTH_MASK);
|
||||
self->cells[at].fg = (cursor->fg & COL_MASK);
|
||||
self->cells[at].bg = (cursor->bg & COL_MASK);
|
||||
self->cells[at].decoration_fg = cursor->decoration_fg & COL_MASK;
|
||||
self->gpu_cells[at].attrs = CURSOR_TO_ATTRS(cursor, width & WIDTH_MASK);
|
||||
self->gpu_cells[at].fg = (cursor->fg & COL_MASK);
|
||||
self->gpu_cells[at].bg = (cursor->bg & COL_MASK);
|
||||
self->gpu_cells[at].decoration_fg = cursor->decoration_fg & COL_MASK;
|
||||
}
|
||||
self->cells[at].ch = ch;
|
||||
memset(self->cells[at].cc_idx, 0, sizeof(self->cells[at].cc_idx));
|
||||
self->cpu_cells[at].ch = ch;
|
||||
memset(self->cpu_cells[at].cc_idx, 0, sizeof(self->cpu_cells[at].cc_idx));
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
@ -516,7 +517,7 @@ set_attribute(Line *self, PyObject *args) {
|
||||
unsigned int shift, val;
|
||||
if (!PyArg_ParseTuple(args, "II", &shift, &val)) return NULL;
|
||||
if (shift < DECORATION_SHIFT || shift > DIM_SHIFT) { PyErr_SetString(PyExc_ValueError, "Unknown attribute"); return NULL; }
|
||||
set_attribute_on_line(self->cells, shift, val, self->xnum);
|
||||
set_attribute_on_line(self->gpu_cells, shift, val, self->xnum);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
@ -525,8 +526,9 @@ __len__(PyObject *self) {
|
||||
return (Py_ssize_t)(((Line*)self)->xnum);
|
||||
}
|
||||
|
||||
static int __eq__(Line *a, Line *b) {
|
||||
return a->xnum == b->xnum && memcmp(a->cells, b->cells, sizeof(Cell) * a->xnum) == 0;
|
||||
static int
|
||||
__eq__(Line *a, Line *b) {
|
||||
return a->xnum == b->xnum && memcmp(a->cpu_cells, b->cpu_cells, sizeof(CPUCell) * a->xnum) == 0 && memcmp(a->gpu_cells, b->gpu_cells, sizeof(GPUCell) * a->xnum) == 0;
|
||||
}
|
||||
|
||||
// Boilerplate {{{
|
||||
|
||||
@ -9,7 +9,7 @@
|
||||
#include "data-types.h"
|
||||
|
||||
static inline void
|
||||
set_attribute_on_line(Cell *cells, uint32_t shift, uint32_t val, index_type xnum) {
|
||||
set_attribute_on_line(GPUCell *cells, uint32_t shift, uint32_t val, index_type xnum) {
|
||||
// Set a single attribute on all cells in the line
|
||||
attrs_type mask = shift == DECORATION_SHIFT ? 3 : 1;
|
||||
attrs_type aval = (val & mask) << shift;
|
||||
@ -17,21 +17,18 @@ set_attribute_on_line(Cell *cells, uint32_t shift, uint32_t val, index_type xnum
|
||||
for (index_type i = 0; i < xnum; i++) cells[i].attrs = (cells[i].attrs & mask) | aval;
|
||||
}
|
||||
|
||||
static inline void
|
||||
copy_cells(const Cell *src, Cell *dest, index_type xnum) {
|
||||
memcpy(dest, src, sizeof(Cell) * xnum);
|
||||
}
|
||||
|
||||
static inline void
|
||||
copy_line(const Line *src, Line *dest) {
|
||||
copy_cells(src->cells, dest->cells, MIN(src->xnum, dest->xnum));
|
||||
memcpy(dest->cpu_cells, src->cpu_cells, sizeof(CPUCell) * MIN(src->xnum, dest->xnum));
|
||||
memcpy(dest->gpu_cells, src->gpu_cells, sizeof(GPUCell) * MIN(src->xnum, dest->xnum));
|
||||
}
|
||||
|
||||
static inline void
|
||||
clear_chars_in_line(Cell *cells, index_type xnum, char_type ch) {
|
||||
clear_chars_in_line(CPUCell *cpu_cells, GPUCell *gpu_cells, index_type xnum, char_type ch) {
|
||||
// Clear only the char part of each cell, the rest must have been cleared by a memset or similar
|
||||
if (ch) {
|
||||
for (index_type i = 0; i < xnum; i++) { cells[i].ch = ch; cells[i].attrs = 1; }
|
||||
for (index_type i = 0; i < xnum; i++) { cpu_cells[i].ch = ch; gpu_cells[i].attrs = 1; }
|
||||
}
|
||||
}
|
||||
|
||||
@ -39,7 +36,7 @@ static inline index_type
|
||||
xlimit_for_line(Line *line) {
|
||||
index_type xlimit = line->xnum;
|
||||
if (BLANK_CHAR == 0) {
|
||||
while (xlimit > 0 && (line->cells[xlimit - 1].ch) == BLANK_CHAR) xlimit--;
|
||||
while (xlimit > 0 && (line->cpu_cells[xlimit - 1].ch) == BLANK_CHAR) xlimit--;
|
||||
}
|
||||
return xlimit;
|
||||
}
|
||||
@ -54,8 +51,8 @@ index_type line_url_start_at(Line *self, index_type x);
|
||||
index_type line_url_end_at(Line *self, index_type x, bool);
|
||||
index_type line_as_ansi(Line *self, Py_UCS4 *buf, index_type buflen);
|
||||
unsigned int line_length(Line *self);
|
||||
size_t cell_as_unicode(Cell *cell, bool include_cc, Py_UCS4 *buf, char_type);
|
||||
size_t cell_as_utf8(Cell *cell, bool include_cc, char *buf, char_type);
|
||||
size_t cell_as_unicode(CPUCell *cell, bool include_cc, Py_UCS4 *buf, char_type);
|
||||
size_t cell_as_utf8(CPUCell *cell, bool include_cc, char *buf, char_type);
|
||||
PyObject* unicode_in_range(Line *self, index_type start, index_type limit, bool include_cc, char leading_char);
|
||||
PyObject* line_as_unicode(Line *);
|
||||
|
||||
|
||||
@ -43,7 +43,8 @@
|
||||
|
||||
static inline void
|
||||
copy_range(Line *src, index_type src_at, Line* dest, index_type dest_at, index_type num) {
|
||||
memcpy(dest->cells + dest_at, src->cells + src_at, num * sizeof(Cell));
|
||||
memcpy(dest->cpu_cells + dest_at, src->cpu_cells + src_at, num * sizeof(CPUCell));
|
||||
memcpy(dest->gpu_cells + dest_at, src->gpu_cells + src_at, num * sizeof(GPUCell));
|
||||
}
|
||||
|
||||
|
||||
@ -60,7 +61,7 @@ rewrap_inner(BufType *src, BufType *dest, const index_type src_limit, HistoryBuf
|
||||
src_x_limit = src->xnum;
|
||||
if (!src_line_is_continued) {
|
||||
// Trim trailing blanks since there is a hard line break at the end of this line
|
||||
while(src_x_limit && (src->line->cells[src_x_limit - 1].ch) == BLANK_CHAR) src_x_limit--;
|
||||
while(src_x_limit && (src->line->cpu_cells[src_x_limit - 1].ch) == BLANK_CHAR) src_x_limit--;
|
||||
|
||||
}
|
||||
if (is_tracked_line && *track_x >= src_x_limit) *track_x = MAX(1, src_x_limit) - 1;
|
||||
|
||||
@ -280,9 +280,10 @@ screen_designate_charset(Screen *self, uint32_t which, uint32_t as) {
|
||||
}
|
||||
|
||||
static inline void
|
||||
move_widened_char(Screen *self, Cell* cell, index_type xpos, index_type ypos) {
|
||||
move_widened_char(Screen *self, CPUCell* cpu_cell, GPUCell *gpu_cell, index_type xpos, index_type ypos) {
|
||||
self->cursor->x = xpos; self->cursor->y = ypos;
|
||||
Cell src = *cell, *dest;
|
||||
CPUCell src_cpu = *cpu_cell, *dest_cpu;
|
||||
GPUCell src_gpu = *gpu_cell, *dest_gpu;
|
||||
line_clear_text(self->linebuf->line, xpos, 1, BLANK_CHAR);
|
||||
|
||||
if (self->modes.mDECAWM) { // overflow goes onto next line
|
||||
@ -290,14 +291,17 @@ move_widened_char(Screen *self, Cell* cell, index_type xpos, index_type ypos) {
|
||||
screen_linefeed(self);
|
||||
self->linebuf->line_attrs[self->cursor->y] |= CONTINUED_MASK;
|
||||
linebuf_init_line(self->linebuf, self->cursor->y);
|
||||
dest = self->linebuf->line->cells;
|
||||
dest_cpu = self->linebuf->line->cpu_cells;
|
||||
dest_gpu = self->linebuf->line->gpu_cells;
|
||||
self->cursor->x = MIN(2, self->columns);
|
||||
linebuf_mark_line_dirty(self->linebuf, self->cursor->y);
|
||||
} else {
|
||||
dest = cell - 1;
|
||||
dest_cpu = cpu_cell - 1;
|
||||
dest_gpu = gpu_cell - 1;
|
||||
self->cursor->x = self->columns;
|
||||
}
|
||||
*dest = src;
|
||||
*dest_cpu = src_cpu;
|
||||
*dest_gpu = src_gpu;
|
||||
}
|
||||
|
||||
static inline void
|
||||
@ -320,10 +324,11 @@ draw_combining_char(Screen *self, char_type ch) {
|
||||
self->is_dirty = true;
|
||||
linebuf_mark_line_dirty(self->linebuf, ypos);
|
||||
if (ch == 0xfe0f) { // emoji presentation variation marker makes default text presentation emoji (narrow emoji) into wide emoji
|
||||
Cell *cell = self->linebuf->line->cells + xpos;
|
||||
if ((cell->attrs & WIDTH_MASK) != 2 && cell->cc_idx[0] == VS16 && is_emoji_presentation_base(cell->ch)) {
|
||||
cell->attrs = (cell->attrs & !WIDTH_MASK) | 2;
|
||||
if (xpos == self->columns - 1) move_widened_char(self, cell, xpos, ypos);
|
||||
CPUCell *cpu_cell = self->linebuf->line->cpu_cells + xpos;
|
||||
GPUCell *gpu_cell = self->linebuf->line->gpu_cells + xpos;
|
||||
if ((gpu_cell->attrs & WIDTH_MASK) != 2 && cpu_cell->cc_idx[0] == VS16 && is_emoji_presentation_base(cpu_cell->ch)) {
|
||||
gpu_cell->attrs = (gpu_cell->attrs & !WIDTH_MASK) | 2;
|
||||
if (xpos == self->columns - 1) move_widened_char(self, cpu_cell, gpu_cell, xpos, ypos);
|
||||
else self->cursor->x++;
|
||||
}
|
||||
}
|
||||
@ -409,7 +414,7 @@ select_graphic_rendition(Screen *self, unsigned int *params, unsigned int count,
|
||||
num = MIN(num, self->columns - x);
|
||||
for (index_type y = region.top; y < MIN(region.bottom + 1, self->lines); y++) {
|
||||
linebuf_init_line(self->linebuf, y);
|
||||
apply_sgr_to_cells(self->linebuf->line->cells + x, num, params, count);
|
||||
apply_sgr_to_cells(self->linebuf->line->gpu_cells + x, num, params, count);
|
||||
}
|
||||
} else {
|
||||
index_type x, num;
|
||||
@ -418,7 +423,7 @@ select_graphic_rendition(Screen *self, unsigned int *params, unsigned int count,
|
||||
else if (y == region.bottom) { x = 0; num = MIN(region.right + 1, self->columns); }
|
||||
else { x = 0; num = self->columns; }
|
||||
linebuf_init_line(self->linebuf, y);
|
||||
apply_sgr_to_cells(self->linebuf->line->cells + x, num, params, count);
|
||||
apply_sgr_to_cells(self->linebuf->line->gpu_cells + x, num, params, count);
|
||||
}
|
||||
}
|
||||
} else cursor_from_sgr(self->cursor, params, count);
|
||||
@ -1280,8 +1285,8 @@ screen_request_capabilities(Screen *self, char c, PyObject *q) {
|
||||
// Rendering {{{
|
||||
static inline void
|
||||
update_line_data(Line *line, unsigned int dest_y, uint8_t *data) {
|
||||
size_t base = dest_y * line->xnum * sizeof(Cell);
|
||||
memcpy(data + base, line->cells, line->xnum * sizeof(Cell));
|
||||
size_t base = dest_y * line->xnum * sizeof(GPUCell);
|
||||
memcpy(data + base, line->gpu_cells, line->xnum * sizeof(GPUCell));
|
||||
}
|
||||
|
||||
|
||||
@ -1718,8 +1723,8 @@ screen_selection_range_for_line(Screen *self, index_type y, index_type *start, i
|
||||
if (y >= self->lines) { return false; }
|
||||
Line *line = visual_line_(self, y);
|
||||
index_type xlimit = line->xnum, xstart = 0;
|
||||
while (xlimit > 0 && CHAR_IS_BLANK(line->cells[xlimit - 1].ch)) xlimit--;
|
||||
while (xstart < xlimit && CHAR_IS_BLANK(line->cells[xstart].ch)) xstart++;
|
||||
while (xlimit > 0 && CHAR_IS_BLANK(line->cpu_cells[xlimit - 1].ch)) xlimit--;
|
||||
while (xstart < xlimit && CHAR_IS_BLANK(line->cpu_cells[xstart].ch)) xstart++;
|
||||
*start = xstart; *end = xlimit;
|
||||
return true;
|
||||
}
|
||||
@ -1738,7 +1743,7 @@ screen_selection_range_for_word(Screen *self, index_type x, index_type *y1, inde
|
||||
index_type start, end;
|
||||
Line *line = visual_line_(self, *y1);
|
||||
*y2 = *y1;
|
||||
#define is_ok(x) (is_word_char((line->cells[x].ch)) || is_opt_word_char(line->cells[x].ch))
|
||||
#define is_ok(x) (is_word_char((line->cpu_cells[x].ch)) || is_opt_word_char(line->cpu_cells[x].ch))
|
||||
if (!is_ok(x)) {
|
||||
start = x; end = x + 1;
|
||||
} else {
|
||||
|
||||
@ -183,7 +183,7 @@ create_cell_vao() {
|
||||
#define A(name, size, dtype, offset, stride) \
|
||||
add_attribute_to_vao(CELL_PROGRAM, vao_idx, #name, \
|
||||
/*size=*/size, /*dtype=*/dtype, /*stride=*/stride, /*offset=*/offset, /*divisor=*/1);
|
||||
#define A1(name, size, dtype, offset) A(name, size, dtype, (void*)(offsetof(Cell, offset)), sizeof(Cell))
|
||||
#define A1(name, size, dtype, offset) A(name, size, dtype, (void*)(offsetof(GPUCell, offset)), sizeof(GPUCell))
|
||||
|
||||
add_buffer_to_vao(vao_idx, GL_ARRAY_BUFFER);
|
||||
A1(sprite_coords, 4, GL_UNSIGNED_SHORT, sprite_x);
|
||||
@ -267,7 +267,7 @@ cell_prepare_to_render(ssize_t vao_idx, ssize_t gvao_idx, Screen *screen, GLfloa
|
||||
ensure_sprite_map(fonts_data);
|
||||
|
||||
if (screen->scroll_changed || screen->is_dirty) {
|
||||
sz = sizeof(Cell) * screen->lines * screen->columns;
|
||||
sz = sizeof(GPUCell) * screen->lines * screen->columns;
|
||||
address = alloc_and_map_vao_buffer(vao_idx, sz, cell_data_buffer, GL_STREAM_DRAW, GL_WRITE_ONLY);
|
||||
screen_update_cell_data(screen, address, sz, fonts_data);
|
||||
unmap_vao_buffer(vao_idx, cell_data_buffer); address = NULL;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user