[gjs/ewlsh/nova-repl: 6/7] Refactor console utilities




commit 6d11ffa06e0f84481c7a09d36c6e408e55036cd4
Author: Evan Welsh <contact evanwelsh com>
Date:   Sat Jan 29 19:05:36 2022 -0800

    Refactor console utilities

 gjs/debugger.cpp          |  4 +--
 libgjs-private/gjs-util.c | 13 ---------
 meson.build               |  1 +
 modules/console.cpp       | 39 ++++++++++++++++++++-------
 modules/esm/console.js    |  5 ++--
 util/console.cpp          | 67 +++++++++++++++++++++++++++++++++++++++++++++--
 util/console.h            | 33 +++++++++++------------
 7 files changed, 116 insertions(+), 46 deletions(-)
---
diff --git a/gjs/debugger.cpp b/gjs/debugger.cpp
index 310fa3843..282e43c23 100644
--- a/gjs/debugger.cpp
+++ b/gjs/debugger.cpp
@@ -59,7 +59,7 @@ static bool do_readline(JSContext* cx, unsigned argc, JS::Value* vp) {
     do {
         const char* real_prompt = prompt ? prompt.get() : "db> ";
 #ifdef HAVE_READLINE_READLINE_H
-        if (gjs_console_is_tty(stdin_fd)) {
+        if (Gjs::Console::is_tty(Gjs::Console::stdin_fd)) {
             line = readline(real_prompt);
         } else {
 #else
@@ -72,7 +72,7 @@ static bool do_readline(JSContext* cx, unsigned argc, JS::Value* vp) {
                 buf[0] = '\0';
             line.reset(g_strdup(g_strchomp(buf)));
 
-            if (!gjs_console_is_tty(stdin_fd)) {
+            if (!Gjs::Console::is_tty(Gjs::Console::stdin_fd)) {
                 if (feof(stdin)) {
                     g_print("[quit due to end of input]\n");
                     line.reset(g_strdup("quit"));
diff --git a/libgjs-private/gjs-util.c b/libgjs-private/gjs-util.c
index afae2988f..c2399b803 100644
--- a/libgjs-private/gjs-util.c
+++ b/libgjs-private/gjs-util.c
@@ -17,7 +17,6 @@
 #include <glib/gi18n.h> /* for bindtextdomain, bind_textdomain_codeset, textdomain */
 
 #include "libgjs-private/gjs-util.h"
-#include "util/console.h"
 
 char *
 gjs_format_int_alternative_output(int n)
@@ -325,15 +324,3 @@ void gjs_log_set_writer_func(GjsGLogWriterFunc func, void* user_data,
 
     g_log_set_writer_func(gjs_log_writer_func_wrapper, func, NULL);
 }
-
-/**
- * gjs_clear_terminal:
- *
- * Clears the terminal, if possible.
- */
-void gjs_clear_terminal(void) {
-    if (!gjs_console_is_tty(stdout_fd))
-        return;
-
-    gjs_console_clear();
-}
diff --git a/meson.build b/meson.build
index 136e812b3..0d4d54a99 100644
--- a/meson.build
+++ b/meson.build
@@ -334,6 +334,7 @@ if build_readline
 endif
 header_conf.set('USE_UNITY_BUILD', get_option('unity'))
 header_conf.set('HAVE_SYS_SYSCALL_H', cxx.check_header('sys/syscall.h'))
+header_conf.set('HAVE_TERMIOS_H', cxx.check_header('termios.h'))
 header_conf.set('HAVE_UNISTD_H', cxx.check_header('unistd.h'))
 header_conf.set('HAVE_SIGNAL_H', cxx.check_header('signal.h',
     required: build_profiler))
diff --git a/modules/console.cpp b/modules/console.cpp
index e54554b6d..c21942490 100644
--- a/modules/console.cpp
+++ b/modules/console.cpp
@@ -274,13 +274,34 @@ gjs_console_interact(JSContext *context,
     return true;
 }
 
-bool
-gjs_define_console_stuff(JSContext              *context,
-                         JS::MutableHandleObject module)
-{
-    module.set(JS_NewPlainObject(context));
-    const GjsAtoms& atoms = GjsContextPrivate::atoms(context);
-    return JS_DefineFunctionById(context, module, atoms.interact(),
-                                 gjs_console_interact, 1,
-                                 GJS_MODULE_PROP_FLAGS);
+GJS_JSAPI_RETURN_CONVENTION
+static bool gjs_console_clear_terminal(JSContext* cx, unsigned argc,
+                                       JS::Value* vp) {
+    JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+    if (!gjs_parse_call_args(cx, "clearTerminal", args, ""))
+        return false;
+
+    if (!Gjs::Console::is_tty(Gjs::Console::stdout_fd)) {
+        args.rval().setBoolean(false);
+        return true;
+    }
+
+    args.rval().setBoolean(Gjs::Console::clear());
+    return true;
+}
+
+static JSFunctionSpec console_module_funcs[] = {
+    JS_FN("clearTerminal", gjs_console_clear_terminal, 1,
+          GJS_MODULE_PROP_FLAGS),
+    JS_FN("interact", gjs_console_interact, 1, GJS_MODULE_PROP_FLAGS),
+    JS_FS_END,
+};
+
+bool gjs_define_console_private_stuff(JSContext* cx,
+                                      JS::MutableHandleObject module) {
+    module.set(JS_NewPlainObject(cx));
+    if (!module)
+        return false;
+
+    return JS_DefineFunctions(cx, module, console_module_funcs);
 }
diff --git a/modules/esm/console.js b/modules/esm/console.js
index bdbfc69dd..210298c1e 100644
--- a/modules/esm/console.js
+++ b/modules/esm/console.js
@@ -2,7 +2,8 @@
 // SPDX-FileCopyrightText: 2021 Evan Welsh <contact evanwelsh com>
 
 import GLib from 'gi://GLib';
-import GjsPrivate from 'gi://GjsPrivate';
+
+const NativeConsole = import.meta.importSync('_consoleNative');
 
 const sLogger = Symbol('Logger');
 const sPrinter = Symbol('Printer');
@@ -151,7 +152,7 @@ class Console {
      */
     clear() {
         this[sGroupIndentation] = '';
-        GjsPrivate.clear_terminal();
+        NativeConsole.clearTerminal();
     }
 
     /**
diff --git a/util/console.cpp b/util/console.cpp
index 76451ce11..82ddcc098 100644
--- a/util/console.cpp
+++ b/util/console.cpp
@@ -37,6 +37,9 @@ constexpr const char CLEAR_SCREEN[] = "\x1b[2J";
 
 }  // namespace ANSICode
 
+namespace Gjs {
+namespace Console {
+
 #ifdef HAVE_UNISTD_H
 const int stdin_fd = STDIN_FILENO;
 const int stdout_fd = STDOUT_FILENO;
@@ -51,7 +54,64 @@ const int stdout_fd = 1;
 const int stderr_fd = 2;
 #endif
 
-bool gjs_console_is_tty(int fd) {
+#ifdef HAVE_TERMIOS_H
+struct termios saved_termios;
+#endif
+
+bool disable_raw_mode() {
+#ifdef HAVE_TERMIOS_H
+    return tcsetattr(stdin_fd, TCSAFLUSH, &saved_termios) != -1;
+#else
+    return false;
+#endif
+}
+
+void _disable_raw_mode() {
+    void* _ [[maybe_unused]] = reinterpret_cast<void*>(disable_raw_mode());
+}
+
+bool enable_raw_mode() {
+#ifdef HAVE_TERMIOS_H
+    // Save the current terminal flags to reset later
+    if (tcgetattr(stdin_fd, &saved_termios) == -1) {
+        if (disable_raw_mode())
+            return false;
+
+        return false;
+    }
+
+    // Register an exit handler to restore
+    // the terminal modes on exit.
+    atexit(_disable_raw_mode);
+
+    struct termios raw = saved_termios;
+    // - Disable \r to \n conversion on input
+    // - Disable parity checking
+    // - Disable stripping characters to 7-bits
+    // - Disable START/STOP characters
+    // https://www.gnu.org/software/libc/manual/html_node/Input-Modes.html
+    raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
+    // Enforce 8-bit characters
+    // https://www.gnu.org/software/libc/manual/html_node/Control-Modes.html
+    raw.c_cflag |= (CS8);
+    // Disable echoing (terminal reprinting input)
+    // Disable canonical mode (output reflects input)
+    // Disable "extensions" that allow users to inject
+    // Disable C signal handling
+    // https://www.gnu.org/software/libc/manual/html_node/Other-Special.html
+    raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
+    // Set 0 characters required for a read
+    raw.c_cc[VMIN] = 0;
+    // Set the read timeout to 1 decisecond (0.1 seconds)
+    raw.c_cc[VTIME] = 1;
+
+    return tcsetattr(stdin_fd, TCSAFLUSH, &raw) != -1;
+#else
+    return false;
+#endif
+}
+
+bool is_tty(int fd) {
 #ifdef HAVE_UNISTD_H
     return isatty(fd);
 #elif defined(_WIN32)
@@ -61,9 +121,12 @@ bool gjs_console_is_tty(int fd) {
 #endif
 }
 
-bool gjs_console_clear() {
+bool clear() {
     if (stdout_fd < 0 || !g_log_writer_supports_color(stdout_fd))
         return false;
 
     return fputs(ANSICode::CLEAR_SCREEN, stdout) > 0 && fflush(stdout) > 0;
 }
+
+}  // namespace Console
+}  // namespace Gjs
diff --git a/util/console.h b/util/console.h
index df27f305e..306721ed2 100644
--- a/util/console.h
+++ b/util/console.h
@@ -1,31 +1,28 @@
-/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
-/*
- * SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
- * SPDX-FileCopyrightText: 2021 Evan Welsh <contact evanwelsh com>
- */
+// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
+// SPDX-FileCopyrightText: 2021 Evan Welsh <contact evanwelsh com>
 
 #ifndef UTIL_CONSOLE_H_
 #define UTIL_CONSOLE_H_
 
-/* This file has to be valid C, because it's used in libgjs-private */
-
-#include <stdbool.h> /* IWYU pragma: keep */
-
-#include <glib.h>
-
-#include "gjs/macros.h"
-
-G_BEGIN_DECLS
+#include <config.h>
 
+namespace Gjs {
+namespace Console {
 extern const int stdout_fd;
 extern const int stdin_fd;
 extern const int stderr_fd;
 
-GJS_USE
-bool gjs_console_is_tty(int fd);
+[[nodiscard]] bool is_tty(int fd = stdout_fd);
+
+[[nodiscard]] bool clear();
+
+[[nodiscard]] bool enable_raw_mode();
+
+[[nodiscard]] bool disable_raw_mode();
 
-bool gjs_console_clear(void);
+[[nodiscard]] bool get_size(int* width, int* height);
 
-G_END_DECLS
+};  // namespace Console
+};  // namespace Gjs
 
 #endif  // UTIL_CONSOLE_H_


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]