diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e45489f..1091441 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -40,7 +40,7 @@ env: # `em++`'s C compiler is `emcc` and not `em` (a C translation unit in the # conformance suite does not compile without that), and the wasm target rows # resolve the emsdk payload. - MCPP_VERSION: 2026.9.12.2 + MCPP_VERSION: 2026.9.18.3 XLINGS_VERSION: v2026.8.17.2 XLINGS_NON_INTERACTIVE: '1' @@ -189,24 +189,32 @@ jobs: work="$RUNNER_TEMP/absent" rm -rf "$work"; mkdir -p "$work/src" impl="$(pwd)" - # BOTH DEPENDENCIES NAMED THE SAME WAY, and this step therefore runs - # BEFORE the manifest is pointed at the specification's working tree. - # mcpp refuses a graph in which one package reaches `openkal` by path - # and another by version -- measured: + # BOTH DEPENDENCIES NAMED THE SAME WAY. mcpp refuses a graph in + # which one package reaches `openkal` by path and another by version + # -- measured: # # error: dependency 'mcpplibs.openkal' is requested as both a path # dep (by 'absent') and a version dep (by # 'mcpplibs.openkal-emscripten@path'). Pick one. # - # so the consumer uses the version this implementation's committed - # manifest names, which is the arrangement an ordinary consumer has. + # AND BOTH NAME THE WORKING TREE RATHER THAN A PUBLISHED VERSION. + # They named the version this implementation's manifest asks for, + # which is the arrangement an ordinary consumer has --- and which + # made this step unrunnable during the release it is part of: a + # branch that raises the version asks the index for one that is not + # registered yet, and the step then reports "the build did not fail + # naming kal_process_spawn" for a build that never started. What this + # step asserts is a property of the two trees, so it takes the trees. + sed 's|^openkal = .*$|openkal = { path = "'"$impl"'/.spec" }|' \ + "$impl/mcpp.toml" > "$impl/mcpp.toml.next" + mv "$impl/mcpp.toml.next" "$impl/mcpp.toml" cat > "$work/mcpp.toml" << TOML [package] name = "absent" version = "0.1.0" [dependencies] - openkal = "$(grep -oP '^openkal = "\K[0-9.]+' "$impl/mcpp.toml")" + openkal = { path = "$impl/.spec" } openkal-emscripten = { path = "$impl" } TOML cat > "$work/src/main.cpp" << 'CPP' diff --git a/README.md b/README.md index d86914c..0154eb9 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,7 @@ So the interface is carried by a feature: ```toml [dependencies] -openkal-emscripten = { version = "0.2.0", features = ["threads"] } +openkal-emscripten = { version = "0.3.0", features = ["threads"] } ``` Without it, `src/threads/task.cpp` compiles to nothing, the eight `kal_task_*` @@ -143,10 +143,10 @@ implementation in its source: ```toml [dependencies] -openkal = "0.13.0" +openkal = "0.14.0" [target.'cfg(os = "emscripten")'.dependencies] -openkal-emscripten = "0.2.0" +openkal-emscripten = "0.3.0" ``` ## Licence diff --git a/mcpp.toml b/mcpp.toml index 5fc8b79..bb02995 100644 --- a/mcpp.toml +++ b/mcpp.toml @@ -1,7 +1,7 @@ [package] namespace = "mcpplibs" name = "openkal-emscripten" -version = "0.2.0" +version = "0.3.0" description = "An implementation of openkal for Emscripten, written ABOVE a C library rather than beneath one, because on this platform there is no kernel to issue calls to." license = "Apache-2.0" @@ -18,7 +18,7 @@ authors = ["mcpplibs"] repo = "https://github.com/mcpplibs/openkal-emscripten" [dependencies] -openkal = "0.13.0" +openkal = "0.14.0" [build] # THE DIALECT FLAGS ARE THE SAME AS EVERY OTHER IMPLEMENTATION'S AND THE REASON diff --git a/src/terminal.cpp b/src/terminal.cpp index 65e910b..ed07eaf 100644 --- a/src/terminal.cpp +++ b/src/terminal.cpp @@ -29,6 +29,15 @@ int kal_terminal_get_mode(kal_stream s, kal_uintptr* mode) { kal_uintptr m = 0; if ((t.c_lflag & ICANON) != 0) m |= KAL_TERM_LINE_EDIT; if ((t.c_lflag & ECHO) != 0) m |= KAL_TERM_ECHO; + // 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 environment 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. Under node these are the host's own; in a browser there is no + // terminal and the property word says so before any of this is reached. + if ((t.c_lflag & (ISIG | IEXTEN)) == 0 && + (t.c_iflag & IXON) == 0) m |= KAL_TERM_PASS_CONTROL; *mode = m; return kal_ok; } @@ -37,12 +46,37 @@ int kal_terminal_set_mode(kal_stream s, kal_uintptr mode) { struct termios t{}; const int fd = static_cast(s.h); if (::tcgetattr(fd, &t) != 0) return oke::last(); - // READ, MODIFY, WRITE, AND ONLY THE TWO BITS openkal NAMES. A mode + const bool reserved_none = (t.c_lflag & (ISIG | IEXTEN)) == 0 && + (t.c_iflag & IXON) == 0; + // READ, MODIFY, WRITE, AND ONLY THE POSITIONS openkal NAMES. A mode // composed from scratch would silently reset every other attribute of the - // terminal -- flow control, the special characters, the baud rate -- none - // of which this interface claims to own. + // terminal -- the special characters, the baud rate -- none of which this + // interface claims to own. if (mode & KAL_TERM_LINE_EDIT) t.c_lflag |= ICANON; else t.c_lflag &= ~ICANON; if (mode & KAL_TERM_ECHO) t.c_lflag |= ECHO; else t.c_lflag &= ~ECHO; + + // A POSITION WHOSE REQUESTED VALUE IS THE ONE IN EFFECT IS NOT WRITTEN. + // This one stands for three flags, so establishing it again would settle + // two mechanisms the caller never asked about. + if (((mode & KAL_TERM_PASS_CONTROL) != 0) != reserved_none) { + if (mode & KAL_TERM_PASS_CONTROL) { + t.c_lflag &= ~(ISIG | IEXTEN); + t.c_iflag &= ~IXON; + } else { + t.c_lflag |= (ISIG | IEXTEN); + t.c_iflag |= 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, and a terminal left at + // VMIN=0 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.c_cc[VMIN] = 1; + t.c_cc[VTIME] = 0; + } // TCSANOW and not TCSADRAIN: openkal's caller has just been told what the // mode is and is entitled to have it take effect before its next read. if (::tcsetattr(fd, TCSANOW, &t) != 0) return oke::last();