2020-09-02 10:55:56 +03:00
|
|
|
#include <stdint.h>
|
|
|
|
#include <stddef.h>
|
|
|
|
#include <stdbool.h>
|
|
|
|
#include <lib/term.h>
|
2022-01-25 10:41:17 +03:00
|
|
|
#include <lib/real.h>
|
2022-08-27 00:44:47 +03:00
|
|
|
#include <lib/misc.h>
|
2021-12-14 08:47:28 +03:00
|
|
|
#include <mm/pmm.h>
|
2022-10-04 21:39:50 +03:00
|
|
|
#include <drivers/vga_textmode.h>
|
|
|
|
#include <term/backends/framebuffer.h>
|
2020-09-02 10:55:56 +03:00
|
|
|
|
2022-11-19 01:59:31 +03:00
|
|
|
#if defined (BIOS)
|
2022-09-23 21:53:14 +03:00
|
|
|
int current_video_mode = -1;
|
2022-11-19 01:59:31 +03:00
|
|
|
#endif
|
2022-09-23 21:53:14 +03:00
|
|
|
|
2022-11-19 01:59:31 +03:00
|
|
|
struct term_context **terms = NULL;
|
|
|
|
size_t terms_i = 0;
|
2022-09-23 21:53:14 +03:00
|
|
|
|
2022-11-19 01:59:31 +03:00
|
|
|
int term_backend = _NOT_READY;
|
2022-09-23 21:53:14 +03:00
|
|
|
|
2022-11-19 01:59:31 +03:00
|
|
|
void term_notready(void) {
|
|
|
|
for (size_t i = 0; i < terms_i; i++) {
|
|
|
|
struct term_context *term = terms[i];
|
2022-10-04 01:58:00 +03:00
|
|
|
|
|
|
|
term->deinit(term, pmm_free);
|
|
|
|
}
|
|
|
|
|
2022-11-19 01:59:31 +03:00
|
|
|
pmm_free(terms, terms_i * sizeof(void *));
|
2022-10-04 01:58:00 +03:00
|
|
|
|
2022-11-19 01:59:31 +03:00
|
|
|
terms_i = 0;
|
|
|
|
terms = NULL;
|
2022-10-04 01:58:00 +03:00
|
|
|
|
2022-11-19 01:59:31 +03:00
|
|
|
term_backend = _NOT_READY;
|
2022-10-04 01:58:00 +03:00
|
|
|
}
|
2022-09-23 21:53:14 +03:00
|
|
|
|
|
|
|
// --- fallback ---
|
|
|
|
|
|
|
|
#if defined (BIOS)
|
2022-10-04 01:58:00 +03:00
|
|
|
static void fallback_raw_putchar(struct term_context *ctx, uint8_t c) {
|
|
|
|
(void)ctx;
|
2022-09-23 21:53:14 +03:00
|
|
|
struct rm_regs r = {0};
|
|
|
|
r.eax = 0x0e00 | c;
|
|
|
|
rm_int(0x10, &r, &r);
|
|
|
|
}
|
|
|
|
|
2022-10-04 01:58:00 +03:00
|
|
|
static void fallback_set_cursor_pos(struct term_context *ctx, size_t x, size_t y);
|
|
|
|
static void fallback_get_cursor_pos(struct term_context *ctx, size_t *x, size_t *y);
|
2022-09-28 02:08:12 +03:00
|
|
|
|
2022-10-04 01:58:00 +03:00
|
|
|
static void fallback_clear(struct term_context *ctx, bool move) {
|
|
|
|
(void)ctx;
|
2022-09-28 02:08:12 +03:00
|
|
|
size_t x, y;
|
2022-10-04 01:58:00 +03:00
|
|
|
fallback_get_cursor_pos(NULL, &x, &y);
|
2022-09-23 21:53:14 +03:00
|
|
|
struct rm_regs r = {0};
|
|
|
|
rm_int(0x11, &r, &r);
|
|
|
|
switch ((r.eax >> 4) & 3) {
|
|
|
|
case 0:
|
|
|
|
r.eax = 3;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
r.eax = 1;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
r.eax = 3;
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
r.eax = 7;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
rm_int(0x10, &r, &r);
|
2022-09-28 02:08:12 +03:00
|
|
|
if (move) {
|
|
|
|
x = y = 0;
|
|
|
|
}
|
2022-10-04 01:58:00 +03:00
|
|
|
fallback_set_cursor_pos(NULL, x, y);
|
2022-09-23 21:53:14 +03:00
|
|
|
}
|
|
|
|
|
2022-10-04 01:58:00 +03:00
|
|
|
static void fallback_set_cursor_pos(struct term_context *ctx, size_t x, size_t y) {
|
|
|
|
(void)ctx;
|
2022-09-23 21:53:14 +03:00
|
|
|
struct rm_regs r = {0};
|
|
|
|
r.eax = 0x0200;
|
|
|
|
r.ebx = 0;
|
|
|
|
r.edx = (y << 8) + x;
|
|
|
|
rm_int(0x10, &r, &r);
|
|
|
|
}
|
|
|
|
|
2022-10-04 01:58:00 +03:00
|
|
|
static void fallback_get_cursor_pos(struct term_context *ctx, size_t *x, size_t *y) {
|
|
|
|
(void)ctx;
|
2022-09-23 21:53:14 +03:00
|
|
|
struct rm_regs r = {0};
|
|
|
|
r.eax = 0x0300;
|
|
|
|
r.ebx = 0;
|
|
|
|
rm_int(0x10, &r, &r);
|
|
|
|
*x = r.edx & 0xff;
|
|
|
|
*y = r.edx >> 8;
|
|
|
|
}
|
|
|
|
|
2022-10-04 01:58:00 +03:00
|
|
|
static void fallback_scroll(struct term_context *ctx) {
|
|
|
|
(void)ctx;
|
2022-09-28 02:08:12 +03:00
|
|
|
size_t x, y;
|
2022-10-04 01:58:00 +03:00
|
|
|
fallback_get_cursor_pos(NULL, &x, &y);
|
2022-11-19 01:59:31 +03:00
|
|
|
fallback_set_cursor_pos(NULL, ctx->cols - 1, ctx->rows - 1);
|
2022-10-04 01:58:00 +03:00
|
|
|
fallback_raw_putchar(NULL, ' ');
|
|
|
|
fallback_set_cursor_pos(NULL, x, y);
|
2022-09-28 02:08:12 +03:00
|
|
|
}
|
|
|
|
|
2022-09-23 21:53:14 +03:00
|
|
|
#elif defined (UEFI)
|
2022-09-28 02:08:12 +03:00
|
|
|
|
|
|
|
static size_t cursor_x = 0, cursor_y = 0;
|
|
|
|
|
2022-10-04 01:58:00 +03:00
|
|
|
static void fallback_scroll(struct term_context *ctx) {
|
|
|
|
(void)ctx;
|
2022-11-19 01:59:31 +03:00
|
|
|
gST->ConOut->SetCursorPosition(gST->ConOut, ctx->cols - 1, ctx->rows - 1);
|
2022-09-28 02:08:12 +03:00
|
|
|
CHAR16 string[2];
|
|
|
|
string[0] = ' ';
|
|
|
|
string[1] = 0;
|
|
|
|
gST->ConOut->OutputString(gST->ConOut, string);
|
|
|
|
gST->ConOut->SetCursorPosition(gST->ConOut, cursor_x, cursor_y);
|
|
|
|
}
|
2022-09-23 21:53:14 +03:00
|
|
|
|
2022-10-04 01:58:00 +03:00
|
|
|
static void fallback_raw_putchar(struct term_context *ctx, uint8_t c) {
|
2022-11-19 01:59:31 +03:00
|
|
|
if (!ctx->scroll_enabled && cursor_x == ctx->cols - 1 && cursor_y == ctx->rows - 1) {
|
2022-10-06 06:29:13 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
gST->ConOut->EnableCursor(gST->ConOut, true);
|
2022-09-23 21:53:14 +03:00
|
|
|
CHAR16 string[2];
|
|
|
|
string[0] = c;
|
|
|
|
string[1] = 0;
|
|
|
|
gST->ConOut->OutputString(gST->ConOut, string);
|
2022-11-19 01:59:31 +03:00
|
|
|
if (++cursor_x >= ctx->cols) {
|
2022-09-28 02:08:12 +03:00
|
|
|
cursor_x = 0;
|
2022-11-19 01:59:31 +03:00
|
|
|
if (++cursor_y >= ctx->rows) {
|
2022-09-28 02:08:12 +03:00
|
|
|
cursor_y--;
|
|
|
|
}
|
2022-09-23 21:53:14 +03:00
|
|
|
}
|
2022-09-28 02:08:12 +03:00
|
|
|
gST->ConOut->SetCursorPosition(gST->ConOut, cursor_x, cursor_y);
|
2022-09-23 21:53:14 +03:00
|
|
|
}
|
|
|
|
|
2022-10-04 01:58:00 +03:00
|
|
|
static void fallback_clear(struct term_context *ctx, bool move) {
|
|
|
|
(void)ctx;
|
2022-09-23 21:53:14 +03:00
|
|
|
gST->ConOut->ClearScreen(gST->ConOut);
|
2022-09-28 02:08:12 +03:00
|
|
|
if (move) {
|
|
|
|
cursor_x = cursor_y = 0;
|
|
|
|
}
|
|
|
|
gST->ConOut->SetCursorPosition(gST->ConOut, cursor_x, cursor_y);
|
2022-09-23 21:53:14 +03:00
|
|
|
}
|
|
|
|
|
2022-10-04 01:58:00 +03:00
|
|
|
static void fallback_set_cursor_pos(struct term_context *ctx, size_t x, size_t y) {
|
|
|
|
(void)ctx;
|
2022-11-19 01:59:31 +03:00
|
|
|
if (x >= ctx->cols || y >= ctx->rows) {
|
2022-09-23 21:53:14 +03:00
|
|
|
return;
|
2022-09-28 02:08:12 +03:00
|
|
|
}
|
2022-09-23 21:53:14 +03:00
|
|
|
gST->ConOut->SetCursorPosition(gST->ConOut, x, y);
|
|
|
|
cursor_x = x;
|
|
|
|
cursor_y = y;
|
|
|
|
}
|
|
|
|
|
2022-10-04 01:58:00 +03:00
|
|
|
static void fallback_get_cursor_pos(struct term_context *ctx, size_t *x, size_t *y) {
|
|
|
|
(void)ctx;
|
2022-09-23 21:53:14 +03:00
|
|
|
*x = cursor_x;
|
|
|
|
*y = cursor_y;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2022-11-19 01:59:31 +03:00
|
|
|
static bool dummy_handle(void) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-09-23 21:53:14 +03:00
|
|
|
void term_fallback(void) {
|
2022-09-27 07:36:04 +03:00
|
|
|
term_notready();
|
|
|
|
|
2022-09-23 21:53:14 +03:00
|
|
|
#if defined (UEFI)
|
|
|
|
if (!efi_boot_services_exited) {
|
|
|
|
#endif
|
2022-11-19 01:59:31 +03:00
|
|
|
|
|
|
|
terms = ext_mem_alloc(sizeof(void *));
|
|
|
|
terms_i = 1;
|
|
|
|
|
|
|
|
terms[0] = ext_mem_alloc(sizeof(struct term_context));
|
|
|
|
|
|
|
|
struct term_context *term = terms[0];
|
|
|
|
|
2022-10-04 01:58:00 +03:00
|
|
|
fallback_clear(NULL, true);
|
2022-11-19 01:59:31 +03:00
|
|
|
|
|
|
|
term->enable_cursor = (void *)dummy_handle;
|
|
|
|
term->disable_cursor = (void *)dummy_handle;
|
|
|
|
term->set_text_fg = (void *)dummy_handle;
|
|
|
|
term->set_text_bg = (void *)dummy_handle;
|
|
|
|
term->set_text_fg_bright = (void *)dummy_handle;
|
|
|
|
term->set_text_bg_bright = (void *)dummy_handle;
|
|
|
|
term->set_text_fg_rgb = (void *)dummy_handle;
|
|
|
|
term->set_text_bg_rgb = (void *)dummy_handle;
|
|
|
|
term->set_text_fg_default = (void *)dummy_handle;
|
|
|
|
term->set_text_bg_default = (void *)dummy_handle;
|
|
|
|
term->move_character = (void *)dummy_handle;
|
|
|
|
term->revscroll = (void *)dummy_handle;
|
|
|
|
term->swap_palette = (void *)dummy_handle;
|
|
|
|
term->save_state = (void *)dummy_handle;
|
|
|
|
term->restore_state = (void *)dummy_handle;
|
|
|
|
term->double_buffer_flush = (void *)dummy_handle;
|
|
|
|
term->full_refresh = (void *)dummy_handle;
|
|
|
|
term->deinit = (void *)dummy_handle;
|
|
|
|
|
2022-10-04 01:58:00 +03:00
|
|
|
term->raw_putchar = fallback_raw_putchar;
|
|
|
|
term->clear = fallback_clear;
|
|
|
|
term->set_cursor_pos = fallback_set_cursor_pos;
|
|
|
|
term->get_cursor_pos = fallback_get_cursor_pos;
|
|
|
|
term->scroll = fallback_scroll;
|
2022-09-28 02:08:12 +03:00
|
|
|
#if defined (UEFI)
|
|
|
|
UINTN uefi_term_x_size, uefi_term_y_size;
|
|
|
|
gST->ConOut->QueryMode(gST->ConOut, gST->ConOut->Mode->Mode, &uefi_term_x_size, &uefi_term_y_size);
|
2022-10-04 01:58:00 +03:00
|
|
|
term->cols = uefi_term_x_size;
|
|
|
|
term->rows = uefi_term_y_size;
|
2022-09-28 02:08:12 +03:00
|
|
|
#elif defined (BIOS)
|
2022-10-04 01:58:00 +03:00
|
|
|
term->cols = 80;
|
|
|
|
term->rows = 25;
|
2022-09-28 02:08:12 +03:00
|
|
|
#endif
|
2022-09-23 21:53:14 +03:00
|
|
|
term_backend = FALLBACK;
|
2022-10-04 01:58:00 +03:00
|
|
|
term_context_reinit(term);
|
|
|
|
|
|
|
|
term->in_bootloader = true;
|
2022-09-23 21:53:14 +03:00
|
|
|
#if defined (UEFI)
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2022-01-26 05:58:28 +03:00
|
|
|
extern void reset_term(void);
|
2022-02-07 04:10:57 +03:00
|
|
|
extern void set_cursor_pos_helper(size_t x, size_t y);
|
2022-01-26 05:58:28 +03:00
|
|
|
|
2022-01-25 10:41:17 +03:00
|
|
|
#if defined (__i386__)
|
|
|
|
#define TERM_XFER_CHUNK 8192
|
|
|
|
|
|
|
|
static uint8_t xfer_buf[TERM_XFER_CHUNK];
|
|
|
|
#endif
|
|
|
|
|
2022-11-19 01:59:31 +03:00
|
|
|
static uint64_t context_size(struct term_context *term) {
|
2022-10-04 21:39:50 +03:00
|
|
|
switch (term_backend) {
|
2022-10-04 21:47:19 +03:00
|
|
|
#if defined (BIOS)
|
2022-10-04 21:39:50 +03:00
|
|
|
case TEXTMODE:
|
|
|
|
return sizeof(struct textmode_context) + (VD_ROWS * VD_COLS) * 2;
|
2022-10-04 21:47:19 +03:00
|
|
|
#endif
|
2022-10-04 21:39:50 +03:00
|
|
|
case GTERM: {
|
|
|
|
struct fbterm_context *ctx = (void *)term;
|
|
|
|
return sizeof(struct fbterm_context) +
|
|
|
|
ctx->font_bits_size +
|
|
|
|
ctx->font_bool_size +
|
|
|
|
ctx->canvas_size +
|
|
|
|
ctx->grid_size +
|
|
|
|
ctx->queue_size +
|
|
|
|
ctx->map_size;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-19 01:59:31 +03:00
|
|
|
static void context_save(struct term_context *term, uint64_t buf) {
|
2022-10-04 21:39:50 +03:00
|
|
|
switch (term_backend) {
|
2022-10-04 21:47:19 +03:00
|
|
|
#if defined (BIOS)
|
2022-10-04 21:39:50 +03:00
|
|
|
case TEXTMODE: {
|
|
|
|
struct textmode_context *ctx = (void *)term;
|
|
|
|
memcpy32to64(buf, (uintptr_t)ctx, sizeof(struct textmode_context));
|
|
|
|
buf += sizeof(struct textmode_context);
|
|
|
|
memcpy32to64(buf, (uintptr_t)ctx->back_buffer, VD_ROWS * VD_COLS);
|
|
|
|
buf += VD_ROWS * VD_COLS;
|
|
|
|
memcpy32to64(buf, (uintptr_t)ctx->front_buffer, VD_ROWS * VD_COLS);
|
|
|
|
buf += VD_ROWS * VD_COLS;
|
|
|
|
break;
|
|
|
|
}
|
2022-10-04 21:47:19 +03:00
|
|
|
#endif
|
2022-10-04 21:39:50 +03:00
|
|
|
case GTERM: {
|
|
|
|
struct fbterm_context *ctx = (void *)term;
|
|
|
|
memcpy32to64(buf, (uintptr_t)ctx, sizeof(struct fbterm_context));
|
|
|
|
buf += sizeof(struct fbterm_context);
|
|
|
|
memcpy32to64(buf, (uintptr_t)ctx->font_bits, ctx->font_bits_size);
|
|
|
|
buf += ctx->font_bits_size;
|
|
|
|
memcpy32to64(buf, (uintptr_t)ctx->font_bool, ctx->font_bool_size);
|
|
|
|
buf += ctx->font_bool_size;
|
|
|
|
memcpy32to64(buf, (uintptr_t)ctx->canvas, ctx->canvas_size);
|
|
|
|
buf += ctx->canvas_size;
|
|
|
|
memcpy32to64(buf, (uintptr_t)ctx->grid, ctx->grid_size);
|
|
|
|
buf += ctx->grid_size;
|
|
|
|
memcpy32to64(buf, (uintptr_t)ctx->queue, ctx->queue_size);
|
|
|
|
buf += ctx->queue_size;
|
|
|
|
memcpy32to64(buf, (uintptr_t)ctx->map, ctx->map_size);
|
|
|
|
buf += ctx->map_size;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-19 01:59:31 +03:00
|
|
|
static void context_restore(struct term_context *term, uint64_t buf) {
|
2022-10-04 21:39:50 +03:00
|
|
|
switch (term_backend) {
|
2022-10-04 21:47:19 +03:00
|
|
|
#if defined (BIOS)
|
2022-10-04 21:39:50 +03:00
|
|
|
case TEXTMODE: {
|
|
|
|
struct textmode_context *ctx = (void *)term;
|
|
|
|
memcpy32to64((uintptr_t)ctx, buf, sizeof(struct textmode_context));
|
|
|
|
buf += sizeof(struct textmode_context);
|
|
|
|
memcpy32to64((uintptr_t)ctx->back_buffer, buf, VD_ROWS * VD_COLS);
|
|
|
|
buf += VD_ROWS * VD_COLS;
|
|
|
|
memcpy32to64((uintptr_t)ctx->front_buffer, buf, VD_ROWS * VD_COLS);
|
|
|
|
buf += VD_ROWS * VD_COLS;
|
|
|
|
break;
|
|
|
|
}
|
2022-10-04 21:47:19 +03:00
|
|
|
#endif
|
2022-10-04 21:39:50 +03:00
|
|
|
case GTERM: {
|
|
|
|
struct fbterm_context *ctx = (void *)term;
|
|
|
|
memcpy32to64((uintptr_t)ctx, buf, sizeof(struct fbterm_context));
|
|
|
|
buf += sizeof(struct fbterm_context);
|
|
|
|
memcpy32to64((uintptr_t)ctx->font_bits, buf, ctx->font_bits_size);
|
|
|
|
buf += ctx->font_bits_size;
|
|
|
|
memcpy32to64((uintptr_t)ctx->font_bool, buf, ctx->font_bool_size);
|
|
|
|
buf += ctx->font_bool_size;
|
|
|
|
memcpy32to64((uintptr_t)ctx->canvas, buf, ctx->canvas_size);
|
|
|
|
buf += ctx->canvas_size;
|
|
|
|
memcpy32to64((uintptr_t)ctx->grid, buf, ctx->grid_size);
|
|
|
|
buf += ctx->grid_size;
|
|
|
|
memcpy32to64((uintptr_t)ctx->queue, buf, ctx->queue_size);
|
|
|
|
buf += ctx->queue_size;
|
|
|
|
memcpy32to64((uintptr_t)ctx->map, buf, ctx->map_size);
|
|
|
|
buf += ctx->map_size;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-19 01:59:31 +03:00
|
|
|
void _term_write(struct term_context *term, uint64_t buf, uint64_t count) {
|
2022-01-25 10:41:17 +03:00
|
|
|
switch (count) {
|
|
|
|
case TERM_CTX_SIZE: {
|
2022-11-19 01:59:31 +03:00
|
|
|
uint64_t ret = context_size(term);
|
2022-10-04 21:39:50 +03:00
|
|
|
memcpy32to64(buf, (uint64_t)(uintptr_t)&ret, sizeof(uint64_t));
|
2022-01-25 10:41:17 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
case TERM_CTX_SAVE: {
|
2022-11-19 01:59:31 +03:00
|
|
|
context_save(term, buf);
|
2022-01-25 10:41:17 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
case TERM_CTX_RESTORE: {
|
2022-11-19 01:59:31 +03:00
|
|
|
context_restore(term, buf);
|
2022-01-25 10:41:17 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
case TERM_FULL_REFRESH: {
|
2022-10-04 01:58:00 +03:00
|
|
|
term->full_refresh(term);
|
2022-01-25 10:41:17 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool native = false;
|
2022-08-18 18:32:54 +03:00
|
|
|
#if defined (__x86_64__) || defined (__aarch64__)
|
2022-01-25 10:41:17 +03:00
|
|
|
native = true;
|
2022-08-18 18:32:54 +03:00
|
|
|
#elif !defined (__i386__)
|
|
|
|
#error Unknown architecture
|
2022-01-25 10:41:17 +03:00
|
|
|
#endif
|
|
|
|
|
2022-10-04 01:58:00 +03:00
|
|
|
bool autoflush = term->autoflush;
|
|
|
|
term->autoflush = false;
|
|
|
|
|
|
|
|
if (term->in_bootloader || native) {
|
2022-01-25 10:41:17 +03:00
|
|
|
const char *s = (const char *)(uintptr_t)buf;
|
|
|
|
|
2022-10-04 01:58:00 +03:00
|
|
|
term_write(term, s, count);
|
2022-01-25 10:41:17 +03:00
|
|
|
} else {
|
|
|
|
#if defined (__i386__)
|
|
|
|
while (count != 0) {
|
|
|
|
uint64_t chunk;
|
|
|
|
if (count > TERM_XFER_CHUNK) {
|
|
|
|
chunk = TERM_XFER_CHUNK;
|
|
|
|
} else {
|
|
|
|
chunk = count;
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy32to64((uint64_t)(uintptr_t)xfer_buf, buf, chunk);
|
|
|
|
|
2022-10-04 01:58:00 +03:00
|
|
|
term_write(term, (const char *)xfer_buf, chunk);
|
2022-01-25 10:41:17 +03:00
|
|
|
|
|
|
|
count -= chunk;
|
|
|
|
buf += chunk;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2022-10-04 01:58:00 +03:00
|
|
|
if (autoflush) {
|
|
|
|
term->double_buffer_flush(term);
|
2022-01-25 10:41:17 +03:00
|
|
|
}
|
|
|
|
|
2022-10-04 01:58:00 +03:00
|
|
|
term->autoflush = autoflush;
|
2022-01-25 10:41:17 +03:00
|
|
|
}
|