Как добиться чего-то вроде V-syn c для каждой строки в эмуляторах терминала, использующих C? - PullRequest
0 голосов
/ 14 марта 2020

Я пытаюсь написать некоторый код, который позволил бы отображать трехмерную графику в консоли, используя символы и escape-последовательности (для цвета). Мне это нужно для одной конкретной c программы, которую я хочу написать, но, если возможно, я бы хотел сделать ее более универсальной. Я испытываю что-то вроде разрыва экрана и хочу избавиться от него (чтобы весь экран печатался «сразу»). Тест просто отображает экран, заполненный пробелами с белым и черным фоном (один полный белый кадр, а затем один полный черный) с интервалом в одну секунду.

Я пробовал:

  1. В начале я думал о буферизации строки на стандартный вывод. Попробовал как отключить его, так и создать полный буфер с размером, достаточным для удержания каждого символа на экране Второй вариант обеспечивает лучшие результаты, и под этим я подразумеваю, что меньше кадров разворачивается, но они все еще есть.

  2. Я подумал, что это может быть проблема с моим эмулятором терминала ( this вопрос натолкнул меня на мысль ) поэтому я начал возиться с другими. У меня лучший результат с Китти, но его пока нет.

  3. Следующее, что нужно было сделать, - это испортить конфигурацию Китти. Я заметил, что если я увеличу настройку 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
}

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...