emoji: repair one-shot candidate selection
This commit is contained in:
@@ -5,9 +5,9 @@ LIBS = -lthread -lbio
|
||||
|
||||
PROG = unit_test
|
||||
TESTSRC = unit_test.c test_util.c str_test.c hash_test.c trie_test.c \
|
||||
ko_test.c vi_test.c engine_test.c dict_test.c
|
||||
ko_test.c vi_test.c engine_test.c dict_test.c ipc_test.c
|
||||
TESTOBJ = $(TESTSRC:.c=.o)
|
||||
PARENTSRC = str.c hash.c trie.c dict.c ko.c vi.c
|
||||
PARENTSRC = str.c hash.c trie.c dict.c ko.c vi.c ipc.c
|
||||
PARENTOBJ = $(PARENTSRC:%.c=unit_%.o)
|
||||
OBJS = $(TESTOBJ) $(PARENTOBJ)
|
||||
|
||||
|
||||
@@ -302,11 +302,19 @@ emojibegin(Emojifix *f, int showpre)
|
||||
f->popup = popup;
|
||||
f->visible = visible;
|
||||
draindraw(nil);
|
||||
l->dict = hmapalloc(8);
|
||||
l->dict = hmapalloc(32);
|
||||
setemoji(&l->dict, "a", "A B");
|
||||
setemoji(&l->dict, "ㅁ", "B C");
|
||||
setemoji(&l->dict, "alpha", "α");
|
||||
setemoji(&l->dict, "smil", "😀 😄");
|
||||
setemoji(&l->dict, "smile", "😀 😄");
|
||||
setemoji(&l->dict, "웃음", "😀 😄 😂");
|
||||
setemoji(&l->dict, "^", "¹ ² ³ ⁴ ⁵ ⁶ ⁷ ⁸ ⁹ ⁽");
|
||||
setemoji(&l->dict, "^0", "⁰");
|
||||
setemoji(&l->dict, "_", "₁ ₂ ₃ ₄ ₅ ₆ ₇ ₈ ₉ ₍");
|
||||
setemoji(&l->dict, "<", "← ≤ ♥ ≠");
|
||||
setemoji(&l->dict, "many",
|
||||
"c1 c2 c3 c4 c5 c6 c7 c8 c9 c10 c11 c12");
|
||||
init();
|
||||
im.l = getlang(LangKO);
|
||||
popup = showpre;
|
||||
@@ -345,8 +353,10 @@ engine_emoji_single_candidate(struct ct *t)
|
||||
Emojifix f;
|
||||
Str com;
|
||||
|
||||
emojibegin(&f, 0);
|
||||
emojibegin(&f, 1);
|
||||
sclear(&com);
|
||||
CT_CHECK(t, keystroke('p', Mctrl, &com));
|
||||
CT_EQ_INT(t, 0, popup);
|
||||
CT_CHECK(t, keystroke('e', Mctrl, &com));
|
||||
CT_CHECK(t, search.on);
|
||||
CT_EQ_INT(t, LangKO, im.l->lang);
|
||||
@@ -363,6 +373,10 @@ engine_emoji_single_candidate(struct ct *t)
|
||||
checkstr(t, "explicit candidate commit", "α", &com);
|
||||
CT_CHECK(t, !search.on);
|
||||
CT_EQ_INT(t, LangKO, im.l->lang);
|
||||
show();
|
||||
CT_CHECK(t, draindraw(&dc) > 0);
|
||||
CT_EQ_INT(t, 0, dc.pre.n);
|
||||
CT_EQ_INT(t, 0, dc.nkouho);
|
||||
}
|
||||
emojiend(&f);
|
||||
}
|
||||
@@ -376,6 +390,18 @@ engine_emoji_queries(struct ct *t)
|
||||
|
||||
emojibegin(&f, 1);
|
||||
sclear(&com);
|
||||
CT_CHECK(t, keystroke('e', Mctrl, &com));
|
||||
CT_CHECK(t, keystroke('a', 0, &com));
|
||||
checkstr(t, "raw query wins", "a", &search.text);
|
||||
CT_EQ_INT(t, 3, im.nkouho);
|
||||
checkstr(t, "raw candidate", "A", &im.kouho[0]);
|
||||
checkstr(t, "deduplicated candidate", "B", &im.kouho[1]);
|
||||
checkstr(t, "local candidate", "C", &im.kouho[2]);
|
||||
CT_CHECK(t, draindraw(&dc) > 0);
|
||||
checkstr(t, "prefix popup query", "a", &dc.pre);
|
||||
CT_EQ_INT(t, 3, dc.nkouho);
|
||||
CT_CHECK(t, keystroke(Kesc, 0, &com));
|
||||
|
||||
CT_CHECK(t, keystroke('e', Mctrl, &com));
|
||||
if(typekeys(t, "smile", &com)){
|
||||
checkstr(t, "English alias", "smile", &search.text);
|
||||
@@ -384,11 +410,19 @@ engine_emoji_queries(struct ct *t)
|
||||
checkstr(t, "backspaced query", "smil", &search.text);
|
||||
CT_EQ_INT(t, 2, im.nkouho);
|
||||
CT_CHECK(t, keystroke('e', 0, &com));
|
||||
CT_CHECK(t, keystroke('2', Malt, &com));
|
||||
CT_CHECK(t, keystroke('2', 0, &com));
|
||||
checkstr(t, "selected English alias", "😄", &com);
|
||||
CT_EQ_INT(t, LangKO, im.l->lang);
|
||||
}
|
||||
|
||||
sclear(&com);
|
||||
CT_CHECK(t, keystroke('e', Mctrl, &com));
|
||||
if(typekeys(t, "SMILE", &com)){
|
||||
checkstr(t, "folded English alias", "smile", &search.text);
|
||||
CT_EQ_INT(t, 2, im.nkouho);
|
||||
CT_CHECK(t, keystroke(Kesc, 0, &com));
|
||||
}
|
||||
|
||||
sclear(&com);
|
||||
CT_CHECK(t, keystroke('e', Mctrl, &com));
|
||||
if(typekeys(t, "dntdma", &com)){
|
||||
@@ -405,11 +439,187 @@ engine_emoji_queries(struct ct *t)
|
||||
emojiend(&f);
|
||||
}
|
||||
|
||||
void
|
||||
engine_emoji_digit_aliases(struct ct *t)
|
||||
{
|
||||
static const struct {
|
||||
char *name;
|
||||
Rune prefix;
|
||||
Rune digit;
|
||||
char *want;
|
||||
} cases[] = {
|
||||
{ "^1", '^', '1', "¹" },
|
||||
{ "_2", '_', '2', "₂" },
|
||||
{ "<3", '<', '3', "♥" },
|
||||
};
|
||||
Emojifix f;
|
||||
Str com;
|
||||
int i;
|
||||
|
||||
emojibegin(&f, 0);
|
||||
for(i = 0; i < nelem(cases); i++){
|
||||
sclear(&com);
|
||||
CT_CHECK(t, keystroke('e', Mctrl, &com));
|
||||
CT_CHECK(t, keystroke(cases[i].prefix, Mshift, &com));
|
||||
CT_CHECK(t, im.nkouho >= cases[i].digit - '0');
|
||||
CT_CHECK(t, keystroke(cases[i].digit, 0, &com));
|
||||
checkstr(t, cases[i].name, cases[i].want, &com);
|
||||
CT_CHECK(t, !search.on);
|
||||
CT_EQ_INT(t, LangKO, im.l->lang);
|
||||
}
|
||||
|
||||
sclear(&com);
|
||||
CT_CHECK(t, keystroke('e', Mctrl, &com));
|
||||
CT_CHECK(t, keystroke('^', Mshift, &com));
|
||||
CT_CHECK(t, keystroke('0', 0, &com));
|
||||
checkstr(t, "zero query", "^0", &search.text);
|
||||
checkstr(t, "zero does not commit", "", &com);
|
||||
CT_EQ_INT(t, 1, im.nkouho);
|
||||
CT_CHECK(t, keystroke(Kret, 0, &com));
|
||||
checkstr(t, "zero alias", "⁰", &com);
|
||||
|
||||
sclear(&com);
|
||||
CT_CHECK(t, keystroke('e', Mctrl, &com));
|
||||
if(typekeys(t, "smile", &com)){
|
||||
CT_CHECK(t, keystroke('9', 0, &com));
|
||||
checkstr(t, "missing number query", "smile9", &search.text);
|
||||
checkstr(t, "missing number commit", "", &com);
|
||||
CT_CHECK(t, search.on);
|
||||
CT_EQ_INT(t, 0, im.nkouho);
|
||||
CT_CHECK(t, keystroke(Kret, 0, &com));
|
||||
checkstr(t, "missing number text", "smile9", &com);
|
||||
}
|
||||
emojiend(&f);
|
||||
}
|
||||
|
||||
void
|
||||
engine_emoji_navigation(struct ct *t)
|
||||
{
|
||||
Drawcmd dc = {0};
|
||||
Emojifix f;
|
||||
Str com;
|
||||
int i;
|
||||
|
||||
emojibegin(&f, 0);
|
||||
sclear(&com);
|
||||
CT_CHECK(t, keystroke('e', Mctrl, &com));
|
||||
if(typekeys(t, "many", &com)){
|
||||
CT_EQ_INT(t, 12, im.nkouho);
|
||||
for(i = 0; i < Maxdisp+1; i++)
|
||||
CT_CHECK(t, keystroke(Kdown, 0, &com));
|
||||
CT_EQ_INT(t, Maxdisp, im.sel);
|
||||
CT_CHECK(t, draindraw(&dc) > 0);
|
||||
checkstr(t, "scrolled first row", "c2", &dc.kouho[0]);
|
||||
checkstr(t, "scrolled ninth row", "c10", &dc.kouho[8]);
|
||||
CT_EQ_INT(t, 8, dc.sel);
|
||||
CT_CHECK(t, keystroke('1', 0, &com));
|
||||
checkstr(t, "scrolled row one", "c2", &com);
|
||||
}
|
||||
|
||||
sclear(&com);
|
||||
CT_CHECK(t, keystroke('e', Mctrl, &com));
|
||||
if(typekeys(t, "many", &com)){
|
||||
for(i = 0; i < 12; i++)
|
||||
CT_CHECK(t, keystroke(Kdown, 0, &com));
|
||||
CT_EQ_INT(t, 11, im.sel);
|
||||
CT_CHECK(t, keystroke('9', 0, &com));
|
||||
checkstr(t, "scrolled row nine", "c12", &com);
|
||||
}
|
||||
|
||||
sclear(&com);
|
||||
CT_CHECK(t, keystroke('e', Mctrl, &com));
|
||||
if(typekeys(t, "smile", &com)){
|
||||
CT_CHECK(t, keystroke(Ktab, 0, &com));
|
||||
CT_EQ_INT(t, 0, im.sel);
|
||||
CT_CHECK(t, keystroke(Kdown, 0, &com));
|
||||
CT_EQ_INT(t, 1, im.sel);
|
||||
CT_CHECK(t, keystroke(Kup, 0, &com));
|
||||
CT_EQ_INT(t, 0, im.sel);
|
||||
CT_CHECK(t, keystroke(Ktab, Mshift, &com));
|
||||
CT_EQ_INT(t, 1, im.sel);
|
||||
CT_CHECK(t, keystroke(Kret, 0, &com));
|
||||
checkstr(t, "navigated candidate", "😄", &com);
|
||||
}
|
||||
emojiend(&f);
|
||||
}
|
||||
|
||||
void
|
||||
engine_emoji_preedit_languages(struct ct *t)
|
||||
{
|
||||
Emojifix f;
|
||||
Str com, shown;
|
||||
int p;
|
||||
|
||||
emojibegin(&f, 0);
|
||||
init();
|
||||
im.l = getlang(LangJP);
|
||||
sclear(&com);
|
||||
if(typekeys(t, "ka", &com)){
|
||||
CT_CHECK(t, !keystroke(Kmodfirst+2, 0, &com));
|
||||
checkstr(t, "modifier preserves Japanese", "ka", &im.pre);
|
||||
CT_CHECK(t, keystroke('E', Mctrl|Mshift, &com));
|
||||
checkstr(t, "committed Japanese preedit", "か", &com);
|
||||
CT_CHECK(t, search.on);
|
||||
CT_EQ_INT(t, LangJP, im.l->lang);
|
||||
CT_CHECK(t, keystroke('a', 0, &com));
|
||||
p = popup;
|
||||
CT_CHECK(t, !keystroke(Kmodfirst+2, 0, &com));
|
||||
CT_CHECK(t, search.on);
|
||||
checkstr(t, "modifier preserves search", "a", &search.text);
|
||||
CT_CHECK(t, keystroke('P', Mctrl|Mshift, &com));
|
||||
CT_EQ_INT(t, !p, popup);
|
||||
CT_CHECK(t, search.on);
|
||||
CT_CHECK(t, !keystroke(Kmodfirst+2, 0, &com));
|
||||
CT_CHECK(t, keystroke('E', Mctrl|Mshift, &com));
|
||||
CT_CHECK(t, !search.on);
|
||||
sclear(&com);
|
||||
if(typekeys(t, "na", &com)){
|
||||
shown = shownpre(&im);
|
||||
checkstr(t, "Japanese resumed", "な", &shown);
|
||||
}
|
||||
}
|
||||
|
||||
init();
|
||||
im.l = getlang(LangKO);
|
||||
sclear(&com);
|
||||
if(typekeys(t, "rk", &com)){
|
||||
CT_CHECK(t, keystroke('e', Mctrl, &com));
|
||||
checkstr(t, "committed Korean preedit", "가", &com);
|
||||
if(typekeys(t, "smile", &com)){
|
||||
CT_CHECK(t, keystroke('1', 0, &com));
|
||||
checkstr(t, "Korean selection", "가😀", &com);
|
||||
sclear(&com);
|
||||
if(typekeys(t, "rk", &com))
|
||||
checkstr(t, "Korean resumed", "가", &im.pre);
|
||||
}
|
||||
}
|
||||
|
||||
init();
|
||||
im.l = &testvi;
|
||||
sclear(&com);
|
||||
if(typekeys(t, "as", &com)){
|
||||
CT_CHECK(t, keystroke('e', Mctrl, &com));
|
||||
checkstr(t, "committed Telex preedit", "á", &com);
|
||||
CT_CHECK(t, keystroke(Kesc, 0, &com));
|
||||
CT_CHECK(t, !search.on);
|
||||
CT_EQ_PTR(t, &testvi, im.l);
|
||||
sclear(&com);
|
||||
if(typekeys(t, "as", &com)){
|
||||
shown = shownpre(&im);
|
||||
checkstr(t, "Telex resumed", "á", &shown);
|
||||
checkstr(t, "Telex raw resumed", "as", &im.raw);
|
||||
}
|
||||
}
|
||||
emojiend(&f);
|
||||
}
|
||||
|
||||
void
|
||||
engine_emoji_start_and_unknown(struct ct *t)
|
||||
{
|
||||
Emojifix f;
|
||||
Str com;
|
||||
char want[Maxrunes+1];
|
||||
int i;
|
||||
|
||||
emojibegin(&f, 0);
|
||||
sclear(&com);
|
||||
@@ -427,5 +637,67 @@ engine_emoji_start_and_unknown(struct ct *t)
|
||||
CT_EQ_INT(t, LangKO, im.l->lang);
|
||||
}
|
||||
}
|
||||
|
||||
sclear(&com);
|
||||
CT_CHECK(t, keystroke('e', Mctrl, &com));
|
||||
if(typekeys(t, "xyz", &com)){
|
||||
CT_CHECK(t, !keystroke(Kspec|0x51, 0, &com));
|
||||
checkstr(t, "special commits shown query", "xyz", &com);
|
||||
CT_CHECK(t, !search.on);
|
||||
CT_EQ_INT(t, LangKO, im.l->lang);
|
||||
}
|
||||
|
||||
sclear(&com);
|
||||
CT_CHECK(t, keystroke('e', Mctrl, &com));
|
||||
for(i = 0; i < Maxrunes; i++)
|
||||
CT_CHECK(t, keystroke('q', 0, &com));
|
||||
CT_EQ_INT(t, Maxrunes, search.raw.n);
|
||||
CT_CHECK(t, search.on);
|
||||
CT_CHECK(t, !keystroke('z', 0, &com));
|
||||
memset(want, 'q', Maxrunes);
|
||||
want[Maxrunes] = '\0';
|
||||
checkstr(t, "bounded query commit", want, &com);
|
||||
CT_CHECK(t, !search.on);
|
||||
emojiend(&f);
|
||||
}
|
||||
|
||||
void
|
||||
engine_emoji_dictionary_identity(struct ct *t)
|
||||
{
|
||||
Dictres res;
|
||||
Emojifix f;
|
||||
|
||||
emojibegin(&f, 0);
|
||||
im.pre = mkstr("same");
|
||||
im.kouho[0] = mkstr("old");
|
||||
im.nkouho = 1;
|
||||
im.sel = 0;
|
||||
memset(&res, 0, sizeof res);
|
||||
res.key = im.pre;
|
||||
res.kouho[0] = mkstr("wrong");
|
||||
res.nkouho = 1;
|
||||
res.lang = LangJP;
|
||||
dictresult(&res);
|
||||
CT_EQ_INT(t, 1, im.nkouho);
|
||||
checkstr(t, "wrong language ignored", "old", &im.kouho[0]);
|
||||
|
||||
res.lang = LangKO;
|
||||
res.key = mkstr("other");
|
||||
dictresult(&res);
|
||||
CT_EQ_INT(t, 1, im.nkouho);
|
||||
checkstr(t, "wrong preedit ignored", "old", &im.kouho[0]);
|
||||
|
||||
res.key = im.pre;
|
||||
search.on = 1;
|
||||
dictresult(&res);
|
||||
CT_EQ_INT(t, 1, im.nkouho);
|
||||
checkstr(t, "search response ignored", "old", &im.kouho[0]);
|
||||
|
||||
search.on = 0;
|
||||
res.kouho[0] = mkstr("right");
|
||||
dictresult(&res);
|
||||
CT_EQ_INT(t, 1, im.nkouho);
|
||||
CT_EQ_INT(t, -1, im.sel);
|
||||
checkstr(t, "matching response accepted", "right", &im.kouho[0]);
|
||||
emojiend(&f);
|
||||
}
|
||||
|
||||
15
tests/ipc_test.c
Normal file
15
tests/ipc_test.c
Normal file
@@ -0,0 +1,15 @@
|
||||
#include "test.h"
|
||||
|
||||
void
|
||||
ipc_masks_modifiers(struct ct *t)
|
||||
{
|
||||
uchar buf[Ipcreqsz];
|
||||
u32int key, mod;
|
||||
int want;
|
||||
|
||||
ipcpackreq(buf, 1, Mctrl|Malt|(1<<1)|(1<<4)|(1<<7), 0x1f642);
|
||||
ipcunpackreq(buf, &want, &mod, &key);
|
||||
CT_EQ_INT(t, 1, want);
|
||||
CT_EQ_UINT(t, Mctrl|Malt, mod);
|
||||
CT_EQ_UINT(t, 0x1f642, key);
|
||||
}
|
||||
85
tests/mkemoji_test.py
Normal file
85
tests/mkemoji_test.py
Normal file
@@ -0,0 +1,85 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
MKEMOJI = ROOT / "map" / "mkemoji"
|
||||
|
||||
|
||||
def generate(source=None):
|
||||
args = [sys.executable, "-B", str(MKEMOJI)]
|
||||
if source is not None:
|
||||
args.append(str(source))
|
||||
return subprocess.run(args, capture_output=True, text=True, check=False)
|
||||
|
||||
|
||||
def table(output):
|
||||
return dict(line.split("\t", 1) for line in output.splitlines())
|
||||
|
||||
|
||||
class MkemojiTest(unittest.TestCase):
|
||||
def source(self, text):
|
||||
tmp = tempfile.TemporaryDirectory()
|
||||
path = Path(tmp.name) / "emoji.src"
|
||||
path.write_text(text, encoding="utf-8")
|
||||
self.addCleanup(tmp.cleanup)
|
||||
return path
|
||||
|
||||
def test_production_digit_slots(self):
|
||||
result = generate()
|
||||
self.assertEqual(result.returncode, 0, result.stderr)
|
||||
data = table(result.stdout)
|
||||
self.assertEqual(data["^"].split()[:9], list("¹²³⁴⁵⁶⁷⁸⁹"))
|
||||
self.assertEqual(data["_"].split()[:9], list("₁₂₃₄₅₆₇₈₉"))
|
||||
self.assertEqual(data["<"].split()[2], "♥")
|
||||
self.assertEqual(data["^1"], "¹")
|
||||
self.assertEqual(data["_2"], "₂")
|
||||
self.assertEqual(data["<3"], "♥")
|
||||
|
||||
def test_fold_normalize_and_exact_first(self):
|
||||
source = self.source(
|
||||
"β\tALPHABET\n"
|
||||
"α\talpha\n"
|
||||
"e\u0301\tE\u0301\n"
|
||||
"#\thash\n"
|
||||
)
|
||||
first = generate(source)
|
||||
second = generate(source)
|
||||
self.assertEqual(first.returncode, 0, first.stderr)
|
||||
self.assertEqual(first.stdout, second.stdout)
|
||||
data = table(first.stdout)
|
||||
self.assertEqual(data["alpha"].split(), ["α", "β"])
|
||||
self.assertEqual(data["al"].split(), ["β", "α"])
|
||||
self.assertEqual(data["é"], "é")
|
||||
self.assertEqual(data["hash"], "#")
|
||||
|
||||
def test_rejects_malformed_and_oversized_rows(self):
|
||||
bad = [
|
||||
"x\n",
|
||||
"bad result\ta\n",
|
||||
"x\t\n",
|
||||
"x\t a\n",
|
||||
"\0\ta\n",
|
||||
"x\ta\0b\n",
|
||||
"x" * 65 + "\ta\n",
|
||||
"x\t" + "a" * 65 + "\n",
|
||||
]
|
||||
for text in bad:
|
||||
with self.subTest(text=repr(text)):
|
||||
result = generate(self.source(text))
|
||||
self.assertEqual(result.returncode, 1)
|
||||
|
||||
def test_accepts_runtime_boundary(self):
|
||||
source = self.source("x" * 64 + "\t" + "a" * 64 + "\n")
|
||||
result = generate(source)
|
||||
self.assertEqual(result.returncode, 0, result.stderr)
|
||||
self.assertIn("a" * 64 + "\t" + "x" * 64, result.stdout)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -35,8 +35,13 @@ void engine_language_switch_state(struct ct*);
|
||||
void engine_telex_history_bound(struct ct*);
|
||||
void engine_emoji_single_candidate(struct ct*);
|
||||
void engine_emoji_queries(struct ct*);
|
||||
void engine_emoji_digit_aliases(struct ct*);
|
||||
void engine_emoji_navigation(struct ct*);
|
||||
void engine_emoji_preedit_languages(struct ct*);
|
||||
void engine_emoji_start_and_unknown(struct ct*);
|
||||
void engine_emoji_dictionary_identity(struct ct*);
|
||||
void dictionary_candidates(struct ct*);
|
||||
void dictionary_misses_clear_result(struct ct*);
|
||||
void ipc_masks_modifiers(struct ct*);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -82,9 +82,14 @@ static const struct ct_test tests[] = {
|
||||
{ "engine/telex-history-bound", engine_telex_history_bound },
|
||||
{ "engine/emoji-single-candidate", engine_emoji_single_candidate },
|
||||
{ "engine/emoji-queries", engine_emoji_queries },
|
||||
{ "engine/emoji-digit-aliases", engine_emoji_digit_aliases },
|
||||
{ "engine/emoji-navigation", engine_emoji_navigation },
|
||||
{ "engine/emoji-preedit-languages", engine_emoji_preedit_languages },
|
||||
{ "engine/emoji-start-and-unknown", engine_emoji_start_and_unknown },
|
||||
{ "engine/emoji-dictionary-identity", engine_emoji_dictionary_identity },
|
||||
{ "dict/candidates", dictionary_candidates },
|
||||
{ "dict/misses-clear-result", dictionary_misses_clear_result },
|
||||
{ "ipc/masks-modifiers", ipc_masks_modifiers },
|
||||
};
|
||||
|
||||
void
|
||||
@@ -92,7 +97,7 @@ threadmain(int argc, char **argv)
|
||||
{
|
||||
int i, status;
|
||||
|
||||
drawc = chancreate(sizeof(Drawcmd), 1024);
|
||||
drawc = chancreate(sizeof(Drawcmd), 4);
|
||||
keyc = chancreate(sizeof(Keyreq), 0);
|
||||
dictreqc = chancreate(sizeof(Dictreq), 64);
|
||||
dictresc = chancreate(sizeof(Dictres), 0);
|
||||
|
||||
Reference in New Issue
Block a user