Compare commits

..

10 Commits

Author SHA1 Message Date
O_WRONLY
f5f88e8451 update readme 2026-01-13 09:25:22 +09:00
db2b3eacad add vietnam telex input 2026-01-01 18:22:16 +09:00
559b82ec9b strans.c:Fix preedit window is not displayed or cleared. 2026-01-01 13:52:41 +09:00
O_WRONLY
9568893eaf
Add Emoji input shortcut to README 2025-12-28 20:00:10 +09:00
874b941439 add emoji font.
fix: 97ae9b1709
2025-12-28 19:50:38 +09:00
72063f84b7 set default language to English
add dummy map/english.map
2025-12-28 17:33:50 +09:00
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
436a49c3ca remove jp.c 2025-12-28 13:02:53 +09:00
e5ce5fd61e rename line => pre 2025-12-28 12:26:28 +09:00
8a2ef65379 remove hangul.dict 2025-12-28 11:58:18 +09:00
21 changed files with 3933 additions and 662 deletions

View File

@ -1,18 +1,70 @@
# strans
CJK input method
## Switch Key
| Key | Action |
|-----|--------|
| Ctrl+N | Hiragana |
| Ctrl+K | Katakana |
| Ctrl+S | Hangul |
| Ctrl+T | English |
An input method daemon for CJK text entry on X11.
Inspired by 9front's ktrans. Threads communicate via CSP channels.
## Dependencies
- plan9port
- libxcb
## Reference
- plan9port
- gtk+-3.0 (optional, for GTK IM module)
## Build
make
cd xim && make # XIM adapter
cd gtk && make docker && make install # GTK IM module
## Run
./strans map font &
For XIM apps:
./xim/strans-xim &
XMODIFIERS=@im=strans xterm
For GTK apps:
GTK_IM_MODULE=strans gedit
## Usage
Switch input modes with Ctrl + key:
N Hiragana
K Katakana
S Hangul
T English
V Vietnamese (Telex)
E Emoji
Type romanized input. Select candidates with 1-9 or arrow keys.
Tab or Enter to commit.
## Architecture
Four threads communicate via CSP channels:
- [imthread](strans.c#L271): keystroke processing, transliteration
- [dictthread](dict.c#L38): dictionary lookup
- [drawthread](win.c#L133): preedit window rendering
- [srvthread](srv.c#L42): IPC via unix socket
Adapters (strans-xim, im-strans.so) bridge X11/GTK events.
## Files
strans.c input method engine
dict.c dictionary queries
win.c xcb window management
font.c truetype rendering (stb_truetype)
map/ transliteration tables
font/ bundled CJK fonts
## References
- https://git.9front.org/plan9front/plan9front/HEAD/info.html
- https://np.mkv.li/kor/9front-ktrans-%ED%95%9C%EA%B8%80-%EB%98%91%EB%B0%94%EB%A1%9C-%EB%A7%8C%EB%93%A4%EA%B8%B0/
- https://en.wikipedia.org/wiki/Telex_(input_method)
- https://gist.github.com/hieuthi/0f5adb7d3f79e7fb67e0e499004bf558

9
dat.h
View File

@ -13,10 +13,12 @@ enum
LangJP = 0x0e,
LangJPK = 0x0b,
LangKO = 0x13,
LangEMOJI = 0x05,
LangVI = 0x16,
Fontsz = 32,
Fontbase = 4,
Nglyphs = 65536,
Nglyphs = 0x20000,
};
enum
@ -79,13 +81,12 @@ struct Lang
char *dictname;
Hmap *map;
Hmap *dict;
Emit (*trans)(Im*, Rune);
};
struct Im
{
Lang *l;
Str line;
Str pre;
Str kouho[Maxkouho];
int nkouho;
int sel;
@ -112,7 +113,7 @@ typedef struct Dictreq Dictreq;
struct Dictreq
{
Str key;
Str line;
Str pre;
int lang;
};

2
dict.c
View File

@ -47,7 +47,7 @@ dictthread(void*)
while(channbrecv(dictreqc, &req) > 0)
;
dictlkup(&req, &res);
res.key = req.line;
res.key = req.pre;
chansend(dictresc, &res);
}
}

1
fn.h
View File

@ -23,7 +23,6 @@ void dictthread(void*);
void drawthread(void*);
void imthread(void*);
Emit trans(Im*, Rune);
Emit jptrans(Im*, Rune);
void srvthread(void*);

72
font.c
View File

@ -10,9 +10,12 @@ struct Glyph
int w, h, ox, oy;
};
static stbtt_fontinfo font;
static uchar *fontdata;
static float scale;
enum { Maxfonts = 4 };
static stbtt_fontinfo fonts[Maxfonts];
static uchar *fontdata[Maxfonts];
static float scale[Maxfonts];
static int nfonts;
static Glyph cache[Nglyphs];
static u32int blendtab[2][256];
@ -28,25 +31,64 @@ blend(u32int bg, int a)
return (r << 16) | (g << 8) | b;
}
void
fontinit(char *path)
static void
loadfont(char *path)
{
int fd, a;
int fd;
long sz, n;
if(nfonts >= Maxfonts)
die("too many fonts");
fd = open(path, OREAD);
if(fd < 0)
die("can't open font: %s", path);
sz = seek(fd, 0, 2);
seek(fd, 0, 0);
fontdata = emalloc(sz);
n = readn(fd, fontdata, sz);
fontdata[nfonts] = emalloc(sz);
n = readn(fd, fontdata[nfonts], sz);
close(fd);
if(n != sz)
die("can't read font: %s", path);
if(!stbtt_InitFont(&font, fontdata, stbtt_GetFontOffsetForIndex(fontdata, 0)))
if(!stbtt_InitFont(&fonts[nfonts], fontdata[nfonts], stbtt_GetFontOffsetForIndex(fontdata[nfonts], 0)))
die("can't init font: %s", path);
scale = stbtt_ScaleForPixelHeight(&font, Fontsz);
scale[nfonts] = stbtt_ScaleForPixelHeight(&fonts[nfonts], Fontsz);
nfonts++;
}
static int
isfont(char *name)
{
char *p;
p = strrchr(name, '.');
if(p == nil)
return 0;
return strcmp(p, ".ttf") == 0 || strcmp(p, ".otf") == 0;
}
void
fontinit(char *dir)
{
int fd, n, i, a;
Dir *d;
char path[256];
fd = open(dir, OREAD);
if(fd < 0)
die("can't open font dir: %s", dir);
n = dirreadall(fd, &d);
close(fd);
if(n < 0)
die("can't read font dir: %s", dir);
for(i = 0; i < n; i++){
if(isfont(d[i].name)){
snprint(path, sizeof path, "%s/%s", dir, d[i].name);
loadfont(path);
}
}
free(d);
if(nfonts == 0)
die("no fonts in %s", dir);
for(a = 0; a < 256; a++){
blendtab[0][a] = blend(Colbg, a);
blendtab[1][a] = blend(Colsel, a);
@ -57,14 +99,20 @@ void
putfont(u32int *buf, int w, int h, int px, int py, Rune r)
{
Glyph *g;
int i, j, x, y, a, sel;
int i, j, x, y, a, sel, f;
u32int *p;
if(r >= Nglyphs)
return;
g = &cache[r];
if(g->bmp == nil){
g->bmp = stbtt_GetCodepointBitmap(&font, scale, scale, r, &g->w, &g->h, &g->ox, &g->oy);
for(f = 0; f < nfonts; f++){
if(stbtt_FindGlyphIndex(&fonts[f], r) == 0)
continue;
g->bmp = stbtt_GetCodepointBitmap(&fonts[f], scale[f], scale[f], r, &g->w, &g->h, &g->ox, &g->oy);
if(g->bmp != nil)
break;
}
if(g->bmp == nil)
return;
}

BIN
font/NotoEmoji-Regular.ttf Normal file

Binary file not shown.

BIN
font/NotoSans-Regular.ttf Normal file

Binary file not shown.

View File

@ -1 +0,0 @@
NotoSansMonoCJKsc-Regular.otf

80
jp.c
View File

@ -1,80 +0,0 @@
#include "dat.h"
#include "fn.h"
Emit
jptrans(Im *im, Rune c)
{
Emit e;
Str key, kana;
Hmap *h;
Rune last;
int hira;
memset(&e, 0, sizeof(e));
h = im->l->map;
hira = (im->l->lang == LangJP);
key = im->line;
sputr(&key, c);
if(hmapget(h, &key)){
e.eat = 1;
e.next = key;
mapget(h, &key, &e.dict);
return e;
}
last = slastr(&im->line);
if(last == 0)
goto flush;
key = im->line;
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;
}
}
if(c == 'n' && last == 'n'){
key = im->line;
key.n--;
if(mapget(h, &key, &kana)){
e.eat = 1;
e.s = kana;
sputr(&e.s, hira ? 0x3093 : 0x30F3);
sputr(&e.next, c);
mapget(h, &e.next, &e.dict);
return e;
}
}
if(c == last && strchr("kgsztdbpmjfchryw", c)){
key = im->line;
key.n--;
if(mapget(h, &key, &kana)){
e.eat = 1;
e.s = kana;
sputr(&e.s, hira ? 0x3063 : 0x30C3);
sputr(&e.next, c);
mapget(h, &e.next, &e.dict);
return e;
}
}
flush:
mapget(h, &im->line, &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;
}

6
main.c
View File

@ -5,7 +5,7 @@ Channel *drawc;
Channel *keyc;
Channel *dictreqc;
Channel *dictresc;
char *fontpath;
char *fontdir;
int
threadmaybackground(void)
@ -16,7 +16,7 @@ threadmaybackground(void)
void
usage(void)
{
fprint(2, "usage: strans mapdir fontpath\n");
fprint(2, "usage: strans mapdir fontdir\n");
threadexitsall("usage");
}
@ -60,7 +60,7 @@ threadmain(int argc, char **argv)
if(argc != 3)
usage();
fontpath = argv[2];
fontdir = argv[2];
drawc = chancreate(sizeof(Drawcmd), 0);
keyc = chancreate(sizeof(Keyreq), 0);
dictreqc = chancreate(sizeof(Dictreq), 4);

179
map/emoji.dict Normal file
View File

@ -0,0 +1,179 @@
! ⚠ ≠
!! ⚠
!= ≠
* ★
** ★
+ ±
+- ±
- →
-> →
. · … ÷
.. · …
... …
./ ÷
: ☹ ☺
:( ☹
:) ☺
< ← ≤ ≠ ♥
<- ←
<= ≤
<> ≠
= ≡ ⇒
== ≡
=> ⇒
> ≥
>= ≥
^ ⁽ ⁾ ⁺ ⁻ ⁼ ⁰ ¹ ² ³ ⁴ ⁵ ⁶ ⁷ ⁸ ⁹ ⁱ ⁿ
^( ⁽
^) ⁾
^+ ⁺
^- ⁻
^= ⁼
_ ₍ ₎ ₊ ₋ ₌ ₀ ₁ ₂ ₃ ₄ ₅ ₆ ₇ ₈ ₉ ₐ ₑ ₒ ₓ
_( ₍
_) ₎
_+ ₊
_- ₋
_= ₌
~ ≈
~= ≈
^0 ⁰
_0 ₀
^1 ¹
_1 ₁
^2 ²
_2 ₂
<3 ♥
^3 ³
_3 ₃
^4 ⁴
_4 ₄
^5 ⁵
_5 ₅
^6 ⁶
_6 ₆
^7 ⁷
_7 ₇
^8 ⁸
_8 ₈
^9 ⁹
_9 ₉
_a ₐ
a α
al α
alp α
alph α
alpha α
b β
be β
bet β
beta β
c χ
ch χ
chi χ
d ° δ ↓
D Δ
de ° δ
De Δ
deg °
del δ
delt δ
delta δ
dn ↓
_e ₑ
e ε η
ep ε
eps ε
et η
eta η
g γ
G Γ
ga γ
Ga Γ
gam γ
gamm γ
gamma γ
^i ⁱ
i ∞ ι
I ∫
II ∫
in ∞
inf ∞
io ι
iot ι
iota ι
k κ
ka κ
kap κ
kapp κ
kappa κ
l λ
L Λ
la λ
La Λ
lam λ
lamb λ
lambd λ
lambda λ
m × μ
mu × μ
mul ×
^n ⁿ
n ν
nu ν
_o ₒ
o ω ●
O Ω
om ω
Om Ω
ome ω
omeg ω
omega ω
oo ●
p φ π ψ
P Φ Π ∏ Ψ
ph φ
Ph Φ
phi φ
pi π
Pi Π
PP ∏
ps ψ
Ps Ψ
psi ψ
r ρ
rh ρ
rho ρ
s σ
S Σ ∑
si σ
Si Σ
sig σ
sigm σ
sigma σ
sq √
SS ∑
t τ θ
T Θ
ta τ
tau τ
th θ
Th Θ
the θ
thet θ
theta θ
u ↑ υ
up ↑ υ
ups υ
v ✓
vv ✓
_x ₓ
x ξ ✗
X Ξ
xi ξ
Xi Ξ
xx ✗
z ζ
ze ζ
zet ζ
zeta ζ

179
map/emoji.map Normal file
View File

@ -0,0 +1,179 @@
! !
!! ⚠
!= ≠
* *
** ★
+ +
+- ±
- -
-> →
. .
.. ·
... …
./ ÷
: :
:( ☹
:) ☺
< <
<- ←
<= ≤
<> ≠
= =
== ≡
=> ⇒
> >
>= ≥
^ ^
^( ⁽
^) ⁾
^+ ⁺
^- ⁻
^= ⁼
_ _
_( ₍
_) ₎
_+ ₊
_- ₋
_= ₌
~ ~
~= ≈
^0 ⁰
_0 ₀
^1 ¹
_1 ₁
^2 ²
_2 ₂
<3 ♥
^3 ³
_3 ₃
^4 ⁴
_4 ₄
^5 ⁵
_5 ₅
^6 ⁶
_6 ₆
^7 ⁷
_7 ₇
^8 ⁸
_8 ₈
^9 ⁹
_9 ₉
_a ₐ
a a
al al
alp alp
alph alph
alpha α
b b
be be
bet bet
beta β
c c
ch ch
chi χ
d d
D D
de de
De Δ
deg °
del del
delt delt
delta δ
dn ↓
_e ₑ
e e
ep ep
eps ε
et et
eta η
g g
G G
ga ga
Ga Γ
gam gam
gamm gamm
gamma γ
^i ⁱ
i i
I I
II ∫
in in
inf ∞
io io
iot iot
iota ι
k k
ka ka
kap kap
kapp kapp
kappa κ
l l
L L
la la
La Λ
lam lam
lamb lamb
lambd lambd
lambda λ
m m
mu μ
mul ×
^n ⁿ
n n
nu ν
_o ₒ
o o
O O
om om
Om Ω
ome ome
omeg omeg
omega ω
oo ●
p p
P P
ph ph
Ph Φ
phi φ
pi π
Pi Π
PP ∏
ps ps
Ps Ψ
psi ψ
r r
rh rh
rho ρ
s s
S S
si si
Si Σ
sig sig
sigm sigm
sigma σ
sq √
SS ∑
t t
T T
ta ta
tau τ
th th
Th Θ
the the
thet thet
theta θ
u u
up ↑
ups υ
v v
vv ✓
_x ₓ
x x
X X
xi ξ
Xi Ξ
xx ✗
z z
ze ze
zet zet
zeta ζ

99
map/emoji.src Normal file
View File

@ -0,0 +1,99 @@
!! ⚠
!= ≠
** ★
+- ±
-> →
.. ·
... …
./ ÷
:( ☹
:) ☺
<- ←
<= ≤
<> ≠
== ≡
=> ⇒
>= ≥
^( ⁽
^) ⁾
^+ ⁺
^- ⁻
^= ⁼
_( ₍
_) ₎
_+ ₊
_- ₋
_= ₌
~= ≈
^0 ⁰
_0 ₀
^1 ¹
_1 ₁
^2 ²
_2 ₂
<3 ♥
^3 ³
_3 ₃
^4 ⁴
_4 ₄
^5 ⁵
_5 ₅
^6 ⁶
_6 ₆
^7 ⁷
_7 ₇
^8 ⁸
_8 ₈
^9 ⁹
_9 ₉
_a ₐ
alpha α
beta β
chi χ
deg °
delta δ
De Δ
dn ↓
_e ₑ
eps ε
eta η
gamma γ
Ga Γ
^i ⁱ
II ∫
inf ∞
iota ι
kappa κ
lambda λ
La Λ
mul ×
mu μ
^n ⁿ
nu ν
omega ω
Om Ω
oo ●
_o ₒ
phi φ
Ph Φ
pi π
Pi Π
PP ∏
psi ψ
Ps Ψ
rho ρ
sigma σ
Si Σ
sq √
SS ∑
tau τ
theta θ
Th Θ
up ↑
ups υ
vv ✓
xi ξ
Xi Ξ
xx ✗
_x ₓ
zeta ζ

0
map/english.map Normal file
View File

View File

@ -1,382 +0,0 @@
;; Korean autocomplete dictionary for ktrans
;; Format: prefix completion1 completion2 ...
;;
;; Initial consonants (초성)
ㄱ 가 감사 감사합니다 강남 고마워 고맙습니다 괜찮아 그래 그럼 기다려 가능 가격 가게 가방 가족 간단 갈게 같이 거기 건강 검색 결과 경우 계속 고객 공부 관련 괜찮습니다 교육 구매 그냥 그런데 그리고 근데 금방 기능 기대 기본 길이
ㄴ 나 네 내일 너무 노래 누구 뭐 나중에 남자 내용 넣어 놓고 느낌 다음
ㄷ 다 대 더 도 동 다시 다음 단어 답변 대단 대화 도움 도착 동생 등록 드릴게요
ㄹ 라 로 리 레 뭐라고
ㅁ 만 먼저 메시지 모르겠어 모두 뭐 뭐야 뭐라고 뭔가 미안 미안해 문제 문의
ㅂ 바로 반갑습니다 방금 방법 별로 보내 보여줘 부탁 부탁드립니다 분명 비밀번호
ㅅ 사 사람 사랑 사용 사진 상관없어 생각 서비스 선택 설명 성공 소개 수고 수정 시간 시작 신청 실패 싶어
ㅇ 아 아니 아니요 아마 안녕 안녕하세요 알겠습니다 알았어 어디 어떻게 어서오세요 없어 에러 여기 여보세요 연락 열심히 예 오늘 오랜만 오케이 완료 왜 요청 우리 원래 위치 유저 으로 응 이거 이것 이름 이메일 이미 이상 이유 이해 인사 일단 입력 있어
ㅈ 자 잘 잘가 잘됐다 잠깐 잠시 저 전화 정말 제가 제발 조금 좀 좋아 좋겠다 주문 주세요 중요 즐거운 지금 직접 진짜 질문 집
ㅊ 참 참고 찾아 처리 처음 천만에요 최고 최근 추가 축하 출발 취소
ㅋ 카드 커피 컴퓨터 코드 클릭 큰
ㅌ 탈퇴 통화 특히
ㅍ 파일 편하게 포함 필요 필수
ㅎ 하 하나 하루 하세요 하지마 한 한번 항상 해결 해봐 해야 해주세요 핸드폰 행복 확인 회원 회의 휴대폰
;;
;; Common prefixes (일반 접두어)
가 가격 가게 가능 가다 가방 가수 가요 가을 가장 가족 가지
간 간단 간식 간장 간호사
갈 갈게 갈비 갈색
감 감기 감사 감사합니다 감사해요 감자 감정
강 강남 강아지 강의 강조
같 같아 같아요 같은 같이
개 개발 개발자 개선 개인 개정
거 거기 거래 거리 거의 거짓말
건 건강 건물 건의
걱 걱정 걱정마
검 검사 검색 검토
게 게시판 게임
결 결과 결론 결정 결제 결혼
경 경기 경험 경우 경제
계 계속 계시 계약 계정 계좌 계획
고 고객 고급 고기 고등학교 고마워 고맙습니다 고민 고양이 고장
곧 곧 곧바로
공 공간 공개 공부 공사 공유 공지 공항
과 과거 과연 과일 과장 과정
관 관계 관련 관리 관심
괜 괜찮아 괜찮아요 괜찮습니다
교 교사 교수 교육 교통 교환
구 구글 구독 구매 구분 구성 구입 구체적
국 국가 국내 국민 국어 국제
그 그거 그게 그것 그냥 그녀 그니까 그대 그대로 그때 그래 그래도 그래서 그러나 그러니까 그러면 그런 그런데 그럼 그렇게 그리고 그림 그만
근 근데 근무 근본 근처
글 글 글쎄 글자
금 금방 금액 금요일 금지
급 급하게 급해
기 기간 기능 기다려 기대 기본 기분 기사 기술 기억 기업 기준 기타 기회
길 길 길이
김 김밥 김치
까 까먹었어 까지
깜 깜빡 깜짝
꼭 꼭
끝 끝 끝났어 끝내
나 나 나도 나라 나머지 나빠 나오다 나와 나이 나중 나중에
날 날씨 날짜
남 남기다 남자 남편
내 내가 내년 내려 내리다 내용 내일
너 너 너무 너희
넣 넣다 넣어
네 네 네가 네이버
넷 넷플릭스
노 노래 노력 노트북
놀 놀라 놀러
농 농담
높 높은 높이
누 누가 누구 누나
눈 눈 눈물
뉴 뉴스
느 느낌 느린
늦 늦게 늦었어
다 다 다녀오다 다니다 다른 다르다 다시 다양 다음 다행 다행이다
단 단계 단순 단어 단위 단점 단체
달 달 달라 달러 달리
담 담당 담당자 담배
답 답글 답변 답장
당 당근 당분간 당신 당연 당장
대 대기 대단 대략 대부분 대신 대체 대충 대표 대학 대학교 대화 대해
더 더 더욱
덕 덕분
데 데이터
도 도대체 도서관 도시 도와줘 도움 도착 도전
독 독서
돈 돈
동 동네 동생 동시 동안 동영상 동의
돼 돼 돼요 됐어
되 되게 되다 되면
두 두 두고 두번째
뒤 뒤 뒤에
드 드디어 드라마 드리다 드릴게요 드세요
등 등록 등등
따 따라 따로
딱 딱
때 때 때문 때문에
또 또 또는 또한
똑 똑같이
뛰 뛰어
라 라면 라이브
로 로그인 로그아웃
리 리뷰
마 마다 마셔 마시다 마음 마지막 마찬가지 마침
막 막 막상
만 만 만나 만나다 만나서 반가워요 만들다 만약 만원 만큼 만족
많 많다 많아 많이
말 말 말고 말씀 말야 말이야
맛 맛 맛있어 맛있다
망 망했다
맞 맞아 맞아요 맞춤
매 매번 매우 매일 매장
머 머리
먹 먹고 먹다 먹어
먼 먼저
멀 멀다 멀리
메 메뉴 메모 메시지 메일
며 며칠
면 면접
명 명 명단 명확
몇 몇 몇개 몇번
모 모두 모든 모르겠어 모르다 모레 모양 모임
목 목록 목요일 목적
몰 몰라 몰랐어
못 못 못하다 못해
무 무료 무슨 무엇 무조건
문 문서 문의 문자 문제
물 물 물건 물론 물어보다
뭐 뭐 뭐야 뭐라고 뭔가 뭘
미 미국 미래 미리 미안 미안해 미안합니다 미팅
믿 믿다 믿어
밑 밑에
바 바꾸다 바뀌다 바라다 바로 바쁘다 바쁘면
박 박사
반 반 반갑습니다 반드시 반복
받 받다 받아 받았어
발 발생 발전 발표
밤 밤
밥 밥
방 방금 방법 방문 방송 방식 방향
배 배 배달 배송 배우다
백 백 백만
버 버그 버리다 버전
번 번 번역 번호
벌 벌써
범 범위
법 법
별 별로
변 변경 변화
병 병원
보 보고 보고서 보내다 보다 보면 보여줘 보이다 보통 보호
복 복사 복잡
본 본인
봐 봐 봐요 봐줘
부 부끄러워 부디 부모님 부분 부장 부족 부탁 부탁드립니다 부탁해
분 분 분명 분석 분위기
불 불가능 불편 불행
비 비교 비밀 비밀번호 비슷 비용
빠 빠르게 빠르다 빠른 빨리
빼 빼다 빼고
사 사고 사람 사랑 사랑해 사무실 사실 사업 사용 사용자 사이 사이트 사장 사전 사진 사회
산 산
살 살다 살펴보다
상 상관 상관없어 상담 상대 상세 상태 상품 상황
새 새로 새로운 새벽
색 색 색상
생 생각 생각해 생기다 생산 생일 생활
서 서로 서류 서버 서비스 서울
선 선물 선배 선생님 선택
설 설명 설정 설치
성 성공 성격 성능 성함
세 세계 세금 세부 세상 세요
소 소개 소리 소비자 소식 소중 소프트웨어
속 속도
손 손 손님
솔 솔직히
송 송금
쇼 쇼핑
수 수 수고 수고하세요 수량 수업 수요일 수정 수준 수출
숙 숙제
순 순서
술 술
쉬 쉬다 쉬운 쉽게
스 스마트폰 스케줄 스트레스 스타일
슬 슬프다
시 시 시간 시스템 시작 시장 시험
식 식당 식사
신 신경 신고 신용 신제품 신청 신호 신뢰 신규
실 실례 실수 실제 실패 실행 실험 실화
심 심각 심심
싶 싶다 싶어 싶으면
싸 싸다 싸움
쓰 쓰다 쓰레기 쓸
씨 씨
아 아 아까 아끼다 아니 아니요 아님 아마 아무 아버지 아이 아이디 아직 아침 아파 아프다
안 안 안내 안녕 안녕하세요 안되다 안전
알 알겠습니다 알겠어 알다 알려줘 알림 알바 알았어
앞 앞으로 앞에
애 애인
야 야
약 약 약간 약속
얘 얘기
어 어 어느 어디 어떤 어떻게 어렵다 어머니 어서 어제 어쩌면 어차피 어플
언 언니 언제 언젠가
얼 얼굴 얼마 얼마나
업 업그레이드 업데이트 업로드 업무 업체
없 없다 없어 없으면
에 에러
여 여기 여러 여름 여보세요 여자 여전히 여행
역 역시
연 연결 연구 연락 연습 연휴
열 열다 열심히
영 영상 영수증 영어 영업 영향 영화
예 예 예를 들어 예상 예약 예전 예정
오 오늘 오다 오래 오랜만 오른쪽 오빠 오전 오케이 오후
올 올라가다 올려 올리다 올해
와 와 와이파이 왔어
완 완료 완벽 완전
왜 왜
외 외국
요 요금 요새 요즘 요청
욕 욕
용 용 용도
우 우리 우선 우연히
운 운동 운영 운전
울 울다 울어
원 원 원래 원인 원하다 원해
월 월 월급 월요일
웹 웹사이트
위 위 위에 위주 위치 위해
유 유명 유용 유저 유지 유튜브 유행
으 으로 으면
은 은 은행
을 을
음 음 음식 음악
응 응 응답
의 의견 의미 의문 의사
이 이 이거 이것 이곳 이기다 이따 이때 이래 이런 이렇게 이름 이리 이메일 이미 이번 이상 이야기 이용 이유 이제 이전 이쪽 이틀 이해 이해하다
인 인간 인기 인사 인스타그램 인정 인증 인터넷 인터뷰
일 일 일단 일반 일부 일상 일요일 일을 일자 일정 일찍 일하다
읽 읽다 읽어
잃 잃다 잃어버리다
입 입금 입력 입장 입학
있 있다 있어 있으면
자 자 자기 자꾸 자다 자동 자료 자세히 자신 자연 자유 자주
작 작년 작동 작성 작업 작은 작품
잔 잔
잘 잘 잘가 잘됐다 잘못 잘하다
잠 잠 잠깐 잠시
장 장 장기 장난 장소 장점
잡 잡다 잡아
재 재미 재밌다 재밌어
저 저 저거 저기 저녁 저도 저렇게 저번 저장 저희
적 적다 적용 적절 적어도
전 전 전공 전달 전문 전부 전에 전자 전체 전혀 전화
절 절대 절반
점 점 점검 점심 점점
접 접속 접수 접근
정 정기 정도 정리 정말 정보 정상 정신 정식 정확
제 제가 제공 제대로 제목 제발 제안 제외 제일 제품
젤 젤
조 조건 조금 조사 조심 조절 조용 조치
존 존경 존재 존중
졸 졸려
종 종류 종종
좀 좀
좋 좋겠다 좋다 좋아 좋아요 좋아하다 좋은
죄 죄송 죄송합니다
주 주 주간 주로 주말 주문 주변 주세요 주소 주요 주의 주인 주제
준 준비
줄 줄 줄게
중 중간 중국 중심 중에 중요
즉 즉시
즐 즐거운 즐겁다 즐기다
증 증가
지 지각 지금 지나다 지난 지루 지역 지원 지인 지정 지출
직 직업 직원 직장 직접
진 진심 진짜 진행
질 질문 질서
집 집 집중
짧 짧은
짧 짧다
쪽 쪽 쪽으로
찍 찍다 찍어
차 차 차이 차후
참 참 참고 참여 참으로
찾 찾고 찾다 찾아 찾으면
채 채널 채팅
책 책 책임
처 처리 처음
천 천만에요 천원
철 철저
첫 첫 첫번째
청 청소 청구
체 체크 체험
초 초 초대 초반 초보
최 최고 최근 최대 최소 최선 최신 최적 최종
추 추가 추석 추천 추후
축 축하 축하드립니다 축하합니다 축하해
출 출근 출력 출발 출시 출장
충 충분 충전
취 취미 취소 취업 취향
치 치킨
친 친구 친절 친척
카 카드 카메라 카카오 카카오톡 카페
칼 칼퇴
커 커뮤니티 커피 커피숍
컨 컨디션 컨텐츠
컴 컴퓨터
케 케이스
코 코드 코로나
콘 콘서트 콘텐츠
클 클래스 클라이언트 클릭
큰 큰 큰일
타 타다 타이밍
탈 탈퇴
테 테스트 테이블
토 토요일
통 통신 통역 통장 통화
투 투자
특 특별 특별히 특이 특징 특히
틀 틀리다 틀림없이
티 티켓 티비
파 파악 파일 파티
판 판단 판매
팔 팔다 팔로우
팀 팀 팀장
패 패스워드
편 편리 편안 편의 편지 편하게 편하다
평 평가 평균 평소 평일
폰 폰
표 표 표시 표현
품 품질
프 프로그램 프로젝트 프로필
플 플랜
피 피곤 피드백 피하다
필 필수 필요 필요하다
하 하 하고 하나 하는 하다 하루 하면 하세요 하지마 하지만
한 한 한국 한글 한달 한두 한번 한편 한테
할 할 할게 할인
함 함께 함수
합 합계 합니다 합의
항 항목 항상
해 해 해결 해도 해봐 해볼게 해서 해야 해요 해외 해주세요
핵 핵심
행 행동 행복 행사
향 향상
허 허락
현 현금 현상 현재 현황
혹 혹시
홈 홈페이지
화 화 화나다 화요일 화이팅 화인 화장실
확 확대 확보 확실 확실히 확인
환 환경 환불 환영
활 활동 활용
회 회 회사 회식 회원 회의
효 효과
후 후 후기 후에
휴 휴가 휴대폰 휴식 휴일
흔 흔히
힘 힘 힘내 힘들다 힘들어
;;
;; Common phrases (자주 쓰는 문장)
감사합 감사합니다
고맙습 고맙습니다
고마워 고마워요
괜찮습 괜찮습니다
반갑습 반갑습니다
안녕하 안녕하세요 안녕하십니까
알겠습 알겠습니다
죄송합 죄송합니다
축하합 축하합니다
수고하 수고하세요 수고하셨습니다
미안합 미안합니다
실례합 실례합니다
부탁드 부탁드립니다
감사해 감사해요
고마워 고마워요
미안해 미안해요
안녕히 안녕히 가세요 안녕히 계세요
잘부탁 잘부탁드립니다
오랜만 오랜만이에요 오랜만입니다
좋은하 좋은하루

39
map/mkemoji Executable file
View File

@ -0,0 +1,39 @@
#!/bin/sh
awk '
BEGIN { FS = "\t" }
/^$/ { next }
{
key = $1
val = $2
map[key] = val
for(i = 1; i < length(key); i++){
p = substr(key, 1, i)
if(!(p in map))
map[p] = p
}
}
END {
for(k in map)
print k "\t" map[k]
}
' $1 | sort -k1,1 > emoji.map
awk '
BEGIN { FS = "\t" }
/^$/ { next }
{
key = $1
val = $2
for(i = 1; i <= length(key); i++){
p = substr(key, 1, i)
dict[p] = dict[p] " " val
}
}
END {
for(p in dict){
sub(/^ /, "", dict[p])
print p "\t" dict[p]
}
}
' $1 | sort -k1,1 > emoji.dict

159
map/mktelex.py Executable file
View File

@ -0,0 +1,159 @@
#!/usr/bin/env python3
# tone marks: s=rising f=falling r=hook x=tilde j=dot
tone = {
's': str.maketrans("aăâeêioôơuưy", "áắấéếíóốớúứý"),
'f': str.maketrans("aăâeêioôơuưy", "àằầèềìòồờùừỳ"),
'r': str.maketrans("aăâeêioôơuưy", "ảẳẩẻểỉỏổởủửỷ"),
'x': str.maketrans("aăâeêioôơuưy", "ãẵẫẽễĩõỗỡũữỹ"),
'j': str.maketrans("aăâeêioôơuưy", "ạặậẹệịọộợụựỵ"),
}
# modified vowels: input -> output
modvowel = [
("aw", "ă"),
("aa", "â"),
("ee", "ê"),
("oo", "ô"),
("ow", "ơ"),
("uw", "ư"),
]
# modified cons: input -> output
modcons = [
("dd", "đ"),
]
upper = str.maketrans(
"aăâeêioôơuưyđáắấéếíóốớúứýàằầèềìòồờùừỳảẳẩẻểỉỏổởủửỷãẵẫẽễĩõỗỡũữỹạặậẹệịọộợụựỵ",
"AĂÂEÊIOÔƠUƯYĐÁẮẤÉẾÍÓỐỚÚỨÝÀẰẦÈỀÌÒỒỜÙỪỲẢẲẨẺỂỈỎỔỞỦỬỶÃẴẪẼỄĨÕỖỠŨỮỸẠẶẬẸỆỊỌỘỢỤỰỴ")
def addtone(v, t):
return v.translate(tone[t])
def emit(input, output):
print(f"{input}\t{output}")
def up(s):
c = s[0].translate(upper)
if c == s[0]:
c = s[0].upper()
return c + s[1:]
print(f"{up(input)}\t{up(output)}")
def vowel1():
for v in "aeiouy":
emit(v, v)
for t in tone:
emit(v+t, addtone(v, t))
def vowel2():
# input, output, vowel
tab = [
("oa", "oa", "a"), ("oe", "oe", "e"), ("ai", "ai", "a"),
("ao", "ao", "a"), ("au", "au", "a"), ("ay", "ay", "a"),
("eu", "eu", "e"), ("iu", "iu", "i"), ("oi", "oi", "o"),
("ui", "ui", "u"), ("uy", "uy", "y"),
("iee", "", "ê"), ("yee", "", "ê"), ("uoo", "", "ô"),
("uow", "ươ", "ơ"), ("uaa", "", "â"), ("oaw", "", "ă"),
("uwa", "ưa", "a"), ("uwow", "ươ", "ơ"),
]
for i, o, v in tab:
emit(i, o)
for t in tone:
emit(i+t, o.replace(v, addtone(v, t), 1))
emit("ie", "ie")
emit("ye", "ye")
emit("uo", "uo")
emit("ua", "ua")
def vowel3():
# input, output, vowel
tab = [
("ieeu", "iêu", "ê"), ("yeeu", "yêu", "ê"),
("uooi", "uôi", "ô"), ("uowi", "ươi", "ơ"),
("oai", "oai", "a"), ("oay", "oay", "a"),
("uyee", "uyê", "ê"),
]
for i, o, v in tab:
emit(i, o)
for t in tone:
emit(i+t, o.replace(v, addtone(v, t), 1))
emit("uya", "uya")
def modvowels():
for i, o in modvowel:
emit(i, o)
def modconss():
for i, o in modcons:
emit(i, o)
def mod1tone():
# aw+s -> ắ
for i, o in modvowel:
for t in tone:
emit(i+t, addtone(o, t))
modvowel = [
("aw", "ă"),
("aa", "â"),
("ee", "ê"),
("oo", "ô"),
("ow", "ơ"),
("uw", "ư"),
]
def tone1mod():
# a+s+w -> ắ
for i, o in modvowel:
for t in tone:
emit(i[0]+t+i[1], addtone(o, t))
def tone2mod():
# ie+s+e -> iế
# input, output, vowel, suffix
tab = [
("ie", "", "ê", "e"), ("ye", "", "ê", "e"),
("uo", "", "ô", "o"), ("uo", "ươ", "ơ", "w"),
("ua", "", "â", "a"), ("oa", "", "ă", "w"),
("uwo", "ươ", "ơ", "w"),
]
for i, o, v, s in tab:
for t in tone:
emit(i+t+s, o.replace(v, addtone(v, t), 1))
def escape():
# aww -> aw
for e in ["aw", "aa", "ee", "oo", "ow", "uw", "dd"]:
emit(e+e[-1], e)
def final():
# codas
coda = ["c", "m", "n", "p", "t", "ch", "ng", "nh"]
# input, output, vowel
tab = [
("a", "a", "a"), ("e", "e", "e"), ("i", "i", "i"),
("o", "o", "o"), ("u", "u", "u"), ("y", "y", "y"),
("aw", "ă", "ă"), ("aa", "â", "â"), ("ee", "ê", "ê"),
("oo", "ô", "ô"), ("ow", "ơ", "ơ"), ("uw", "ư", "ư"),
("iee", "", "ê"), ("yee", "", "ê"), ("uoo", "", "ô"),
("uow", "ươ", "ơ"), ("uaa", "", "â"), ("oaw", "", "ă"),
("ai", "ai", "a"), ("ao", "ao", "a"), ("au", "au", "a"),
("ay", "ay", "a"), ("oa", "oa", "a"), ("oi", "oi", "o"),
("ui", "ui", "u"), ("uy", "uy", "y"),
]
for i, o, v in tab:
for c in coda:
emit(i+c, o+c)
for t in tone:
emit(i+c+t, o.replace(v, addtone(v, t), 1)+c)
vowel1()
vowel2()
vowel3()
modvowels()
modconss()
mod1tone()
tone1mod()
tone2mod()
escape()
final()

File diff suppressed because it is too large Load Diff

2
run.sh
View File

@ -4,6 +4,6 @@ pkill strans
pkill strans-xim
sleep 1
./strans map font/font.otf &
./strans map font &
sleep 1
xim/strans-xim &

View File

@ -4,9 +4,12 @@
static Im im;
Lang langs[] = {
{LangJP, "hira", "kanji", nil, nil, jptrans},
{LangJPK, "kata", "kanji", nil, nil, jptrans},
{LangKO, "hangul", "hangul", nil, nil, trans},
{LangEN, "english", nil, nil, nil},
{LangJP, "hira", "kanji", nil, nil},
{LangJPK, "kata", "kanji", nil, nil},
{LangKO, "hangul", nil, nil, nil},
{LangEMOJI, "emoji", "emoji", nil, nil},
{LangVI, "telex", nil, nil, nil},
};
int nlang = nelem(langs);
@ -36,7 +39,8 @@ show(void)
int i;
sclear(&dc.preedit);
mapget(im.l->map, &im.line, &dc.preedit);
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++)
@ -44,20 +48,28 @@ show(void)
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.line, &dict)){
if(!mapget(im.l->map, &im.pre, &dict)){
clearkouho();
show();
return;
}
req.key = dict;
req.lang = im.l->lang;
req.line = im.line;
req.pre = im.pre;
channbsend(dictreqc, &req);
}
@ -67,7 +79,7 @@ checklang(int c)
Lang *l;
l = getlang(c);
if(l == nil && c != LangEN)
if(l == nil)
return 0;
im.l = l;
return 1;
@ -78,9 +90,11 @@ commit(Str *com)
{
Str kana;
if(mapget(im.l->map, &im.line, &kana))
if(mapget(im.l->map, &im.pre, &kana))
sappend(com, &kana);
sclear(&im.line);
else
sappend(com, &im.pre);
sclear(&im.pre);
}
static int
@ -89,19 +103,17 @@ dotrans(Rune c, Str *com)
Emit e;
Dictreq req;
e = im.l->trans(&im, c);
e = trans(&im, c);
if(e.s.n > 0)
sappend(com, &e.s);
sclear(&im.line);
sappend(&im.line, &e.next);
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.line = im.line;
req.pre = im.pre;
channbsend(dictreqc, &req);
}
return e.eat;
}
@ -125,7 +137,7 @@ trans(Im *im, Rune c)
Rune last;
h = im->l->map;
key = im->line;
key = im->pre;
sputr(&key, c);
if(hmapget(h, &key)){
e.eat = 1;
@ -133,12 +145,10 @@ trans(Im *im, Rune c)
mapget(h, &key, &e.dict);
return e;
}
last = slastr(&im->line);
last = slastr(&im->pre);
if(last == 0)
goto flush;
key = im->line;
key = im->pre;
key.n--;
if(mapget(h, &key, &kana)){
sclear(&key);
@ -155,8 +165,8 @@ trans(Im *im, Rune c)
}
flush:
mapget(h, &im->line, &e.s);
if(!mapget(h, &im->pre, &e.s))
e.s = im->pre;
sclear(&key);
sputr(&key, c);
if(hmapget(h, &key) == nil){
@ -164,21 +174,12 @@ flush:
sputr(&e.s, c);
return e;
}
e.eat = 1;
sputr(&e.next, c);
mapget(h, &e.next, &e.dict);
return e;
}
static void
reset(void)
{
sclear(&im.line);
clearkouho();
show();
}
static int
keystroke(u32int ks, u32int mod, Str *com)
{
@ -211,7 +212,7 @@ keystroke(u32int ks, u32int mod, Str *com)
reset();
return 1;
}
if(im.line.n > 0){
if(im.pre.n > 0){
commit(com);
reset();
return 1;
@ -219,10 +220,10 @@ keystroke(u32int ks, u32int mod, Str *com)
return 0;
}
if(ks == Kback){
if(im.line.n == 0)
if(im.pre.n == 0)
return 0;
spopr(&im.line);
if(im.line.n == 0){
spopr(&im.pre);
if(im.pre.n == 0){
reset();
return 1;
}
@ -230,7 +231,7 @@ keystroke(u32int ks, u32int mod, Str *com)
return 1;
}
if(ks == Kesc){
if(im.line.n == 0)
if(im.pre.n == 0)
return 0;
reset();
return 1;
@ -248,21 +249,22 @@ keystroke(u32int ks, u32int mod, Str *com)
return 1;
return 0;
}
if(im.l == nil)
return 0;
if(ks > 0x7f || !isalpha(ks)){
if(ks > 0x7f || ks == ' '){
commit(com);
sputr(com, ks);
reset();
return 1;
}
return dotrans(ks, com);
dotrans(ks, com);
show();
return 1;
}
static void
init(void)
{
memset(&im, 0, sizeof(im));
im.l = getlang(LangEN);
}
void
@ -297,7 +299,7 @@ imthread(void*)
write(kr.fd, out, n);
break;
case 1:
if(scmp(&res.key, &im.line) == 0){
if(scmp(&res.key, &im.pre) == 0){
setkouho(&res);
show();
}

4
win.c
View File

@ -6,7 +6,7 @@ enum {
Asciitofull = 0xFEE0,
};
extern char *fontpath;
extern char *fontdir;
static xcb_connection_t *conn;
static xcb_screen_t *scr;
@ -52,7 +52,7 @@ wininit(void)
gc = xcb_generate_id(conn);
xcb_create_gc(conn, gc, win, 0, nil);
img = emalloc(Imgw * Imgh * sizeof(img[0]));
fontinit(fontpath);
fontinit(fontdir);
}
static void