]> git.ipfire.org Git - thirdparty/plymouth.git/commitdiff
wip! Add ply-terminal-buffer object
authorRay Strode <rstrode@redhat.com>
Fri, 5 May 2023 20:22:11 +0000 (16:22 -0400)
committerRay Strode <rstrode@redhat.com>
Thu, 1 Jun 2023 17:25:50 +0000 (13:25 -0400)
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.

src/libply-splash-core/meson.build
src/libply-splash-core/ply-terminal-buffer.c [new file with mode: 0644]
src/libply-splash-core/ply-terminal-buffer.h [new file with mode: 0644]

index b54e7f6d34da17d69326c4e5a0f1a5a46b6ce080..b962ae9fb4731aaa9262b6b3eb421da385d1efad 100644 (file)
@@ -8,6 +8,7 @@ libply_splash_core_sources = files(
   'ply-pixel-display.c',
   'ply-renderer.c',
   'ply-terminal.c',
+  'ply-terminal-buffer.c',
   'ply-terminal-emulator.c',
   'ply-text-display.c',
   'ply-text-progress-bar.c',
@@ -63,6 +64,7 @@ libply_splash_core_headers = files(
   'ply-renderer-plugin.h',
   'ply-renderer.h',
   'ply-terminal.h',
+  'ply-terminal-buffer.h',
   'ply-terminal-emulator.h',
   'ply-text-display.h',
   'ply-text-progress-bar.h',
diff --git a/src/libply-splash-core/ply-terminal-buffer.c b/src/libply-splash-core/ply-terminal-buffer.c
new file mode 100644 (file)
index 0000000..68e4d45
--- /dev/null
@@ -0,0 +1,194 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "ply-terminal-buffer.h"
+
+#define PLY_TERMINAL_COLOR_ATTRIBUTE_OFFSET 30
+
+struct _ply_terminal_buffer
+{
+        ply_buffer_t *text_buffer;
+        ply_list_t   *attributes;
+};
+
+typedef struct
+{
+        ply_terminal_color_t color;
+        size_t               start_index;
+        size_t               span_length;
+} ply_text_attribute_t;
+
+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 terminal_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;
+        size_t parameter_start = 0;
+        ssize_t start_index;
+        ssize_t end_index;
+
+        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);
+        }
+}
+
+const char *
+ply_terminal_buffer_get_bytes (ply_terminal_buffer_t *buffer,
+                               size_t                *size)
+{
+        if (size != NULL)
+                *size = ply_buffer_get_size (buffer->text_buffer);
+
+        return ply_buffer_get_bytes (buffer->text_buffer);
+}
+
+void
+ply_terminal_buffer_iterator_init (ply_terminal_buffer_iterator_t *iterator,
+                                   ply_terminal_buffer_t          *buffer)
+{
+        iterator->buffer = buffer;
+        iterator->node = ply_list_get_first_node (buffer->attributes);
+}
+
+bool
+ply_terminal_buffer_iterator_next (ply_terminal_buffer_iterator_t  *iterator,
+                                   ply_terminal_color_t            *color,
+                                   const char                     **text,
+                                   size_t                          *start_index,
+                                   size_t                          *end_index)
+{
+        ply_terminal_buffer_t *terminal_buffer;
+        ply_text_attribute_t *current_attribute, *next_attribute;
+        const char *bytes;
+
+        if (iterator->node == NULL) {
+                return false;
+        }
+
+        terminal_buffer = iterator->buffer;
+        bytes = ply_terminal_buffer_get_bytes (terminal_buffer, NULL);
+
+        current_attribute = ply_list_node_get_data (iterator->node);
+        if (text != NULL)
+                *text = bytes + current_attribute->start_index;
+
+        if (color != NULL)
+                *color = current_attribute->color;
+
+        if (start_index != NULL)
+                *start_index = current_attribute->start_index;
+
+        iterator->node = ply_list_get_next_node (terminal_buffer->attributes, iterator->node);
+
+        if (iterator->node != NULL) {
+                next_attribute = ply_list_node_get_data (iterator->node);
+                if (end_index != NULL)
+                        *end_index = next_attribute->start_index - 1;
+        } else {
+                size_t last_index = ply_buffer_get_size (terminal_buffer->text_buffer) - 1;
+                if (end_index != NULL)
+                        *end_index = last_index;
+        }
+
+        return true;
+}
diff --git a/src/libply-splash-core/ply-terminal-buffer.h b/src/libply-splash-core/ply-terminal-buffer.h
new file mode 100644 (file)
index 0000000..c0388e1
--- /dev/null
@@ -0,0 +1,62 @@
+/* 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"
+#include "ply-list.h"
+#include "ply-terminal.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_iterator_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);
+const char *ply_terminal_buffer_get_bytes (ply_terminal_buffer_t *buffer,
+                                           size_t                *size);
+
+void ply_terminal_buffer_inject (ply_terminal_buffer_t *buffer,
+                                 const char            *input,
+                                 size_t                 length);
+void ply_terminal_buffer_iterator_init (ply_terminal_buffer_iterator_t *iterator,
+                                        ply_terminal_buffer_t      *buffer);
+bool ply_terminal_buffer_iterator_next (ply_terminal_buffer_iterator_t  *iterator,
+                                        ply_terminal_color_t            *color,
+                                        const char                     **text,
+                                        size_t                          *start_index,
+                                        size_t                          *end_index);
+
+#endif
+
+#endif /* PLY_TERMINAL_BUFFER_H */