wip! Add ply-terminal-buffer object

We're going to need to do at least basic parsing of terminal text
so we can show the console to the user in color when they hit
escape and VTs are disabled.

This commit adds the start of a class to do this basic parsing.

The way it works is the terminal text is inject into the object
and all escape sequences are filtered out. When a color control
sequence is found, the current color is noted, along with which
characters it applies to. A list of the text spans and their
color attributes is then iteratable using a an api.
This commit is contained in:
Ray Strode 2023-05-05 16:22:11 -04:00
parent fcaa382d53
commit 1559e1d781
3 changed files with 234 additions and 0 deletions

View file

@ -7,6 +7,7 @@ libply_splash_core_sources = files(
'ply-pixel-display.c',
'ply-renderer.c',
'ply-terminal.c',
'ply-terminal-buffer.c',
'ply-text-display.c',
'ply-text-progress-bar.c',
'ply-text-step-bar.c',
@ -60,6 +61,7 @@ libply_splash_core_headers = files(
'ply-renderer-plugin.h',
'ply-renderer.h',
'ply-terminal.h',
'ply-terminal-buffer.h',
'ply-text-display.h',
'ply-text-progress-bar.h',
'ply-text-step-bar.h',

View file

@ -0,0 +1,174 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define PLY_TERMINAL_COLOR_ATTRIBUTE_OFFSET 30
typedef struct
{
ply_terminal_color_t color;
size_t start_index;
size_t span_length;
} ply_text_attribute_t;
struct _ply_terminal_buffer
{
ply_buffer_t *text_buffer;
ply_list_t *attributes;
};
typedef enum
{
PLY_TERMINAL_BUFFER_PARSER_STATE_UNESCAPED,
PLY_TERMINAL_BUFFER_PARSER_STATE_ESCAPED,
PLY_TERMINAL_BUFFER_PARSER_STATE_CONTROL_SEQUENCE,
PLY_TERMINAL_BUFFER_PARSER_STATE_CONTROL_SEQUENCE_PARAMETER
} ply_terminal_buffer_parser_state_t;
ply_terminal_buffer_t *
ply_terminal_buffer_new (void)
{
ply_terminal_buffer_t *terminal_buffer = calloc (1, sizeof(ply_terminal_buffer_t));
terminal_buffer->text_buffer = ply_buffer_new ();
return buffer;
}
void
ply_terminal_buffer_free (ply_terminal_buffer_t *terminal_buffer)
{
ply_list_node_t *node;
ply_buffer_free (terminal_buffer->text_buffer);
ply_list_foreach (terminal_buffer->attributes, node) {
ply_text_attribute_t *attribute;
attribute = ply_list_node_get_data (node);
free (attribute);
}
ply_list_free (terminal_buffer->attributes);
free (terminal_buffer);
}
void
ply_terminal_buffer_inject (ply_terminal_buffer_t *buffer,
const char *input,
size_t length)
{
ply_terminal_buffer_parser_state_t state = PLY_TERMINAL_BUFFER_PARSER_STATE_UNESCAPED;
ply_terminal_color_t color = PLY_TERMINAL_COLOR_WHITE;
size_t i = 0, end_index;
size_t parameter_start = 0;
start_index = ply_buffer_get_size (buffer->text_buffer);
end_index = start_index;
while (i < length) {
switch (state) {
case PLY_TERMINAL_BUFFER_PARSER_STATE_UNESCAPED:
if (input[i] == '\033') {
state = PLY_TERMINAL_BUFFER_PARSER_STATE_ESCAPED;
} else {
ply_buffer_append (buffer->text_buffer, "%c", input[i]);
end_index++;
}
break;
case PLY_TERMINAL_BUFFER_PARSER_STATE_ESCAPED:
if (input[i] == '[') {
state = PLY_TERMINAL_BUFFER_PARSER_STATE_CONTROL_SEQUENCE;
}
break;
case PLY_TERMINAL_BUFFER_PARSER_STATE_CONTROL_SEQUENCE:
if (isdigit (input[i])) {
parameter_start = i;
state = PLY_TERMINAL_BUFFER_PARSER_STATE_CONTROL_SEQUENCE_PARAMETER;
}
break;
case PLY_TERMINAL_BUFFER_PARSER_STATE_CONTROL_SEQUENCE_PARAMETER:
if (input[i] == ';' || input[i] == 'm') {
int parameter_value;
parameter_value = atoi (&input[parameter_start]) - PLY_TERMINAL_COLOR_ATTRIBUTE_OFFSET;
if (parameter_value >= PLY_TERMINAL_COLOR_BLACK && parameter_value <= PLY_TERMINAL_COLOR_WHITE) {
color = (ply_terminal_color_t) (parameter_value);
}
}
if (input[i] == ';') {
parameter_start = i + 1;
} else if (input[i] == 'm') {
if (end_index > start_index) {
ply_text_attribute_t *attribute;
attribute = calloc (1, sizeof(ply_text_attribute_t));
attribute->color = color;
attribute->start_index = start_index;
attribute->span_length = end_index - start_index;
ply_list_append_data (buffer->attributes, attribute);
}
state = PLY_TERMINAL_BUFFER_PARSER_STATE_UNESCAPED;
}
break;
}
i++;
}
if (state == PLY_TERMINAL_BUFFER_PARSER_STATE_CONTROL_SEQUENCE_PARAMETER &&
end_index > start_index) {
ply_text_attribute_t *attribute;
attribute = calloc (1, sizeof(ply_text_attribute_t));
attribute->color = color;
attribute->start_index = start_index;
attribute->span_length = end_index - start_index;
ply_list_append_data (buffer->attributes, attribute);
}
}
void
ply_terminal_buffer_iter_init (ply_terminal_buffer_iter_t *iter,
ply_terminal_buffer_t *buffer)
{
iter->buffer = buffer;
iter->node = ply_list_get_First_node (buffer->attributes);
}
int
ply_terminal_buffer_iter_next (ply_terminal_buffer_iter_t *iter,
ply_text_attribute_t **attribute,
const char **text,
size_t *start_index,
size_t *end_index)
{
ply_terminal_buffer_t *buffer;
ply_text_attribute_t *current_attribute, *next_attribute;
const char *bytes;
if (iter->node == NULL) {
return false;
}
buffer = iter->buffer;
bytes = ply_terminal_buffer_get_bytes (buffer);
current_attribute = ply_list_node_get_data (iter->node);
*text = bytes + current_attribute->start_index;
*attribute = current_attribute;
*start_index = current_attribute->start_index;
iter->node = ply_list_get_next_node (buffer->attributes, iter->node);
if (iter->node != NULL) {
next_attribute = ply_list_node_get_data (iter->node);
*end_index = next_attribute->start_index - 1;
} else {
size_t last_index = ply_buffer_get_size (buffer) - 1;
*end_index = last_index;
}
return true;
}

View file

@ -0,0 +1,58 @@
/* ply-terminal-buffer.h - APIs for handling terminal text
*
* Copyright (C) 2023 Red Hat, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* Written By: Ray Strode <rstrode@redhat.com>
*/
#ifndef PLY_TERMINAL_BUFFER_H
#define PLY_TERMINAL_BUFFER_H
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <unistd.h>
#include "ply-buffer.h"
#include "ply-event-loop.h"
typedef struct _ply_terminal_buffer ply_terminal_buffer_t;
typedef struct
{
ply_terminal_buffer_t *buffer;
ply_list_node_t *node;
} ply_terminal_buffer_iter_t;
#ifndef PLY_HIDE_FUNCTION_DECLARATIONS
ply_terminal_buffer_t *ply_terminal_buffer_new (void) :
void ply_terminal_buffer_free (ply_terminal_buffer_t *terminal_buffer);
void ply_terminal_buffer_inject (ply_terminal_buffer_t *buffer,
const char *input,
size_t length);
void ply_terminal_buffer_iter_init (ply_terminal_buffer_iter_t *iter,
ply_terminal_buffer_t *buffer);
int ply_terminal_buffer_iter_next (ply_terminal_buffer_iter_t *iter,
ply_text_attribute_t **attribute,
const char **text,
size_t *start_index,
size_t *end_index);
#endif
#endif /* PLY_TERMINAL_BUFFER_H */