#include <stdio.h>
#include <string.h>
#include "nvim/grid.h"
#include "nvim/mbyte.h"
#include "nvim/tui/termkey/termkey.h"
#include "nvim/vterm/pen.h"
#include "nvim/vterm/screen.h"
#include "nvim/vterm/state.h"
#include "nvim/vterm/vterm.h"
#include "nvim/vterm/vterm_defs.h"
#include "nvim/vterm/vterm_internal_defs.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "vterm/screen.c.generated.h"
#endif
#define UNICODE_SPACE 0x20
#define UNICODE_LINEFEED 0x0a
#undef DEBUG_REFLOW
static inline void clearcell(const VTermScreen *screen, ScreenCell *cell)
{
cell->schar = 0;
cell->pen = screen->pen;
}
ScreenCell *getcell(const VTermScreen *screen, int row, int col)
{
if (row < 0 || row >= screen->rows) {
return NULL;
}
if (col < 0 || col >= screen->cols) {
return NULL;
}
return screen->buffer + (screen->cols * row) + col;
}
static ScreenCell *alloc_buffer(VTermScreen *screen, int rows, int cols)
{
ScreenCell *new_buffer = vterm_allocator_malloc(screen->vt,
sizeof(ScreenCell) * (size_t)rows * (size_t)cols);
for (int row = 0; row < rows; row++) {
for (int col = 0; col < cols; col++) {
clearcell(screen, &new_buffer[row * cols + col]);
}
}
return new_buffer;
}
static void damagerect(VTermScreen *screen, VTermRect rect)
{
VTermRect emit;
switch (screen->damage_merge) {
case VTERM_DAMAGE_CELL:
// Always emit damage event
emit = rect;
break;
case VTERM_DAMAGE_ROW:
// Emit damage longer than one row. Try to merge with existing damage in the same row
if (rect.end_row > rect.start_row + 1) {
// Bigger than 1 line - flush existing, emit this
vterm_screen_flush_damage(screen);
emit = rect;
} else if (screen->damaged.start_row == -1) {
// None stored yet
screen->damaged = rect;
return;
} else if (rect.start_row == screen->damaged.start_row) {
// Merge with the stored line
if (screen->damaged.start_col > rect.start_col) {
screen->damaged.start_col = rect.start_col;
}
if (screen->damaged.end_col < rect.end_col) {
screen->damaged.end_col = rect.end_col;
}
return;
} else {
// Emit the currently stored line, store a new one
emit = screen->damaged;
screen->damaged = rect;
}
break;
case VTERM_DAMAGE_SCREEN:
case VTERM_DAMAGE_SCROLL:
// Never emit damage event
if (screen->damaged.start_row == -1) {
screen->damaged = rect;
} else {
rect_expand(&screen->damaged, &rect);
}
return;
default:
DEBUG_LOG("TODO: Maybe merge damage for level %d\n", screen->damage_merge);
return;
}
if (screen->callbacks && screen->callbacks->damage) {
(*screen->callbacks->damage)(emit, screen->cbdata);
}
}
static void damagescreen(VTermScreen *screen)
{
VTermRect rect = {
.start_row = 0,
.end_row = screen->rows,
.start_col = 0,
.end_col = screen->cols,
};
damagerect(screen, rect);
}
static int putglyph(VTermGlyphInfo *info, VTermPos pos, void *user)
{
VTermScreen *screen = user;
ScreenCell *cell = getcell(screen, pos.row, pos.col);
if (!cell) {
return 0;
}
cell->schar = info->schar;
if (info->schar != 0) {
cell->pen = screen->pen;
}
for (int col = 1; col < info->width; col++) {
getcell(screen, pos.row, pos.col + col)->schar = (uint32_t)-1;
}
VTermRect rect = {
.start_row = pos.row,
.end_row = pos.row + 1,
.start_col = pos.col,
.end_col = pos.col + info->width,
};
cell->pen.protected_cell = info->protected_cell;
cell->pen.dwl = info->dwl;
cell->pen.dhl = info->dhl;
damagerect(screen, rect);
return 1;
}
static void sb_pushline_from_row(VTermScreen *screen, int row)
{
VTermPos pos = { .row = row };
for (pos.col = 0; pos.col < screen->cols; pos.col++) {
vterm_screen_get_cell(screen, pos, screen->sb_buffer + pos.col);
}
(screen->callbacks->sb_pushline)(screen->cols, screen->sb_buffer, screen->cbdata);
}
static int moverect_internal(VTermRect dest, VTermRect src, void *user)
{
VTermScreen *screen = user;
if (screen->callbacks && screen->callbacks->sb_pushline
&& dest.start_row == 0 && dest.start_col == 0 // starts top-left corner
&& dest.end_col == screen->cols // full width
&& screen->buffer == screen->buffers[BUFIDX_PRIMARY]) { // not altscreen
for (int row = 0; row < src.start_row; row++) {
sb_pushline_from_row(screen, row);
}
}
int cols = src.end_col - src.start_col;
int downward = src.start_row - dest.start_row;
int init_row, test_row, inc_row;
if (downward < 0) {
init_row = dest.end_row - 1;
test_row = dest.start_row - 1;
inc_row = -1;
} else {
init_row = dest.start_row;
test_row = dest.end_row;
inc_row = +1;
}
for (int row = init_row; row != test_row; row += inc_row) {
memmove(getcell(screen, row, dest.start_col),
getcell(screen, row + downward, src.start_col),
(size_t)cols * sizeof(ScreenCell));
}
return 1;
}
static int moverect_user(VTermRect dest, VTermRect src, void *user)
{
VTermScreen *screen = user;
if (screen->callbacks
|