aboutsummaryrefslogtreecommitdiffstats
path: root/surfaces/main.c
diff options
context:
space:
mode:
authoreunhhu <[email protected]>2026-09-27 20:03:30 +0100
committereunhhu <[email protected]>2026-09-27 20:03:30 +0100
commit7b93a66dbffaf2b20b0a1925786f56db54066c9b (patch)
tree000f214ae6743dcc3d6e861fc2a27b49d875ee2d /surfaces/main.c
parentc20c8fa13c77d08d093aede126a1ec7646da0905 (diff)
downloadvega-7b93a66dbffaf2b20b0a1925786f56db54066c9b.tar.gz
vega-7b93a66dbffaf2b20b0a1925786f56db54066c9b.zip
Type Hangul on the keyboard (두벌식)
Surfaces that read keys (the display, through evdev) had only a US layout's letters. After the 한/영 key (Lang1, or AltRight standing for it), letter keys now give jamo that compose syllables in the entry line (surfaces/hangul.c): an initial, a vowel and an optional final, joined vowels and finals, a final that moves on to a vowel after it (도깨비불), Shift's doubled consonants, a lone jamo as its compatibility letter. The syllable being composed shows after the entry line; Backspace takes it back a jamo at a time before deleting what was typed; Enter, Tab, the toggle or any key but a letter finishes it. The script language gains hangul, letters HEX and back to drive it. The check types ten cases through it (안녕하세요, 값을, 안자, 와원, 까원, 갑 after Backspace, 한글, ㅏ, 안ㅈ, 안녕 2) and follows the composing line jamo by jamo; the messenger check has Alice type 안녕 on her display with recorded keys, drawn on Bob's.
Diffstat (limited to 'surfaces/main.c')
-rw-r--r--surfaces/main.c65
1 files changed, 57 insertions, 8 deletions
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];