strans/strans.c
Hojun-Cho 97ae9b1709 add eomji(ctrl+e)
TODO: Some emojis can't be displayed. A font fallback or font merge is required.
2025-12-28 16:49:26 +09:00

378 lines
5.6 KiB
C

#include "dat.h"
#include "fn.h"
static Im im;
Lang langs[] = {
{LangJP, "hira", "kanji", nil, nil},
{LangJPK, "kata", "kanji", nil, nil},
{LangKO, "hangul", nil, nil, nil},
{LangEMOJI, "emoji", "emoji", nil, nil},
};
int nlang = nelem(langs);
static void
clearkouho(void)
{
im.nkouho = 0;
im.sel = 0;
}
static void
setkouho(Dictres *res)
{
int i;
clearkouho();
for(i = 0; i < res->nkouho; i++){
im.kouho[i] = res->kouho[i];
im.nkouho++;
}
}
static void
show(void)
{
Drawcmd dc;
int i;
sclear(&dc.preedit);
if(im.l == nil)
return;
if(!mapget(im.l->map, &im.pre, &dc.preedit))
dc.preedit = im.pre;
dc.nkouho = im.nkouho;
dc.sel = im.sel;
for(i = 0; i < dc.nkouho; i++)
dc.kouho[i] = im.kouho[i];
chansend(drawc, &dc);
}
static void
reset(void)
{
sclear(&im.pre);
clearkouho();
show();
}
static void
dictq(void)
{
Str dict;
Dictreq req;
if(!mapget(im.l->map, &im.pre, &dict)){
clearkouho();
show();
return;
}
req.key = dict;
req.lang = im.l->lang;
req.pre = im.pre;
channbsend(dictreqc, &req);
}
static int
checklang(int c)
{
Lang *l;
l = getlang(c);
if(l == nil && c != LangEN)
return 0;
im.l = l;
return 1;
}
static void
commit(Str *com)
{
Str kana;
if(im.l == nil){
sclear(&im.pre);
return;
}
if(mapget(im.l->map, &im.pre, &kana))
sappend(com, &kana);
sclear(&im.pre);
}
static int
dotrans(Rune c, Str *com)
{
Emit e;
Dictreq req;
e = trans(&im, c);
if(e.s.n > 0)
sappend(com, &e.s);
sclear(&im.pre);
sappend(&im.pre, &e.next);
if(e.eat && e.dict.n > 0){
req.key = e.dict;
req.lang = im.l->lang;
req.pre = im.pre;
channbsend(dictreqc, &req);
}
return e.eat;
}
Lang*
getlang(int lang)
{
int i;
for(i = 0; i < nelem(langs); i++)
if(langs[i].lang == lang)
return &langs[i];
return nil;
}
Emit
trans(Im *im, Rune c)
{
Emit e = {0};
Str key, kana;
Hmap *h;
Rune last;
h = im->l->map;
key = im->pre;
sputr(&key, c);
if(hmapget(h, &key)){
e.eat = 1;
e.next = key;
mapget(h, &key, &e.dict);
return e;
}
last = slastr(&im->pre);
if(last == 0)
goto flush;
key = im->pre;
key.n--;
if(mapget(h, &key, &kana)){
sclear(&key);
sputr(&key, last);
sputr(&key, c);
if(hmapget(h, &key)){
e.eat = 1;
e.s = kana;
sputr(&e.next, last);
sputr(&e.next, c);
mapget(h, &e.next, &e.dict);
return e;
}
}
flush:
mapget(h, &im->pre, &e.s);
sclear(&key);
sputr(&key, c);
if(hmapget(h, &key) == nil){
e.flush = 1;
sputr(&e.s, c);
return e;
}
e.eat = 1;
sputr(&e.next, c);
mapget(h, &e.next, &e.dict);
return e;
}
static int
keystroke(u32int ks, u32int mod, Str *com)
{
int n, off;
if(ks == Ksuper || ks == Kshift)
return 1;
if(ks == Kdown || ks == Kup){
if(im.nkouho == 0)
return 0;
if(ks == Kdown && im.sel < im.nkouho - 1)
im.sel++;
if(ks == Kup && im.sel > 0)
im.sel--;
show();
return 1;
}
n = ks - '1';
off = 0;
if(im.sel >= Maxdisp)
off = im.sel - Maxdisp + 1;
if(n >= 0 && n < Maxdisp && off + n < im.nkouho){
sappend(com, &im.kouho[off + n]);
reset();
return 1;
}
if(ks == Ktab || ks == Kret){
if(im.sel >= 0 && im.sel < im.nkouho){
sappend(com, &im.kouho[im.sel]);
reset();
return 1;
}
if(im.pre.n > 0){
commit(com);
reset();
return 1;
}
return 0;
}
if(ks == Kback){
if(im.pre.n == 0)
return 0;
spopr(&im.pre);
if(im.pre.n == 0){
reset();
return 1;
}
dictq();
return 1;
}
if(ks == Kesc){
if(im.pre.n == 0)
return 0;
reset();
return 1;
}
if(ks >= Kspec || (mod & (Malt|Msuper))){
commit(com);
reset();
return 0;
}
if(mod & Mctrl){
reset();
if(ks >= 'a' && ks <= 'z')
ks -= 'a' - 1;
if(checklang(ks))
return 1;
return 0;
}
if(im.l == nil)
return 0;
if(ks > 0x7f || ks == ' '){
commit(com);
sputr(com, ks);
reset();
return 1;
}
dotrans(ks, com);
return 1;
}
static void
init(void)
{
memset(&im, 0, sizeof(im));
}
void
imthread(void*)
{
Keyreq kr;
Dictres res;
Str com;
uchar out[256];
int n, len, r;
Alt alts[] = {
{keyc, &kr, CHANRCV, nil},
{dictresc, &res, CHANRCV, nil},
{nil, nil, CHANEND, nil},
};
threadsetname("im");
init();
for(;;){
switch(alt(alts)){
case 0:
sclear(&com);
r = keystroke(kr.ks, kr.mod, &com);
out[0] = r;
out[1] = 0;
n = 2;
if(com.n > 0){
len = stoutf(&com, (char*)(out+2), sizeof(out)-2);
out[1] = len;
n += len;
}
write(kr.fd, out, n);
break;
case 1:
if(scmp(&res.key, &im.pre) == 0){
setkouho(&res);
show();
}
break;
}
}
}
int
mapget(Hmap *h, Str *key, Str *out)
{
Hnode *n;
if(key->n == 0)
return 0;
n = hmapget(h, key);
if(n == nil || n->kanalen == 0)
return 0;
sinit(out, n->kana, n->kanalen);
return 1;
}
static Hmap*
openmap(char *path)
{
Hmap *h;
Biobuf *b;
Str key, val, empty;
char *line, *tab;
int i, klen;
b = Bopen(path, OREAD);
if(b == nil)
die("can't open: %s", path);
h = hmapalloc(1024, 0);
sclear(&empty);
while((line = Brdstr(b, '\n', 1)) != nil){
if(line[0] == '\0'){
free(line);
continue;
}
tab = strchr(line, '\t');
if(tab == nil || tab[1] == '\0')
die("malformed map: %s", path);
*tab = '\0';
klen = strlen(line);
sinit(&key, line, klen);
sinit(&val, tab+1, strlen(tab+1));
hmapset(&h, &key, &val);
for(i = 1; i < klen; i++){
sinit(&key, line, i);
if(hmapget(h, &key) == nil)
hmapset(&h, &key, &empty);
}
free(line);
}
Bterm(b);
return h;
}
void
mapinit(char *dir)
{
char path[1024];
int i;
for(i = 0; i < nelem(langs); i++){
if(langs[i].mapname == nil)
continue;
snprint(path, sizeof(path), "%s/%s.map", dir, langs[i].mapname);
langs[i].map = openmap(path);
}
}