Я пытаюсь написать некоторый код, который позволил бы отображать трехмерную графику в консоли, используя символы и escape-последовательности (для цвета). Мне это нужно для одной конкретной c программы, которую я хочу написать, но, если возможно, я бы хотел сделать ее более универсальной. Я испытываю что-то вроде разрыва экрана и хочу избавиться от него (чтобы весь экран печатался «сразу»). Тест просто отображает экран, заполненный пробелами с белым и черным фоном (один полный белый кадр, а затем один полный черный) с интервалом в одну секунду.
Я пробовал:
В начале я думал о буферизации строки на стандартный вывод. Попробовал как отключить его, так и создать полный буфер с размером, достаточным для удержания каждого символа на экране Второй вариант обеспечивает лучшие результаты, и под этим я подразумеваю, что меньше кадров разворачивается, но они все еще есть.
Я подумал, что это может быть проблема с моим эмулятором терминала ( this вопрос натолкнул меня на мысль ) поэтому я начал возиться с другими. У меня лучший результат с Китти, но его пока нет.
- Следующее, что нужно было сделать, - это испортить конфигурацию Китти. Я заметил, что если я увеличу настройку input_delay примерно до 20 мс, проблема почти исчезнет. Лишь немногие, и не каждый кадр будет разорван.
Итак, я пришел к выводу, что на самом деле терминальные эмуляторы (или, по крайней мере, котята) работают слишком быстро, и здесь может быть какое-то состояние гонки, когда буфер еще не очищен полностью и TE отображает то, что было частично очищено и является частью старой рамы. Я ошибаюсь? Если нет, то каким-либо образом я могу заставить терминалы ждать ввода для finni sh перед его отображением или, по крайней мере, принудительно установить задержку ввода в C?
, вот соответствующая часть кода: main . c
#include "TermCTRL/termCTRL.h"
#include <stdint.h>
#include <stdio.h>
#include <unistd.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
int main()
{
termcell_t cell;
int k;
uint16_t x,y;
termCTRL_get_term_size(&x, &y);
sleep(1);
termCTRL_init();
uint8_t a = 0;
for(k=0; k<200; k++)
{
a^=255;
cell.bg.B = a;
cell.bg.G = a;
cell.bg.R = a;
cell.fg.B = a;
cell.fg.G = a;
cell.fg.R = a;
cell.symbol[0] = ' '; //symbol is in fact a string, because I want to use UTF chars too
cell.symbol[1] = '\0';
for(int xd=0; xd<x; xd++)
for(int yd=0; yd<y; yd++)
{
termCTRL_load_termcell(xd, yd, &cell);
}
termCTRL_update_screen();
sleep(1);
}
termCTRL_close();
return 0;
}
termCTRL.h
#pragma once
#include <stdint.h>
#define INPLACE_TERMCELL(FG_R, FG_G, FG_B, BG_R, BG_G, BG_B, SYMBOL) \
(termcell_t) { {FG_R, FG_G, FG_B}, {BG_R, BG_G, BG_B}, SYMBOL }
#define termCTRL_black_fill_screen() \
termCTRL_fill_screen(&INPLACE_TERMCELL(0, 0, 0, 0, 0, 0, " "))
typedef struct termcell_color_t
{
uint16_t R;
uint16_t G;
uint16_t B;
} termcell_color_t;
typedef struct termcell_t
{
termcell_color_t fg;
termcell_color_t bg;
char symbol[4];
} termcell_t;
typedef enum termCTRL_ERRNO
{
termCTRL_OUT_OF_BORDER = -2,
termCTRL_INVALID_TERMCELL = -1,
termCTRL_INTERNAL_ERROR = 0,
termCTRL_OK = 1,
} termCTRL_ERRNO;
void termCTRL_init();
void termCTRL_close();
void termCTRL_get_term_size(uint16_t *col, uint16_t *row);
termCTRL_ERRNO termCTRL_load_termcell(uint16_t x, uint16_t y, termcell_t *in);
void termCTRL_update_screen();
termCTRL_ERRNO termCTRL_fill_screen(termcell_t *cell);
termCTRL. c
#include "termCTRL.h"
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/ioctl.h>
#define CONVERTED_TERMCELL_SIZE 44
#define CAST_SCREEN_TO_BUFFER \
char (*screen_buffer)[term_xsize][term_ysize][CONVERTED_TERMCELL_SIZE]; \
screen_buffer = _screen_buffer
static void *_screen_buffer = NULL;
static uint16_t term_xsize, term_ysize;
static char *IO_buff = NULL;
void termCTRL_get_term_size(uint16_t *col, uint16_t *row)
{
struct winsize w;
ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
*col = w.ws_col;
*row = w.ws_row;
}
void int_decompose(uint8_t in, char *out)
{
uint8_t x = in/100;
out[0] = x + '0';
in -= x*100;
x = in/10;
out[1] = x + '0';
in -= x*10;
out[2] = in + '0';
}
termCTRL_ERRNO termCTRL_move_cursor(uint16_t x, uint16_t y)
{
char mov_str[] = "\x1b[000;000H";
if(x<term_xsize && y<term_ysize)
{
int_decompose(y, &mov_str[2]);
int_decompose(x, &mov_str[6]);
if(fputs(mov_str, stdout) == EOF) return termCTRL_INTERNAL_ERROR;
else return termCTRL_OK;
}
else
{
return termCTRL_OUT_OF_BORDER;
}
}
termCTRL_ERRNO termCTRL_load_termcell(uint16_t x, uint16_t y, termcell_t *in)
{
CAST_SCREEN_TO_BUFFER;
if(in == NULL) return termCTRL_INVALID_TERMCELL;
if(x >= term_xsize || y >= term_ysize) return termCTRL_OUT_OF_BORDER;
//because screen buffer was initialized, it is only needed to replace RGB values and symbol.
//whole escape sequence is already there
int_decompose(in->fg.R, &(*screen_buffer)[x][y][7]);
int_decompose(in->fg.G, &(*screen_buffer)[x][y][11]);
int_decompose(in->fg.B, &(*screen_buffer)[x][y][15]);
int_decompose(in->bg.R, &(*screen_buffer)[x][y][26]);
int_decompose(in->bg.G, &(*screen_buffer)[x][y][30]);
int_decompose(in->bg.B, &(*screen_buffer)[x][y][34]);
strcpy(&(*screen_buffer)[x][y][38], in->symbol); //copy symbol, note that it could be UTF char
return termCTRL_OK;
}
termCTRL_ERRNO termCTRL_fill_screen(termcell_t *cell)
{
uint16_t x, y;
termCTRL_ERRNO ret;
for(y=0; y<term_ysize; y++)
for(x=0; x<term_xsize; x++)
{
ret = termCTRL_load_termcell(x, y, cell);
if(ret != termCTRL_OK)
return ret;
}
return ret;
}
void termCTRL_update_screen()
{
uint16_t x, y;
CAST_SCREEN_TO_BUFFER;
termCTRL_move_cursor(0, 0);
for(y=0; y<term_ysize-1; y++)
{
for(x=0; x<term_xsize; x++)
fputs((*screen_buffer)[x][y], stdout);
fputs("\n", stdout);
}
//last line got special treatment because it can't have \n
for(x=0; x<term_xsize; x++)
fputs((*screen_buffer)[x][y], stdout);
fflush(stdout);
}
void termCTRL_init()
{
uint16_t x, y;
termCTRL_get_term_size(&term_xsize, &term_ysize);
IO_buff = calloc(term_xsize*term_ysize, CONVERTED_TERMCELL_SIZE);
setvbuf(stdout, IO_buff, _IOFBF, term_xsize*term_ysize*CONVERTED_TERMCELL_SIZE);
_screen_buffer = calloc(term_xsize*term_ysize, CONVERTED_TERMCELL_SIZE);
fputs("\e[?25l", stdout); //hide cursor
fputs("\x1b[2J", stdout); //clear screen
CAST_SCREEN_TO_BUFFER;
for(y=0; y<term_ysize; y++)
for (x=0; x<term_xsize; x++)
sprintf( (*screen_buffer)[x][y], "\x1b[38;2;200;200;000m\x1b[48;2;000;000;000m ");
termCTRL_update_screen();
}
void termCTRL_close()
{
free(_screen_buffer);
setvbuf(stdout, NULL, _IONBF, 0);
free(IO_buff);
printf("\e[?25h"); //show cursor
printf("\x1b[m"); //reset colors
printf("\x1b[2J"); //clear screen
}