* [PATCH v9 0/9] Input/Output Terminal Abstraction
@ 2025-04-06 22:05 Grant Erickson
2025-04-06 22:05 ` [PATCH v9 1/9] term: Initial revision Grant Erickson
` (9 more replies)
0 siblings, 10 replies; 14+ messages in thread
From: Grant Erickson @ 2025-04-06 22:05 UTC (permalink / raw)
To: ell
This expands on Marcel Holtman's 2023-12-22 RFCv4 patch for an
input/output terminal abstraction.
Substantive changes from the v7 version:
* Fixed a typo in the ASCII C0 and C1 control code mnemonics.
Grant Erickson (9):
term: Initial revision.
ell: Add include directive for 'ell/term.h'.
ell/Makefile: Added 'term.[ch]' to HEADERS and SOURCES.
term: Added 'l_term_*' symbols.
ell/term: Do not return -EPERM for 'putnstr' and 'vprint' if not
running.
ell/term: Return error on writes if the output descriptor is invalid.
ell/edit: Rename 'l_term_{open,close}'.
ell/term: Add an 'l_term_is_acquired' introspection function.
ell/term: Added ASCII C0 and C1 control code mnemonics.
Makefile.am | 2 +
ell/ell.h | 1 +
ell/ell.sym | 22 +++
ell/term.c | 501 ++++++++++++++++++++++++++++++++++++++++++++++++++++
ell/term.h | 215 ++++++++++++++++++++++
5 files changed, 741 insertions(+)
create mode 100644 ell/term.c
create mode 100644 ell/term.h
--
2.45.0
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v9 1/9] term: Initial revision.
2025-04-06 22:05 [PATCH v9 0/9] Input/Output Terminal Abstraction Grant Erickson
@ 2025-04-06 22:05 ` Grant Erickson
2025-04-06 22:05 ` [PATCH v9 2/9] ell: Add include directive for 'ell/term.h' Grant Erickson
` (8 subsequent siblings)
9 siblings, 0 replies; 14+ messages in thread
From: Grant Erickson @ 2025-04-06 22:05 UTC (permalink / raw)
To: ell
Initial revision of 'term.[hc]', an input/output terminal abstraction.
---
ell/term.c | 487 +++++++++++++++++++++++++++++++++++++++++++++++++++++
ell/term.h | 69 ++++++++
2 files changed, 556 insertions(+)
create mode 100644 ell/term.c
create mode 100644 ell/term.h
diff --git a/ell/term.c b/ell/term.c
new file mode 100644
index 000000000000..81df771ff07f
--- /dev/null
+++ b/ell/term.c
@@ -0,0 +1,487 @@
+/*
+ * Embedded Linux library
+ * Copyright (C) 2023-2024 Intel Corporation
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <termios.h>
+#include <sys/ioctl.h>
+
+#include "private.h"
+#include "signal.h"
+#include "io.h"
+#include "term.h"
+
+// MARK: Preprocessor Definitions
+
+#define IO_HANDLER(term, fd, readable, writable) \
+ do { \
+ if (term && term->io_handler) { \
+ term->io_handler(term, \
+ fd, \
+ readable, \
+ writable, \
+ term->io_data); \
+ } \
+ } while (0)
+
+// MARK: Type Declarations
+
+struct term_ops {
+ bool color_support;
+ bool use_sigwinch;
+ int (*get_winsize) (int fd, unsigned short *row, unsigned short *col);
+ int (*get_attr) (int fd, struct termios *c);
+ int (*set_attr) (int fd, const struct termios *c);
+};
+
+static int null_get_winsize(int fd, unsigned short *row, unsigned short *col)
+{
+ if (row) *row = 24;
+ if (col) *col = 80;
+ return 0;
+}
+
+static int null_get_attr(int fd, struct termios *c)
+{
+ return 0;
+}
+
+static int null_set_attr(int fd, const struct termios *c)
+{
+ return 0;
+}
+
+static const struct term_ops default_null_ops = {
+ .color_support = false,
+ .use_sigwinch = false,
+ .get_winsize = null_get_winsize,
+ .get_attr = null_get_attr,
+ .set_attr = null_set_attr,
+};
+
+static int tty_get_winsize(int fd, unsigned short *row, unsigned short *col)
+{
+ struct winsize ws;
+ int res;
+
+ res = ioctl(fd, TIOCGWINSZ, &ws);
+ if (!res) {
+ if (row) *row = ws.ws_row;
+ if (col) *col = ws.ws_col;
+ }
+ else
+ res = -errno;
+
+ return res;
+}
+
+static int tty_get_attr(int fd, struct termios *c)
+{
+ int retval = 0;
+
+ if (tcgetattr(fd, c) != 0)
+ retval = -errno;
+
+ return retval;
+}
+
+static int tty_set_attr(int fd, const struct termios *c)
+{
+ int retval = 0;
+
+ if (tcsetattr(fd, TCSANOW, c) != 0)
+ retval = -errno;
+
+ return retval;
+}
+
+static const struct term_ops default_tty_ops = {
+ .color_support = true,
+ .use_sigwinch = true,
+ .get_winsize = tty_get_winsize,
+ .get_attr = tty_get_attr,
+ .set_attr = tty_set_attr,
+};
+
+struct l_term {
+ int in_fd;
+ int out_fd;
+ l_term_io_func_t io_handler;
+ void *io_data;
+ const struct term_ops *in_ops;
+ const struct term_ops *out_ops;
+ struct termios in_termios;
+ struct termios out_termios;
+ unsigned short num_row;
+ unsigned short num_col;
+ struct l_signal *sigwinch;
+ bool is_running;
+ char key_buf[8];
+ size_t key_len;
+ l_term_key_func_t key_handler;
+ void *key_data;
+};
+
+LIB_EXPORT struct l_term *l_term_new(void)
+{
+ struct l_term *term;
+
+ term = l_new(struct l_term, 1);
+
+ term->in_fd = -1;
+ term->in_ops = NULL;
+
+ term->out_fd = -1;
+ term->out_ops = NULL;
+
+ term->is_running = false;
+
+ return term;
+}
+
+LIB_EXPORT void l_term_free(struct l_term *term)
+{
+ if (!term)
+ return;
+
+ l_free(term);
+}
+
+LIB_EXPORT int l_term_set_io_handler(struct l_term *term,
+ l_term_io_func_t handler, void *user_data)
+{
+ if (!term)
+ return -EINVAL;
+
+ term->io_handler = handler;
+ term->io_data = user_data;
+
+ return 0;
+}
+
+LIB_EXPORT int l_term_set_input(struct l_term *term, int fd)
+{
+ if (!term)
+ return -EINVAL;
+
+ if (fd < 0)
+ return -EBADF;
+
+ term->in_fd = fd;
+ term->in_ops = NULL;
+
+ IO_HANDLER(term, fd, 1, 0);
+
+ return 0;
+}
+
+LIB_EXPORT int l_term_set_output(struct l_term *term, int fd)
+{
+ if (!term)
+ return -EINVAL;
+
+ if (fd < 0)
+ return -EBADF;
+
+ term->out_fd = fd;
+ term->out_ops = NULL;
+
+ IO_HANDLER(term, fd, 0, 1);
+
+ return 0;
+}
+
+LIB_EXPORT int l_term_set_input_stdin(struct l_term *term)
+{
+ return l_term_set_input(term, STDIN_FILENO);
+}
+
+LIB_EXPORT int l_term_set_output_stdout(struct l_term *term)
+{
+ return l_term_set_output(term, STDOUT_FILENO);
+}
+
+LIB_EXPORT int l_term_set_key_handler(struct l_term *term,
+ l_term_key_func_t handler, void *user_data)
+{
+ if (!term)
+ return -EINVAL;
+
+ term->key_handler = handler;
+ term->key_data = user_data;
+
+ return 0;
+}
+
+LIB_EXPORT bool l_term_io_callback(struct l_io *io, void *user_data)
+{
+ struct l_term *term = user_data;
+
+ l_term_process(term);
+
+ return true;
+}
+
+static void sigwinch_handler(void *user_data)
+{
+ struct l_term *term = user_data;
+
+ term->out_ops->get_winsize(term->out_fd,
+ &term->num_row, &term->num_col);
+}
+
+LIB_EXPORT int l_term_open(struct l_term *term)
+{
+ struct termios termios;
+ int retval = 0;
+
+ if (!term)
+ return -EINVAL;
+
+ /* Missing input or output file descriptor is a non-recoverable
+ * situation at this point.
+ */
+ if (term->in_fd < 0 || term->out_fd < 0)
+ return -EBADF;
+
+ /* If no input operations are provided, fallback to use TTY
+ * defaults or null setting.
+ */
+ if (!term->in_ops) {
+ if (isatty(term->in_fd))
+ term->in_ops = &default_tty_ops;
+ else
+ term->in_ops = &default_null_ops;
+ }
+
+ /* If no output operations are provided, fallback to use TTY
+ * defaults or null setting.
+ */
+ if (!term->out_ops) {
+ if (isatty(term->out_fd))
+ term->out_ops = &default_tty_ops;
+ else
+ term->out_ops = &default_null_ops;
+ }
+
+ /* Save current termios setting of input */
+ memset(&term->in_termios, 0, sizeof(term->in_termios));
+ retval = term->in_ops->get_attr(term->in_fd, &term->in_termios);
+ if (retval < 0)
+ return retval;
+
+ /* Save current termios setting of output */
+ memset(&term->out_termios, 0, sizeof(term->out_termios));
+ retval = term->out_ops->get_attr(term->out_fd, &term->out_termios);
+ if (retval < 0)
+ return retval;
+
+ /* Disable canonical mode (ICANON), disable echoing of input
+ * characters (ECHO) and disable generating signals.
+ *
+ * In noncanonical mode input is available immediately (without
+ * the user having to type a line-delimiter character), no input
+ * processing is performed, and line editing is disabled.
+ *
+ * When any of the characters INTR, QUIT, SUSP, or DSUSP are
+ * received, don't generate the corresponding signal.
+ */
+ memcpy(&termios, &term->in_termios, sizeof(termios));
+ termios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG);
+ retval = term->in_ops->set_attr(term->in_fd, &termios);
+ if (retval < 0)
+ return retval;
+
+ /* Send TIOCGWINSZ ioctl to retrieve col and row number */
+ retval = term->out_ops->get_winsize(term->out_fd,
+ &term->num_row, &term->num_col);
+ if (retval < 0)
+ return retval;
+
+ /* Setup SIGWINCH window resize signal handler if supported */
+ if (term->out_ops->use_sigwinch)
+ term->sigwinch = l_signal_create(SIGWINCH, sigwinch_handler,
+ term, NULL);
+
+ IO_HANDLER(term, term->in_fd, 1, 0);
+ IO_HANDLER(term, term->out_fd, 0, 1);
+
+ term->is_running = true;
+
+ return retval;
+}
+
+LIB_EXPORT int l_term_close(struct l_term *term)
+{
+ int retval = 0;
+
+ if (!term)
+ return -EINVAL;
+
+ term->is_running = false;
+
+ IO_HANDLER(term, term->in_fd, 0, 0);
+ IO_HANDLER(term, term->out_fd, 0, 0);
+
+ /* Remove SIGWINCH window resize signal handler */
+ if (term->out_ops->use_sigwinch)
+ l_signal_remove(term->sigwinch);
+
+ /* Restore previous termios setting from input and output */
+ retval = term->in_ops->set_attr(term->in_fd, &term->in_termios);
+ if (retval < 0)
+ return retval;
+
+ retval = term->out_ops->set_attr(term->out_fd, &term->out_termios);
+ if (retval < 0)
+ return retval;
+
+ return retval;
+}
+
+LIB_EXPORT void l_term_process(struct l_term *term)
+{
+ wchar_t wstr[2];
+ ssize_t len;
+ mbstate_t ps;
+
+ if (!term)
+ return;
+
+ len = read(term->in_fd, term->key_buf + term->key_len,
+ sizeof(term->key_buf) - term->key_len);
+ if (len < 0)
+ return;
+
+ term->key_len += len;
+
+ while (term->key_len > 0) {
+ memset(&ps, 0, sizeof(ps));
+
+ len = mbrtowc(wstr, term->key_buf, term->key_len, &ps);
+ if (len < 0)
+ break;
+
+ memmove(term->key_buf, term->key_buf + len,
+ term->key_len - len);
+ term->key_len -= len;
+
+ if (term->key_handler) {
+ wint_t wch = wstr[0];
+ term->key_handler(term, wch, term->key_data);
+ }
+ }
+}
+
+LIB_EXPORT int l_term_putnstr(struct l_term *term, const char *str, size_t n)
+{
+ ssize_t res;
+
+ if (!term)
+ return -EINVAL;
+
+ if (!term->is_running)
+ return -EPERM;
+
+ res = write(term->out_fd, str, n);
+ if (res < 0)
+ return -errno;
+
+ return 0;
+}
+
+LIB_EXPORT int l_term_putstr(struct l_term *term, const char *str)
+{
+ if (!str)
+ return -EINVAL;
+
+ return l_term_putnstr(term, str, strlen(str));
+}
+
+LIB_EXPORT int l_term_putchar(struct l_term *term, int ch)
+{
+ char c = ch;
+
+ return l_term_putnstr(term, &c, 1);
+}
+
+LIB_EXPORT int l_term_print(struct l_term *term, const char *str, ...)
+{
+ va_list ap;
+ int retval;
+
+ va_start(ap, str);
+
+ retval = l_term_vprint(term, str, ap);
+
+ va_end(ap);
+
+ return retval;
+}
+
+LIB_EXPORT int l_term_vprint(struct l_term *term, const char *str, va_list ap)
+{
+ if (!term || !str)
+ return -EINVAL;
+
+ if (!term->is_running)
+ return -EPERM;
+
+ if (vdprintf(term->out_fd, str, ap) < 0)
+ return -errno;
+
+ return 0;
+}
+
+LIB_EXPORT int l_term_set_bounds(struct l_term *term, uint16_t rows,
+ uint16_t columns)
+{
+ if (!term)
+ return -EINVAL;
+
+ term->num_row = rows;
+ term->num_col = columns;
+
+ return 0;
+}
+
+LIB_EXPORT int l_term_get_rows(struct l_term *term, uint16_t *rows)
+{
+ int retval = 0;
+
+ if (!term)
+ return -EINVAL;
+
+ if (!term->out_ops)
+ return -ENOSYS;
+
+ retval = term->out_ops->get_winsize(term->out_fd, rows, NULL);
+
+ return retval;
+}
+
+LIB_EXPORT int l_term_get_columns(struct l_term *term, uint16_t *columns)
+{
+ int retval = 0;
+
+ if (!term)
+ return -EINVAL;
+
+ if (!term->out_ops)
+ return -ENOSYS;
+
+ retval = term->out_ops->get_winsize(term->out_fd, NULL, columns);
+
+ return retval;
+}
diff --git a/ell/term.h b/ell/term.h
new file mode 100644
index 000000000000..89ed6c97c7ce
--- /dev/null
+++ b/ell/term.h
@@ -0,0 +1,69 @@
+/*
+ * Embedded Linux library
+ * Copyright (C) 2023-2024 Intel Corporation
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#ifndef __ELL_TERM_H
+#define __ELL_TERM_H
+
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <wchar.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct l_term;
+
+struct l_term *l_term_new(void);
+void l_term_free(struct l_term *term);
+
+typedef void (*l_term_io_func_t)(struct l_term *term,
+ int fd,
+ bool readable,
+ bool writable,
+ void *user_data);
+
+int l_term_set_io_handler(struct l_term *term,
+ l_term_io_func_t handler,
+ void *user_data);
+
+int l_term_set_input(struct l_term *term, int fd);
+int l_term_set_output(struct l_term *term, int fd);
+
+int l_term_set_input_stdin(struct l_term *term);
+int l_term_set_output_stdout(struct l_term *term);
+
+typedef void (*l_term_key_func_t) (struct l_term *term, wint_t wch, void *user_data);
+
+int l_term_set_key_handler(struct l_term *term,
+ l_term_key_func_t handler, void *user_data);
+
+int l_term_open(struct l_term *term);
+int l_term_close(struct l_term *term);
+
+bool l_term_io_callback(struct l_io *io, void *user_data);
+
+void l_term_process(struct l_term *term);
+
+int l_term_putnstr(struct l_term *term, const char *str, size_t n);
+int l_term_putstr(struct l_term *term, const char *str);
+int l_term_putchar(struct l_term *term, int ch);
+int l_term_print(struct l_term *term, const char *str, ...);
+int l_term_vprint(struct l_term *term, const char *str, va_list ap);
+
+int l_term_set_bounds(struct l_term *term, uint16_t rows, uint16_t columns);
+
+int l_term_get_rows(struct l_term *term, uint16_t *rows);
+int l_term_get_columns(struct l_term *term, uint16_t *columns);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __ELL_TERM_H */
--
2.45.0
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH v9 2/9] ell: Add include directive for 'ell/term.h'.
2025-04-06 22:05 [PATCH v9 0/9] Input/Output Terminal Abstraction Grant Erickson
2025-04-06 22:05 ` [PATCH v9 1/9] term: Initial revision Grant Erickson
@ 2025-04-06 22:05 ` Grant Erickson
2025-04-06 22:05 ` [PATCH v9 3/9] ell/Makefile: Added 'term.[ch]' to HEADERS and SOURCES Grant Erickson
` (7 subsequent siblings)
9 siblings, 0 replies; 14+ messages in thread
From: Grant Erickson @ 2025-04-06 22:05 UTC (permalink / raw)
To: ell
---
ell/ell.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/ell/ell.h b/ell/ell.h
index 7a263e9376de..1176c163f4f9 100644
--- a/ell/ell.h
+++ b/ell/ell.h
@@ -46,6 +46,7 @@
#include <ell/ecc.h>
#include <ell/ecdh.h>
#include <ell/time.h>
+#include <ell/term.h>
#include <ell/gpio.h>
#include <ell/path.h>
#include <ell/acd.h>
--
2.45.0
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH v9 3/9] ell/Makefile: Added 'term.[ch]' to HEADERS and SOURCES.
2025-04-06 22:05 [PATCH v9 0/9] Input/Output Terminal Abstraction Grant Erickson
2025-04-06 22:05 ` [PATCH v9 1/9] term: Initial revision Grant Erickson
2025-04-06 22:05 ` [PATCH v9 2/9] ell: Add include directive for 'ell/term.h' Grant Erickson
@ 2025-04-06 22:05 ` Grant Erickson
2025-04-06 22:06 ` [PATCH v9 4/9] term: Added 'l_term_*' symbols Grant Erickson
` (6 subsequent siblings)
9 siblings, 0 replies; 14+ messages in thread
From: Grant Erickson @ 2025-04-06 22:05 UTC (permalink / raw)
To: ell
---
Makefile.am | 2 ++
1 file changed, 2 insertions(+)
diff --git a/Makefile.am b/Makefile.am
index 84d008febe0c..abe5ac905684 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -55,6 +55,7 @@ pkginclude_HEADERS = ell/ell.h \
ell/ecc.h \
ell/ecdh.h \
ell/time.h \
+ ell/term.h \
ell/gpio.h \
ell/path.h \
ell/icmp6.h \
@@ -148,6 +149,7 @@ ell_libell_la_SOURCES = $(linux_headers) \
ell/ecdh.c \
ell/time.c \
ell/time-private.h \
+ ell/term.c \
ell/gpio.c \
ell/path.c \
ell/icmp6.c \
--
2.45.0
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH v9 4/9] term: Added 'l_term_*' symbols.
2025-04-06 22:05 [PATCH v9 0/9] Input/Output Terminal Abstraction Grant Erickson
` (2 preceding siblings ...)
2025-04-06 22:05 ` [PATCH v9 3/9] ell/Makefile: Added 'term.[ch]' to HEADERS and SOURCES Grant Erickson
@ 2025-04-06 22:06 ` Grant Erickson
2025-04-06 22:06 ` [PATCH v9 5/9] ell/term: Do not return -EPERM for 'putnstr' and 'vprint' if not running Grant Erickson
` (5 subsequent siblings)
9 siblings, 0 replies; 14+ messages in thread
From: Grant Erickson @ 2025-04-06 22:06 UTC (permalink / raw)
To: ell
---
ell/ell.sym | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)
diff --git a/ell/ell.sym b/ell/ell.sym
index f192c471a40c..0d009e2d1927 100644
--- a/ell/ell.sym
+++ b/ell/ell.sym
@@ -643,6 +643,27 @@ global:
l_ecdh_generate_shared_secret;
/* time */
l_time_now;
+ /* term */
+ l_term_new;
+ l_term_free;
+ l_term_set_io_handler;
+ l_term_set_input;
+ l_term_set_input_stdin;
+ l_term_set_output;
+ l_term_set_output_stdout;
+ l_term_set_key_handler;
+ l_term_open;
+ l_term_close;
+ l_term_io_callback;
+ l_term_process;
+ l_term_putnstr;
+ l_term_putstr;
+ l_term_putchar;
+ l_term_print;
+ l_term_vprint;
+ l_term_set_bounds;
+ l_term_get_rows;
+ l_term_get_columns;
/* gpio */
l_gpio_chips_with_line_label;
l_gpio_chip_new;
--
2.45.0
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH v9 5/9] ell/term: Do not return -EPERM for 'putnstr' and 'vprint' if not running.
2025-04-06 22:05 [PATCH v9 0/9] Input/Output Terminal Abstraction Grant Erickson
` (3 preceding siblings ...)
2025-04-06 22:06 ` [PATCH v9 4/9] term: Added 'l_term_*' symbols Grant Erickson
@ 2025-04-06 22:06 ` Grant Erickson
2025-04-06 22:06 ` [PATCH v9 6/9] ell/term: Return error on writes if the output descriptor is invalid Grant Erickson
` (4 subsequent siblings)
9 siblings, 0 replies; 14+ messages in thread
From: Grant Erickson @ 2025-04-06 22:06 UTC (permalink / raw)
To: ell
If the 'is_running' flag is not asserted there is no need to return
-EPERM since it is still valid to write to the terminal when it is not
running.
---
ell/term.c | 6 ------
1 file changed, 6 deletions(-)
diff --git a/ell/term.c b/ell/term.c
index 81df771ff07f..c867b240090c 100644
--- a/ell/term.c
+++ b/ell/term.c
@@ -391,9 +391,6 @@ LIB_EXPORT int l_term_putnstr(struct l_term *term, const char *str, size_t n)
if (!term)
return -EINVAL;
- if (!term->is_running)
- return -EPERM;
-
res = write(term->out_fd, str, n);
if (res < 0)
return -errno;
@@ -435,9 +432,6 @@ LIB_EXPORT int l_term_vprint(struct l_term *term, const char *str, va_list ap)
if (!term || !str)
return -EINVAL;
- if (!term->is_running)
- return -EPERM;
-
if (vdprintf(term->out_fd, str, ap) < 0)
return -errno;
--
2.45.0
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH v9 6/9] ell/term: Return error on writes if the output descriptor is invalid.
2025-04-06 22:05 [PATCH v9 0/9] Input/Output Terminal Abstraction Grant Erickson
` (4 preceding siblings ...)
2025-04-06 22:06 ` [PATCH v9 5/9] ell/term: Do not return -EPERM for 'putnstr' and 'vprint' if not running Grant Erickson
@ 2025-04-06 22:06 ` Grant Erickson
2025-04-06 22:06 ` [PATCH v9 7/9] ell/edit: Rename 'l_term_{open,close}' Grant Erickson
` (3 subsequent siblings)
9 siblings, 0 replies; 14+ messages in thread
From: Grant Erickson @ 2025-04-06 22:06 UTC (permalink / raw)
To: ell
Returns -EBADF for 'putnstr' and 'vdprintf' if the output descriptor
for the terminal is invalid.
---
ell/term.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/ell/term.c b/ell/term.c
index c867b240090c..7ec92476e14e 100644
--- a/ell/term.c
+++ b/ell/term.c
@@ -391,6 +391,9 @@ LIB_EXPORT int l_term_putnstr(struct l_term *term, const char *str, size_t n)
if (!term)
return -EINVAL;
+ if (term->out_fd < 0)
+ return -EBADF;
+
res = write(term->out_fd, str, n);
if (res < 0)
return -errno;
@@ -432,6 +435,9 @@ LIB_EXPORT int l_term_vprint(struct l_term *term, const char *str, va_list ap)
if (!term || !str)
return -EINVAL;
+ if (term->out_fd < 0)
+ return -EBADF;
+
if (vdprintf(term->out_fd, str, ap) < 0)
return -errno;
--
2.45.0
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH v9 7/9] ell/edit: Rename 'l_term_{open,close}'.
2025-04-06 22:05 [PATCH v9 0/9] Input/Output Terminal Abstraction Grant Erickson
` (5 preceding siblings ...)
2025-04-06 22:06 ` [PATCH v9 6/9] ell/term: Return error on writes if the output descriptor is invalid Grant Erickson
@ 2025-04-06 22:06 ` Grant Erickson
2025-04-06 22:06 ` [PATCH v9 8/9] ell/term: Add an 'l_term_is_acquired' introspection function Grant Erickson
` (2 subsequent siblings)
9 siblings, 0 replies; 14+ messages in thread
From: Grant Erickson @ 2025-04-06 22:06 UTC (permalink / raw)
To: ell
Renames 'l_term_{open,close}' to 'l_term_{acquire,release}' since this
aligns more closely with what is happening with the underlying file
descriptors. In particular, those descriptors are not being opened or
closed. Rather, their TTY parameters are being saved and altered on
'open' (now 'acquire') and restored on 'close' (now 'release'). At no
point are the descriptors being transformed with the 'open' or 'close'
system calls.
---
ell/ell.sym | 4 ++--
ell/term.c | 18 ++++++++++++------
ell/term.h | 4 ++--
3 files changed, 16 insertions(+), 10 deletions(-)
diff --git a/ell/ell.sym b/ell/ell.sym
index 0d009e2d1927..2e198bd57461 100644
--- a/ell/ell.sym
+++ b/ell/ell.sym
@@ -652,8 +652,8 @@ global:
l_term_set_output;
l_term_set_output_stdout;
l_term_set_key_handler;
- l_term_open;
- l_term_close;
+ l_term_acquire;
+ l_term_release;
l_term_io_callback;
l_term_process;
l_term_putnstr;
diff --git a/ell/term.c b/ell/term.c
index 7ec92476e14e..f3801bcb1200 100644
--- a/ell/term.c
+++ b/ell/term.c
@@ -126,7 +126,7 @@ struct l_term {
unsigned short num_row;
unsigned short num_col;
struct l_signal *sigwinch;
- bool is_running;
+ bool is_acquired;
char key_buf[8];
size_t key_len;
l_term_key_func_t key_handler;
@@ -145,7 +145,7 @@ LIB_EXPORT struct l_term *l_term_new(void)
term->out_fd = -1;
term->out_ops = NULL;
- term->is_running = false;
+ term->is_acquired = false;
return term;
}
@@ -241,7 +241,7 @@ static void sigwinch_handler(void *user_data)
&term->num_row, &term->num_col);
}
-LIB_EXPORT int l_term_open(struct l_term *term)
+LIB_EXPORT int l_term_acquire(struct l_term *term)
{
struct termios termios;
int retval = 0;
@@ -249,6 +249,9 @@ LIB_EXPORT int l_term_open(struct l_term *term)
if (!term)
return -EINVAL;
+ if (term->is_acquired)
+ return -EALREADY;
+
/* Missing input or output file descriptor is a non-recoverable
* situation at this point.
*/
@@ -317,19 +320,20 @@ LIB_EXPORT int l_term_open(struct l_term *term)
IO_HANDLER(term, term->in_fd, 1, 0);
IO_HANDLER(term, term->out_fd, 0, 1);
- term->is_running = true;
+ term->is_acquired = true;
return retval;
}
-LIB_EXPORT int l_term_close(struct l_term *term)
+LIB_EXPORT int l_term_release(struct l_term *term)
{
int retval = 0;
if (!term)
return -EINVAL;
- term->is_running = false;
+ if (!term->is_acquired)
+ return -EALREADY;
IO_HANDLER(term, term->in_fd, 0, 0);
IO_HANDLER(term, term->out_fd, 0, 0);
@@ -347,6 +351,8 @@ LIB_EXPORT int l_term_close(struct l_term *term)
if (retval < 0)
return retval;
+ term->is_acquired = false;
+
return retval;
}
diff --git a/ell/term.h b/ell/term.h
index 89ed6c97c7ce..50831c166c78 100644
--- a/ell/term.h
+++ b/ell/term.h
@@ -44,8 +44,8 @@ typedef void (*l_term_key_func_t) (struct l_term *term, wint_t wch, void *user_d
int l_term_set_key_handler(struct l_term *term,
l_term_key_func_t handler, void *user_data);
-int l_term_open(struct l_term *term);
-int l_term_close(struct l_term *term);
+int l_term_acquire(struct l_term *term);
+int l_term_release(struct l_term *term);
bool l_term_io_callback(struct l_io *io, void *user_data);
--
2.45.0
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH v9 8/9] ell/term: Add an 'l_term_is_acquired' introspection function.
2025-04-06 22:05 [PATCH v9 0/9] Input/Output Terminal Abstraction Grant Erickson
` (6 preceding siblings ...)
2025-04-06 22:06 ` [PATCH v9 7/9] ell/edit: Rename 'l_term_{open,close}' Grant Erickson
@ 2025-04-06 22:06 ` Grant Erickson
2025-04-06 22:06 ` [PATCH v9 9/9] ell/term: Added ASCII C0 and C1 control code mnemonics Grant Erickson
2025-05-27 17:20 ` [PATCH v9 0/9] Input/Output Terminal Abstraction Marcel Holtmann
9 siblings, 0 replies; 14+ messages in thread
From: Grant Erickson @ 2025-04-06 22:06 UTC (permalink / raw)
To: ell
Adds an 'l_term_is_acquired' introspection function that indicates
whether the terminal has acquired and modified the TTY settings for
the input and output descriptors associated with the terminal.
---
ell/ell.sym | 1 +
ell/term.c | 8 ++++++++
ell/term.h | 2 ++
3 files changed, 11 insertions(+)
diff --git a/ell/ell.sym b/ell/ell.sym
index 2e198bd57461..9c149d717e48 100644
--- a/ell/ell.sym
+++ b/ell/ell.sym
@@ -652,6 +652,7 @@ global:
l_term_set_output;
l_term_set_output_stdout;
l_term_set_key_handler;
+ l_term_is_acquired;
l_term_acquire;
l_term_release;
l_term_io_callback;
diff --git a/ell/term.c b/ell/term.c
index f3801bcb1200..a56ede4e98ec 100644
--- a/ell/term.c
+++ b/ell/term.c
@@ -241,6 +241,14 @@ static void sigwinch_handler(void *user_data)
&term->num_row, &term->num_col);
}
+LIB_EXPORT bool l_term_is_acquired(struct l_term *term)
+{
+ if (!term)
+ return false;
+
+ return term->is_acquired;
+}
+
LIB_EXPORT int l_term_acquire(struct l_term *term)
{
struct termios termios;
diff --git a/ell/term.h b/ell/term.h
index 50831c166c78..66059212b436 100644
--- a/ell/term.h
+++ b/ell/term.h
@@ -44,6 +44,8 @@ typedef void (*l_term_key_func_t) (struct l_term *term, wint_t wch, void *user_d
int l_term_set_key_handler(struct l_term *term,
l_term_key_func_t handler, void *user_data);
+bool l_term_is_acquired(struct l_term *term);
+
int l_term_acquire(struct l_term *term);
int l_term_release(struct l_term *term);
--
2.45.0
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH v9 9/9] ell/term: Added ASCII C0 and C1 control code mnemonics.
2025-04-06 22:05 [PATCH v9 0/9] Input/Output Terminal Abstraction Grant Erickson
` (7 preceding siblings ...)
2025-04-06 22:06 ` [PATCH v9 8/9] ell/term: Add an 'l_term_is_acquired' introspection function Grant Erickson
@ 2025-04-06 22:06 ` Grant Erickson
2025-05-27 17:20 ` [PATCH v9 0/9] Input/Output Terminal Abstraction Marcel Holtmann
9 siblings, 0 replies; 14+ messages in thread
From: Grant Erickson @ 2025-04-06 22:06 UTC (permalink / raw)
To: ell
Added ASCII C0 and C1 control code mnemonics useful to anyone
implementing a l_term key handler.
---
ell/term.h | 144 +++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 144 insertions(+)
diff --git a/ell/term.h b/ell/term.h
index 66059212b436..7a0338bd95e0 100644
--- a/ell/term.h
+++ b/ell/term.h
@@ -18,6 +18,150 @@
extern "C" {
#endif
+enum {
+ L_TERM_CONTROL_CODE_ASCII_C0_NUL = 0x00,
+ L_TERM_CONTROL_CODE_ASCII_C0_SOH = 0x01,
+ L_TERM_CONTROL_CODE_ASCII_C0_TC1 = L_TERM_CONTROL_CODE_ASCII_C0_SOH,
+ L_TERM_CONTROL_CODE_ASCII_C0_CTRL_A = L_TERM_CONTROL_CODE_ASCII_C0_SOH,
+ L_TERM_CONTROL_CODE_ASCII_C0_STX = 0x02,
+ L_TERM_CONTROL_CODE_ASCII_C0_TC2 = L_TERM_CONTROL_CODE_ASCII_C0_STX,
+ L_TERM_CONTROL_CODE_ASCII_C0_CTRL_B = L_TERM_CONTROL_CODE_ASCII_C0_STX,
+ L_TERM_CONTROL_CODE_ASCII_C0_ETX = 0x03,
+ L_TERM_CONTROL_CODE_ASCII_C0_TC3 = L_TERM_CONTROL_CODE_ASCII_C0_ETX,
+ L_TERM_CONTROL_CODE_ASCII_C0_CTRL_C = L_TERM_CONTROL_CODE_ASCII_C0_ETX,
+ L_TERM_CONTROL_CODE_ASCII_C0_EOT = 0x04,
+ L_TERM_CONTROL_CODE_ASCII_C0_TC4 = L_TERM_CONTROL_CODE_ASCII_C0_EOT,
+ L_TERM_CONTROL_CODE_ASCII_C0_CTRL_D = L_TERM_CONTROL_CODE_ASCII_C0_EOT,
+ L_TERM_CONTROL_CODE_ASCII_C0_ENQ = 0x05,
+ L_TERM_CONTROL_CODE_ASCII_C0_TC5 = L_TERM_CONTROL_CODE_ASCII_C0_ENQ,
+ L_TERM_CONTROL_CODE_ASCII_C0_CTRL_E = L_TERM_CONTROL_CODE_ASCII_C0_ENQ,
+ L_TERM_CONTROL_CODE_ASCII_C0_ACK = 0x06,
+ L_TERM_CONTROL_CODE_ASCII_C0_TC6 = L_TERM_CONTROL_CODE_ASCII_C0_ACK,
+ L_TERM_CONTROL_CODE_ASCII_C0_CTRL_F = L_TERM_CONTROL_CODE_ASCII_C0_ACK,
+ L_TERM_CONTROL_CODE_ASCII_C0_BEL = 0x07,
+ L_TERM_CONTROL_CODE_ASCII_C0_BS = 0x08,
+ L_TERM_CONTROL_CODE_ASCII_C0_FE0 = L_TERM_CONTROL_CODE_ASCII_C0_BS,
+ L_TERM_CONTROL_CODE_ASCII_C0_CTRL_H = L_TERM_CONTROL_CODE_ASCII_C0_BS,
+ L_TERM_CONTROL_CODE_ASCII_C0_HT = 0x09,
+ L_TERM_CONTROL_CODE_ASCII_C0_FE1 = L_TERM_CONTROL_CODE_ASCII_C0_HT,
+ L_TERM_CONTROL_CODE_ASCII_C0_CTRL_I = L_TERM_CONTROL_CODE_ASCII_C0_HT,
+ L_TERM_CONTROL_CODE_ASCII_C0_LF = 0x0A,
+ L_TERM_CONTROL_CODE_ASCII_C0_FE2 = L_TERM_CONTROL_CODE_ASCII_C0_LF,
+ L_TERM_CONTROL_CODE_ASCII_C0_CTRL_J = L_TERM_CONTROL_CODE_ASCII_C0_LF,
+ L_TERM_CONTROL_CODE_ASCII_C0_VT = 0x0B,
+ L_TERM_CONTROL_CODE_ASCII_C0_FE3 = L_TERM_CONTROL_CODE_ASCII_C0_VT,
+ L_TERM_CONTROL_CODE_ASCII_C0_CTRL_K = L_TERM_CONTROL_CODE_ASCII_C0_VT,
+ L_TERM_CONTROL_CODE_ASCII_C0_FF = 0x0C,
+ L_TERM_CONTROL_CODE_ASCII_C0_FE4 = L_TERM_CONTROL_CODE_ASCII_C0_FF,
+ L_TERM_CONTROL_CODE_ASCII_C0_CTRL_L = L_TERM_CONTROL_CODE_ASCII_C0_FF,
+ L_TERM_CONTROL_CODE_ASCII_C0_CR = 0x0D,
+ L_TERM_CONTROL_CODE_ASCII_C0_FE5 = L_TERM_CONTROL_CODE_ASCII_C0_CR,
+ L_TERM_CONTROL_CODE_ASCII_C0_CTRL_M = L_TERM_CONTROL_CODE_ASCII_C0_CR,
+ L_TERM_CONTROL_CODE_ASCII_C0_SO = 0x0E,
+ L_TERM_CONTROL_CODE_ASCII_C0_LS0 = L_TERM_CONTROL_CODE_ASCII_C0_SO,
+ L_TERM_CONTROL_CODE_ASCII_C0_CTRL_N = L_TERM_CONTROL_CODE_ASCII_C0_SO,
+ L_TERM_CONTROL_CODE_ASCII_C0_SI = 0x0F,
+ L_TERM_CONTROL_CODE_ASCII_C0_LS1 = L_TERM_CONTROL_CODE_ASCII_C0_SI,
+ L_TERM_CONTROL_CODE_ASCII_C0_CTRL_O = L_TERM_CONTROL_CODE_ASCII_C0_SI,
+ L_TERM_CONTROL_CODE_ASCII_C0_DLE = 0x10,
+ L_TERM_CONTROL_CODE_ASCII_C0_TC7 = L_TERM_CONTROL_CODE_ASCII_C0_DLE,
+ L_TERM_CONTROL_CODE_ASCII_C0_DC0 = L_TERM_CONTROL_CODE_ASCII_C0_DLE,
+ L_TERM_CONTROL_CODE_ASCII_C0_CTRL_P = L_TERM_CONTROL_CODE_ASCII_C0_DLE,
+ L_TERM_CONTROL_CODE_ASCII_C0_XON = 0x11,
+ L_TERM_CONTROL_CODE_ASCII_C0_DC1 = L_TERM_CONTROL_CODE_ASCII_C0_XON,
+ L_TERM_CONTROL_CODE_ASCII_C0_CTRL_Q = L_TERM_CONTROL_CODE_ASCII_C0_XON,
+ L_TERM_CONTROL_CODE_ASCII_C0_TAPE = 0x12,
+ L_TERM_CONTROL_CODE_ASCII_C0_DC2 = L_TERM_CONTROL_CODE_ASCII_C0_TAPE,
+ L_TERM_CONTROL_CODE_ASCII_C0_CTRL_R = L_TERM_CONTROL_CODE_ASCII_C0_TAPE,
+ L_TERM_CONTROL_CODE_ASCII_C0_XOFF = 0x13,
+ L_TERM_CONTROL_CODE_ASCII_C0_DC3 = L_TERM_CONTROL_CODE_ASCII_C0_XOFF,
+ L_TERM_CONTROL_CODE_ASCII_C0_CTRL_S = L_TERM_CONTROL_CODE_ASCII_C0_XOFF,
+ L_TERM_CONTROL_CODE_ASCII_C0_DC4 = 0x14,
+ L_TERM_CONTROL_CODE_ASCII_C0_CTRL_T = L_TERM_CONTROL_CODE_ASCII_C0_DC4,
+ L_TERM_CONTROL_CODE_ASCII_C0_NAK = 0x15,
+ L_TERM_CONTROL_CODE_ASCII_C0_TC8 = L_TERM_CONTROL_CODE_ASCII_C0_NAK,
+ L_TERM_CONTROL_CODE_ASCII_C0_CTRL_U = 0x15,
+ L_TERM_CONTROL_CODE_ASCII_C0_SYN = 0x16,
+ L_TERM_CONTROL_CODE_ASCII_C0_TC9 = L_TERM_CONTROL_CODE_ASCII_C0_SYN,
+ L_TERM_CONTROL_CODE_ASCII_C0_CTRL_V = 0x16,
+ L_TERM_CONTROL_CODE_ASCII_C0_ETB = 0x17,
+ L_TERM_CONTROL_CODE_ASCII_C0_TC10 = L_TERM_CONTROL_CODE_ASCII_C0_ETB,
+ L_TERM_CONTROL_CODE_ASCII_C0_CTRL_W = L_TERM_CONTROL_CODE_ASCII_C0_ETB,
+ L_TERM_CONTROL_CODE_ASCII_C0_CAN = 0x18,
+ L_TERM_CONTROL_CODE_ASCII_C0_EM = 0x19,
+ L_TERM_CONTROL_CODE_ASCII_C0_SUB = 0x1A,
+ L_TERM_CONTROL_CODE_ASCII_C0_ESC = 0x1B,
+ L_TERM_CONTROL_CODE_ASCII_C0_FS = 0x1C,
+ L_TERM_CONTROL_CODE_ASCII_C0_IS4 = L_TERM_CONTROL_CODE_ASCII_C0_FS,
+ L_TERM_CONTROL_CODE_ASCII_C0_GS = 0x1D,
+ L_TERM_CONTROL_CODE_ASCII_C0_IS3 = L_TERM_CONTROL_CODE_ASCII_C0_GS,
+ L_TERM_CONTROL_CODE_ASCII_C0_RS = 0x1E,
+ L_TERM_CONTROL_CODE_ASCII_C0_IS2 = L_TERM_CONTROL_CODE_ASCII_C0_RS,
+ L_TERM_CONTROL_CODE_ASCII_C0_US = 0x1F,
+ L_TERM_CONTROL_CODE_ASCII_C0_IS1 = L_TERM_CONTROL_CODE_ASCII_C0_US,
+ L_TERM_CONTROL_CODE_ASCII_C0_DEL = 0x7F,
+
+ L_TERM_CONTROL_CODE_ASCII_C1_PAD = 0x80,
+ L_TERM_CONTROL_CODE_ASCII_C1_HOP = 0x81,
+ L_TERM_CONTROL_CODE_ASCII_C1_ESC_A = L_TERM_CONTROL_CODE_ASCII_C1_HOP,
+ L_TERM_CONTROL_CODE_ASCII_C1_BPH = 0x82,
+ L_TERM_CONTROL_CODE_ASCII_C1_ESC_B = L_TERM_CONTROL_CODE_ASCII_C1_BPH,
+ L_TERM_CONTROL_CODE_ASCII_C1_NBH = 0x83,
+ L_TERM_CONTROL_CODE_ASCII_C1_ESC_C = L_TERM_CONTROL_CODE_ASCII_C1_NBH,
+ L_TERM_CONTROL_CODE_ASCII_C1_IND = 0x84,
+ L_TERM_CONTROL_CODE_ASCII_C1_ESC_D = L_TERM_CONTROL_CODE_ASCII_C1_IND,
+ L_TERM_CONTROL_CODE_ASCII_C1_NEL = 0x85,
+ L_TERM_CONTROL_CODE_ASCII_C1_ESC_E = L_TERM_CONTROL_CODE_ASCII_C1_NEL,
+ L_TERM_CONTROL_CODE_ASCII_C1_SSA = 0x86,
+ L_TERM_CONTROL_CODE_ASCII_C1_ESC_F = L_TERM_CONTROL_CODE_ASCII_C1_SSA,
+ L_TERM_CONTROL_CODE_ASCII_C1_ESA = 0x87,
+ L_TERM_CONTROL_CODE_ASCII_C1_ESC_G = L_TERM_CONTROL_CODE_ASCII_C1_ESA,
+ L_TERM_CONTROL_CODE_ASCII_C1_HTS = 0x88,
+ L_TERM_CONTROL_CODE_ASCII_C1_ESC_H = L_TERM_CONTROL_CODE_ASCII_C1_HTS,
+ L_TERM_CONTROL_CODE_ASCII_C1_HTJ = 0x89,
+ L_TERM_CONTROL_CODE_ASCII_C1_ESC_I = L_TERM_CONTROL_CODE_ASCII_C1_HTJ,
+ L_TERM_CONTROL_CODE_ASCII_C1_VTS = 0x8A,
+ L_TERM_CONTROL_CODE_ASCII_C1_ESC_J = L_TERM_CONTROL_CODE_ASCII_C1_VTS,
+ L_TERM_CONTROL_CODE_ASCII_C1_PLD = 0x8B,
+ L_TERM_CONTROL_CODE_ASCII_C1_ESC_K = L_TERM_CONTROL_CODE_ASCII_C1_PLD,
+ L_TERM_CONTROL_CODE_ASCII_C1_PLU = 0x8C,
+ L_TERM_CONTROL_CODE_ASCII_C1_ESC_L = L_TERM_CONTROL_CODE_ASCII_C1_PLU,
+ L_TERM_CONTROL_CODE_ASCII_C1_RI = 0x8D,
+ L_TERM_CONTROL_CODE_ASCII_C1_ESC_M = L_TERM_CONTROL_CODE_ASCII_C1_RI,
+ L_TERM_CONTROL_CODE_ASCII_C1_SS2 = 0x8E,
+ L_TERM_CONTROL_CODE_ASCII_C1_ESC_N = L_TERM_CONTROL_CODE_ASCII_C1_SS2,
+ L_TERM_CONTROL_CODE_ASCII_C1_SS3 = 0x8F,
+ L_TERM_CONTROL_CODE_ASCII_C1_ESC_O = L_TERM_CONTROL_CODE_ASCII_C1_SS3,
+ L_TERM_CONTROL_CODE_ASCII_C1_DCS = 0x90,
+ L_TERM_CONTROL_CODE_ASCII_C1_ESC_P = L_TERM_CONTROL_CODE_ASCII_C1_DCS,
+ L_TERM_CONTROL_CODE_ASCII_C1_PU1 = 0x91,
+ L_TERM_CONTROL_CODE_ASCII_C1_ESC_Q = L_TERM_CONTROL_CODE_ASCII_C1_PU1,
+ L_TERM_CONTROL_CODE_ASCII_C1_PU2 = 0x92,
+ L_TERM_CONTROL_CODE_ASCII_C1_ESC_R = L_TERM_CONTROL_CODE_ASCII_C1_PU2,
+ L_TERM_CONTROL_CODE_ASCII_C1_STS = 0x93,
+ L_TERM_CONTROL_CODE_ASCII_C1_ESC_S = L_TERM_CONTROL_CODE_ASCII_C1_STS,
+ L_TERM_CONTROL_CODE_ASCII_C1_CCH = 0x94,
+ L_TERM_CONTROL_CODE_ASCII_C1_ESC_T = L_TERM_CONTROL_CODE_ASCII_C1_CCH,
+ L_TERM_CONTROL_CODE_ASCII_C1_MW = 0x95,
+ L_TERM_CONTROL_CODE_ASCII_C1_ESC_U = L_TERM_CONTROL_CODE_ASCII_C1_MW,
+ L_TERM_CONTROL_CODE_ASCII_C1_SPA = 0x96,
+ L_TERM_CONTROL_CODE_ASCII_C1_ESC_V = L_TERM_CONTROL_CODE_ASCII_C1_SPA,
+ L_TERM_CONTROL_CODE_ASCII_C1_EPA = 0x97,
+ L_TERM_CONTROL_CODE_ASCII_C1_ESC_W = L_TERM_CONTROL_CODE_ASCII_C1_EPA,
+ L_TERM_CONTROL_CODE_ASCII_C1_SOS = 0x98,
+ L_TERM_CONTROL_CODE_ASCII_C1_ESC_X = L_TERM_CONTROL_CODE_ASCII_C1_SOS,
+ L_TERM_CONTROL_CODE_ASCII_C1_SGC = 0x99,
+ L_TERM_CONTROL_CODE_ASCII_C1_SGCI = L_TERM_CONTROL_CODE_ASCII_C1_SGC,
+ L_TERM_CONTROL_CODE_ASCII_C1_ESC_Y = L_TERM_CONTROL_CODE_ASCII_C1_SGC,
+ L_TERM_CONTROL_CODE_ASCII_C1_SCI = 0x9A,
+ L_TERM_CONTROL_CODE_ASCII_C1_ESC_Z = L_TERM_CONTROL_CODE_ASCII_C1_SCI,
+ L_TERM_CONTROL_CODE_ASCII_C1_CSI = 0x9B,
+ L_TERM_CONTROL_CODE_ASCII_C1_ST = 0x9C,
+ L_TERM_CONTROL_CODE_ASCII_C1_OSC = 0x9D,
+ L_TERM_CONTROL_CODE_ASCII_C1_PM = 0x9E,
+ L_TERM_CONTROL_CODE_ASCII_C1_APC = 0x9F
+};
+
struct l_term;
struct l_term *l_term_new(void);
--
2.45.0
^ permalink raw reply related [flat|nested] 14+ messages in thread
* Re: [PATCH v9 0/9] Input/Output Terminal Abstraction
2025-04-06 22:05 [PATCH v9 0/9] Input/Output Terminal Abstraction Grant Erickson
` (8 preceding siblings ...)
2025-04-06 22:06 ` [PATCH v9 9/9] ell/term: Added ASCII C0 and C1 control code mnemonics Grant Erickson
@ 2025-05-27 17:20 ` Marcel Holtmann
2025-05-27 18:08 ` Grant Erickson
9 siblings, 1 reply; 14+ messages in thread
From: Marcel Holtmann @ 2025-05-27 17:20 UTC (permalink / raw)
To: Grant Erickson; +Cc: ell
Hi Grant,
> This expands on Marcel Holtman's 2023-12-22 RFCv4 patch for an
> input/output terminal abstraction.
>
> Substantive changes from the v7 version:
>
> * Fixed a typo in the ASCII C0 and C1 control code mnemonics.
>
> Grant Erickson (9):
> term: Initial revision.
I actually reverted it back to my version and included printf / vprintf helpers.
What is the purpose of l_term_set_bounds? Is it important for non-TTY setups since for TTY compatible terminals, this should not be needed.
The l_term_io_callback and l_term_process is something I don’t really like since I rather keep that internal.
> ell: Add include directive for 'ell/term.h'.
> ell/Makefile: Added 'term.[ch]' to HEADERS and SOURCES.
> term: Added 'l_term_*' symbols.
These 3 were already in my latest set.
> ell/term: Do not return -EPERM for 'putnstr' and 'vprint' if not
> running.
Why is that important. It seems like a hack. If you haven’t successfully acquired the terminal, there is no point in writing to it since you have no idea what termios setting are dealing with.
> ell/term: Return error on writes if the output descriptor is invalid.
Seems like a fix for the previous change.
> ell/edit: Rename 'l_term_{open,close}'.
I incorporated that into my set. Makes sense to me.
> ell/term: Add an 'l_term_is_acquired' introspection function.
I added it, but don’t really know how it would be used.
> ell/term: Added ASCII C0 and C1 control code mnemonics.
Hmm. We could do that, but the constant name is too long for my taste. And why use an enum instead of defines?
Regards
Marcel
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v9 0/9] Input/Output Terminal Abstraction
2025-05-27 17:20 ` [PATCH v9 0/9] Input/Output Terminal Abstraction Marcel Holtmann
@ 2025-05-27 18:08 ` Grant Erickson
2025-05-28 14:34 ` Grant Erickson
0 siblings, 1 reply; 14+ messages in thread
From: Grant Erickson @ 2025-05-27 18:08 UTC (permalink / raw)
To: Marcel Holtmann; +Cc: ell
On May 27, 2025, at 10:20 AM, Marcel Holtmann <marcel@holtmann.org> wrote:
>
>
> Hi Grant,
>
>> This expands on Marcel Holtman's 2023-12-22 RFCv4 patch for an
>> input/output terminal abstraction.
>>
>> Substantive changes from the v7 version:
>>
>> * Fixed a typo in the ASCII C0 and C1 control code mnemonics.
>>
>> Grant Erickson (9):
>> term: Initial revision.
>
> I actually reverted it back to my version and included printf / vprintf helpers.
>
> What is the purpose of l_term_set_bounds? Is it important for non-TTY setups since for TTY compatible terminals, this should not be needed.
It’s been a year and a half since I first deploy this, so bear with me as I attempt to clear some dust and cobwebs from some of the details.
Regarding l_term_set_bounds, It was a function that I appeared to have used during early prototyping but never ended up leveraging in this implementation wrapper:
status_t
Terminal :: SetBounds(
const uint16_t &inRows,
const uint16_t &inColumns
)
{
status_t lRetval = STATUS_SUCCESS;
nlEXPECT_ACTION(mEllTerminal != nullptr,
done,
lRetval = ERROR_NOT_INITIALIZED);
lRetval = l_term_set_bounds(mEllTerminal.get(),
inRows,
inColumns);
done:
return (lRetval);
}
I suspect it was just a symmetric peer to my GetBounds observation method:
status_t
Terminal :: GetBounds(
uint16_t &outRows,
uint16_t &outColumns
) const
{
status_t lRetval = STATUS_SUCCESS;
nlEXPECT_ACTION(mEllTerminal != nullptr,
done,
lRetval = ERROR_NOT_INITIALIZED);
lRetval = l_term_get_rows(mEllTerminal.get(),
&outRows);
nlREQUIRE_SUCCESS(lRetval, done);
lRetval = l_term_get_columns(mEllTerminal.get(),
&outColumns);
nlREQUIRE_SUCCESS(lRetval, done);
done:
return (lRetval);
}
> The l_term_io_callback and l_term_process is something I don’t really like since I rather keep that internal.
The implementation needs run loop adaptation. Absent those functions, what would you propose instead?
My initializer, deinitializer, and run loop adaptation:
status_t
Terminal :: Init(
FILE *inInput,
FILE *inOutput
)
{
status_t lRetval = STATUS_SUCCESS;
nlREQUIRE_ACTION(inInput != nullptr, done, lRetval = -EINVAL);
nlREQUIRE_ACTION(inOutput != nullptr, done, lRetval = -EINVAL);
lRetval = Init(fileno(inInput),
fileno(inOutput));
done:
return (lRetval);
}
status_t
Terminal :: Init(
const os_descriptor_t &inInput,
const os_descriptor_t &inOutput
)
{
status_t lRetval = STATUS_SUCCESS;
lRetval = osDescriptorValidate(inInput);
nlREQUIRE_SUCCESS(lRetval, done);
lRetval = osDescriptorValidate(inOutput);
nlREQUIRE_SUCCESS(lRetval, done);
mEllTerminal.reset(l_term_new());
nlREQUIRE_ACTION(mEllTerminal != nullptr, done, lRetval = -ENOMEM);
lRetval = l_term_set_io_handler(mEllTerminal.get(), IoCallback, this);
nlREQUIRE_SUCCESS(lRetval, done);
lRetval = l_term_set_key_handler(mEllTerminal.get(), KeyCallback, this);
nlREQUIRE_SUCCESS(lRetval, done);
lRetval = l_term_set_input(mEllTerminal.get(), inInput);
nlREQUIRE_SUCCESS(lRetval, done);
lRetval = l_term_set_output(mEllTerminal.get(), inOutput);
nlREQUIRE_SUCCESS(lRetval, done);
done:
if (lRetval < STATUS_SUCCESS)
{
if (mEllTerminal != nullptr)
{
l_term_set_key_handler(mEllTerminal.get(), nullptr, nullptr);
l_term_set_io_handler(mEllTerminal.get(), nullptr, nullptr);
mEllTerminal.reset();
}
}
return (lRetval);
}
status_t
Terminal :: Shutdown(void)
{
status_t lRetval = STATUS_SUCCESS;
nlEXPECT(mEllTerminal != nullptr, done);
if (IsAcquired())
{
Release();
}
l_term_set_key_handler(mEllTerminal.get(), nullptr, nullptr);
l_term_set_io_handler(mEllTerminal.get(), nullptr, nullptr);
mEllTerminal.reset();
done:
return (lRetval);
}
// MARK: Introspection
bool
Terminal :: IsAcquired(void) const
{
bool lRetval = false;
nlEXPECT(mEllTerminal != nullptr, done);
lRetval = l_term_is_acquired(mEllTerminal.get());
done:
return (lRetval);
}
void
Terminal :: IoCallback(
const os_descriptor_t &inDescriptor,
const bool &inReadable,
const bool &inWritable
)
{
nlREQUIRE(osDescriptorIsValid(inDescriptor), done);
if (inReadable)
{
mDescriptorEventMap[inDescriptor] |= POLLIN;
}
else
{
mDescriptorEventMap[inDescriptor] &= ~POLLIN;
}
if (inWritable)
{
mDescriptorEventMap[inDescriptor] |= POLLOUT;
}
else
{
mDescriptorEventMap[inDescriptor] &= ~POLLOUT;
}
done:
return;
}
status_t
Terminal :: GetDescriptors(
os_descriptor_t &inOutHighestDescriptor,
fd_set &inOutReadDescriptors,
fd_set &inOutWriteDescriptors,
fd_set &inOutExceptionDescriptors
) const
{
DescriptorEventMap::const_iterator lDescriptorEventMapCurrent =
mDescriptorEventMap.cbegin();
DescriptorEventMap::const_iterator lDescriptorEventMapLast =
mDescriptorEventMap.cend();
status_t lRetval =
STATUS_SUCCESS;
while (lDescriptorEventMapCurrent !=
lDescriptorEventMapLast)
{
// Simply handle the input descriptor. We assume that the
// output descriptor is always ready for writing.
if ((lDescriptorEventMapCurrent->second & POLLIN) &&
osDescriptorIsValid(lDescriptorEventMapCurrent->first))
{
FD_SET(lDescriptorEventMapCurrent->first, &inOutReadDescriptors);
inOutHighestDescriptor = max(inOutHighestDescriptor, lDescriptorEventMapCurrent->first);
}
advance(lDescriptorEventMapCurrent, 1);
}
return (lRetval);
}
status_t
Terminal :: HandleDescriptors(
fd_set &inOutReadDescriptors,
fd_set &inOutWriteDescriptors,
fd_set &inOutExceptionDescriptors
)
{
DescriptorEventMap::iterator lDescriptorEventMapCurrent =
mDescriptorEventMap.begin();
DescriptorEventMap::iterator lDescriptorEventMapLast =
mDescriptorEventMap.end();
status_t lRetval =
STATUS_SUCCESS;
while (lDescriptorEventMapCurrent !=
lDescriptorEventMapLast)
{
if (FD_ISSET(lDescriptorEventMapCurrent->first, &inOutReadDescriptors))
{
l_term_process(mEllTerminal.get());
}
advance(lDescriptorEventMapCurrent, 1);
}
return (lRetval);
}
There’s a similar set of methods to adapt it to poll rather than select; however, I’ve elided those since you can more or less guess how they are implemented.
>> ell: Add include directive for 'ell/term.h'.
>> ell/Makefile: Added 'term.[ch]' to HEADERS and SOURCES.
>> term: Added 'l_term_*' symbols.
>
> These 3 were already in my latest set.
Acknowledged; I started with your patch set as a baseline so that reviewers could see the entire context given that your patches were not yet in-tree.
>
>> ell/term: Do not return -EPERM for 'putnstr' and 'vprint' if not
>> running.
>
> Why is that important. It seems like a hack. If you haven’t successfully acquired the terminal, there is no point in writing to it since you have no idea what termios setting are dealing with.
Per the above, regrettably, at this point I don’t recall the issue that I had run into. For what they are worth, my output methods are:
status_t
Terminal :: Write(
const char &inCharacter
)
{
status_t lRetval = STATUS_SUCCESS;
nlEXPECT_ACTION(mEllTerminal != nullptr,
done,
lRetval = ERROR_NOT_INITIALIZED);
lRetval = l_term_putchar(mEllTerminal.get(),
inCharacter);
done:
return (lRetval);
}
status_t
Terminal :: Write(
const char *inString
)
{
status_t lRetval = STATUS_SUCCESS;
nlEXPECT_ACTION(mEllTerminal != nullptr,
done,
lRetval = ERROR_NOT_INITIALIZED);
lRetval = l_term_putstr(mEllTerminal.get(),
inString);
done:
return (lRetval);
}
status_t
Terminal :: Write(
const char *inString,
const size_t &inStringLength
)
{
status_t lRetval = STATUS_SUCCESS;
nlEXPECT_ACTION(mEllTerminal != nullptr,
done,
lRetval = ERROR_NOT_INITIALIZED);
lRetval = l_term_putnstr(mEllTerminal.get(),
inString,
inStringLength);
done:
return (lRetval);
}
status_t
Terminal :: WriteWithFormat(
const char *inFormat,
...
)
{
va_list lArguments;
status_t lRetval = STATUS_SUCCESS;
va_start(lArguments, inFormat);
lRetval = WriteWithFormat(inFormat, lArguments);
va_end(lArguments);
return (lRetval);
}
status_t
Terminal :: WriteWithFormat(
const char *inFormat,
va_list inArguments
)
{
status_t lRetval = STATUS_SUCCESS;
nlEXPECT_ACTION(mEllTerminal != nullptr,
done,
lRetval = ERROR_NOT_INITIALIZED);
lRetval = l_term_vprint(mEllTerminal.get(),
inFormat,
inArguments);
done:
return (lRetval);
}
>> ell/term: Return error on writes if the output descriptor is invalid.
>
> Seems like a fix for the previous change.
It might well be. Again, per the comment above about getting back into the context of a year and a half ago, it seems like there was a desire to be able to initialize an instance with the stdin / stdout file stream pointers and have things work as-is and then bootstrap into / out of those with the terminal as my line oriented UI/UX acquired / released the terminal as UI/UX elements were pushed/popped off the stack.
>> ell/edit: Rename 'l_term_{open,close}'.
>
> I incorporated that into my set. Makes sense to me.
Re-reviewing this particular patch and recollecting the acquire/release push/pop semantics around the UI/UX stack and stdin/stdout, I believe that starts to get at the -EPERM / -EBADF changes. Otherwise, I think the UI/UX stack would have effectively had to toggle in / toggle out a “NULL Terminal” as the actual terminal was acquired / released.
>> ell/term: Add an 'l_term_is_acquired' introspection function.
>
> I added it, but don’t really know how it would be used.
See above use case in the ‘Shutdown’ deinitializer.
>> ell/term: Added ASCII C0 and C1 control code mnemonics.
>
> Hmm. We could do that, but the constant name is too long for my taste. And why use an enum instead of defines?
enumerations seemed to be the prevailing style in ELL.
Best,
Grant
--
Principal
Nuovations
gerickson@nuovations.com
https://www.nuovations.com/
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v9 0/9] Input/Output Terminal Abstraction
2025-05-27 18:08 ` Grant Erickson
@ 2025-05-28 14:34 ` Grant Erickson
2025-06-02 6:18 ` Marcel Holtmann
0 siblings, 1 reply; 14+ messages in thread
From: Grant Erickson @ 2025-05-28 14:34 UTC (permalink / raw)
To: Marcel Holtmann; +Cc: ell
On May 27, 2025, at 11:08 AM, Grant Erickson <gerickson@nuovations.com> wrote:
> On May 27, 2025, at 10:20 AM, Marcel Holtmann <marcel@holtmann.org> wrote:
>>> This expands on Marcel Holtman's 2023-12-22 RFCv4 patch for an
>>> input/output terminal abstraction.
>>>
>>> Substantive changes from the v7 version:
>>>
>>> * Fixed a typo in the ASCII C0 and C1 control code mnemonics.
>>>
>>> Grant Erickson (9):
>>> term: Initial revision.
>>
>> I actually reverted it back to my version and included printf / vprintf helpers.
>>
>> What is the purpose of l_term_set_bounds? Is it important for non-TTY setups since for TTY compatible terminals, this should not be needed.
>
> It’s been a year and a half since I first deploy[ed] this, so bear with me as I attempt to clear some dust and cobwebs from some of the details.
>
>>
>>> ell/term: Do not return -EPERM for 'putnstr' and 'vprint' if not
>>> running.
>>
>> Why is that important. It seems like a hack. If you haven’t successfully acquired the terminal, there is no point in writing to it since you have no idea what termios setting are dealing with.
>
> Per the above, regrettably, at this point I don’t recall the issue that I had run into. For what they are worth, my output methods are:
>
> ...
>
>>> ell/term: Return error on writes if the output descriptor is invalid.
>>
>> Seems like a fix for the previous change.
>
> It might well be. Again, per the comment above about getting back into the context of a year and a half ago, it seems like there was a desire to be able to initialize an instance with the stdin / stdout file stream pointers and have things work as-is and then bootstrap into / out of those with the terminal as my line oriented UI/UX acquired / released the terminal as UI/UX elements were pushed/popped off the stack.
Thinking about this further, I suspect my original changes about is_running (now, is_acquired) / -EPERM and -EBADF were about early prototyping and refining my stack based, line-oriented UI/UX.
I suspect if we left the code as-it was in those two regards, the stack based, line-oriented UI/UX would work fine. If that’s not the case, I can revisit that specific issue later.
Best,
Grant
--
Principal
Nuovations
gerickson@nuovations.com
https://www.nuovations.com/
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v9 0/9] Input/Output Terminal Abstraction
2025-05-28 14:34 ` Grant Erickson
@ 2025-06-02 6:18 ` Marcel Holtmann
0 siblings, 0 replies; 14+ messages in thread
From: Marcel Holtmann @ 2025-06-02 6:18 UTC (permalink / raw)
To: Grant Erickson; +Cc: ell
Hi Gran,
>>>> This expands on Marcel Holtman's 2023-12-22 RFCv4 patch for an
>>>> input/output terminal abstraction.
>>>>
>>>> Substantive changes from the v7 version:
>>>>
>>>> * Fixed a typo in the ASCII C0 and C1 control code mnemonics.
>>>>
>>>> Grant Erickson (9):
>>>> term: Initial revision.
>>>
>>> I actually reverted it back to my version and included printf / vprintf helpers.
>>>
>>> What is the purpose of l_term_set_bounds? Is it important for non-TTY setups since for TTY compatible terminals, this should not be needed.
>>
>> It’s been a year and a half since I first deploy[ed] this, so bear with me as I attempt to clear some dust and cobwebs from some of the details.
>>
>>>
>>>> ell/term: Do not return -EPERM for 'putnstr' and 'vprint' if not
>>>> running.
>>>
>>> Why is that important. It seems like a hack. If you haven’t successfully acquired the terminal, there is no point in writing to it since you have no idea what termios setting are dealing with.
>>
>> Per the above, regrettably, at this point I don’t recall the issue that I had run into. For what they are worth, my output methods are:
>>
>> ...
>>
>>>> ell/term: Return error on writes if the output descriptor is invalid.
>>>
>>> Seems like a fix for the previous change.
>>
>> It might well be. Again, per the comment above about getting back into the context of a year and a half ago, it seems like there was a desire to be able to initialize an instance with the stdin / stdout file stream pointers and have things work as-is and then bootstrap into / out of those with the terminal as my line oriented UI/UX acquired / released the terminal as UI/UX elements were pushed/popped off the stack.
>
> Thinking about this further, I suspect my original changes about is_running (now, is_acquired) / -EPERM and -EBADF were about early prototyping and refining my stack based, line-oriented UI/UX.
>
> I suspect if we left the code as-it was in those two regards, the stack based, line-oriented UI/UX would work fine. If that’s not the case, I can revisit that specific issue later.
then I am starting to leave things out and see how far we get.
Regards
Marcel
^ permalink raw reply [flat|nested] 14+ messages in thread
end of thread, other threads:[~2025-06-02 6:19 UTC | newest]
Thread overview: 14+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-04-06 22:05 [PATCH v9 0/9] Input/Output Terminal Abstraction Grant Erickson
2025-04-06 22:05 ` [PATCH v9 1/9] term: Initial revision Grant Erickson
2025-04-06 22:05 ` [PATCH v9 2/9] ell: Add include directive for 'ell/term.h' Grant Erickson
2025-04-06 22:05 ` [PATCH v9 3/9] ell/Makefile: Added 'term.[ch]' to HEADERS and SOURCES Grant Erickson
2025-04-06 22:06 ` [PATCH v9 4/9] term: Added 'l_term_*' symbols Grant Erickson
2025-04-06 22:06 ` [PATCH v9 5/9] ell/term: Do not return -EPERM for 'putnstr' and 'vprint' if not running Grant Erickson
2025-04-06 22:06 ` [PATCH v9 6/9] ell/term: Return error on writes if the output descriptor is invalid Grant Erickson
2025-04-06 22:06 ` [PATCH v9 7/9] ell/edit: Rename 'l_term_{open,close}' Grant Erickson
2025-04-06 22:06 ` [PATCH v9 8/9] ell/term: Add an 'l_term_is_acquired' introspection function Grant Erickson
2025-04-06 22:06 ` [PATCH v9 9/9] ell/term: Added ASCII C0 and C1 control code mnemonics Grant Erickson
2025-05-27 17:20 ` [PATCH v9 0/9] Input/Output Terminal Abstraction Marcel Holtmann
2025-05-27 18:08 ` Grant Erickson
2025-05-28 14:34 ` Grant Erickson
2025-06-02 6:18 ` Marcel Holtmann
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.