Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 16 additions & 8 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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'

Expand Down Expand Up @@ -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'
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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_*`
Expand Down Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions mcpp.toml
Original file line number Diff line number Diff line change
@@ -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"

Expand All @@ -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
Expand Down
40 changes: 37 additions & 3 deletions src/terminal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand All @@ -37,12 +46,37 @@ int kal_terminal_set_mode(kal_stream s, kal_uintptr mode) {
struct termios t{};
const int fd = static_cast<int>(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();
Expand Down
Loading