aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--surfaces/hangul.c148
-rw-r--r--surfaces/input.c1
-rw-r--r--surfaces/main.c65
-rw-r--r--surfaces/vega.h19
-rw-r--r--tests/check.py52
6 files changed, 267 insertions, 20 deletions
diff --git a/Makefile b/Makefile
index 5506e78..b99d26c 100644
--- a/Makefile
+++ b/Makefile
@@ -12,7 +12,7 @@ LIBPURE = $(PURE)/build/libpure.a
# host: TUI, CLI, NUI sentences, web), symbols/ (glyphs and icons as one
# format, drawn by CPU or GPU).
SURFACES = surfaces/screen.c surfaces/draw.c surfaces/width.c surfaces/web.c surfaces/pixels.c surfaces/display.c \
- surfaces/input.c surfaces/main.c
+ surfaces/input.c surfaces/hangul.c surfaces/main.c
.PHONY: all check FORCE
all: $(BUILD)/vega $(BUILD)/vega-symbols $(BUILD)/vega-surface.so
diff --git a/surfaces/hangul.c b/surfaces/hangul.c
new file mode 100644
index 0000000..7a6ad42
--- /dev/null
+++ b/surfaces/hangul.c
@@ -0,0 +1,148 @@
+#include "vega.h"
+#include <string.h>
+
+/* Hangul typed on a US keyboard as the standard two-set layout (두벌식): a
+ * letter key gives a jamo, and jamo compose a syllable as they come. A
+ * syllable is an initial consonant (19), a vowel (21) and an optional final
+ * (27), which Unicode composes as U+AC00 + (initial * 21 + vowel) * 28 +
+ * final. Two vowels or two finals may join into one (ㅗ+ㅏ ㅘ, ㄹ+ㄱ ㄺ); a
+ * vowel after a final takes the final, or a joined final's second half, as
+ * its own initial (도깨비불). A jamo alone shows as its compatibility letter. */
+
+/* Each letter key's jamo: an initial's index + 1, or a vowel's index + 1 as
+ * a negative number; lower case first, then with Shift (the doubled
+ * consonants ㄲ ㄸ ㅃ ㅆ ㅉ and the vowels ㅒ ㅖ; other letters as lower case). */
+static const signed char plain[26] = {
+ /* a ㅁ */ 7, /* b ㅠ */ -18, /* c ㅊ */ 15, /* d ㅇ */ 12, /* e ㄷ */ 4, /* f ㄹ */ 6, /* g ㅎ */ 19,
+ /* h ㅗ */ -9, /* i ㅑ */ -3, /* j ㅓ */ -5, /* k ㅏ */ -1, /* l ㅣ */ -21, /* m ㅡ */ -19, /* n ㅜ */ -14,
+ /* o ㅐ */ -2, /* p ㅔ */ -6, /* q ㅂ */ 8, /* r ㄱ */ 1, /* s ㄴ */ 3, /* t ㅅ */ 10, /* u ㅕ */ -7,
+ /* v ㅍ */ 18, /* w ㅈ */ 13, /* x ㅌ */ 17, /* y ㅛ */ -13, /* z ㅋ */ 16,
+};
+static signed char shifted(char upper) {
+ switch (upper) {
+ case 'Q': return 9; /* ㅃ */
+ case 'W': return 14; /* ㅉ */
+ case 'E': return 5; /* ㄸ */
+ case 'R': return 2; /* ㄲ */
+ case 'T': return 11; /* ㅆ */
+ case 'O': return -4; /* ㅒ */
+ case 'P': return -8; /* ㅖ */
+ default: return plain[upper - 'A'];
+ }
+}
+
+/* An initial as a final (0: it cannot end a syllable: ㄸ ㅃ ㅉ), and a
+ * single final as an initial. */
+static const unsigned char final_of[19] = {1, 2, 4, 7, 0, 8, 16, 17, 0, 19, 20, 21, 22, 0, 23, 24, 25, 26, 27};
+static signed char initial_of(unsigned final) {
+ for (int i = 0; i < 19; ++i)
+ if (final_of[i] == final && final) return (signed char)i;
+ return -1;
+}
+
+/* Joined finals: the first final, the initial added, the joined final. */
+static const unsigned char joined_finals[][3] = {
+ {1, 9, 3}, {4, 12, 5}, {4, 18, 6}, {8, 0, 9}, {8, 6, 10}, {8, 7, 11},
+ {8, 9, 12}, {8, 16, 13}, {8, 17, 14}, {8, 18, 15}, {17, 9, 18},
+};
+/* Joined vowels: the first vowel, the vowel added, the joined vowel. */
+static const unsigned char joined_vowels[][3] = {{8, 0, 9}, {8, 1, 10}, {8, 20, 11}, {13, 4, 14}, {13, 5, 15},
+ {13, 20, 16}, {18, 20, 19}};
+
+/* The compatibility letters (U+3131..) of the initials; the vowels' are
+ * U+314F + index. */
+static const uint16_t initial_letters[19] = {0x3131, 0x3132, 0x3134, 0x3137, 0x3138, 0x3139, 0x3141,
+ 0x3142, 0x3143, 0x3145, 0x3146, 0x3147, 0x3148, 0x3149,
+ 0x314A, 0x314B, 0x314C, 0x314D, 0x314E};
+
+void vega_hangul_reset(VegaHangul *h) { *h = (VegaHangul){-1, -1, 0, 0, {{0}}}; }
+
+/* The syllable (or lone jamo) as UTF-8 into out: its bytes (0 when none). */
+static size_t spell(signed char initial, signed char vowel, unsigned char final, char out[4]) {
+ uint32_t c = initial >= 0 && vowel >= 0 ? 0xAC00u + ((unsigned)initial * 21 + (unsigned)vowel) * 28 + final
+ : initial >= 0 ? initial_letters[initial]
+ : vowel >= 0 ? 0x314Fu + (unsigned)vowel
+ : 0;
+ if (!c) return 0;
+ out[0] = (char)(0xE0 | c >> 12);
+ out[1] = (char)(0x80 | (c >> 6 & 0x3F));
+ out[2] = (char)(0x80 | (c & 0x3F));
+ return 3;
+}
+
+size_t vega_hangul_composing(const VegaHangul *h, char out[4]) { return spell(h->initial, h->vowel, h->final, out); }
+
+size_t vega_hangul_finish(VegaHangul *h, char out[4]) {
+ size_t length = vega_hangul_composing(h, out);
+ vega_hangul_reset(h);
+ return length;
+}
+
+/* What it composed so far is kept for Backspace, then it changes. */
+static void step(VegaHangul *h, signed char initial, signed char vowel, unsigned char final) {
+ if (h->steps < sizeof(h->before) / sizeof(*h->before))
+ h->before[h->steps++] = (struct VegaJamo){h->initial, h->vowel, h->final};
+ h->initial = initial, h->vowel = vowel, h->final = final;
+}
+
+/* A new syllable from here: what was composed is finished into done. */
+static size_t begin(VegaHangul *h, signed char initial, signed char vowel, char done[4]) {
+ size_t length = vega_hangul_finish(h, done);
+ step(h, initial, vowel, 0);
+ return length;
+}
+
+bool vega_hangul_type(VegaHangul *h, char key, char done[4], size_t *done_length) {
+ *done_length = 0;
+ signed char jamo = key >= 'a' && key <= 'z' ? plain[key - 'a'] : key >= 'A' && key <= 'Z' ? shifted(key) : 0;
+ if (!jamo) return false;
+ if (jamo > 0) { /* a consonant */
+ signed char consonant = (signed char)(jamo - 1);
+ if (h->vowel < 0 || h->initial < 0) { /* nothing, a lone initial or a lone vowel: this one starts anew */
+ if (h->initial < 0 && h->vowel < 0) step(h, consonant, -1, 0);
+ else *done_length = begin(h, consonant, -1, done);
+ } else if (!h->final && final_of[consonant]) step(h, h->initial, h->vowel, final_of[consonant]);
+ else {
+ unsigned char joined = 0;
+ for (size_t i = 0; i < sizeof(joined_finals) / sizeof(*joined_finals) && !joined; ++i)
+ if (joined_finals[i][0] == h->final && joined_finals[i][1] == (unsigned char)consonant) joined = joined_finals[i][2];
+ if (joined) step(h, h->initial, h->vowel, joined);
+ else *done_length = begin(h, consonant, -1, done);
+ }
+ return true;
+ }
+ signed char vowel = (signed char)(-jamo - 1);
+ if (h->final) { /* 도깨비불: the final (or its second half) moves on to start the next syllable */
+ signed char moved = -1;
+ unsigned char kept = 0;
+ for (size_t i = 0; i < sizeof(joined_finals) / sizeof(*joined_finals); ++i)
+ if (joined_finals[i][2] == h->final) kept = joined_finals[i][0], moved = (signed char)joined_finals[i][1];
+ if (moved < 0) moved = initial_of(h->final);
+ char out[4];
+ *done_length = spell(h->initial, h->vowel, kept, out);
+ memcpy(done, out, *done_length);
+ vega_hangul_reset(h);
+ step(h, moved, -1, 0); /* so Backspace leaves the initial first */
+ step(h, moved, vowel, 0);
+ return true;
+ }
+ if (h->vowel >= 0) {
+ unsigned char joined = 0;
+ for (size_t i = 0; i < sizeof(joined_vowels) / sizeof(*joined_vowels) && !joined; ++i)
+ if (joined_vowels[i][0] == (unsigned char)h->vowel && joined_vowels[i][1] == (unsigned char)vowel)
+ joined = joined_vowels[i][2];
+ if (joined) step(h, h->initial, (signed char)joined, 0);
+ else *done_length = begin(h, -1, vowel, done);
+ return true;
+ }
+ step(h, h->initial, vowel, 0); /* after an initial, or alone */
+ return true;
+}
+
+bool vega_hangul_back(VegaHangul *h) {
+ if (h->initial < 0 && h->vowel < 0) return false;
+ if (!h->steps) { vega_hangul_reset(h); return true; }
+ struct VegaJamo before = h->before[--h->steps];
+ h->initial = before.initial, h->vowel = before.vowel, h->final = before.final;
+ return true;
+}
diff --git a/surfaces/input.c b/surfaces/input.c
index 9403a75..e13693d 100644
--- a/surfaces/input.c
+++ b/surfaces/input.c
@@ -52,6 +52,7 @@ static const struct { unsigned short code; const char *name; char plain, shifted
{KEY_HOME, "Home", 0, 0}, {KEY_UP, "ArrowUp", 0, 0}, {KEY_PAGEUP, "PageUp", 0, 0}, {KEY_LEFT, "ArrowLeft", 0, 0},
{KEY_RIGHT, "ArrowRight", 0, 0}, {KEY_END, "End", 0, 0}, {KEY_DOWN, "ArrowDown", 0, 0},
{KEY_PAGEDOWN, "PageDown", 0, 0}, {KEY_INSERT, "Insert", 0, 0}, {KEY_DELETE, "Delete", 0, 0},
+ {KEY_HANGEUL, "Lang1", 0, 0}, {KEY_RIGHTALT, "AltRight", 0, 0}, /* 한/영, or the right Alt that stands for it */
};
static bool has(int fd, unsigned type, unsigned code) {
diff --git a/surfaces/main.c b/surfaces/main.c
index 010a1bb..b0c32b5 100644
--- a/surfaces/main.c
+++ b/surfaces/main.c
@@ -37,9 +37,12 @@
* written again replaces the running one and states keep their values by
* name: aup watch app.aup -o app.pmod beside it makes a saved edit show at once.
* Script commands, one per line: key KEYNAME | text HEX|- | type HEX | enter |
- * tab | frame [N] | point VIEW ROW | click LINE COLUMN | time MS | tick TIMER | swap MODULE | show. key presses and
+ * tab | hangul | letters HEX | back | frame [N] | point VIEW ROW | click LINE COLUMN | time MS | tick TIMER |
+ * swap MODULE | show. key presses and
* releases the input bound to that key; text commits text to the screen's text
- * input; type, enter and tab act on the TUI's input line; point focuses a row
+ * input; type, enter and tab act on the TUI's input line, and so do hangul (the
+ * 한/영 key), letters (keys typed, each byte one, through Hangul when it is on)
+ * and back (Backspace); point focuses a row
* (0 first) of a list or table; click is a mouse click on the last shown TUI
* frame (1 first, as terminals report it); time sets the time the next
* events see (live surfaces use the clock) and tick ticks a timer (live
@@ -61,6 +64,10 @@ static VegaGrid *drawn; /* the TUI's last frame, which a click lands on */
static char entry[VEGA_TEXT];
static size_t entry_length;
static bool typing;
+/* Hangul typed on the keyboard (Lang1, the 한/영 key, or AltRight switches):
+ * the syllable being composed shows after the entry line until it is done. */
+static bool korean;
+static VegaHangul hangul = {-1, -1, 0, 0, {{0, 0, 0}}};
/* The module file last read, and a digest of its bytes. A loaded module
* keeps nothing of them, so one buffer, grown to the largest, serves every load. */
@@ -357,15 +364,38 @@ static bool raw_mode(void) {
/* Whether the app takes Tab itself; otherwise Tab switches typing and keys. */
static bool tab_switches(void) { return screen.prompt >= 0 && key_input("Tab") < 0; }
+static void append(const char *bytes, size_t length) {
+ if (entry_length + length < sizeof(entry)) { memcpy(entry + entry_length, bytes, length); entry_length += length; }
+}
+
+/* The syllable being composed, done: into the entry line. */
+static void finish_hangul(void) {
+ char done[4];
+ append(done, vega_hangul_finish(&hangul, done));
+}
+
+/* The entry line as shown: what was typed, then the syllable being composed. */
+static size_t shown_entry(char out[VEGA_TEXT + 4]) {
+ memcpy(out, entry, entry_length);
+ return entry_length + vega_hangul_composing(&hangul, out + entry_length);
+}
+
/* One key from the terminal, as typed text or as a Pure key name. */
static void handle_key(const char *name) {
int input;
- if (!strcmp(name, "Tab") && tab_switches()) { typing = !typing; return; }
+ if (!strcmp(name, "Lang1") || !strcmp(name, "AltRight")) { /* 한/영 */
+ finish_hangul();
+ korean = !korean;
+ return;
+ }
+ if (!strcmp(name, "Tab") && tab_switches()) { finish_hangul(); typing = !typing; return; }
if (typing && !strcmp(name, "Enter")) {
+ finish_hangul();
if (entry_length && commit(entry, entry_length)) entry_length = 0;
return;
}
if (typing && !strcmp(name, "Backspace")) {
+ if (vega_hangul_back(&hangul)) return; /* a jamo of the syllable being composed */
while (entry_length && ((unsigned char)entry[entry_length - 1] & 0xC0) == 0x80) --entry_length;
if (entry_length) --entry_length;
return;
@@ -375,7 +405,14 @@ static void handle_key(const char *name) {
static void handle_text(const char *bytes, size_t length) {
if (typing) {
- if (entry_length + length < sizeof(entry)) { memcpy(entry + entry_length, bytes, length); entry_length += length; }
+ char done[4];
+ size_t finished;
+ if (korean && length == 1 && vega_hangul_type(&hangul, bytes[0], done, &finished)) {
+ append(done, finished);
+ return;
+ }
+ finish_hangul(); /* anything but a letter ends the syllable */
+ append(bytes, length);
return;
}
if (length != 1) return;
@@ -457,7 +494,8 @@ static int interactive(void) {
/* Tick what is due, draw, then wait for input, a frame or the next tick. */
bool ticked = false;
long due = vega_timers_run(&timers, &app, vega_now(), &ticked);
- vega_draw(&grid, &app, &screen, entry, entry_length, typing);
+ char line[VEGA_TEXT + 4];
+ vega_draw(&grid, &app, &screen, line, shown_entry(line), typing);
paint_terminal(&grid);
struct pollfd wait = {STDIN_FILENO, POLLIN, 0};
int timeout = screen.ticks ? 16 : watched ? 200 : -1;
@@ -597,7 +635,8 @@ static int live(VegaInput *input, unsigned columns, unsigned rows, bool recorded
bool ticked = false;
long due = node ? -1 : vega_timers_run(&timers, &app, vega_now(), &ticked);
PureSnapshot *snapshot = node ? node->read(node->host, &app) : NULL;
- if (!node || snapshot) vega_draw(&grid, &app, &screen, entry, entry_length, typing);
+ char line[VEGA_TEXT + 4];
+ if (!node || snapshot) vega_draw(&grid, &app, &screen, line, shown_entry(line), typing);
if (snapshot) node->release(node->host, snapshot);
if (!display) { /* a node's pixels: each frame into a file */
if (!write_frame(&grid, path)) { fprintf(stderr, "vega: cannot write %s\n", path); status = 1; break; }
@@ -688,6 +727,14 @@ static int script(Surface surface, unsigned width, unsigned height, bool paintin
} else if (!strcmp(command, "tab")) {
handle_key("Tab");
acted = false;
+ } else if (!strcmp(command, "hangul") || !strcmp(command, "back")) {
+ handle_key(!strcmp(command, "hangul") ? "Lang1" : "Backspace");
+ acted = false;
+ } else if (!strcmp(command, "letters")) {
+ char keys[VEGA_TEXT];
+ size_t count = unhex(argument, keys, sizeof(keys));
+ for (size_t i = 0; i < count; ++i) handle_text(keys + i, 1);
+ acted = false;
} else if (!strcmp(command, "point")) {
char name[PURE_MODULE_NAME];
long row = -1;
@@ -728,14 +775,16 @@ static int script(Surface surface, unsigned width, unsigned height, bool paintin
else if (surface == SURFACE_SAY) vega_say(stdout, &app, &screen, true);
else if ((shown = true) && painting) {
Out out = {0};
- vega_draw(&grid, &app, &screen, entry, entry_length, typing);
+ char prompt[VEGA_TEXT + 4];
+ vega_draw(&grid, &app, &screen, prompt, shown_entry(prompt), typing);
paint(&grid, &out);
printf("paint %zu\n", out.used);
fwrite(out.bytes, 1, out.used, stdout);
putchar('\n');
free(out.bytes);
} else {
- vega_draw(&grid, &app, &screen, entry, entry_length, typing);
+ char prompt[VEGA_TEXT + 4];
+ vega_draw(&grid, &app, &screen, prompt, shown_entry(prompt), typing);
dump(&grid);
printf("cells %lu\n", vega_evaluations() - counted);
char path[512];
diff --git a/surfaces/vega.h b/surfaces/vega.h
index fd8780b..cd6b1ee 100644
--- a/surfaces/vega.h
+++ b/surfaces/vega.h
@@ -153,6 +153,25 @@ bool vega_display_show(VegaDisplay *);
const uint32_t *vega_display_captured(const VegaDisplay *, size_t *stride);
void vega_display_close(VegaDisplay *);
+/* Hangul typed on a US keyboard as the two-set layout (두벌식, hangul.c):
+ * letter keys give jamo that compose syllables as they come. type takes a
+ * letter (A-Z with Shift) and gives what it finished (UTF-8, at most one
+ * syllable) into done; false for a key that is not a letter. composing is
+ * the syllable being composed (0 bytes: none); back takes back its last jamo
+ * (false: none was being composed); finish ends it into out. */
+struct VegaJamo { signed char initial, vowel; unsigned char final; };
+typedef struct {
+ signed char initial, vowel; /* -1: none */
+ unsigned char final; /* 0: none */
+ unsigned char steps; /* what it was before each jamo, for Backspace */
+ struct VegaJamo before[8];
+} VegaHangul;
+void vega_hangul_reset(VegaHangul *);
+bool vega_hangul_type(VegaHangul *, char key, char done[4], size_t *done_length);
+size_t vega_hangul_composing(const VegaHangul *, char out[4]);
+bool vega_hangul_back(VegaHangul *);
+size_t vega_hangul_finish(VegaHangul *, char out[4]);
+
/* Input from evdev: keyboards (key names and the text they type on a US
* layout), mice and touch screens (presses at a pixel of a width x height
* screen). With count paths, recorded events are read from those files
diff --git a/tests/check.py b/tests/check.py
index ad30654..de26a3f 100644
--- a/tests/check.py
+++ b/tests/check.py
@@ -1160,6 +1160,31 @@ def live_display(vega, module, work):
return {'events': len(events), 'lines': len(got)}
+def hangul(vega, module):
+ """Hangul typed on the keyboard (두벌식, surfaces/hangul.c): letter keys
+ compose syllables in the entry line after the 한/영 key, and Enter commits
+ them. Joined vowels and finals, a final moving on to a vowel after it,
+ Shift's doubled consonants, a lone jamo, Backspace taking back one jamo at
+ a time, and a space or digit ending a syllable, each as an item."""
+ cases = [('dkssudgktpdy', '', '안녕하세요'), ('rkqtdmf', '', '값을'), ('dkswk', '', '안자'),
+ ('dhkdnjs', '', '와원'), ('Rkdnjs', '', '까원'), ('rkqt', 'back', '갑'), ('gksrmf', '', '한글'),
+ ('k', '', 'ㅏ'), ('dkswk', 'back', '안ㅈ'), ('dkssud 2', '', '안녕 2')]
+ script = 'hangul\n' + ''.join(f'letters {keys.encode().hex()}\n' + (f'{after}\n' if after else '') + 'enter\n'
+ for keys, after, _ in cases) + 'show\n'
+ shown = run(vega, 'tui', module, '--dump', '40x16', stdin=script)
+ assert shown.returncode == 0, shown.stderr
+ labels = [line[1:].strip().split(' ')[0].strip() for line in shown.stdout.splitlines()
+ if line[1:].startswith(' ') or line[1:].startswith('> ')]
+ labels = [label.lstrip('> ').strip() for label in labels]
+ assert labels[:len(cases)] == [want for _, _, want in cases], (labels, shown.stdout)
+ # The syllable being composed shows on the entry line; Backspace takes it back jamo by jamo, then a letter.
+ typing = run(vega, 'tui', module, '--dump', '30x5',
+ stdin=f'hangul\nletters {"dkssu".encode().hex()}\nshow\nback\nshow\nback\nback\nshow\n')
+ prompts = [line for line in typing.stdout.splitlines() if line.startswith('|') and '>' in line[:6] and 'add' in line]
+ assert [p.split('>', 1)[1].split('\t')[0].strip() for p in prompts] == ['안녀', '안ㄴ', ''], typing.stdout
+ return {'items': len(cases)}
+
+
def node_display(vega, serve, module, work):
"""Vega in a Pure node (vega-surface.so run by pure-serve --surface): the
node's app, which an HTTP client gave an item, is on its display, and
@@ -1247,9 +1272,10 @@ def messenger(vega, serve, module, work):
"""Two nodes of examples/messenger.aup, Alice's and Bob's, each with a key
of its own, sealing to the other's and taking lines only from the other
(pure-serve --key, --send, --seal, --sender), each showing its app
- through Vega's pixels in the node. A line typed on Alice's display
- (recorded keys) reaches Bob's node sealed and is drawn on his; Bob's
- answer, posted by a program, is drawn on hers. Each last frame is the one
+ through Vega's pixels in the node. Lines typed on Alice's display
+ (recorded keys: one in English, one in Hangul after the 한/영 key) reach
+ Bob's node sealed and are drawn on his; Bob's answer, posted by a
+ program, is drawn on hers. Each last frame is the one
the vega command draws from that node's store, and the relay between the
nodes saw no line's text nor a field's name, only seals made in one
session each and naming the sender's key."""
@@ -1341,9 +1367,11 @@ def messenger(vega, serve, module, work):
'--surface', library, '--', 'pixels', '--symbols', face, '--size', 16, '--grid', '40x8',
'--out', work / f'{name}-frame'] for name in ('alice', 'bob')}
start('bob', *wiring['bob'])
- codes = {'h': 35, 'e': 18, 'l': 38, 'o': 24, 'b': 48, 'space': 57, 'enter': 28}
+ codes = {'h': 35, 'e': 18, 'l': 38, 'o': 24, 'b': 48, 'space': 57, 'enter': 28, 'lang1': 122, 'd': 32,
+ 'k': 37, 's': 31, 'u': 22}
events = []
- for name in ('h', 'e', 'l', 'l', 'o', 'space', 'b', 'o', 'b', 'enter'):
+ for name in ('h', 'e', 'l', 'l', 'o', 'space', 'b', 'o', 'b', 'enter', 'lang1', 'd', 'k', 's', 's', 'u', 'd',
+ 'enter'):
events.extend([(1, codes[name], 1), (0, 0, 0), (1, codes[name], 0), (0, 0, 0)])
recording = work / 'alice-events.bin'
recording.write_bytes(b''.join(struct.pack('llHHi', 0, 0, *event) for event in events))
@@ -1353,10 +1381,10 @@ def messenger(vega, serve, module, work):
while '\nframe ' not in '\n' + printed and time.time() < deadline:
if select.select([process.stdout], [], [], 1)[0]:
printed += os.read(process.stdout.fileno(), 65536).decode()
- assert 'hello bob' in printed and '\nframe ' in '\n' + printed, printed
- assert wait_for(lambda: call('bob', 'GET', '/views/title')[1] == 'bob: 1 lines'), call('bob', 'GET', '/views/title')
+ assert 'hello bob' in printed and '안녕' in printed and '\nframe ' in '\n' + printed, printed
+ assert wait_for(lambda: call('bob', 'GET', '/views/title')[1] == 'bob: 2 lines'), call('bob', 'GET', '/views/title')
assert call('bob', 'POST', '/inputs/say', b'hi alice')[0] == 200
- assert wait_for(lambda: call('alice', 'GET', '/views/title')[1] == 'alice: 2 lines')
+ assert wait_for(lambda: call('alice', 'GET', '/views/title')[1] == 'alice: 3 lines')
drawn = {}
for name in ('alice', 'bob'):
# Its surface draws after each save: the last frame shows the last save.
@@ -1374,7 +1402,7 @@ def messenger(vega, serve, module, work):
expected = run(vega, 'pixels', module, '--store', copy, '--symbols', face, '--size', 16, '--grid', '40x8',
'--out', work / f'{name}-expected', stdin='show\n')
assert expected.returncode == 0, expected.stderr
- assert 'hello bob' in expected.stdout and 'hi alice' in expected.stdout, expected.stdout
+ assert all(line in expected.stdout for line in ('hello bob', '안녕', 'hi alice')), expected.stdout
assert drawn[name].read_bytes() == (work / f'{name}-expected-1.ppm').read_bytes(), name
finally:
for process, _ in nodes.values():
@@ -1383,10 +1411,11 @@ def messenger(vega, serve, module, work):
process.wait()
relay.shutdown()
assert seen and all(len(headers['X-Pure-Seal'].split()) == 4 for _, headers, _ in seen), seen[:1]
- assert not any(word in body for _, _, body in seen for word in (b'hello', b'hi alice', b'body', b'from'))
+ assert not any(word in body for _, _, body in seen
+ for word in (b'hello', '안녕'.encode(), b'hi alice', b'body', b'from'))
sessions = {(headers['X-Pure-Source'], headers['X-Pure-Seal'].split()[1]) for _, headers, _ in seen}
assert len(sessions) == 2, sessions # one session each way
- return {'relayed': len(seen), 'lines': 2}
+ return {'relayed': len(seen), 'lines': 3}
def width_of(character):
@@ -1427,6 +1456,7 @@ def main():
report['interactive_sessions'] = interactive(vega, modules['todo']) + \
interactive(vega, modules['todo'], work / 'interactive-store')
report['stored_surface_lines'] = stored_surfaces(vega, modules['todo'], work, range(args.seeds))
+ report['hangul'] = hangul(vega, modules['todo'])
report['convention_findings'] = conventions(vega, aup, work, modules)
report['examples'], report['robustness_runs'] = robustness(vega, aup, work, args.rounds)
if args.symbols: