#include "vterm_internal.h"
#include <stdio.h>
#include <string.h>
#include "rect.h"
#include "utf8.h"
#define UNICODE_SPACE 0x20
#define UNICODE_LINEFEED 0x0a
/* State of the pen at some moment in time, also used in a cell */
typedef struct
{
/* After the bitfield */
VTermColor fg, bg;
unsigned int bold : 1;
unsigned int underline : 2;
unsigned int italic : 1;
unsigned int blink : 1;
unsigned int reverse : 1;
unsigned int strike : 1;
unsigned int font : 4; /* 0 to 9 */
/* Extra state storage that isn't strictly pen-related */
unsigned int protected_cell : 1;
unsigned int dwl : 1; /* on a DECDWL or DECDHL line */
unsigned int dhl : 2; /* on a DECDHL line (1=top 2=bottom) */
} ScreenPen;
/* Internal representation of a screen cell */
typedef struct
{
uint32_t chars[VTERM_MAX_CHARS_PER_CELL];
ScreenPen pen;
} ScreenCell;
static int vterm_screen_set_cell(VTermScreen *screen, VTermPos pos, const VTermScreenCell *cell);
struct VTermScreen
{
VTerm *vt;
VTermState *state;
const VTermScreenCallbacks *callbacks;
void *cbdata;
VTermDamageSize damage_merge;
/* start_row == -1 => no damage */
VTermRect damaged;
VTermRect pending_scrollrect;
int pending_scroll_downward, pending_scroll_rightward;
int rows;
int cols;
int global_reverse;
/* Primary and Altscreen. buffers[1] is lazily allocated as needed */
ScreenCell *buffers[2];
/* buffer will == buffers[0] or buffers[1], depending on altscreen */
ScreenCell *buffer;
/* buffer for a single screen row used in scrollback storage callbacks */
VTermScreenCell *sb_buffer;
ScreenPen pen;
};
static 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 *realloc_buffer(VTermScreen *screen, ScreenCell *buffer, int new_rows, int new_cols)
{
ScreenCell *new_buffer = vterm_allocator_malloc(screen->vt, sizeof(ScreenCell) * new_rows * new_cols);
int row, col;
for(row = 0; row < new_rows; row++) {
for(col = 0; col < new_cols; col++) {
ScreenCell *new_cell = new_buffer + row*new_cols + col;
if(buffer && row < screen->rows && col < screen->cols)
*new_cell = buffer[row * screen->cols + col];
else {
new_cell->chars[0] = 0;
new_cell->pen = screen->pen;
}
}
}
if(buffer)
vterm_allocator_free(screen->vt, buffer);
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