Skip to content

Commit f5ca3ed

Browse files
authored
0.14.0 --- the keystrokes this kernel keeps for itself are a position now (#28)
1 parent 6d14dff commit f5ca3ed

5 files changed

Lines changed: 78 additions & 7 deletions

File tree

‎README.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ for Linux, written on the kernel's own system-call interface.
55

66
```toml
77
[dependencies]
8-
openkal = "0.13.0"
8+
openkal = "0.14.0"
99

1010
[target.'cfg(os = "linux")'.dependencies]
11-
openkal-linux = "0.13.0"
11+
openkal-linux = "0.14.0"
1212
```
1313

1414
## Why it does not use a C library

‎mcpp.toml‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[package]
22
namespace = "mcpplibs"
33
name = "openkal-linux"
4-
version = "0.13.0"
4+
version = "0.14.0"
55
description = "The reference implementation of openkal for Linux, written on the kernel's own system-call interface so that it can be placed beneath a C library as well as above one."
66
license = "Apache-2.0"
77

@@ -18,7 +18,7 @@ authors = ["mcpplibs"]
1818
repo = "https://github.com/mcpplibs/openkal-linux"
1919

2020
[dependencies]
21-
openkal = "0.13.0"
21+
openkal = "0.14.0"
2222

2323
# The package contributes definitions and no modules. The interface it
2424
# implements is declared by the specification package, which this package

‎src/sys.h‎

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -506,9 +506,22 @@ enum : okl_long {
506506
tcgets = 0x5401, tcsets = 0x5402, tiocgwinsz = 0x5413,
507507
};
508508

509-
// Positions within ktermios::lflag. Named here for the same reason the numbers
510-
// above are: they belong to the kernel and not to any library.
511-
enum : okl_u32 { t_icanon = 0000002u, t_echo = 0000010u };
509+
// Positions within ktermios::lflag and ktermios::iflag, and the two entries of
510+
// ktermios::cc that decide how long a read waits. Named here for the same
511+
// reason the numbers above are: they belong to the kernel and not to any
512+
// library.
513+
//
514+
// `t_isig', `t_ixon' and `t_iexten' are the three mechanisms by which this
515+
// kernel keeps a keystroke for itself --- the interrupt and its neighbours, the
516+
// pair that stops and starts output, and the one that takes the next keystroke
517+
// literally. KAL_TERM_PASS_CONTROL is the whole of them, because a program that
518+
// asked for every keystroke and was given two mechanisms out of three would
519+
// find one key missing and nothing to ask about it with.
520+
enum : okl_u32 {
521+
t_isig = 0000001u, t_icanon = 0000002u, t_echo = 0000010u, t_iexten = 0100000u,
522+
};
523+
enum : okl_u32 { t_ixon = 0002000u };
524+
enum { v_time = 5, v_min = 6 };
512525

513526
// --- openkal.net and openkal.datagram --------------------------------------
514527
//

‎src/terminal.cpp‎

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,21 @@ namespace {
1616
// positions in this interface because a program that wants a password prompt
1717
// turns off one and leaves the other, and a backend that conflated them would
1818
// make that program's intent inexpressible.
19+
//
20+
// KAL_TERM_PASS_CONTROL IS READ FROM THREE FLAGS AND NOT FROM ISIG. The
21+
// position states that the environment reserves NO keystroke, so it is set only
22+
// where every mechanism by which this kernel reserves one is off: ISIG for the
23+
// interrupt and its neighbours, IXON for the pair that stops and starts output,
24+
// IEXTEN for the one that takes the next keystroke literally. A terminal upon
25+
// which some of them had been released reads as clear and is restored to the
26+
// set this kernel ordinarily reserves, which is the cost the specification
27+
// records beside the position.
1928
kal_uintptr mode_of(const okl::ktermios& t) {
2029
kal_uintptr m = 0;
2130
if ((t.lflag & okl::t_icanon) != 0) m |= KAL_TERM_LINE_EDIT;
2231
if ((t.lflag & okl::t_echo) != 0) m |= KAL_TERM_ECHO;
32+
if ((t.lflag & (okl::t_isig | okl::t_iexten)) == 0 &&
33+
(t.iflag & okl::t_ixon) == 0) m |= KAL_TERM_PASS_CONTROL;
2334
return m;
2435
}
2536

@@ -59,12 +70,41 @@ int kal_terminal_set_mode(kal_stream s, kal_uintptr mode) {
5970
okl::ktermios t{};
6071
const int rc = get_termios(s, t);
6172
if (rc != kal_ok) return rc;
73+
const kal_uintptr in_effect = mode_of(t);
6274

6375
if ((mode & KAL_TERM_LINE_EDIT) != 0) t.lflag |= okl::t_icanon;
6476
else t.lflag &= ~okl::t_icanon;
6577
if ((mode & KAL_TERM_ECHO) != 0) t.lflag |= okl::t_echo;
6678
else t.lflag &= ~okl::t_echo;
6779

80+
// A POSITION WHOSE REQUESTED VALUE IS THE ONE IN EFFECT IS NOT WRITTEN, and
81+
// this position is why the specification states the rule: it stands for
82+
// three of the kernel's flags, so establishing it again would settle two
83+
// mechanisms the caller never asked about. A user who had released the
84+
// keystroke that stops output keeps it released while a program turns the
85+
// echo off and back on.
86+
if (((mode ^ in_effect) & KAL_TERM_PASS_CONTROL) != 0) {
87+
if ((mode & KAL_TERM_PASS_CONTROL) != 0) {
88+
t.lflag &= ~(okl::t_isig | okl::t_iexten);
89+
t.iflag &= ~okl::t_ixon;
90+
} else {
91+
t.lflag |= (okl::t_isig | okl::t_iexten);
92+
t.iflag |= okl::t_ixon;
93+
}
94+
}
95+
96+
// AND A MODE IS NOT A WAY TO END THE INPUT. With line assembly off, how
97+
// long a read waits is decided by VMIN and VTIME rather than by a newline,
98+
// and a terminal left at VMIN=0 by whatever ran before makes
99+
// `kal_stream_read' report zero --- which clause 7.4 says denotes the end
100+
// of the input. The least count is therefore established together with the
101+
// mode that needs it; a caller that wants a read which gives up asks
102+
// `kal_timeout_read' for one.
103+
if ((mode & KAL_TERM_LINE_EDIT) == 0) {
104+
t.cc[okl::v_min] = 1;
105+
t.cc[okl::v_time] = 0;
106+
}
107+
68108
// A position this implementation does not distinguish is ignored rather
69109
// than refused, which is what clause 6.2 requires of a word: a program
70110
// compiled against a later revision sets a position this build has never

‎tests/conformance_v08.cpp‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,24 @@ void terminal_section() {
5656
check(m.e == kal_ok, "an interactive stream reports its mode");
5757
check(kal::terminal::set_mode(out, m.m) == kal_ok,
5858
"the mode that was read can be set back");
59+
60+
// THE POSITION VERSION 0.14 ADDED, UPON A TERMINAL THAT EXISTS. This
61+
// implementation distinguishes it, so the answer here is the stronger
62+
// of the two clause 6.2 permits: what was asked for is what is read
63+
// back. Whether the keystroke then arrives as a byte is a question for
64+
// something that can type, which is the pty probe a C environment above
65+
// this implementation runs; what is established here is that the
66+
// position reaches the kernel and that the terminal survives it.
67+
using kal::terminal::pass_control;
68+
const auto wanted = m.m | pass_control;
69+
check(kal::terminal::set_mode(out, wanted) == kal_ok,
70+
"every keystroke can be asked for");
71+
const auto after = kal::terminal::get_mode(out);
72+
check(after.e == kal_ok && after.m.has(pass_control),
73+
"the position asked for is the position read back");
74+
check(kal::terminal::set_mode(out, m.m) == kal_ok &&
75+
kal::terminal::get_mode(out).m.bits == m.m.bits,
76+
"the mode found is the mode left behind");
5977
} else {
6078
check(m.e == kal_err_not_supported,
6179
"a stream that is not interactive refuses get_mode");

0 commit comments

Comments
 (0)