From 13dca83c9fa0beb831601beb7207cd2a308888d7 Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Sun, 20 Sep 2026 16:35:10 +0800 Subject: [PATCH] 0.11.0 --- the keystrokes this kernel keeps for itself are a position now openkal 0.14 adds KAL_TERM_PASS_CONTROL, and this implementation reads and writes it. The mapping is the one the other termios kernel uses with this kernel's own constants: ISIG, IXON and IEXTEN together, because the position states that NO keystroke is reserved and an implementation mapping ISIG alone would leave one mode word meaning two things on two systems. A position whose requested value is the one in effect is not written, which the specification states beside set_mode and which matters here because this position stands for three flags. And with line assembly off the least number of bytes a read waits for is established, so that a terminal left at VMIN=0 does not make kal_stream_read report the end of an input that has not ended (clause 7.4). --- README.md | 4 ++-- mcpp.toml | 4 ++-- src/terminal.cpp | 46 ++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 48 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index a19b8b2..2c83fda 100644 --- a/README.md +++ b/README.md @@ -5,10 +5,10 @@ written on the kernel's own calls. ```toml [dependencies] -openkal = "0.13.0" +openkal = "0.14.0" [target.'cfg(os = "macos")'.dependencies] -openkal-macos = "0.10.0" +openkal-macos = "0.11.0" ``` Its purpose is as much to test the specification as to be used. A specification diff --git a/mcpp.toml b/mcpp.toml index f75b6ab..fa95e2e 100644 --- a/mcpp.toml +++ b/mcpp.toml @@ -1,7 +1,7 @@ [package] namespace = "mcpplibs" name = "openkal-macos" -version = "0.10.0" +version = "0.11.0" description = "An implementation of openkal for macOS, written on the kernel's own calls. Its purpose is as much to test the specification as to be used." license = "Apache-2.0" @@ -18,7 +18,7 @@ authors = ["mcpplibs"] repo = "https://github.com/mcpplibs/openkal-macos" [dependencies] -openkal = "0.13.0" +openkal = "0.14.0" [build] # The flags are attached to this package's own sources rather than to the whole diff --git a/src/terminal.cpp b/src/terminal.cpp index f0f8b25..1da4a8f 100644 --- a/src/terminal.cpp +++ b/src/terminal.cpp @@ -40,14 +40,29 @@ constexpr okm_long tiocgeta = 0x40000000L | (72L << 16) | ('t' << 8) | 19; constexpr okm_long tiocseta = 0x80000000L | (72L << 16) | ('t' << 8) | 20; constexpr okm_long tiocgwinsz = 0x40000000L | (8L << 16) | ('t' << 8) | 104; -// Positions within lflag. This kernel's values, which are not the other's. +// Positions within lflag and iflag, and the two entries of cc that decide how +// long a read waits. This kernel's values, which are not the other's. constexpr okm_ulong t_echo = 0x00000008u; +constexpr okm_ulong t_isig = 0x00000080u; constexpr okm_ulong t_icanon = 0x00000100u; - +constexpr okm_ulong t_iexten = 0x00000400u; +constexpr okm_ulong t_ixon = 0x00000200u; // iflag +constexpr unsigned v_min = 16, v_time = 17; + +// KAL_TERM_PASS_CONTROL IS READ FROM THREE FLAGS AND NOT FROM ISIG. The +// position states that the environment reserves NO keystroke, so it is set only +// where every mechanism by which this kernel reserves one is off: ISIG for the +// interrupt and its neighbours, IXON for the pair that stops and starts output, +// IEXTEN for the one that takes the next keystroke literally. A terminal upon +// which some of them had been released reads as clear and is restored to the +// set this kernel ordinarily reserves, which is the cost the specification +// records beside the position. kal_uintptr mode_of(const oktermios& t) { kal_uintptr m = 0; if ((t.lflag & t_icanon) != 0) m |= KAL_TERM_LINE_EDIT; if ((t.lflag & t_echo) != 0) m |= KAL_TERM_ECHO; + if ((t.lflag & (t_isig | t_iexten)) == 0 && + (t.iflag & t_ixon) == 0) m |= KAL_TERM_PASS_CONTROL; return m; } @@ -81,12 +96,39 @@ int kal_terminal_set_mode(kal_stream s, kal_uintptr mode) { oktermios t{}; const int rc = get_termios(s, t); if (rc != kal_ok) return rc; + const kal_uintptr in_effect = mode_of(t); if ((mode & KAL_TERM_LINE_EDIT) != 0) t.lflag |= t_icanon; else t.lflag &= ~t_icanon; if ((mode & KAL_TERM_ECHO) != 0) t.lflag |= t_echo; else t.lflag &= ~t_echo; + // A POSITION WHOSE REQUESTED VALUE IS THE ONE IN EFFECT IS NOT WRITTEN. + // This position stands for three of the kernel's flags, so establishing it + // again would settle two mechanisms the caller never asked about: a user + // who had released the keystroke that stops output keeps it released while + // a program turns the echo off and back on. + if (((mode ^ in_effect) & KAL_TERM_PASS_CONTROL) != 0) { + if ((mode & KAL_TERM_PASS_CONTROL) != 0) { + t.lflag &= ~(t_isig | t_iexten); + t.iflag &= ~t_ixon; + } else { + t.lflag |= (t_isig | t_iexten); + t.iflag |= t_ixon; + } + } + + // AND A MODE IS NOT A WAY TO END THE INPUT. With line assembly off, how + // long a read waits is decided by VMIN and VTIME rather than by a newline, + // and a terminal left at VMIN=0 by whatever ran before makes + // `kal_stream_read' report zero --- which clause 7.4 says denotes the end + // of the input. A caller that wants a read which gives up asks + // `kal_timeout_read' for one. + if ((mode & KAL_TERM_LINE_EDIT) == 0) { + t.cc[v_min] = 1; + t.cc[v_time] = 0; + } + // A position this implementation does not distinguish is ignored rather than // refused, which clause 6.2 requires: a program compiled against a later // revision sets a position this build has never heard of.