Compare commits
8 Commits
b63122dd14
...
82276bf37f
| Author | SHA1 | Date | |
|---|---|---|---|
| 82276bf37f | |||
| 9b3f2c46f0 | |||
| 099acdf2a6 | |||
| 42823420ab | |||
| 9dc116a035 | |||
| 4bfb659b6d | |||
| 7b40ef419f | |||
| e70278a3d2 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,5 +1,7 @@
|
|||||||
strans
|
strans
|
||||||
strans-xim
|
strans-xim
|
||||||
|
/tests/unit_test
|
||||||
|
/bench/bench
|
||||||
|
|
||||||
# Prerequisites
|
# Prerequisites
|
||||||
*.d
|
*.d
|
||||||
|
|||||||
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
[submodule "cutest"]
|
||||||
|
path = cutest
|
||||||
|
url = ../cutest.git
|
||||||
25
Makefile
25
Makefile
@@ -1,9 +1,15 @@
|
|||||||
CC = 9c
|
CC = 9c
|
||||||
LD = 9l
|
LD = 9l
|
||||||
|
PKGGOALS := $(filter-out check test verify-map clean xim bench,$(MAKECMDGOALS))
|
||||||
|
ifeq ($(MAKECMDGOALS),)
|
||||||
|
PKGGOALS := all
|
||||||
|
endif
|
||||||
|
ifneq ($(PKGGOALS),)
|
||||||
DBUS_CFLAGS := $(shell pkg-config --cflags dbus-1)
|
DBUS_CFLAGS := $(shell pkg-config --cflags dbus-1)
|
||||||
DBUS_LIBS := $(shell pkg-config --libs dbus-1)
|
DBUS_LIBS := $(shell pkg-config --libs dbus-1)
|
||||||
WL_CFLAGS := $(shell pkg-config --cflags wayland-client xkbcommon)
|
WL_CFLAGS := $(shell pkg-config --cflags wayland-client xkbcommon)
|
||||||
WL_LIBS := $(shell pkg-config --libs wayland-client xkbcommon)
|
WL_LIBS := $(shell pkg-config --libs wayland-client xkbcommon)
|
||||||
|
endif
|
||||||
CFLAGS = -Wall -Wextra -O2 -g $(DBUS_CFLAGS) $(WL_CFLAGS)
|
CFLAGS = -Wall -Wextra -O2 -g $(DBUS_CFLAGS) $(WL_CFLAGS)
|
||||||
PROG = strans
|
PROG = strans
|
||||||
|
|
||||||
@@ -19,13 +25,22 @@ $(OBJS): dat.h fn.h ipc.h
|
|||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f $(OBJS) $(PROG)
|
rm -f $(OBJS) $(PROG)
|
||||||
make -C xim/ clean
|
$(MAKE) -C tests clean
|
||||||
make -C bench/ clean
|
$(MAKE) -C xim/ clean
|
||||||
|
$(MAKE) -C bench/ clean
|
||||||
|
|
||||||
xim:
|
xim:
|
||||||
make -C xim/
|
$(MAKE) -C xim/
|
||||||
|
|
||||||
bench:
|
bench:
|
||||||
make -C bench/
|
$(MAKE) -C bench/
|
||||||
|
|
||||||
.PHONY: all clean xim bench
|
check: verify-map test
|
||||||
|
|
||||||
|
test:
|
||||||
|
$(MAKE) -C tests check TESTARGS="$(TESTARGS)"
|
||||||
|
|
||||||
|
verify-map:
|
||||||
|
python3 map/mktelex.py | cmp - map/telex.map
|
||||||
|
|
||||||
|
.PHONY: all check test verify-map clean xim bench
|
||||||
|
|||||||
24
README.md
24
README.md
@@ -7,6 +7,7 @@ Inspired by 9front's ktrans. Threads communicate via CSP channels.
|
|||||||
## Dependencies
|
## Dependencies
|
||||||
|
|
||||||
- plan9port
|
- plan9port
|
||||||
|
- Python 3 (for generated-map checks)
|
||||||
- dbus-1
|
- dbus-1
|
||||||
- wayland-client, libxkbcommon (for Wayland support)
|
- wayland-client, libxkbcommon (for Wayland support)
|
||||||
- gtk+-3.0 (optional, for GTK IM module)
|
- gtk+-3.0 (optional, for GTK IM module)
|
||||||
@@ -17,6 +18,21 @@ Inspired by 9front's ktrans. Threads communicate via CSP channels.
|
|||||||
cd xim && make # XIM adapter
|
cd xim && make # XIM adapter
|
||||||
cd gtk && make docker && make install # GTK IM module
|
cd gtk && make docker && make install # GTK IM module
|
||||||
|
|
||||||
|
Unit tests use the sibling `cutest` Git repository as a submodule and do not
|
||||||
|
require the Wayland, D-Bus, GTK, or X11 development packages. Repository
|
||||||
|
mirrors must provide the same sibling; initialize it after cloning with
|
||||||
|
`git submodule update --init`:
|
||||||
|
|
||||||
|
make check # generated map + unit tests
|
||||||
|
make test TESTARGS=hangul # filtered unit tests only
|
||||||
|
make verify-map # generated-map check only
|
||||||
|
|
||||||
|
The suite covers the fixed-size UTF-8 string, hash table, trie fixtures,
|
||||||
|
Japanese/Hangul/Telex state transitions, dictionary candidates, engine
|
||||||
|
selection state, and production map loading. Tests use explicit case tables,
|
||||||
|
boundary values, and small regressions for repaired core contracts. IPC and GUI
|
||||||
|
protocol stacks are outside this suite and require separate integration checks.
|
||||||
|
|
||||||
## Run
|
## Run
|
||||||
|
|
||||||
./strans map font &
|
./strans map font &
|
||||||
@@ -60,10 +76,10 @@ Tab or Enter to commit.
|
|||||||
|
|
||||||
Threads communicate via CSP channels:
|
Threads communicate via CSP channels:
|
||||||
|
|
||||||
- [imthread](strans.c#L271): keystroke processing, transliteration
|
- [imthread](strans.c): keystroke processing, transliteration
|
||||||
- [dictthread](dict.c#L38): dictionary lookup
|
- [dictthread](dict.c): dictionary lookup
|
||||||
- [drawthread](win.c#L133): preedit window rendering
|
- [drawthread](win.c): preedit window rendering
|
||||||
- [srvthread](srv.c#L42): IPC via unix socket
|
- [srvthread](srv.c): IPC via unix socket
|
||||||
- [ibusthread](ibus.c): IBus D-Bus endpoint for GLFW/kitty
|
- [ibusthread](ibus.c): IBus D-Bus endpoint for GLFW/kitty
|
||||||
- [waylandthread](wayland.c): Wayland input-method-v2 + virtual-keyboard-v1
|
- [waylandthread](wayland.c): Wayland input-method-v2 + virtual-keyboard-v1
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
CC = cc
|
CC = cc
|
||||||
CFLAGS = -Wall -O2
|
CFLAGS = -Wall -O2
|
||||||
|
|
||||||
bench: main.c
|
bench: main.c ../ipc.c ../ipc.h
|
||||||
$(CC) $(CFLAGS) -o $@ main.c
|
$(CC) $(CFLAGS) -I.. -o $@ main.c ../ipc.c
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f bench
|
rm -f bench
|
||||||
|
|||||||
BIN
bench/bench
BIN
bench/bench
Binary file not shown.
32
bench/main.c
32
bench/main.c
@@ -1,3 +1,4 @@
|
|||||||
|
#define _POSIX_C_SOURCE 200809L
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@@ -5,13 +6,7 @@
|
|||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <sys/un.h>
|
#include <sys/un.h>
|
||||||
|
#include "../ipc.h"
|
||||||
enum {
|
|
||||||
Kback = 0xf008,
|
|
||||||
Kret = 0xf00d,
|
|
||||||
Kesc = 0xf01b,
|
|
||||||
Mctrl = 1<<2,
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef struct Key Key;
|
typedef struct Key Key;
|
||||||
struct Key {
|
struct Key {
|
||||||
@@ -93,8 +88,7 @@ dial(void)
|
|||||||
die("socket");
|
die("socket");
|
||||||
memset(&addr, 0, sizeof(addr));
|
memset(&addr, 0, sizeof(addr));
|
||||||
addr.sun_family = AF_UNIX;
|
addr.sun_family = AF_UNIX;
|
||||||
snprintf(addr.sun_path, sizeof(addr.sun_path),
|
snprintf(addr.sun_path, sizeof(addr.sun_path), IPCPATH, getuid());
|
||||||
"/tmp/strans.%d", getuid());
|
|
||||||
if(connect(fd, (struct sockaddr*)&addr, sizeof(addr)) < 0)
|
if(connect(fd, (struct sockaddr*)&addr, sizeof(addr)) < 0)
|
||||||
die("connect");
|
die("connect");
|
||||||
}
|
}
|
||||||
@@ -102,28 +96,20 @@ dial(void)
|
|||||||
static void
|
static void
|
||||||
sendkey(int key, int mod)
|
sendkey(int key, int mod)
|
||||||
{
|
{
|
||||||
unsigned char req[4];
|
unsigned char req[Ipcreqsz];
|
||||||
|
|
||||||
req[0] = 0;
|
ipcpackreq(req, 0, mod, key);
|
||||||
req[1] = mod;
|
if(ipcsend(fd, req, sizeof req) < 0)
|
||||||
req[2] = key & 0xff;
|
|
||||||
req[3] = (key >> 8) & 0xff;
|
|
||||||
if(write(fd, req, 4) != 4)
|
|
||||||
die("write");
|
die("write");
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
readresp(void)
|
readresp(void)
|
||||||
{
|
{
|
||||||
unsigned char buf[256];
|
char buf[Ipcfieldmax+1];
|
||||||
int n;
|
Ipcresp resp;
|
||||||
|
|
||||||
if(read(fd, buf, 2) != 2)
|
return ipcreadresp(fd, 0, buf, sizeof buf, NULL, 0, &resp);
|
||||||
return -1;
|
|
||||||
n = buf[1];
|
|
||||||
if(n > 0 && read(fd, buf + 2, n) != n)
|
|
||||||
return -1;
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static double
|
static double
|
||||||
|
|||||||
1
cutest
Submodule
1
cutest
Submodule
Submodule cutest added at 7894ba7a26
23
dat.h
23
dat.h
@@ -19,15 +19,17 @@ enum
|
|||||||
Fontsz = 32,
|
Fontsz = 32,
|
||||||
Fontbase = 4,
|
Fontbase = 4,
|
||||||
Nglyphs = 0x20000,
|
Nglyphs = 0x20000,
|
||||||
|
Maxrunes = 64,
|
||||||
|
Maxutf = Maxrunes * UTFmax + 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
Maxclients = 64,
|
Maxclients = 64,
|
||||||
Maxkouho = 32,
|
Maxkouho = 32,
|
||||||
Maxdisp = 8,
|
Maxdisp = 9,
|
||||||
Imgw = 512,
|
Imgw = (Maxrunes + 3) * Fontsz,
|
||||||
Imgh = Maxdisp * Fontsz + 8,
|
Imgh = (Maxdisp + 1) * Fontsz,
|
||||||
|
|
||||||
Colfg = 0x000000,
|
Colfg = 0x000000,
|
||||||
Colbg = 0xffffff,
|
Colbg = 0xffffff,
|
||||||
@@ -37,7 +39,7 @@ enum
|
|||||||
typedef struct Str Str;
|
typedef struct Str Str;
|
||||||
struct Str
|
struct Str
|
||||||
{
|
{
|
||||||
Rune r[64];
|
Rune r[Maxrunes];
|
||||||
int n;
|
int n;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -108,6 +110,7 @@ struct Im
|
|||||||
{
|
{
|
||||||
Lang *l;
|
Lang *l;
|
||||||
Str pre;
|
Str pre;
|
||||||
|
Str raw; /* physical keys for the current Telex preedit */
|
||||||
Str kouho[Maxkouho];
|
Str kouho[Maxkouho];
|
||||||
int nkouho;
|
int nkouho;
|
||||||
int sel;
|
int sel;
|
||||||
@@ -123,12 +126,20 @@ struct Drawcmd
|
|||||||
};
|
};
|
||||||
|
|
||||||
typedef struct Keyreq Keyreq;
|
typedef struct Keyreq Keyreq;
|
||||||
|
typedef struct Keyres Keyres;
|
||||||
|
struct Keyres
|
||||||
|
{
|
||||||
|
int eaten;
|
||||||
|
Str commit;
|
||||||
|
Str preedit;
|
||||||
|
};
|
||||||
|
|
||||||
struct Keyreq
|
struct Keyreq
|
||||||
{
|
{
|
||||||
int fd;
|
|
||||||
u32int ks;
|
u32int ks;
|
||||||
u32int mod;
|
u32int mod;
|
||||||
int want; /* nonzero: append preedit after commit */
|
int want; /* nonzero: include preedit in reply */
|
||||||
|
Channel *reply;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct Dictreq Dictreq;
|
typedef struct Dictreq Dictreq;
|
||||||
|
|||||||
18
dict.c
18
dict.c
@@ -1,7 +1,7 @@
|
|||||||
#include "dat.h"
|
#include "dat.h"
|
||||||
#include "fn.h"
|
#include "fn.h"
|
||||||
|
|
||||||
static void
|
void
|
||||||
dictlookup(Dictreq *req, Dictres *res)
|
dictlookup(Dictreq *req, Dictres *res)
|
||||||
{
|
{
|
||||||
Lang *l;
|
Lang *l;
|
||||||
@@ -10,6 +10,7 @@ dictlookup(Dictreq *req, Dictres *res)
|
|||||||
char *p, *e, *sp;
|
char *p, *e, *sp;
|
||||||
Str tmp;
|
Str tmp;
|
||||||
|
|
||||||
|
res->key = req->pre;
|
||||||
res->nkouho = 0;
|
res->nkouho = 0;
|
||||||
if(req->key.n == 0)
|
if(req->key.n == 0)
|
||||||
return;
|
return;
|
||||||
@@ -23,6 +24,10 @@ dictlookup(Dictreq *req, Dictres *res)
|
|||||||
p = n->val;
|
p = n->val;
|
||||||
e = p + n->vlen;
|
e = p + n->vlen;
|
||||||
while(res->nkouho < Maxkouho && p < e){
|
while(res->nkouho < Maxkouho && p < e){
|
||||||
|
while(p < e && *p == ' ')
|
||||||
|
p++;
|
||||||
|
if(p == e)
|
||||||
|
break;
|
||||||
sp = p;
|
sp = p;
|
||||||
while(p < e && *p != ' ')
|
while(p < e && *p != ' ')
|
||||||
p++;
|
p++;
|
||||||
@@ -47,7 +52,6 @@ dictthread(void*)
|
|||||||
while(channbrecv(dictreqc, &req) > 0)
|
while(channbrecv(dictreqc, &req) > 0)
|
||||||
;
|
;
|
||||||
dictlookup(&req, &res);
|
dictlookup(&req, &res);
|
||||||
res.key = req.pre;
|
|
||||||
chansend(dictresc, &res);
|
chansend(dictresc, &res);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -57,16 +61,18 @@ dictopen(char *path)
|
|||||||
{
|
{
|
||||||
Hmap *h;
|
Hmap *h;
|
||||||
Biobuf *b;
|
Biobuf *b;
|
||||||
Str key, val;
|
Str key;
|
||||||
char *line, *tab;
|
char *line, *tab;
|
||||||
int len;
|
int len;
|
||||||
|
|
||||||
b = Bopen(path, OREAD);
|
b = Bopen(path, OREAD);
|
||||||
if(b == nil)
|
if(b == nil)
|
||||||
die("can't open: %s", path);
|
die("can't open: %s", path);
|
||||||
h = hmapalloc(4096, 0);
|
h = hmapalloc(4096);
|
||||||
while((line = Brdstr(b, '\n', 1)) != nil){
|
while((line = Brdstr(b, '\n', 1)) != nil){
|
||||||
len = strlen(line);
|
len = strlen(line);
|
||||||
|
if(len > 0 && line[len-1] == '\r')
|
||||||
|
line[--len] = '\0';
|
||||||
if(len == 0 || line[0] == ';'){
|
if(len == 0 || line[0] == ';'){
|
||||||
free(line);
|
free(line);
|
||||||
continue;
|
continue;
|
||||||
@@ -78,8 +84,7 @@ dictopen(char *path)
|
|||||||
}
|
}
|
||||||
*tab = '\0';
|
*tab = '\0';
|
||||||
sinit(&key, line, tab - line);
|
sinit(&key, line, tab - line);
|
||||||
sinit(&val, tab+1, len - (tab - line) - 1);
|
hmapset(&h, &key, tab+1, len - (tab - line) - 1);
|
||||||
hmapset(&h, &key, &val);
|
|
||||||
free(line);
|
free(line);
|
||||||
}
|
}
|
||||||
Bterm(b);
|
Bterm(b);
|
||||||
@@ -99,4 +104,3 @@ dictinit(char *dir)
|
|||||||
langs[i].dict = dictopen(path);
|
langs[i].dict = dictopen(path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
9
fn.h
9
fn.h
@@ -9,12 +9,14 @@ int scmp(Str*, Str*);
|
|||||||
int stoutf(Str*, char*, int);
|
int stoutf(Str*, char*, int);
|
||||||
Rune slastr(Str*);
|
Rune slastr(Str*);
|
||||||
|
|
||||||
Hmap* hmapalloc(int, int);
|
Hmap* hmapalloc(int);
|
||||||
int hmapset(Hmap**, Str*, Str*);
|
void hmapfree(Hmap*);
|
||||||
|
void hmapset(Hmap**, Str*, const char*, int);
|
||||||
Hnode* hmapget(Hmap*, Str*);
|
Hnode* hmapget(Hmap*, Str*);
|
||||||
int mapget(Trie*, Str*, Str*);
|
int mapget(Trie*, Str*, Str*);
|
||||||
|
|
||||||
Trie* trieopen(char*);
|
Trie* trieopen(char*);
|
||||||
|
void trieclose(Trie*);
|
||||||
char* trieget(Trie*, char*, int, int*);
|
char* trieget(Trie*, char*, int, int*);
|
||||||
int trielookup(Trie*, char*, int, char**, int*);
|
int trielookup(Trie*, char*, int, char**, int*);
|
||||||
|
|
||||||
@@ -23,6 +25,7 @@ void mapinit(char*);
|
|||||||
void dictinit(char*);
|
void dictinit(char*);
|
||||||
|
|
||||||
void dictthread(void*);
|
void dictthread(void*);
|
||||||
|
void dictlookup(Dictreq*, Dictres*);
|
||||||
|
|
||||||
void drawthread(void*);
|
void drawthread(void*);
|
||||||
void imthread(void*);
|
void imthread(void*);
|
||||||
@@ -30,8 +33,10 @@ Emit transmap(Im*, Rune);
|
|||||||
Emit transko(Im*, Rune);
|
Emit transko(Im*, Rune);
|
||||||
Emit transvi(Im*, Rune);
|
Emit transvi(Im*, Rune);
|
||||||
void backko(Im*);
|
void backko(Im*);
|
||||||
|
void backvi(Im*);
|
||||||
void dictsend(Im*, Str*);
|
void dictsend(Im*, Str*);
|
||||||
|
|
||||||
|
int srvreadkey(int, Keyreq*);
|
||||||
void srvthread(void*);
|
void srvthread(void*);
|
||||||
void ibusthread(void*);
|
void ibusthread(void*);
|
||||||
void waylandthread(void*);
|
void waylandthread(void*);
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ CFLAGS = -Wall -O2 -I.. `pkg-config --cflags gtk+-3.0`
|
|||||||
LDFLAGS = `pkg-config --libs gtk+-3.0`
|
LDFLAGS = `pkg-config --libs gtk+-3.0`
|
||||||
DSTDIR ?= $(shell find /usr/lib* -type d -path "*/gtk-3.0/*/immodules" 2>/dev/null | head -1)
|
DSTDIR ?= $(shell find /usr/lib* -type d -path "*/gtk-3.0/*/immodules" 2>/dev/null | head -1)
|
||||||
|
|
||||||
im-strans.so: main.c
|
im-strans.so: main.c ../ipc.c ../ipc.h
|
||||||
$(CC) -shared -fPIC $(CFLAGS) -o $@ $< $(LDFLAGS)
|
$(CC) -shared -fPIC $(CFLAGS) -o $@ main.c ../ipc.c $(LDFLAGS)
|
||||||
|
|
||||||
docker: main.c Dockerfile
|
docker: main.c Dockerfile
|
||||||
docker build -t strans-gtk .
|
docker build -t strans-gtk .
|
||||||
|
|||||||
102
gtk/main.c
102
gtk/main.c
@@ -12,7 +12,7 @@ struct Im
|
|||||||
{
|
{
|
||||||
GtkIMContext parent;
|
GtkIMContext parent;
|
||||||
int fd;
|
int fd;
|
||||||
char pre[256];
|
char pre[Ipcfieldmax+1];
|
||||||
int prelen;
|
int prelen;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -36,52 +36,61 @@ srvconnect(Im *im)
|
|||||||
return;
|
return;
|
||||||
memset(&addr, 0, sizeof(addr));
|
memset(&addr, 0, sizeof(addr));
|
||||||
addr.sun_family = AF_UNIX;
|
addr.sun_family = AF_UNIX;
|
||||||
snprintf(addr.sun_path, sizeof(addr.sun_path), "/tmp/strans.%d", getuid());
|
snprintf(addr.sun_path, sizeof(addr.sun_path), IPCPATH, getuid());
|
||||||
if(connect(im->fd, (struct sockaddr*)&addr, sizeof(addr)) < 0){
|
if(connect(im->fd, (struct sockaddr*)&addr, sizeof(addr)) < 0){
|
||||||
close(im->fd);
|
close(im->fd);
|
||||||
im->fd = -1;
|
im->fd = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
srvclose(Im *im)
|
||||||
|
{
|
||||||
|
int was;
|
||||||
|
|
||||||
|
if(im->fd >= 0)
|
||||||
|
close(im->fd);
|
||||||
|
im->fd = -1;
|
||||||
|
was = im->prelen;
|
||||||
|
im->pre[0] = '\0';
|
||||||
|
im->prelen = 0;
|
||||||
|
if(was > 0){
|
||||||
|
g_signal_emit_by_name(im, "preedit-changed");
|
||||||
|
g_signal_emit_by_name(im, "preedit-end");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
readresp(Im *im, char *buf, int bufsz)
|
readresp(Im *im, char *buf, int bufsz)
|
||||||
{
|
{
|
||||||
unsigned char hdr[2], pl;
|
Ipcresp resp;
|
||||||
int n, was;
|
int was;
|
||||||
|
|
||||||
if(read(im->fd, hdr, 2) != 2)
|
|
||||||
return -1;
|
|
||||||
n = hdr[1];
|
|
||||||
buf[0] = '\0';
|
|
||||||
if(n > 0 && n < bufsz){
|
|
||||||
if(read(im->fd, buf, n) != n)
|
|
||||||
return -1;
|
|
||||||
buf[n] = '\0';
|
|
||||||
}
|
|
||||||
if(read(im->fd, &pl, 1) != 1)
|
|
||||||
return -1;
|
|
||||||
if(pl >= sizeof(im->pre))
|
|
||||||
return -1;
|
|
||||||
was = im->prelen;
|
was = im->prelen;
|
||||||
if(pl > 0 && read(im->fd, im->pre, pl) != pl)
|
if(ipcreadresp(im->fd, 1, buf, bufsz, im->pre,
|
||||||
|
sizeof(im->pre), &resp) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
im->pre[pl] = '\0';
|
im->prelen = resp.npreedit;
|
||||||
im->prelen = pl;
|
if(was == 0 && im->prelen > 0)
|
||||||
if(was == 0 && pl > 0)
|
|
||||||
g_signal_emit_by_name(im, "preedit-start");
|
g_signal_emit_by_name(im, "preedit-start");
|
||||||
if(was != 0 || pl != 0)
|
if(was != 0 || im->prelen != 0)
|
||||||
g_signal_emit_by_name(im, "preedit-changed");
|
g_signal_emit_by_name(im, "preedit-changed");
|
||||||
if(was > 0 && pl == 0)
|
if(was > 0 && im->prelen == 0)
|
||||||
g_signal_emit_by_name(im, "preedit-end");
|
g_signal_emit_by_name(im, "preedit-end");
|
||||||
return hdr[0];
|
return resp.eaten;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint32_t
|
static uint32_t
|
||||||
kget(uint32_t gdk)
|
kget(uint32_t gdk)
|
||||||
{
|
{
|
||||||
if(gdk >= 0xff00)
|
uint32_t u;
|
||||||
|
|
||||||
|
u = gdk_keyval_to_unicode(gdk);
|
||||||
|
if((gdk & 0xff000000) == 0x01000000 && u != 0)
|
||||||
|
return u;
|
||||||
|
if(gdk >= 0xff00 && gdk <= 0xffff)
|
||||||
return Kspec + (gdk - 0xff00);
|
return Kspec + (gdk - 0xff00);
|
||||||
return gdk_keyval_to_unicode(gdk);
|
return u;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint32_t
|
static uint32_t
|
||||||
@@ -104,17 +113,17 @@ mget(uint32_t state)
|
|||||||
static void
|
static void
|
||||||
sendreset(Im *im)
|
sendreset(Im *im)
|
||||||
{
|
{
|
||||||
unsigned char buf[4];
|
unsigned char buf[Ipcreqsz];
|
||||||
char resp[64];
|
char resp[Ipcfieldmax+1];
|
||||||
|
|
||||||
if(im->fd < 0)
|
if(im->fd < 0){
|
||||||
|
srvclose(im);
|
||||||
return;
|
return;
|
||||||
buf[0] = 1;
|
}
|
||||||
buf[1] = 0;
|
ipcpackreq(buf, 1, 0, Kesc);
|
||||||
buf[2] = Kesc & 0xff;
|
if(ipcsend(im->fd, buf, sizeof buf) < 0 ||
|
||||||
buf[3] = Kesc >> 8;
|
readresp(im, resp, sizeof(resp)) < 0)
|
||||||
if(send(im->fd, buf, 4, MSG_NOSIGNAL) == 4)
|
srvclose(im);
|
||||||
readresp(im, resp, sizeof(resp));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
@@ -137,8 +146,8 @@ static gboolean
|
|||||||
kpress(GtkIMContext *ctx, GdkEventKey *ev)
|
kpress(GtkIMContext *ctx, GdkEventKey *ev)
|
||||||
{
|
{
|
||||||
Im *im;
|
Im *im;
|
||||||
unsigned char buf[4];
|
unsigned char buf[Ipcreqsz];
|
||||||
char resp[64];
|
char resp[Ipcfieldmax+1];
|
||||||
uint32_t key, mod;
|
uint32_t key, mod;
|
||||||
int r;
|
int r;
|
||||||
GtkInputPurpose purpose;
|
GtkInputPurpose purpose;
|
||||||
@@ -154,21 +163,18 @@ kpress(GtkIMContext *ctx, GdkEventKey *ev)
|
|||||||
if(purpose == GTK_INPUT_PURPOSE_PASSWORD || purpose == GTK_INPUT_PURPOSE_PIN)
|
if(purpose == GTK_INPUT_PURPOSE_PASSWORD || purpose == GTK_INPUT_PURPOSE_PIN)
|
||||||
return plaincommit(ctx, key, mod);
|
return plaincommit(ctx, key, mod);
|
||||||
srvconnect(im);
|
srvconnect(im);
|
||||||
if(im->fd < 0)
|
if(im->fd < 0){
|
||||||
|
srvclose(im);
|
||||||
return plaincommit(ctx, key, mod);
|
return plaincommit(ctx, key, mod);
|
||||||
buf[0] = 1;
|
}
|
||||||
buf[1] = mod;
|
ipcpackreq(buf, 1, mod, key);
|
||||||
buf[2] = key & 0xff;
|
if(ipcsend(im->fd, buf, sizeof buf) < 0){
|
||||||
buf[3] = key >> 8;
|
srvclose(im);
|
||||||
if(send(im->fd, buf, 4, MSG_NOSIGNAL) != 4){
|
|
||||||
close(im->fd);
|
|
||||||
im->fd = -1;
|
|
||||||
return plaincommit(ctx, key, mod);
|
return plaincommit(ctx, key, mod);
|
||||||
}
|
}
|
||||||
r = readresp(im, resp, sizeof(resp));
|
r = readresp(im, resp, sizeof(resp));
|
||||||
if(r < 0){
|
if(r < 0){
|
||||||
close(im->fd);
|
srvclose(im);
|
||||||
im->fd = -1;
|
|
||||||
return plaincommit(ctx, key, mod);
|
return plaincommit(ctx, key, mod);
|
||||||
}
|
}
|
||||||
if(resp[0] != '\0')
|
if(resp[0] != '\0')
|
||||||
@@ -228,6 +234,8 @@ static void
|
|||||||
init(Im *im)
|
init(Im *im)
|
||||||
{
|
{
|
||||||
im->fd = -1;
|
im->fd = -1;
|
||||||
|
im->pre[0] = '\0';
|
||||||
|
im->prelen = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|||||||
55
hash.c
55
hash.c
@@ -18,13 +18,15 @@ hash(Str *s)
|
|||||||
}
|
}
|
||||||
|
|
||||||
Hmap*
|
Hmap*
|
||||||
hmapalloc(int nbuckets, int size)
|
hmapalloc(int nbuckets)
|
||||||
{
|
{
|
||||||
void *store;
|
void *store;
|
||||||
Hmap *h;
|
Hmap *h;
|
||||||
int nsz;
|
int nsz;
|
||||||
|
|
||||||
nsz = Tagsize + size;
|
if(nbuckets < 1)
|
||||||
|
die("hmapalloc: no buckets");
|
||||||
|
nsz = Tagsize;
|
||||||
store = emalloc(sizeof(*h) + nbuckets * nsz);
|
store = emalloc(sizeof(*h) + nbuckets * nsz);
|
||||||
h = store;
|
h = store;
|
||||||
h->nbs = nbuckets;
|
h->nbs = nbuckets;
|
||||||
@@ -37,7 +39,7 @@ hmapalloc(int nbuckets, int size)
|
|||||||
static int
|
static int
|
||||||
keycmp(Hnode *n, Str *key)
|
keycmp(Hnode *n, Str *key)
|
||||||
{
|
{
|
||||||
char buf[256];
|
char buf[Maxutf];
|
||||||
int len;
|
int len;
|
||||||
|
|
||||||
len = stoutf(key, buf, sizeof(buf));
|
len = stoutf(key, buf, sizeof(buf));
|
||||||
@@ -67,7 +69,7 @@ hmapget(Hmap *h, Str *key)
|
|||||||
static char*
|
static char*
|
||||||
sdup(Str *s)
|
sdup(Str *s)
|
||||||
{
|
{
|
||||||
char buf[256];
|
char buf[Maxutf];
|
||||||
char *p;
|
char *p;
|
||||||
int n;
|
int n;
|
||||||
|
|
||||||
@@ -78,15 +80,48 @@ sdup(Str *s)
|
|||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
static char*
|
||||||
hmapset(Hmap **store, Str *key, Str *val)
|
memdup(const char *src, int n)
|
||||||
{
|
{
|
||||||
|
char *p;
|
||||||
|
|
||||||
|
if(n == 0)
|
||||||
|
return nil;
|
||||||
|
p = emalloc(n + 1);
|
||||||
|
memmove(p, src, n);
|
||||||
|
p[n] = '\0';
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
hmapfree(Hmap *h)
|
||||||
|
{
|
||||||
|
Hnode *n;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if(h == nil)
|
||||||
|
return;
|
||||||
|
for(i = 0; i < h->len; i++){
|
||||||
|
n = (Hnode*)(h->nodes + i * h->nsz);
|
||||||
|
if(!n->filled)
|
||||||
|
continue;
|
||||||
|
free(n->key);
|
||||||
|
free(n->val);
|
||||||
|
}
|
||||||
|
free(h);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
hmapset(Hmap **store, Str *key, const char *val, int vlen)
|
||||||
|
{
|
||||||
|
char *newval;
|
||||||
Hnode *n;
|
Hnode *n;
|
||||||
uchar *v;
|
uchar *v;
|
||||||
Hmap *h;
|
Hmap *h;
|
||||||
int next;
|
int next;
|
||||||
vlong diff;
|
vlong diff;
|
||||||
|
|
||||||
|
newval = memdup(val, vlen);
|
||||||
h = *store;
|
h = *store;
|
||||||
v = h->nodes + (hash(key) % h->nbs) * h->nsz;
|
v = h->nodes + (hash(key) % h->nbs) * h->nsz;
|
||||||
for(;;){
|
for(;;){
|
||||||
@@ -121,9 +156,7 @@ replace:
|
|||||||
n->filled = 1;
|
n->filled = 1;
|
||||||
}
|
}
|
||||||
n->next = next;
|
n->next = next;
|
||||||
if(val->n > 0){
|
free(n->val);
|
||||||
n->val = sdup(val);
|
n->val = newval;
|
||||||
n->vlen = strlen(n->val);
|
n->vlen = vlen;
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|||||||
62
ibus.c
62
ibus.c
@@ -8,6 +8,7 @@
|
|||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <poll.h>
|
#include <poll.h>
|
||||||
#include <dbus/dbus.h>
|
#include <dbus/dbus.h>
|
||||||
|
#include <xkbcommon/xkbcommon.h>
|
||||||
|
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
@@ -30,6 +31,7 @@ static DBusServer *srv;
|
|||||||
static char addrfile[256];
|
static char addrfile[256];
|
||||||
static int icctr;
|
static int icctr;
|
||||||
static int busctr;
|
static int busctr;
|
||||||
|
static Channel *replyc;
|
||||||
|
|
||||||
static DBusHandlerResult onmsg(DBusConnection*, DBusMessage*, void*);
|
static DBusHandlerResult onmsg(DBusConnection*, DBusMessage*, void*);
|
||||||
|
|
||||||
@@ -182,9 +184,14 @@ togglewatch(DBusWatch *w, void *_)
|
|||||||
static u32int
|
static u32int
|
||||||
kget(u32int sym)
|
kget(u32int sym)
|
||||||
{
|
{
|
||||||
|
u32int c;
|
||||||
|
|
||||||
|
c = xkb_keysym_to_utf32(sym);
|
||||||
|
if(c >= ' ' && c != 0x7f)
|
||||||
|
return c;
|
||||||
if(sym >= 0xff00 && sym <= 0xffff)
|
if(sym >= 0xff00 && sym <= 0xffff)
|
||||||
return Kspec + (sym - 0xff00);
|
return Kspec + (sym - 0xff00);
|
||||||
return sym;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
static u32int
|
static u32int
|
||||||
@@ -201,53 +208,24 @@ mget(u32int state)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
sendkey(u32int ks, u32int mod, char *com, int csz, char *pre, int psz,
|
sendkey(u32int ks, u32int mod, Keyres *res)
|
||||||
int *eaten)
|
|
||||||
{
|
{
|
||||||
Keyreq kr;
|
Keyreq kr;
|
||||||
int p[2];
|
|
||||||
uchar hdr[2];
|
|
||||||
uchar pl;
|
|
||||||
int n;
|
|
||||||
|
|
||||||
*eaten = 0;
|
|
||||||
com[0] = '\0';
|
|
||||||
pre[0] = '\0';
|
|
||||||
if(pipe(p) < 0)
|
|
||||||
return;
|
|
||||||
kr.fd = p[1];
|
|
||||||
kr.ks = ks;
|
kr.ks = ks;
|
||||||
kr.mod = mod;
|
kr.mod = mod;
|
||||||
kr.want = 1;
|
kr.want = 1;
|
||||||
|
kr.reply = replyc;
|
||||||
chansend(keyc, &kr);
|
chansend(keyc, &kr);
|
||||||
if(read(p[0], hdr, 2) != 2)
|
chanrecv(replyc, res);
|
||||||
goto out;
|
|
||||||
*eaten = hdr[0];
|
|
||||||
n = hdr[1];
|
|
||||||
if(n > 0 && n < csz){
|
|
||||||
if(read(p[0], com, n) != n)
|
|
||||||
goto out;
|
|
||||||
com[n] = '\0';
|
|
||||||
}
|
|
||||||
if(read(p[0], &pl, 1) != 1)
|
|
||||||
goto out;
|
|
||||||
if(pl > 0 && pl < psz){
|
|
||||||
if(read(p[0], pre, pl) != pl)
|
|
||||||
goto out;
|
|
||||||
pre[pl] = '\0';
|
|
||||||
}
|
|
||||||
out:
|
|
||||||
close(p[0]);
|
|
||||||
close(p[1]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
sendreset(void)
|
sendreset(void)
|
||||||
{
|
{
|
||||||
char com[64], pre[256];
|
Keyres res;
|
||||||
int eaten;
|
|
||||||
|
|
||||||
sendkey(Kesc, 0, com, sizeof(com), pre, sizeof(pre), &eaten);
|
sendkey(Kesc, 0, &res);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@@ -305,7 +283,7 @@ emitpreedit(DBusConnection *c, const char *path, const char *text)
|
|||||||
return;
|
return;
|
||||||
dbus_message_iter_init_append(sig, &it);
|
dbus_message_iter_init_append(sig, &it);
|
||||||
appendibustext(&it, text);
|
appendibustext(&it, text);
|
||||||
cursor = utflen(text);
|
cursor = utflen((char*)text);
|
||||||
visible = text[0] != '\0' ? TRUE : FALSE;
|
visible = text[0] != '\0' ? TRUE : FALSE;
|
||||||
dbus_message_iter_append_basic(&it, DBUS_TYPE_UINT32, &cursor);
|
dbus_message_iter_append_basic(&it, DBUS_TYPE_UINT32, &cursor);
|
||||||
dbus_message_iter_append_basic(&it, DBUS_TYPE_BOOLEAN, &visible);
|
dbus_message_iter_append_basic(&it, DBUS_TYPE_BOOLEAN, &visible);
|
||||||
@@ -353,8 +331,8 @@ handlekey(DBusConnection *c, DBusMessage *m)
|
|||||||
DBusMessage *r;
|
DBusMessage *r;
|
||||||
DBusError err;
|
DBusError err;
|
||||||
dbus_uint32_t sym, code, state;
|
dbus_uint32_t sym, code, state;
|
||||||
char commit[256], preedit[256];
|
Keyres res;
|
||||||
int eaten;
|
char commit[Maxutf], preedit[Maxutf];
|
||||||
dbus_bool_t b;
|
dbus_bool_t b;
|
||||||
u32int ks, mod;
|
u32int ks, mod;
|
||||||
|
|
||||||
@@ -378,13 +356,14 @@ handlekey(DBusConnection *c, DBusMessage *m)
|
|||||||
}
|
}
|
||||||
ks = kget(sym);
|
ks = kget(sym);
|
||||||
mod = mget(state);
|
mod = mget(state);
|
||||||
sendkey(ks, mod, commit, sizeof(commit), preedit, sizeof(preedit),
|
sendkey(ks, mod, &res);
|
||||||
&eaten);
|
stoutf(&res.commit, commit, sizeof commit);
|
||||||
|
stoutf(&res.preedit, preedit, sizeof preedit);
|
||||||
if(commit[0] != '\0')
|
if(commit[0] != '\0')
|
||||||
emitcommit(c, dbus_message_get_path(m), commit);
|
emitcommit(c, dbus_message_get_path(m), commit);
|
||||||
emitpreedit(c, dbus_message_get_path(m), preedit);
|
emitpreedit(c, dbus_message_get_path(m), preedit);
|
||||||
r = dbus_message_new_method_return(m);
|
r = dbus_message_new_method_return(m);
|
||||||
b = eaten ? TRUE : FALSE;
|
b = res.eaten ? TRUE : FALSE;
|
||||||
dbus_message_append_args(r, DBUS_TYPE_BOOLEAN, &b, DBUS_TYPE_INVALID);
|
dbus_message_append_args(r, DBUS_TYPE_BOOLEAN, &b, DBUS_TYPE_INVALID);
|
||||||
dbus_connection_send(c, r, nil);
|
dbus_connection_send(c, r, nil);
|
||||||
dbus_message_unref(r);
|
dbus_message_unref(r);
|
||||||
@@ -581,6 +560,7 @@ ibusthread(void *_)
|
|||||||
threadsetname("ibus");
|
threadsetname("ibus");
|
||||||
if(ibusinit() < 0)
|
if(ibusinit() < 0)
|
||||||
return;
|
return;
|
||||||
|
replyc = chancreate(sizeof(Keyres), 0);
|
||||||
for(;;){
|
for(;;){
|
||||||
n = 0;
|
n = 0;
|
||||||
for(i = 0; i < nwatches && n < Maxwatches; i++){
|
for(i = 0; i < nwatches && n < Maxwatches; i++){
|
||||||
|
|||||||
186
ipc.c
Normal file
186
ipc.c
Normal file
@@ -0,0 +1,186 @@
|
|||||||
|
#include <errno.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include "ipc.h"
|
||||||
|
|
||||||
|
#if !defined(MSG_NOSIGNAL) && !defined(SO_NOSIGPIPE)
|
||||||
|
#error "ipcsend requires MSG_NOSIGNAL or SO_NOSIGPIPE"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static void
|
||||||
|
putlen(unsigned char p[Ipclensz], size_t n)
|
||||||
|
{
|
||||||
|
p[0] = n;
|
||||||
|
p[1] = n >> 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t
|
||||||
|
getlen(const unsigned char p[Ipclensz])
|
||||||
|
{
|
||||||
|
return p[0] | (p[1] << 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ipcpackreq(unsigned char req[Ipcreqsz], int want, unsigned int mod,
|
||||||
|
unsigned int key)
|
||||||
|
{
|
||||||
|
req[0] = want != 0;
|
||||||
|
req[1] = mod;
|
||||||
|
req[2] = key;
|
||||||
|
req[3] = key >> 8;
|
||||||
|
req[4] = key >> 16;
|
||||||
|
req[5] = key >> 24;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ipcunpackreq(const unsigned char req[Ipcreqsz], int *want, unsigned int *mod,
|
||||||
|
unsigned int *key)
|
||||||
|
{
|
||||||
|
*want = req[0] != 0;
|
||||||
|
*mod = req[1];
|
||||||
|
*key = (unsigned int)req[2] |
|
||||||
|
((unsigned int)req[3] << 8) |
|
||||||
|
((unsigned int)req[4] << 16) |
|
||||||
|
((unsigned int)req[5] << 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
ipcpackresp(unsigned char *dst, size_t cap, int eaten,
|
||||||
|
const char *commit, size_t ncommit, const char *preedit, size_t npreedit,
|
||||||
|
int want)
|
||||||
|
{
|
||||||
|
size_t n;
|
||||||
|
|
||||||
|
if(ncommit > Ipcfieldmax || npreedit > Ipcfieldmax)
|
||||||
|
return -1;
|
||||||
|
if((ncommit > 0 && commit == NULL) ||
|
||||||
|
(want && npreedit > 0 && preedit == NULL))
|
||||||
|
return -1;
|
||||||
|
n = Ipcresphdrsz + ncommit + (want ? Ipclensz + npreedit : 0);
|
||||||
|
if(dst == NULL || cap < n)
|
||||||
|
return -1;
|
||||||
|
dst[0] = eaten != 0;
|
||||||
|
putlen(dst + 1, ncommit);
|
||||||
|
if(ncommit > 0)
|
||||||
|
memcpy(dst + Ipcresphdrsz, commit, ncommit);
|
||||||
|
if(want){
|
||||||
|
putlen(dst + Ipcresphdrsz + ncommit, npreedit);
|
||||||
|
if(npreedit > 0)
|
||||||
|
memcpy(dst + Ipcresphdrsz + ncommit + Ipclensz,
|
||||||
|
preedit, npreedit);
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
ipcreadn(int fd, void *buf, size_t n)
|
||||||
|
{
|
||||||
|
unsigned char *p;
|
||||||
|
ssize_t r;
|
||||||
|
|
||||||
|
p = buf;
|
||||||
|
while(n > 0){
|
||||||
|
r = read(fd, p, n);
|
||||||
|
if(r < 0 && errno == EINTR)
|
||||||
|
continue;
|
||||||
|
if(r <= 0)
|
||||||
|
return -1;
|
||||||
|
p += r;
|
||||||
|
n -= r;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
ipcsend(int fd, const void *buf, size_t n)
|
||||||
|
{
|
||||||
|
const unsigned char *p;
|
||||||
|
ssize_t r;
|
||||||
|
int flags;
|
||||||
|
#if !defined(MSG_NOSIGNAL) && defined(SO_NOSIGPIPE)
|
||||||
|
int one, sr;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
p = buf;
|
||||||
|
#ifdef MSG_NOSIGNAL
|
||||||
|
flags = MSG_NOSIGNAL;
|
||||||
|
#else
|
||||||
|
flags = 0;
|
||||||
|
#ifdef SO_NOSIGPIPE
|
||||||
|
one = 1;
|
||||||
|
do
|
||||||
|
sr = setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &one, sizeof one);
|
||||||
|
while(sr < 0 && errno == EINTR);
|
||||||
|
if(sr < 0)
|
||||||
|
return -1;
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
while(n > 0){
|
||||||
|
r = send(fd, p, n, flags);
|
||||||
|
if(r < 0 && errno == EINTR)
|
||||||
|
continue;
|
||||||
|
if(r <= 0)
|
||||||
|
return -1;
|
||||||
|
p += r;
|
||||||
|
n -= r;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
readfield(int fd, size_t n, char *dst, size_t cap)
|
||||||
|
{
|
||||||
|
unsigned char discard[128];
|
||||||
|
size_t keep, part;
|
||||||
|
|
||||||
|
if(cap > 0 && dst == NULL)
|
||||||
|
return -1;
|
||||||
|
keep = 0;
|
||||||
|
if(cap > 0)
|
||||||
|
keep = n >= cap ? cap - 1 : n;
|
||||||
|
if(keep > 0 && ipcreadn(fd, dst, keep) < 0)
|
||||||
|
return -1;
|
||||||
|
if(cap > 0)
|
||||||
|
dst[keep] = '\0';
|
||||||
|
n -= keep;
|
||||||
|
while(n > 0){
|
||||||
|
part = n < sizeof discard ? n : sizeof discard;
|
||||||
|
if(ipcreadn(fd, discard, part) < 0)
|
||||||
|
return -1;
|
||||||
|
n -= part;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
ipcreadresp(int fd, int want, char *commit, size_t ccap,
|
||||||
|
char *preedit, size_t pcap, Ipcresp *resp)
|
||||||
|
{
|
||||||
|
unsigned char hdr[Ipcresphdrsz], npreedit[Ipclensz];
|
||||||
|
|
||||||
|
if(resp == NULL || (ccap > 0 && commit == NULL) ||
|
||||||
|
(pcap > 0 && preedit == NULL))
|
||||||
|
return -1;
|
||||||
|
if(ccap > 0)
|
||||||
|
commit[0] = '\0';
|
||||||
|
if(pcap > 0)
|
||||||
|
preedit[0] = '\0';
|
||||||
|
memset(resp, 0, sizeof *resp);
|
||||||
|
if(ipcreadn(fd, hdr, sizeof hdr) < 0)
|
||||||
|
return -1;
|
||||||
|
resp->eaten = hdr[0] != 0;
|
||||||
|
resp->ncommit = getlen(hdr + 1);
|
||||||
|
if(resp->ncommit > Ipcfieldmax)
|
||||||
|
return -1;
|
||||||
|
if(readfield(fd, resp->ncommit, commit, ccap) < 0)
|
||||||
|
return -1;
|
||||||
|
if(!want)
|
||||||
|
return 0;
|
||||||
|
if(ipcreadn(fd, npreedit, sizeof npreedit) < 0)
|
||||||
|
return -1;
|
||||||
|
resp->npreedit = getlen(npreedit);
|
||||||
|
if(resp->npreedit > Ipcfieldmax)
|
||||||
|
return -1;
|
||||||
|
return readfield(fd, resp->npreedit, preedit, pcap);
|
||||||
|
}
|
||||||
41
ipc.h
41
ipc.h
@@ -1,9 +1,21 @@
|
|||||||
// req: 4 bytes [type, mod, key lo, key hi]
|
#ifndef STRANS_IPC_H
|
||||||
// resp: 2+n bytes [type, n, ...]
|
#define STRANS_IPC_H
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#define IPCPATH "/tmp/strans.%d"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Request: [want-preedit, modifiers, key byte 0, ..., key byte 3].
|
||||||
|
* Response: [eaten, commit-length-low, commit-length-high, commit...],
|
||||||
|
* followed, when requested, by
|
||||||
|
* [preedit-length-low, preedit-length-high, preedit...].
|
||||||
|
* Lengths are little-endian byte counts; fields are at most Ipcfieldmax bytes.
|
||||||
|
*/
|
||||||
|
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
Kspec = 0xf000,
|
Kspec = 0x110000,
|
||||||
Kback = Kspec|0x08,
|
Kback = Kspec|0x08,
|
||||||
Ktab = Kspec|0x09,
|
Ktab = Kspec|0x09,
|
||||||
Kret = Kspec|0x0d,
|
Kret = Kspec|0x0d,
|
||||||
@@ -17,4 +29,27 @@ enum
|
|||||||
Mctrl = 1<<2,
|
Mctrl = 1<<2,
|
||||||
Malt = 1<<3,
|
Malt = 1<<3,
|
||||||
Msuper = 1<<6,
|
Msuper = 1<<6,
|
||||||
|
|
||||||
|
Ipcreqsz = 6,
|
||||||
|
Ipclensz = 2,
|
||||||
|
Ipcresphdrsz = 1 + Ipclensz,
|
||||||
|
Ipcfieldmax = 256,
|
||||||
|
Ipcmaxresp = Ipcresphdrsz + Ipcfieldmax + Ipclensz + Ipcfieldmax,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef struct Ipcresp Ipcresp;
|
||||||
|
struct Ipcresp
|
||||||
|
{
|
||||||
|
int eaten;
|
||||||
|
size_t ncommit;
|
||||||
|
size_t npreedit;
|
||||||
|
};
|
||||||
|
|
||||||
|
void ipcpackreq(unsigned char[Ipcreqsz], int, unsigned int, unsigned int);
|
||||||
|
void ipcunpackreq(const unsigned char[Ipcreqsz], int*, unsigned int*, unsigned int*);
|
||||||
|
int ipcpackresp(unsigned char*, size_t, int, const char*, size_t, const char*, size_t, int);
|
||||||
|
int ipcreadn(int, void*, size_t);
|
||||||
|
int ipcsend(int, const void*, size_t);
|
||||||
|
int ipcreadresp(int, int, char*, size_t, char*, size_t, Ipcresp*);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
sys.stdout.reconfigure(encoding="utf-8")
|
||||||
|
|
||||||
# tone marks: s=rising f=falling r=hook x=tilde j=dot
|
# tone marks: s=rising f=falling r=hook x=tilde j=dot
|
||||||
tone = {
|
tone = {
|
||||||
's': str.maketrans("aăâeêioôơuưy", "áắấéếíóốớúứý"),
|
's': str.maketrans("aăâeêioôơuưy", "áắấéếíóốớúứý"),
|
||||||
@@ -24,24 +28,21 @@ modcons = [
|
|||||||
("dd", "đ"),
|
("dd", "đ"),
|
||||||
]
|
]
|
||||||
|
|
||||||
upper = str.maketrans(
|
|
||||||
"aăâeêioôơuưyđáắấéếíóốớúứýàằầèềìòồờùừỳảẳẩẻểỉỏổởủửỷãẵẫẽễĩõỗỡũữỹạặậẹệịọộợụựỵ",
|
|
||||||
"AĂÂEÊIOÔƠUƯYĐÁẮẤÉẾÍÓỐỚÚỨÝÀẰẦÈỀÌÒỒỜÙỪỲẢẲẨẺỂỈỎỔỞỦỬỶÃẴẪẼỄĨÕỖỠŨỮỸẠẶẬẸỆỊỌỘỢỤỰỴ")
|
|
||||||
|
|
||||||
def addtone(v, t):
|
def addtone(v, t):
|
||||||
return v.translate(tone[t])
|
return v.translate(tone[t])
|
||||||
|
|
||||||
entries = []
|
entries = []
|
||||||
|
|
||||||
def emit(input, output):
|
def initialupper(s):
|
||||||
entries.append((input, output))
|
return s[0].upper() + s[1:]
|
||||||
print(f"{input}\t{output}")
|
|
||||||
def up(s):
|
def emit(src, dst):
|
||||||
c = s[0].translate(upper)
|
entries.append((src, dst))
|
||||||
if c == s[0]:
|
print(f"{src}\t{dst}")
|
||||||
c = s[0].upper()
|
usrc = initialupper(src)
|
||||||
return c + s[1:]
|
udst = initialupper(dst)
|
||||||
print(f"{up(input)}\t{up(output)}")
|
if (usrc, udst) != (src, dst):
|
||||||
|
print(f"{usrc}\t{udst}")
|
||||||
|
|
||||||
def vowel1():
|
def vowel1():
|
||||||
for v in "aeiouy":
|
for v in "aeiouy":
|
||||||
@@ -97,14 +98,6 @@ def mod1tone():
|
|||||||
for t in tone:
|
for t in tone:
|
||||||
emit(i+t, addtone(o, t))
|
emit(i+t, addtone(o, t))
|
||||||
|
|
||||||
modvowel = [
|
|
||||||
("aw", "ă"),
|
|
||||||
("aa", "â"),
|
|
||||||
("ee", "ê"),
|
|
||||||
("oo", "ô"),
|
|
||||||
("ow", "ơ"),
|
|
||||||
("uw", "ư"),
|
|
||||||
]
|
|
||||||
def tone1mod():
|
def tone1mod():
|
||||||
# a+s+w -> ắ
|
# a+s+w -> ắ
|
||||||
for i, o in modvowel:
|
for i, o in modvowel:
|
||||||
@@ -178,4 +171,5 @@ tone1mod()
|
|||||||
tone2mod()
|
tone2mod()
|
||||||
escape()
|
escape()
|
||||||
final()
|
final()
|
||||||
|
emit("\\", "\\")
|
||||||
onsets()
|
onsets()
|
||||||
|
|||||||
5212
map/telex.map
5212
map/telex.map
File diff suppressed because it is too large
Load Diff
59
srv.c
59
srv.c
@@ -2,30 +2,53 @@
|
|||||||
#include "fn.h"
|
#include "fn.h"
|
||||||
|
|
||||||
static char adir[40];
|
static char adir[40];
|
||||||
|
static Channel *clientc;
|
||||||
|
|
||||||
static void
|
int
|
||||||
sendkey(int fd, u32int ks, u32int mod, int want)
|
srvreadkey(int fd, Keyreq *kr)
|
||||||
{
|
{
|
||||||
Keyreq kr;
|
uchar req[Ipcreqsz];
|
||||||
|
u32int ks, mod;
|
||||||
|
int want;
|
||||||
|
|
||||||
kr.fd = fd;
|
if(ipcreadn(fd, req, sizeof req) < 0)
|
||||||
kr.ks = ks;
|
return -1;
|
||||||
kr.mod = mod;
|
ipcunpackreq(req, &want, &mod, &ks);
|
||||||
kr.want = want;
|
kr->ks = ks;
|
||||||
chansend(keyc, &kr);
|
kr->mod = mod;
|
||||||
|
kr->want = want;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
clientthread(void *arg)
|
clientthread(void *arg)
|
||||||
{
|
{
|
||||||
|
Channel *reply;
|
||||||
int fd;
|
int fd;
|
||||||
uchar req[4];
|
Keyreq kr;
|
||||||
|
Keyres res;
|
||||||
|
uchar out[Ipcmaxresp];
|
||||||
|
char commit[Maxutf], preedit[Maxutf];
|
||||||
|
int n, ncommit, npreedit;
|
||||||
|
uchar token;
|
||||||
|
|
||||||
fd = (int)(uintptr)arg;
|
fd = (int)(uintptr)arg;
|
||||||
threadsetname("client %d", fd);
|
threadsetname("client %d", fd);
|
||||||
while(read(fd, req, 4) == 4)
|
reply = chancreate(sizeof(Keyres), 0);
|
||||||
sendkey(fd, req[2] | (req[3] << 8), req[1], req[0]);
|
kr.reply = reply;
|
||||||
|
while(srvreadkey(fd, &kr) == 0){
|
||||||
|
chansend(keyc, &kr);
|
||||||
|
chanrecv(reply, &res);
|
||||||
|
ncommit = stoutf(&res.commit, commit, sizeof commit);
|
||||||
|
npreedit = stoutf(&res.preedit, preedit, sizeof preedit);
|
||||||
|
n = ipcpackresp(out, sizeof out, res.eaten,
|
||||||
|
commit, ncommit, preedit, npreedit, kr.want);
|
||||||
|
if(n < 0 || ipcsend(fd, out, n) < 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
chanfree(reply);
|
||||||
close(fd);
|
close(fd);
|
||||||
|
chanrecv(clientc, &token);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@@ -33,7 +56,7 @@ srvinit(void)
|
|||||||
{
|
{
|
||||||
char addr[64];
|
char addr[64];
|
||||||
|
|
||||||
snprint(addr, sizeof(addr), "unix!/tmp/strans.%d", getuid());
|
snprint(addr, sizeof(addr), "unix!" IPCPATH, getuid());
|
||||||
remove(addr + 5);
|
remove(addr + 5);
|
||||||
if(announce(addr, adir) < 0)
|
if(announce(addr, adir) < 0)
|
||||||
die("announce: %r");
|
die("announce: %r");
|
||||||
@@ -44,13 +67,23 @@ srvthread(void*)
|
|||||||
{
|
{
|
||||||
char ldir[40];
|
char ldir[40];
|
||||||
int fd;
|
int fd;
|
||||||
|
uchar token;
|
||||||
|
|
||||||
threadsetname("srv");
|
threadsetname("srv");
|
||||||
srvinit();
|
srvinit();
|
||||||
|
token = 0;
|
||||||
|
clientc = chancreate(sizeof token, Maxclients);
|
||||||
for(;;){
|
for(;;){
|
||||||
fd = listen(adir, ldir);
|
fd = listen(adir, ldir);
|
||||||
if(fd < 0)
|
if(fd < 0)
|
||||||
continue;
|
continue;
|
||||||
proccreate(clientthread, (void*)(uintptr)fd, 8192);
|
if(channbsend(clientc, &token) <= 0){
|
||||||
|
close(fd);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(proccreate(clientthread, (void*)(uintptr)fd, 8192) < 0){
|
||||||
|
chanrecv(clientc, &token);
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
24
str.c
24
str.c
@@ -1,11 +1,6 @@
|
|||||||
#include "dat.h"
|
#include "dat.h"
|
||||||
#include "fn.h"
|
#include "fn.h"
|
||||||
|
|
||||||
enum
|
|
||||||
{
|
|
||||||
Maxrunes = nelem(((Str*)0)->r),
|
|
||||||
};
|
|
||||||
|
|
||||||
void
|
void
|
||||||
sinit(Str *s, char *src, int n)
|
sinit(Str *s, char *src, int n)
|
||||||
{
|
{
|
||||||
@@ -13,7 +8,11 @@ sinit(Str *s, char *src, int n)
|
|||||||
|
|
||||||
s->n = 0;
|
s->n = 0;
|
||||||
while(n > 0 && s->n < Maxrunes){
|
while(n > 0 && s->n < Maxrunes){
|
||||||
|
if(!fullrune(src, n))
|
||||||
|
break;
|
||||||
len = chartorune(&s->r[s->n], src);
|
len = chartorune(&s->r[s->n], src);
|
||||||
|
if(len > n)
|
||||||
|
break;
|
||||||
s->n++;
|
s->n++;
|
||||||
src += len;
|
src += len;
|
||||||
n -= len;
|
n -= len;
|
||||||
@@ -44,9 +43,10 @@ spopr(Str *s)
|
|||||||
void
|
void
|
||||||
sappend(Str *dst, Str *src)
|
sappend(Str *dst, Str *src)
|
||||||
{
|
{
|
||||||
int i;
|
int i, n;
|
||||||
|
|
||||||
for(i = 0; i < src->n && dst->n < Maxrunes; i++)
|
n = src->n;
|
||||||
|
for(i = 0; i < n && dst->n < Maxrunes; i++)
|
||||||
dst->r[dst->n++] = src->r[i];
|
dst->r[dst->n++] = src->r[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,11 +66,17 @@ scmp(Str *a, Str *b)
|
|||||||
int
|
int
|
||||||
stoutf(Str *s, char *buf, int sz)
|
stoutf(Str *s, char *buf, int sz)
|
||||||
{
|
{
|
||||||
|
char tmp[UTFmax];
|
||||||
int i, n, len;
|
int i, n, len;
|
||||||
|
|
||||||
|
if(sz <= 0)
|
||||||
|
return 0;
|
||||||
n = 0;
|
n = 0;
|
||||||
for(i = 0; i < s->n && n < sz - UTFmax; i++){
|
for(i = 0; i < s->n; i++){
|
||||||
len = runetochar(buf + n, &s->r[i]);
|
len = runetochar(tmp, &s->r[i]);
|
||||||
|
if(len > sz - n - 1)
|
||||||
|
break;
|
||||||
|
memmove(buf + n, tmp, len);
|
||||||
n += len;
|
n += len;
|
||||||
}
|
}
|
||||||
buf[n] = '\0';
|
buf[n] = '\0';
|
||||||
|
|||||||
67
strans.c
67
strans.c
@@ -17,7 +17,7 @@ Lang langs[] = {
|
|||||||
{LangJPK, "kata", "kanji", transmap, backmap, dictqmap, nil, nil},
|
{LangJPK, "kata", "kanji", transmap, backmap, dictqmap, nil, nil},
|
||||||
{LangKO, "hangul", nil, transko, backko, dictqmap, nil, nil},
|
{LangKO, "hangul", nil, transko, backko, dictqmap, nil, nil},
|
||||||
{LangEMOJI, "emoji", "emoji", transmap, backmap, dictqmap, nil, nil},
|
{LangEMOJI, "emoji", "emoji", transmap, backmap, dictqmap, nil, nil},
|
||||||
// {LangVI, "telex", nil, transvi, backmap, dictqmap, nil, nil},
|
{LangVI, "telex", nil, transvi, backvi, dictqmap, nil, nil},
|
||||||
};
|
};
|
||||||
int nlang = nelem(langs);
|
int nlang = nelem(langs);
|
||||||
|
|
||||||
@@ -65,6 +65,7 @@ static void
|
|||||||
reset(void)
|
reset(void)
|
||||||
{
|
{
|
||||||
sclear(&im.pre);
|
sclear(&im.pre);
|
||||||
|
sclear(&im.raw);
|
||||||
clearkouho();
|
clearkouho();
|
||||||
show();
|
show();
|
||||||
}
|
}
|
||||||
@@ -85,11 +86,10 @@ dictqmap(Im *im)
|
|||||||
{
|
{
|
||||||
Str dict;
|
Str dict;
|
||||||
|
|
||||||
if(!mapget(im->l->map, &im->pre, &dict)){
|
clearkouho();
|
||||||
clearkouho();
|
show();
|
||||||
show();
|
if(!mapget(im->l->map, &im->pre, &dict))
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
dictsend(im, &dict);
|
dictsend(im, &dict);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,6 +115,7 @@ commit(Str *com)
|
|||||||
else
|
else
|
||||||
sappend(com, &im.pre);
|
sappend(com, &im.pre);
|
||||||
sclear(&im.pre);
|
sclear(&im.pre);
|
||||||
|
sclear(&im.raw);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
@@ -122,8 +123,24 @@ dotrans(Rune c, Str *com)
|
|||||||
{
|
{
|
||||||
Emit e;
|
Emit e;
|
||||||
Dictreq req;
|
Dictreq req;
|
||||||
|
Str mapped;
|
||||||
|
|
||||||
e = im.l->trans(&im, c);
|
e = im.l->trans(&im, c);
|
||||||
|
if(im.l->lang == LangVI){
|
||||||
|
if(e.s.n > 0 || !e.eat)
|
||||||
|
sclear(&im.raw);
|
||||||
|
if(e.eat && e.next.n > 0){
|
||||||
|
if(im.raw.n >= Maxrunes){
|
||||||
|
if(!mapget(im.l->map, &e.next, &mapped))
|
||||||
|
mapped = e.next;
|
||||||
|
sappend(com, &mapped);
|
||||||
|
reset();
|
||||||
|
return e.eat;
|
||||||
|
}
|
||||||
|
sputr(&im.raw, c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
clearkouho();
|
||||||
if(e.s.n > 0)
|
if(e.s.n > 0)
|
||||||
sappend(com, &e.s);
|
sappend(com, &e.s);
|
||||||
sclear(&im.pre);
|
sclear(&im.pre);
|
||||||
@@ -151,7 +168,7 @@ getlang(int lang)
|
|||||||
static int
|
static int
|
||||||
maplookup(Trie *t, Str *key, Str *out)
|
maplookup(Trie *t, Str *key, Str *out)
|
||||||
{
|
{
|
||||||
char buf[256], *v;
|
char buf[Maxutf], *v;
|
||||||
int klen, vlen;
|
int klen, vlen;
|
||||||
|
|
||||||
if(key->n == 0)
|
if(key->n == 0)
|
||||||
@@ -290,14 +307,24 @@ init(void)
|
|||||||
im.l = getlang(LangEN);
|
im.l = getlang(LangEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
imhandlekey(Keyreq *kr)
|
||||||
|
{
|
||||||
|
Keyres res;
|
||||||
|
|
||||||
|
sclear(&res.commit);
|
||||||
|
sclear(&res.preedit);
|
||||||
|
res.eaten = keystroke(kr->ks, kr->mod, &res.commit);
|
||||||
|
if(kr->want)
|
||||||
|
res.preedit = im.pre;
|
||||||
|
chansend(kr->reply, &res);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
imthread(void*)
|
imthread(void*)
|
||||||
{
|
{
|
||||||
Keyreq kr;
|
Keyreq kr;
|
||||||
Dictres res;
|
Dictres res;
|
||||||
Str com;
|
|
||||||
uchar out[256];
|
|
||||||
int n, len, r;
|
|
||||||
Alt alts[] = {
|
Alt alts[] = {
|
||||||
{keyc, &kr, CHANRCV, nil},
|
{keyc, &kr, CHANRCV, nil},
|
||||||
{dictresc, &res, CHANRCV, nil},
|
{dictresc, &res, CHANRCV, nil},
|
||||||
@@ -309,24 +336,7 @@ imthread(void*)
|
|||||||
for(;;){
|
for(;;){
|
||||||
switch(alt(alts)){
|
switch(alt(alts)){
|
||||||
case 0:
|
case 0:
|
||||||
sclear(&com);
|
imhandlekey(&kr);
|
||||||
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;
|
|
||||||
}
|
|
||||||
if(kr.want){
|
|
||||||
len = im.pre.n > 0 ?
|
|
||||||
stoutf(&im.pre, (char*)(out+n+1),
|
|
||||||
sizeof(out)-n-1) : 0;
|
|
||||||
out[n] = len;
|
|
||||||
n += 1 + len;
|
|
||||||
}
|
|
||||||
write(kr.fd, out, n);
|
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
if(scmp(&res.key, &im.pre) == 0){
|
if(scmp(&res.key, &im.pre) == 0){
|
||||||
@@ -341,7 +351,7 @@ imthread(void*)
|
|||||||
int
|
int
|
||||||
mapget(Trie *t, Str *key, Str *out)
|
mapget(Trie *t, Str *key, Str *out)
|
||||||
{
|
{
|
||||||
char buf[256], *v;
|
char buf[Maxutf], *v;
|
||||||
int klen, vlen;
|
int klen, vlen;
|
||||||
|
|
||||||
if(key->n == 0)
|
if(key->n == 0)
|
||||||
@@ -367,4 +377,3 @@ mapinit(char *dir)
|
|||||||
langs[i].map = trieopen(path);
|
langs[i].map = trieopen(path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
34
tests/Makefile
Normal file
34
tests/Makefile
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
CC = 9c
|
||||||
|
LD = 9l
|
||||||
|
CFLAGS = -std=c99 -Wall -Wextra -O2 -g -I.. -I../cutest
|
||||||
|
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
|
||||||
|
TESTOBJ = $(TESTSRC:.c=.o)
|
||||||
|
PARENTSRC = str.c hash.c trie.c dict.c ko.c vi.c
|
||||||
|
PARENTOBJ = $(PARENTSRC:%.c=unit_%.o)
|
||||||
|
OBJS = $(TESTOBJ) $(PARENTOBJ)
|
||||||
|
|
||||||
|
all: $(PROG)
|
||||||
|
|
||||||
|
check test: $(PROG)
|
||||||
|
./$(PROG) $(TESTARGS)
|
||||||
|
|
||||||
|
$(PROG): $(OBJS)
|
||||||
|
$(LD) -o $@ $(OBJS) $(LIBS)
|
||||||
|
|
||||||
|
$(TESTOBJ): test.h ../dat.h ../fn.h ../ipc.h ../cutest/cutest.h
|
||||||
|
engine_test.o: ../strans.c
|
||||||
|
|
||||||
|
unit_%.o: ../%.c ../dat.h ../fn.h ../ipc.h
|
||||||
|
$(CC) $(CFLAGS) -c -o $@ $<
|
||||||
|
|
||||||
|
%.o: %.c
|
||||||
|
$(CC) $(CFLAGS) -c -o $@ $<
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f $(OBJS) $(PROG)
|
||||||
|
|
||||||
|
.PHONY: all check test clean
|
||||||
4
tests/data/hira.map
Normal file
4
tests/data/hira.map
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
ka か
|
||||||
|
sa さ
|
||||||
|
n ん
|
||||||
|
na な
|
||||||
6
tests/data/trie.map
Normal file
6
tests/data/trie.map
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
a alpha
|
||||||
|
ab beta
|
||||||
|
한 값
|
||||||
|
duplicate first
|
||||||
|
duplicate second
|
||||||
|
tabs one two
|
||||||
88
tests/dict_test.c
Normal file
88
tests/dict_test.c
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
#include "test.h"
|
||||||
|
|
||||||
|
void
|
||||||
|
dictionary_candidates(struct ct *t)
|
||||||
|
{
|
||||||
|
char many[512], item[8];
|
||||||
|
char *p;
|
||||||
|
Dictreq req;
|
||||||
|
Dictres res;
|
||||||
|
Hmap *saved;
|
||||||
|
Lang *lang;
|
||||||
|
Str key;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
lang = getlang(LangJP);
|
||||||
|
saved = lang->dict;
|
||||||
|
lang->dict = hmapalloc(1);
|
||||||
|
key = mkstr("かな");
|
||||||
|
hmapset(&lang->dict, &key, " 候補1 かな 候補2 ",
|
||||||
|
strlen(" 候補1 かな 候補2 "));
|
||||||
|
memset(&req, 0, sizeof req);
|
||||||
|
req.key = key;
|
||||||
|
req.pre = mkstr("preedit-one");
|
||||||
|
req.lang = LangJP;
|
||||||
|
dictlookup(&req, &res);
|
||||||
|
if(!CT_EQ_INT(t, 2, res.nkouho))
|
||||||
|
goto cleanup;
|
||||||
|
CT_EQ_INT(t, 0, scmp(&req.pre, &res.key));
|
||||||
|
checkstr(t, "candidate 1", "候補1", &res.kouho[0]);
|
||||||
|
checkstr(t, "candidate 2", "候補2", &res.kouho[1]);
|
||||||
|
key = mkstr("key");
|
||||||
|
p = many;
|
||||||
|
for(i = 0; i < Maxkouho+1; i++){
|
||||||
|
snprint(item, sizeof item, "c%02d", i);
|
||||||
|
if(i > 0)
|
||||||
|
*p++ = ' ';
|
||||||
|
memmove(p, item, strlen(item));
|
||||||
|
p += strlen(item);
|
||||||
|
}
|
||||||
|
*p = '\0';
|
||||||
|
hmapset(&lang->dict, &key, many, strlen(many));
|
||||||
|
req.key = key;
|
||||||
|
req.pre = mkstr("preedit-two");
|
||||||
|
dictlookup(&req, &res);
|
||||||
|
if(!CT_EQ_INT(t, Maxkouho, res.nkouho))
|
||||||
|
goto cleanup;
|
||||||
|
CT_EQ_INT(t, 0, scmp(&req.pre, &res.key));
|
||||||
|
checkstr(t, "first capped candidate", "c00", &res.kouho[0]);
|
||||||
|
checkstr(t, "last capped candidate", "c31", &res.kouho[31]);
|
||||||
|
cleanup:
|
||||||
|
hmapfree(lang->dict);
|
||||||
|
lang->dict = saved;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
dictionary_misses_clear_result(struct ct *t)
|
||||||
|
{
|
||||||
|
static const struct {
|
||||||
|
char *name;
|
||||||
|
char *key;
|
||||||
|
char *pre;
|
||||||
|
} cases[] = {
|
||||||
|
{ "empty key", "", "empty-preedit" },
|
||||||
|
{ "missing key", "missing", "missing-preedit" },
|
||||||
|
};
|
||||||
|
Dictreq req;
|
||||||
|
Dictres res;
|
||||||
|
Hmap *saved;
|
||||||
|
Lang *lang;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
lang = getlang(LangJP);
|
||||||
|
saved = lang->dict;
|
||||||
|
lang->dict = hmapalloc(1);
|
||||||
|
for(i = 0; i < nelem(cases); i++){
|
||||||
|
memset(&res, 0xa5, sizeof res);
|
||||||
|
req.key = mkstr(cases[i].key);
|
||||||
|
req.pre = mkstr(cases[i].pre);
|
||||||
|
req.lang = LangJP;
|
||||||
|
dictlookup(&req, &res);
|
||||||
|
if(res.nkouho != 0)
|
||||||
|
CT_ERRORF(t, "%s: want 0 candidates, got %d",
|
||||||
|
cases[i].name, res.nkouho);
|
||||||
|
checkstr(t, cases[i].name, cases[i].pre, &res.key);
|
||||||
|
}
|
||||||
|
hmapfree(lang->dict);
|
||||||
|
lang->dict = saved;
|
||||||
|
}
|
||||||
257
tests/engine_test.c
Normal file
257
tests/engine_test.c
Normal file
@@ -0,0 +1,257 @@
|
|||||||
|
#define STRANS_TEST_ENGINE
|
||||||
|
#include "../strans.c"
|
||||||
|
#include "test.h"
|
||||||
|
|
||||||
|
void
|
||||||
|
vietnamese_state_lifetime(struct ct *t)
|
||||||
|
{
|
||||||
|
Str com;
|
||||||
|
|
||||||
|
init();
|
||||||
|
im.l = &testvi;
|
||||||
|
im.pre = mkstr("as");
|
||||||
|
im.raw = mkstr("as");
|
||||||
|
sclear(&com);
|
||||||
|
CT_CHECK(t, keystroke(Kret, 0, &com));
|
||||||
|
checkstr(t, "committed Telex", "á", &com);
|
||||||
|
CT_EQ_INT(t, 0, im.pre.n);
|
||||||
|
CT_EQ_INT(t, 0, im.raw.n);
|
||||||
|
|
||||||
|
init();
|
||||||
|
im.l = &testvi;
|
||||||
|
im.pre = mkstr("as");
|
||||||
|
im.raw = mkstr("as");
|
||||||
|
sclear(&com);
|
||||||
|
CT_CHECK(t, keystroke(Kesc, 0, &com));
|
||||||
|
CT_EQ_INT(t, 0, com.n);
|
||||||
|
CT_EQ_INT(t, 0, im.pre.n);
|
||||||
|
CT_EQ_INT(t, 0, im.raw.n);
|
||||||
|
|
||||||
|
init();
|
||||||
|
im.l = &testvi;
|
||||||
|
im.pre = mkstr("as");
|
||||||
|
im.raw = mkstr("as");
|
||||||
|
sclear(&com);
|
||||||
|
CT_CHECK(t, dotrans('q', &com));
|
||||||
|
checkstr(t, "completed Telex", "á", &com);
|
||||||
|
checkstr(t, "next preedit", "q", &im.pre);
|
||||||
|
checkstr(t, "next raw input", "q", &im.raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
engine_clears_candidates(struct ct *t)
|
||||||
|
{
|
||||||
|
Str com;
|
||||||
|
|
||||||
|
init();
|
||||||
|
im.l = getlang(LangKO);
|
||||||
|
im.pre = mkstr("ㄱ");
|
||||||
|
im.nkouho = 2;
|
||||||
|
im.sel = 1;
|
||||||
|
sclear(&com);
|
||||||
|
dotrans('k', &com);
|
||||||
|
CT_EQ_INT(t, 0, im.nkouho);
|
||||||
|
CT_EQ_INT(t, -1, im.sel);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
engine_backspace_clears_candidates(struct ct *t)
|
||||||
|
{
|
||||||
|
Lang *lang;
|
||||||
|
Str com, shown;
|
||||||
|
|
||||||
|
lang = getlang(LangJP);
|
||||||
|
init();
|
||||||
|
im.l = lang;
|
||||||
|
sclear(&com);
|
||||||
|
CT_CHECK(t, keystroke('n', 0, &com));
|
||||||
|
CT_CHECK(t, keystroke('a', 0, &com));
|
||||||
|
shown = shownpre(&im);
|
||||||
|
checkstr(t, "before backspace", "な", &shown);
|
||||||
|
im.kouho[0] = mkstr("stale-candidate");
|
||||||
|
im.nkouho = 1;
|
||||||
|
im.sel = 0;
|
||||||
|
CT_CHECK(t, keystroke(Kback, 0, &com));
|
||||||
|
checkstr(t, "backspace commit", "", &com);
|
||||||
|
checkstr(t, "raw preedit after backspace", "n", &im.pre);
|
||||||
|
shown = shownpre(&im);
|
||||||
|
checkstr(t, "shown preedit after backspace", "ん", &shown);
|
||||||
|
CT_EQ_INT(t, 0, im.nkouho);
|
||||||
|
CT_EQ_INT(t, -1, im.sel);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
engine_selects_visible_candidate(struct ct *t)
|
||||||
|
{
|
||||||
|
static const struct { int n, sel; Rune key; char *want; } cases[] = {
|
||||||
|
{ Maxdisp, -1, '9', "c9" },
|
||||||
|
{ Maxdisp+3, Maxdisp+1, '1', "c3" },
|
||||||
|
};
|
||||||
|
char name[8];
|
||||||
|
Str com;
|
||||||
|
int i, j;
|
||||||
|
|
||||||
|
for(i = 0; i < nelem(cases); i++){
|
||||||
|
init();
|
||||||
|
for(j = 0; j < cases[i].n; j++){
|
||||||
|
snprint(name, sizeof name, "c%d", j+1);
|
||||||
|
im.kouho[j] = mkstr(name);
|
||||||
|
}
|
||||||
|
im.nkouho = cases[i].n;
|
||||||
|
im.sel = cases[i].sel;
|
||||||
|
sclear(&com);
|
||||||
|
CT_CHECK(t, keystroke(cases[i].key, 0, &com));
|
||||||
|
checkstr(t, cases[i].want, cases[i].want, &com);
|
||||||
|
CT_EQ_INT(t, 0, im.nkouho);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
engine_commit_contract(struct ct *t)
|
||||||
|
{
|
||||||
|
Str com;
|
||||||
|
|
||||||
|
init();
|
||||||
|
im.l = getlang(LangJP);
|
||||||
|
im.pre = mkstr("ka");
|
||||||
|
im.nkouho = 1;
|
||||||
|
im.sel = -1;
|
||||||
|
sclear(&com);
|
||||||
|
CT_CHECK(t, keystroke(Kret, 0, &com));
|
||||||
|
checkstr(t, "return commit", "か", &com);
|
||||||
|
CT_EQ_INT(t, 0, im.pre.n);
|
||||||
|
CT_EQ_INT(t, 0, im.raw.n);
|
||||||
|
CT_EQ_INT(t, 0, im.nkouho);
|
||||||
|
CT_EQ_INT(t, -1, im.sel);
|
||||||
|
|
||||||
|
init();
|
||||||
|
im.l = getlang(LangJP);
|
||||||
|
im.pre = mkstr("ka");
|
||||||
|
im.kouho[0] = mkstr("c1");
|
||||||
|
im.kouho[1] = mkstr("c2");
|
||||||
|
im.nkouho = 2;
|
||||||
|
im.sel = 1;
|
||||||
|
sclear(&com);
|
||||||
|
CT_CHECK(t, keystroke(Kret, 0, &com));
|
||||||
|
checkstr(t, "selected candidate", "c2", &com);
|
||||||
|
CT_EQ_INT(t, 0, im.pre.n);
|
||||||
|
CT_EQ_INT(t, 0, im.nkouho);
|
||||||
|
CT_EQ_INT(t, -1, im.sel);
|
||||||
|
|
||||||
|
init();
|
||||||
|
im.l = getlang(LangJP);
|
||||||
|
im.pre = mkstr("ka");
|
||||||
|
im.nkouho = 1;
|
||||||
|
im.sel = -1;
|
||||||
|
sclear(&com);
|
||||||
|
CT_CHECK(t, !keystroke('q', 0, &com));
|
||||||
|
checkstr(t, "uneaten-key commit", "か", &com);
|
||||||
|
CT_EQ_INT(t, 0, im.pre.n);
|
||||||
|
CT_EQ_INT(t, 0, im.raw.n);
|
||||||
|
CT_EQ_INT(t, 0, im.nkouho);
|
||||||
|
CT_EQ_INT(t, -1, im.sel);
|
||||||
|
|
||||||
|
init();
|
||||||
|
im.l = getlang(LangJP);
|
||||||
|
im.pre = mkstr("ka");
|
||||||
|
sclear(&com);
|
||||||
|
CT_CHECK(t, keystroke(0xf008, 0, &com));
|
||||||
|
CT_EQ_INT(t, 2, com.n);
|
||||||
|
CT_EQ_UINT(t, 0x304b, com.r[0]);
|
||||||
|
CT_EQ_UINT(t, 0xf008, com.r[1]);
|
||||||
|
CT_EQ_INT(t, 0, im.pre.n);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
engine_telex_history_bound(struct ct *t)
|
||||||
|
{
|
||||||
|
Str com;
|
||||||
|
Rune tone;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
init();
|
||||||
|
im.l = &testvi;
|
||||||
|
sclear(&com);
|
||||||
|
CT_CHECK(t, keystroke('a', 0, &com));
|
||||||
|
for(i = 0; i < Maxrunes; i++){
|
||||||
|
tone = i % 2 == 0 ? 's' : 'f';
|
||||||
|
if(!CT_CHECK(t, keystroke(tone, 0, &com)))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
checkstr(t, "bounded Telex commit", "à", &com);
|
||||||
|
CT_EQ_INT(t, 0, im.pre.n);
|
||||||
|
CT_EQ_INT(t, 0, im.raw.n);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
engine_language_switch_state(struct ct *t)
|
||||||
|
{
|
||||||
|
static const struct {
|
||||||
|
char *name;
|
||||||
|
Rune key;
|
||||||
|
int mod;
|
||||||
|
int lang;
|
||||||
|
char *pre;
|
||||||
|
char *raw;
|
||||||
|
char *commit;
|
||||||
|
int stale;
|
||||||
|
} steps[] = {
|
||||||
|
{ "select Vietnamese", 'v', Mctrl, LangVI, "", "", "", 0 },
|
||||||
|
{ "Vietnamese a", 'a', 0, LangVI, "a", "a", "", 0 },
|
||||||
|
{ "Vietnamese tone", 's', 0, LangVI, "as", "as", "", 0 },
|
||||||
|
{ "switch to Korean", 's', Mctrl, LangKO, "", "", "", 1 },
|
||||||
|
{ "Korean initial", 'r', 0, LangKO, "ㄱ", "", "", 0 },
|
||||||
|
{ "Korean syllable", 'k', 0, LangKO, "가", "", "", 0 },
|
||||||
|
{ "switch to Vietnamese", 'v', Mctrl, LangVI, "", "", "", 0 },
|
||||||
|
{ "Vietnamese a again", 'a', 0, LangVI, "a", "a", "", 0 },
|
||||||
|
{ "Vietnamese tone again", 's', 0, LangVI, "as", "as", "", 0 },
|
||||||
|
{ "commit Vietnamese", Kret, 0, LangVI, "", "", "á", 0 },
|
||||||
|
};
|
||||||
|
char where[80];
|
||||||
|
Lang *vi;
|
||||||
|
Trie *saved;
|
||||||
|
Str com;
|
||||||
|
int eaten, i;
|
||||||
|
|
||||||
|
vi = getlang(LangVI);
|
||||||
|
if(!CT_CHECK(t, vi != nil))
|
||||||
|
return;
|
||||||
|
saved = vi->map;
|
||||||
|
vi->map = testvi.map;
|
||||||
|
init();
|
||||||
|
for(i = 0; i < nelem(steps); i++){
|
||||||
|
if(steps[i].stale){
|
||||||
|
im.kouho[0] = mkstr("stale");
|
||||||
|
im.nkouho = 1;
|
||||||
|
im.sel = 0;
|
||||||
|
}
|
||||||
|
sclear(&com);
|
||||||
|
eaten = keystroke(steps[i].key, steps[i].mod, &com);
|
||||||
|
if(eaten != 1){
|
||||||
|
CT_ERRORF(t, "%s: want eaten 1, got %d",
|
||||||
|
steps[i].name, eaten);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(im.l == nil || im.l->lang != steps[i].lang){
|
||||||
|
CT_ERRORF(t, "%s: want language %d, got %d",
|
||||||
|
steps[i].name, steps[i].lang,
|
||||||
|
im.l == nil ? -1 : im.l->lang);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
snprint(where, sizeof where, "%s preedit", steps[i].name);
|
||||||
|
if(!checkstr(t, where, steps[i].pre, &im.pre))
|
||||||
|
break;
|
||||||
|
snprint(where, sizeof where, "%s raw", steps[i].name);
|
||||||
|
if(!checkstr(t, where, steps[i].raw, &im.raw))
|
||||||
|
break;
|
||||||
|
snprint(where, sizeof where, "%s commit", steps[i].name);
|
||||||
|
if(!checkstr(t, where, steps[i].commit, &com))
|
||||||
|
break;
|
||||||
|
if(im.nkouho != 0 || im.sel != -1){
|
||||||
|
CT_ERRORF(t, "%s: want candidates 0 and selection -1, got %d and %d",
|
||||||
|
steps[i].name, im.nkouho, im.sel);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vi->map = saved;
|
||||||
|
}
|
||||||
78
tests/hash_test.c
Normal file
78
tests/hash_test.c
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
#include "test.h"
|
||||||
|
|
||||||
|
void
|
||||||
|
hmap_set_replace_and_grow(struct ct *t)
|
||||||
|
{
|
||||||
|
char keybuf[16], valbuf[16], source[] = "copied";
|
||||||
|
Hmap *h;
|
||||||
|
Hnode *n;
|
||||||
|
Str key;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
h = hmapalloc(1);
|
||||||
|
for(i = 0; i < 4; i++){
|
||||||
|
snprint(keybuf, sizeof keybuf, "key%d", i);
|
||||||
|
snprint(valbuf, sizeof valbuf, "value%d", i);
|
||||||
|
key = mkstr(keybuf);
|
||||||
|
hmapset(&h, &key, valbuf, strlen(valbuf));
|
||||||
|
}
|
||||||
|
for(i = 0; i < 4; i++){
|
||||||
|
snprint(keybuf, sizeof keybuf, "key%d", i);
|
||||||
|
snprint(valbuf, sizeof valbuf, "value%d", i);
|
||||||
|
key = mkstr(keybuf);
|
||||||
|
n = hmapget(h, &key);
|
||||||
|
if(n == nil || strcmp(n->val, valbuf) != 0)
|
||||||
|
CT_ERRORF(t, "%s: collision chain lost value", keybuf);
|
||||||
|
}
|
||||||
|
key = mkstr("key1");
|
||||||
|
hmapset(&h, &key, source, strlen(source));
|
||||||
|
source[0] = 'X';
|
||||||
|
n = hmapget(h, &key);
|
||||||
|
if(!CT_CHECK(t, n != nil))
|
||||||
|
goto cleanup;
|
||||||
|
CT_EQ_STR(t, "copied", n->val);
|
||||||
|
key = mkstr("key2");
|
||||||
|
n = hmapget(h, &key);
|
||||||
|
if(!CT_CHECK(t, n != nil))
|
||||||
|
goto cleanup;
|
||||||
|
CT_EQ_STR(t, "value2", n->val);
|
||||||
|
key = mkstr("key1");
|
||||||
|
hmapset(&h, &key, nil, 0);
|
||||||
|
n = hmapget(h, &key);
|
||||||
|
if(!CT_CHECK(t, n != nil))
|
||||||
|
goto cleanup;
|
||||||
|
CT_EQ_INT(t, 0, n->vlen);
|
||||||
|
CT_EQ_PTR(t, nil, n->val);
|
||||||
|
key = mkstr("missing");
|
||||||
|
CT_EQ_PTR(t, nil, hmapget(h, &key));
|
||||||
|
cleanup:
|
||||||
|
hmapfree(h);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
hmap_long_utf8_keys(struct ct *t)
|
||||||
|
{
|
||||||
|
Hmap *h;
|
||||||
|
Hnode *n;
|
||||||
|
Str a, b;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for(i = 0; i < Maxrunes-1; i++)
|
||||||
|
a.r[i] = b.r[i] = 0x1f600;
|
||||||
|
a.r[Maxrunes-1] = 0x1f601;
|
||||||
|
b.r[Maxrunes-1] = 0x1f602;
|
||||||
|
a.n = b.n = Maxrunes;
|
||||||
|
h = hmapalloc(1);
|
||||||
|
hmapset(&h, &a, "first", 5);
|
||||||
|
hmapset(&h, &b, "second", 6);
|
||||||
|
n = hmapget(h, &a);
|
||||||
|
if(!CT_CHECK(t, n != nil))
|
||||||
|
goto cleanup;
|
||||||
|
CT_EQ_STR(t, "first", n != nil ? n->val : nil);
|
||||||
|
n = hmapget(h, &b);
|
||||||
|
if(!CT_CHECK(t, n != nil))
|
||||||
|
goto cleanup;
|
||||||
|
CT_EQ_STR(t, "second", n != nil ? n->val : nil);
|
||||||
|
cleanup:
|
||||||
|
hmapfree(h);
|
||||||
|
}
|
||||||
60
tests/ko_test.c
Normal file
60
tests/ko_test.c
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
#include "test.h"
|
||||||
|
|
||||||
|
void
|
||||||
|
korean_sequences(struct ct *t)
|
||||||
|
{
|
||||||
|
static const struct { char *keys, *want; } cases[] = {
|
||||||
|
{ "r", "ㄱ" },
|
||||||
|
{ "rk", "가" },
|
||||||
|
{ "rkr", "각" },
|
||||||
|
{ "rkrk", "가가" },
|
||||||
|
{ "rkrtk", "각사" },
|
||||||
|
{ "rhk", "과" },
|
||||||
|
{ "rr", "ㄱㄱ" },
|
||||||
|
{ "Rk", "까" },
|
||||||
|
{ "rk1", "가1" },
|
||||||
|
};
|
||||||
|
Emit e;
|
||||||
|
Im state;
|
||||||
|
Str out;
|
||||||
|
Rune key;
|
||||||
|
char *p;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for(i = 0; i < nelem(cases); i++){
|
||||||
|
memset(&state, 0, sizeof state);
|
||||||
|
state.l = getlang(LangKO);
|
||||||
|
sclear(&out);
|
||||||
|
for(p = cases[i].keys; *p != '\0'; p++){
|
||||||
|
key = (uchar)*p;
|
||||||
|
e = transko(&state, key);
|
||||||
|
sappend(&out, &e.s);
|
||||||
|
state.pre = e.next;
|
||||||
|
if(!e.eat)
|
||||||
|
sputr(&out, key);
|
||||||
|
}
|
||||||
|
sappend(&out, &state.pre);
|
||||||
|
checkstr(t, cases[i].keys, cases[i].want, &out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
korean_backspace(struct ct *t)
|
||||||
|
{
|
||||||
|
static const struct { char *before, *after; } cases[] = {
|
||||||
|
{ "ㄱ", "" },
|
||||||
|
{ "가", "ㄱ" },
|
||||||
|
{ "과", "고" },
|
||||||
|
{ "각", "가" },
|
||||||
|
{ "갃", "각" },
|
||||||
|
};
|
||||||
|
Im state;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
memset(&state, 0, sizeof state);
|
||||||
|
for(i = 0; i < nelem(cases); i++){
|
||||||
|
state.pre = mkstr(cases[i].before);
|
||||||
|
backko(&state);
|
||||||
|
checkstr(t, cases[i].before, cases[i].after, &state.pre);
|
||||||
|
}
|
||||||
|
}
|
||||||
84
tests/str_test.c
Normal file
84
tests/str_test.c
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
#include "test.h"
|
||||||
|
|
||||||
|
void
|
||||||
|
str_init_utf8(struct ct *t)
|
||||||
|
{
|
||||||
|
char full[Maxrunes+2];
|
||||||
|
char incomplete[] = { (char)0xea, (char)0xb0 };
|
||||||
|
Str s;
|
||||||
|
|
||||||
|
s = mkstr("A한😀");
|
||||||
|
CT_EQ_INT(t, 3, s.n);
|
||||||
|
checkstr(t, "round trip", "A한😀", &s);
|
||||||
|
sinit(&s, incomplete, sizeof incomplete);
|
||||||
|
CT_EQ_INT(t, 0, s.n);
|
||||||
|
memset(full, 'a', sizeof full);
|
||||||
|
full[sizeof full-1] = '\0';
|
||||||
|
sinit(&s, full, strlen(full));
|
||||||
|
CT_EQ_INT(t, Maxrunes, s.n);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
str_edit_and_alias(struct ct *t)
|
||||||
|
{
|
||||||
|
Str s;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
s = mkstr("ab");
|
||||||
|
sappend(&s, &s);
|
||||||
|
checkstr(t, "self append", "abab", &s);
|
||||||
|
for(i = 0; i < 40; i++)
|
||||||
|
s.r[i] = 'a' + i % 26;
|
||||||
|
s.n = 40;
|
||||||
|
sappend(&s, &s);
|
||||||
|
CT_EQ_INT(t, Maxrunes, s.n);
|
||||||
|
for(i = 0; i < 24; i++)
|
||||||
|
if(s.r[40+i] != s.r[i])
|
||||||
|
CT_ERRORF(t, "capped self append differs at rune %d", i);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
str_utf8_capacity(struct ct *t)
|
||||||
|
{
|
||||||
|
static const struct {
|
||||||
|
int size;
|
||||||
|
int n;
|
||||||
|
char *want;
|
||||||
|
} cases[] = {
|
||||||
|
{ 0, 0, nil },
|
||||||
|
{ 1, 0, "" },
|
||||||
|
{ 2, 1, "a" },
|
||||||
|
{ 3, 1, "a" },
|
||||||
|
{ 4, 1, "a" },
|
||||||
|
{ 5, 4, "a한" },
|
||||||
|
{ 6, 5, "a한b" },
|
||||||
|
};
|
||||||
|
char buf[16];
|
||||||
|
Str s;
|
||||||
|
int i, n;
|
||||||
|
|
||||||
|
s = mkstr("a한b");
|
||||||
|
for(i = 0; i < nelem(cases); i++){
|
||||||
|
memset(buf, 'Z', sizeof buf);
|
||||||
|
n = stoutf(&s, buf, cases[i].size);
|
||||||
|
if(n != cases[i].n)
|
||||||
|
CT_ERRORF(t, "size %d: want length %d, got %d",
|
||||||
|
cases[i].size, cases[i].n, n);
|
||||||
|
if(cases[i].size == 0){
|
||||||
|
CT_EQ_INT(t, 'Z', buf[0]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(strcmp(cases[i].want, buf) != 0)
|
||||||
|
CT_ERRORF(t, "size %d: want \"%s\", got \"%s\"",
|
||||||
|
cases[i].size, cases[i].want, buf);
|
||||||
|
CT_EQ_INT(t, 'Z', buf[cases[i].size]);
|
||||||
|
}
|
||||||
|
|
||||||
|
s = mkstr("😀");
|
||||||
|
memset(buf, 'Z', sizeof buf);
|
||||||
|
CT_EQ_INT(t, 0, stoutf(&s, buf, 4));
|
||||||
|
CT_EQ_STR(t, "", buf);
|
||||||
|
CT_EQ_INT(t, 4, stoutf(&s, buf, 5));
|
||||||
|
CT_EQ_STR(t, "😀", buf);
|
||||||
|
CT_EQ_INT(t, 'Z', buf[5]);
|
||||||
|
}
|
||||||
39
tests/test.h
Normal file
39
tests/test.h
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
#ifndef STRANS_TEST_H
|
||||||
|
#define STRANS_TEST_H
|
||||||
|
|
||||||
|
#include "../cutest/cutest.h"
|
||||||
|
|
||||||
|
#ifndef STRANS_TEST_ENGINE
|
||||||
|
#include "../dat.h"
|
||||||
|
#include "../fn.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern Lang testvi;
|
||||||
|
|
||||||
|
Str mkstr(char*);
|
||||||
|
int checkstr(struct ct*, char*, char*, Str*);
|
||||||
|
Str shownpre(Im*);
|
||||||
|
|
||||||
|
void str_init_utf8(struct ct*);
|
||||||
|
void str_edit_and_alias(struct ct*);
|
||||||
|
void str_utf8_capacity(struct ct*);
|
||||||
|
void hmap_set_replace_and_grow(struct ct*);
|
||||||
|
void hmap_long_utf8_keys(struct ct*);
|
||||||
|
void trie_exact_prefix_and_duplicate(struct ct*);
|
||||||
|
void production_maps_load(struct ct*);
|
||||||
|
void transmap_states(struct ct*);
|
||||||
|
void korean_sequences(struct ct*);
|
||||||
|
void korean_backspace(struct ct*);
|
||||||
|
void vietnamese_transitions(struct ct*);
|
||||||
|
void vietnamese_backspace(struct ct*);
|
||||||
|
void vietnamese_state_lifetime(struct ct*);
|
||||||
|
void engine_clears_candidates(struct ct*);
|
||||||
|
void engine_backspace_clears_candidates(struct ct*);
|
||||||
|
void engine_selects_visible_candidate(struct ct*);
|
||||||
|
void engine_commit_contract(struct ct*);
|
||||||
|
void engine_language_switch_state(struct ct*);
|
||||||
|
void engine_telex_history_bound(struct ct*);
|
||||||
|
void dictionary_candidates(struct ct*);
|
||||||
|
void dictionary_misses_clear_result(struct ct*);
|
||||||
|
|
||||||
|
#endif
|
||||||
32
tests/test_util.c
Normal file
32
tests/test_util.c
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
#include "test.h"
|
||||||
|
|
||||||
|
Str
|
||||||
|
mkstr(char *s)
|
||||||
|
{
|
||||||
|
Str r;
|
||||||
|
|
||||||
|
sinit(&r, s, strlen(s));
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
checkstr(struct ct *t, char *where, char *want, Str *got)
|
||||||
|
{
|
||||||
|
char buf[Maxutf];
|
||||||
|
|
||||||
|
stoutf(got, buf, sizeof buf);
|
||||||
|
if(strcmp(want, buf) == 0)
|
||||||
|
return 1;
|
||||||
|
return CT_ERRORF(t, "%s: want \"%s\", got \"%s\"",
|
||||||
|
where, want, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
Str
|
||||||
|
shownpre(Im *state)
|
||||||
|
{
|
||||||
|
Str shown;
|
||||||
|
|
||||||
|
if(state->l->map != nil && mapget(state->l->map, &state->pre, &shown))
|
||||||
|
return shown;
|
||||||
|
return state->pre;
|
||||||
|
}
|
||||||
93
tests/trie_test.c
Normal file
93
tests/trie_test.c
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
#include "test.h"
|
||||||
|
|
||||||
|
void
|
||||||
|
trie_exact_prefix_and_duplicate(struct ct *t)
|
||||||
|
{
|
||||||
|
char *v, *sentinel;
|
||||||
|
Trie *trie;
|
||||||
|
int n;
|
||||||
|
|
||||||
|
trie = trieopen("data/trie.map");
|
||||||
|
v = trieget(trie, "a", 1, &n);
|
||||||
|
if(!CT_CHECK(t, v != nil))
|
||||||
|
goto cleanup;
|
||||||
|
CT_EQ_INT(t, 5, n);
|
||||||
|
CT_EQ_MEM(t, "alpha", v, n);
|
||||||
|
sentinel = "unchanged";
|
||||||
|
v = sentinel;
|
||||||
|
n = 77;
|
||||||
|
CT_CHECK(t, trielookup(trie, "dupli", 5, &v, &n));
|
||||||
|
CT_EQ_PTR(t, sentinel, v);
|
||||||
|
CT_EQ_INT(t, 77, n);
|
||||||
|
v = trieget(trie, "duplicate", 9, &n);
|
||||||
|
if(!CT_CHECK(t, v != nil))
|
||||||
|
goto cleanup;
|
||||||
|
CT_EQ_INT(t, 6, n);
|
||||||
|
CT_EQ_MEM(t, "second", v, n);
|
||||||
|
v = trieget(trie, "tabs", 4, &n);
|
||||||
|
if(!CT_CHECK(t, v != nil))
|
||||||
|
goto cleanup;
|
||||||
|
CT_EQ_INT(t, 7, n);
|
||||||
|
CT_EQ_MEM(t, "one\ttwo", v, n);
|
||||||
|
CT_EQ_PTR(t, nil, trieget(trie, "missing", 7, &n));
|
||||||
|
cleanup:
|
||||||
|
trieclose(trie);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
production_maps_load(struct ct *t)
|
||||||
|
{
|
||||||
|
char path[256];
|
||||||
|
Trie *map;
|
||||||
|
int i, loaded, registered;
|
||||||
|
|
||||||
|
loaded = registered = 0;
|
||||||
|
for(i = 0; i < nlang; i++){
|
||||||
|
if(langs[i].mapname == nil)
|
||||||
|
continue;
|
||||||
|
registered++;
|
||||||
|
snprint(path, sizeof path, "../map/%s.map", langs[i].mapname);
|
||||||
|
map = trieopen(path);
|
||||||
|
if(!CT_CHECK(t, map != nil))
|
||||||
|
continue;
|
||||||
|
trieclose(map);
|
||||||
|
loaded++;
|
||||||
|
}
|
||||||
|
CT_CHECK(t, registered > 0);
|
||||||
|
CT_EQ_INT(t, registered, loaded);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
transmap_states(struct ct *t)
|
||||||
|
{
|
||||||
|
static const struct {
|
||||||
|
char *pre;
|
||||||
|
Rune key;
|
||||||
|
int eat;
|
||||||
|
char *emit;
|
||||||
|
char *next;
|
||||||
|
char *mapped;
|
||||||
|
} cases[] = {
|
||||||
|
{ "", 'k', 1, "", "k", "" },
|
||||||
|
{ "k", 'a', 1, "", "ka", "か" },
|
||||||
|
{ "ka", 's', 1, "か", "s", "" },
|
||||||
|
{ "ka", 'q', 0, "か", "", "" },
|
||||||
|
{ "k", 'q', 0, "k", "", "" },
|
||||||
|
};
|
||||||
|
Emit e;
|
||||||
|
Im state;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
memset(&state, 0, sizeof state);
|
||||||
|
state.l = getlang(LangJP);
|
||||||
|
for(i = 0; i < nelem(cases); i++){
|
||||||
|
state.pre = mkstr(cases[i].pre);
|
||||||
|
e = transmap(&state, cases[i].key);
|
||||||
|
if(e.eat != cases[i].eat)
|
||||||
|
CT_ERRORF(t, "case %d: want eat %d, got %d",
|
||||||
|
i, cases[i].eat, e.eat);
|
||||||
|
checkstr(t, "emit", cases[i].emit, &e.s);
|
||||||
|
checkstr(t, "next", cases[i].next, &e.next);
|
||||||
|
checkstr(t, "mapped", cases[i].mapped, &e.dict);
|
||||||
|
}
|
||||||
|
}
|
||||||
109
tests/unit_test.c
Normal file
109
tests/unit_test.c
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
#define CT_IMPLEMENTATION
|
||||||
|
#include "test.h"
|
||||||
|
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
Channel *drawc;
|
||||||
|
Channel *keyc;
|
||||||
|
Channel *dictreqc;
|
||||||
|
Channel *dictresc;
|
||||||
|
Lang testvi;
|
||||||
|
|
||||||
|
void
|
||||||
|
die(char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
fprint(2, "unit_test: ");
|
||||||
|
va_start(ap, fmt);
|
||||||
|
vfprint(2, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
fprint(2, "\n");
|
||||||
|
threadexitsall("die");
|
||||||
|
}
|
||||||
|
|
||||||
|
void*
|
||||||
|
emalloc(ulong n)
|
||||||
|
{
|
||||||
|
void *p;
|
||||||
|
|
||||||
|
p = malloc(n);
|
||||||
|
if(p == nil)
|
||||||
|
die("out of memory");
|
||||||
|
memset(p, 0, n);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
void*
|
||||||
|
erealloc(void *p, ulong n)
|
||||||
|
{
|
||||||
|
p = realloc(p, n);
|
||||||
|
if(p == nil)
|
||||||
|
die("out of memory");
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
testmapinit(void)
|
||||||
|
{
|
||||||
|
Lang *jp;
|
||||||
|
|
||||||
|
jp = getlang(LangJP);
|
||||||
|
if(jp == nil)
|
||||||
|
die("test language is not registered");
|
||||||
|
jp->map = trieopen("data/hira.map");
|
||||||
|
memset(&testvi, 0, sizeof testvi);
|
||||||
|
testvi.lang = LangVI;
|
||||||
|
testvi.mapname = "telex";
|
||||||
|
testvi.trans = transvi;
|
||||||
|
testvi.back = backvi;
|
||||||
|
testvi.map = trieopen("../map/telex.map");
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct ct_test tests[] = {
|
||||||
|
{ "str/init-utf8", str_init_utf8 },
|
||||||
|
{ "str/edit-and-alias", str_edit_and_alias },
|
||||||
|
{ "str/utf8-capacity", str_utf8_capacity },
|
||||||
|
{ "hmap/set-replace-grow", hmap_set_replace_and_grow },
|
||||||
|
{ "hmap/long-utf8-keys", hmap_long_utf8_keys },
|
||||||
|
{ "trie/exact-prefix-duplicate", trie_exact_prefix_and_duplicate },
|
||||||
|
{ "map/production-lifecycle", production_maps_load },
|
||||||
|
{ "transmap/states", transmap_states },
|
||||||
|
{ "hangul/sequences", korean_sequences },
|
||||||
|
{ "hangul/backspace", korean_backspace },
|
||||||
|
{ "telex/transitions", vietnamese_transitions },
|
||||||
|
{ "telex/backspace", vietnamese_backspace },
|
||||||
|
{ "telex/state-lifetime", vietnamese_state_lifetime },
|
||||||
|
{ "engine/clears-candidates", engine_clears_candidates },
|
||||||
|
{ "engine/backspace-clears-candidates", engine_backspace_clears_candidates },
|
||||||
|
{ "engine/selects-visible-candidate", engine_selects_visible_candidate },
|
||||||
|
{ "engine/commit-contract", engine_commit_contract },
|
||||||
|
{ "engine/language-switch-state", engine_language_switch_state },
|
||||||
|
{ "engine/telex-history-bound", engine_telex_history_bound },
|
||||||
|
{ "dict/candidates", dictionary_candidates },
|
||||||
|
{ "dict/misses-clear-result", dictionary_misses_clear_result },
|
||||||
|
};
|
||||||
|
|
||||||
|
void
|
||||||
|
threadmain(int argc, char **argv)
|
||||||
|
{
|
||||||
|
int i, status;
|
||||||
|
|
||||||
|
drawc = chancreate(sizeof(Drawcmd), 4);
|
||||||
|
keyc = chancreate(sizeof(Keyreq), 0);
|
||||||
|
dictreqc = chancreate(sizeof(Dictreq), 64);
|
||||||
|
dictresc = chancreate(sizeof(Dictres), 0);
|
||||||
|
testmapinit();
|
||||||
|
status = CT_RUN_ARGS(tests, argc, argv);
|
||||||
|
for(i = 0; i < nlang; i++){
|
||||||
|
trieclose(langs[i].map);
|
||||||
|
langs[i].map = nil;
|
||||||
|
}
|
||||||
|
trieclose(testvi.map);
|
||||||
|
testvi.map = nil;
|
||||||
|
chanfree(drawc);
|
||||||
|
chanfree(keyc);
|
||||||
|
chanfree(dictreqc);
|
||||||
|
chanfree(dictresc);
|
||||||
|
threadexitsall(status == 0 ? nil : "tests failed");
|
||||||
|
}
|
||||||
84
tests/vi_test.c
Normal file
84
tests/vi_test.c
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
#include "test.h"
|
||||||
|
|
||||||
|
static void
|
||||||
|
typevi(struct ct *t, char *keys, char *want)
|
||||||
|
{
|
||||||
|
Emit e;
|
||||||
|
Im state;
|
||||||
|
Str out, shown;
|
||||||
|
char *p;
|
||||||
|
|
||||||
|
memset(&state, 0, sizeof state);
|
||||||
|
state.l = &testvi;
|
||||||
|
sclear(&out);
|
||||||
|
for(p = keys; *p != '\0'; p++){
|
||||||
|
e = transvi(&state, (uchar)*p);
|
||||||
|
sappend(&out, &e.s);
|
||||||
|
state.pre = e.next;
|
||||||
|
if(!e.eat)
|
||||||
|
sputr(&out, (uchar)*p);
|
||||||
|
}
|
||||||
|
shown = shownpre(&state);
|
||||||
|
sappend(&out, &shown);
|
||||||
|
checkstr(t, keys, want, &out);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
vietnamese_transitions(struct ct *t)
|
||||||
|
{
|
||||||
|
static const struct { char *keys, *want; } cases[] = {
|
||||||
|
{ "as", "á" },
|
||||||
|
{ "As", "Á" },
|
||||||
|
{ "ans", "án" },
|
||||||
|
{ "Ans", "Án" },
|
||||||
|
{ "aws", "ắ" },
|
||||||
|
{ "asw", "ắ" },
|
||||||
|
{ "aww", "aw" },
|
||||||
|
{ "ddd", "dd" },
|
||||||
|
{ "ddas", "đá" },
|
||||||
|
{ "dds", "đs" },
|
||||||
|
{ "quas", "quá" },
|
||||||
|
{ "quaf", "quà" },
|
||||||
|
{ "quar", "quả" },
|
||||||
|
{ "quax", "quã" },
|
||||||
|
{ "quaj", "quạ" },
|
||||||
|
{ "Quas", "Quá" },
|
||||||
|
{ "gias", "giá" },
|
||||||
|
{ "Gias", "Giá" },
|
||||||
|
{ "gif", "gì" },
|
||||||
|
{ "\\s", "s" },
|
||||||
|
{ "a\\s", "as" },
|
||||||
|
};
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for(i = 0; i < nelem(cases); i++)
|
||||||
|
typevi(t, cases[i].keys, cases[i].want);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
vietnamese_backspace(struct ct *t)
|
||||||
|
{
|
||||||
|
static const struct {
|
||||||
|
char *pre, *raw, *rawafter, *after;
|
||||||
|
} cases[] = {
|
||||||
|
{ "as", "as", "a", "a" },
|
||||||
|
{ "quá", "quas", "qua", "qua" },
|
||||||
|
{ "à", "asf", "as", "á" },
|
||||||
|
{ "a", "asz", "as", "á" },
|
||||||
|
{ "quà", "quasf", "quas", "quá" },
|
||||||
|
};
|
||||||
|
Im state;
|
||||||
|
Str shown;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for(i = 0; i < nelem(cases); i++){
|
||||||
|
memset(&state, 0, sizeof state);
|
||||||
|
state.l = &testvi;
|
||||||
|
state.pre = mkstr(cases[i].pre);
|
||||||
|
state.raw = mkstr(cases[i].raw);
|
||||||
|
backvi(&state);
|
||||||
|
checkstr(t, cases[i].raw, cases[i].rawafter, &state.raw);
|
||||||
|
shown = shownpre(&state);
|
||||||
|
checkstr(t, cases[i].raw, cases[i].after, &shown);
|
||||||
|
}
|
||||||
|
}
|
||||||
22
trie.c
22
trie.c
@@ -53,7 +53,10 @@ insert(Trie *t, char *key, int klen, char *val, int vlen)
|
|||||||
ci = add(t, ni, key[i]);
|
ci = add(t, ni, key[i]);
|
||||||
ni = ci;
|
ni = ci;
|
||||||
}
|
}
|
||||||
t->nodes[ni].val = val;
|
free(t->nodes[ni].val);
|
||||||
|
t->nodes[ni].val = emalloc(vlen + 1);
|
||||||
|
memmove(t->nodes[ni].val, val, vlen);
|
||||||
|
t->nodes[ni].val[vlen] = '\0';
|
||||||
t->nodes[ni].vlen = vlen;
|
t->nodes[ni].vlen = vlen;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,6 +77,9 @@ trieopen(char *path)
|
|||||||
t->n = 0;
|
t->n = 0;
|
||||||
t->root = newnode(t);
|
t->root = newnode(t);
|
||||||
while((line = Brdstr(b, '\n', 1)) != nil){
|
while((line = Brdstr(b, '\n', 1)) != nil){
|
||||||
|
vlen = strlen(line);
|
||||||
|
if(vlen > 0 && line[vlen-1] == '\r')
|
||||||
|
line[--vlen] = '\0';
|
||||||
if(line[0] == '\0'){
|
if(line[0] == '\0'){
|
||||||
free(line);
|
free(line);
|
||||||
continue;
|
continue;
|
||||||
@@ -87,11 +93,25 @@ trieopen(char *path)
|
|||||||
val = tab + 1;
|
val = tab + 1;
|
||||||
vlen = strlen(val);
|
vlen = strlen(val);
|
||||||
insert(t, key, klen, val, vlen);
|
insert(t, key, klen, val, vlen);
|
||||||
|
free(line);
|
||||||
}
|
}
|
||||||
Bterm(b);
|
Bterm(b);
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
trieclose(Trie *t)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if(t == nil)
|
||||||
|
return;
|
||||||
|
for(i = 0; i < t->n; i++)
|
||||||
|
free(t->nodes[i].val);
|
||||||
|
free(t->nodes);
|
||||||
|
free(t);
|
||||||
|
}
|
||||||
|
|
||||||
char*
|
char*
|
||||||
trieget(Trie *t, char *key, int klen, int *vlen)
|
trieget(Trie *t, char *key, int klen, int *vlen)
|
||||||
{
|
{
|
||||||
|
|||||||
27
vi.c
27
vi.c
@@ -117,13 +117,16 @@ onsetglide(Str *m)
|
|||||||
Emit
|
Emit
|
||||||
transvi(Im *im, Rune c)
|
transvi(Im *im, Rune c)
|
||||||
{
|
{
|
||||||
Emit e;
|
Emit e, mappedkey;
|
||||||
Str mapped, pre;
|
Str mapped, pre;
|
||||||
int i, tidx, vi, last, penult, glide;
|
int i, tidx, vi, last, penult, glide;
|
||||||
Rune v, b1, b2;
|
Rune v, b1, b2;
|
||||||
|
|
||||||
if(!istone(c) && c != 'z')
|
if(!istone(c) && c != 'z')
|
||||||
return transmap(im, c);
|
return transmap(im, c);
|
||||||
|
mappedkey = transmap(im, c);
|
||||||
|
if(mappedkey.eat)
|
||||||
|
return mappedkey;
|
||||||
|
|
||||||
memset(&e, 0, sizeof e);
|
memset(&e, 0, sizeof e);
|
||||||
if(im->pre.n == 0)
|
if(im->pre.n == 0)
|
||||||
@@ -182,3 +185,25 @@ transvi(Im *im, Rune c)
|
|||||||
e.next = mapped;
|
e.next = mapped;
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
backvi(Im *im)
|
||||||
|
{
|
||||||
|
Emit e;
|
||||||
|
Str raw;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if(im->raw.n == 0){
|
||||||
|
spopr(&im->pre);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
raw = im->raw;
|
||||||
|
spopr(&raw);
|
||||||
|
sclear(&im->pre);
|
||||||
|
for(i = 0; i < raw.n; i++){
|
||||||
|
e = transvi(im, raw.r[i]);
|
||||||
|
sclear(&im->pre);
|
||||||
|
sappend(&im->pre, &e.next);
|
||||||
|
}
|
||||||
|
im->raw = raw;
|
||||||
|
}
|
||||||
|
|||||||
97
wayland.c
97
wayland.c
@@ -25,55 +25,40 @@ static struct xkb_state *xkbst;
|
|||||||
static int active;
|
static int active;
|
||||||
static int pending;
|
static int pending;
|
||||||
static u32int imserial;
|
static u32int imserial;
|
||||||
|
static Channel *replyc;
|
||||||
|
|
||||||
static void
|
static void
|
||||||
sendkey(u32int ks, u32int mod, char *com, int csz, char *pre, int psz,
|
sendkey(u32int ks, u32int mod, Keyres *res)
|
||||||
int *eaten)
|
|
||||||
{
|
{
|
||||||
Keyreq kr;
|
Keyreq kr;
|
||||||
int p[2];
|
|
||||||
uchar hdr[2];
|
|
||||||
uchar pl;
|
|
||||||
int n;
|
|
||||||
|
|
||||||
*eaten = 0;
|
|
||||||
com[0] = '\0';
|
|
||||||
pre[0] = '\0';
|
|
||||||
if(pipe(p) < 0)
|
|
||||||
return;
|
|
||||||
kr.fd = p[1];
|
|
||||||
kr.ks = ks;
|
kr.ks = ks;
|
||||||
kr.mod = mod;
|
kr.mod = mod;
|
||||||
kr.want = 1;
|
kr.want = 1;
|
||||||
|
kr.reply = replyc;
|
||||||
chansend(keyc, &kr);
|
chansend(keyc, &kr);
|
||||||
if(read(p[0], hdr, 2) != 2)
|
chanrecv(replyc, res);
|
||||||
goto out;
|
|
||||||
*eaten = hdr[0];
|
|
||||||
n = hdr[1];
|
|
||||||
if(n > 0 && n < csz){
|
|
||||||
if(read(p[0], com, n) != n)
|
|
||||||
goto out;
|
|
||||||
com[n] = '\0';
|
|
||||||
}
|
|
||||||
if(read(p[0], &pl, 1) != 1)
|
|
||||||
goto out;
|
|
||||||
if(pl > 0 && pl < psz){
|
|
||||||
if(read(p[0], pre, pl) != pl)
|
|
||||||
goto out;
|
|
||||||
pre[pl] = '\0';
|
|
||||||
}
|
|
||||||
out:
|
|
||||||
close(p[0]);
|
|
||||||
close(p[1]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
sendreset(void)
|
sendreset(void)
|
||||||
{
|
{
|
||||||
char com[64], pre[256];
|
Keyres res;
|
||||||
int eaten;
|
|
||||||
|
|
||||||
sendkey(Kesc, 0, com, sizeof(com), pre, sizeof(pre), &eaten);
|
sendkey(Kesc, 0, &res);
|
||||||
|
}
|
||||||
|
|
||||||
|
static u32int
|
||||||
|
kget(xkb_keysym_t ks)
|
||||||
|
{
|
||||||
|
u32int c;
|
||||||
|
|
||||||
|
c = xkb_keysym_to_utf32(ks);
|
||||||
|
if(c >= ' ' && c != 0x7f)
|
||||||
|
return c;
|
||||||
|
if(ks >= 0xff00 && ks <= 0xffff)
|
||||||
|
return Kspec + (ks - 0xff00);
|
||||||
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
static u32int
|
static u32int
|
||||||
@@ -104,29 +89,32 @@ kpress(uint32_t time, uint32_t keycode, uint32_t state)
|
|||||||
{
|
{
|
||||||
xkb_keysym_t ks;
|
xkb_keysym_t ks;
|
||||||
u32int k, mod;
|
u32int k, mod;
|
||||||
char com[64], pre[256];
|
Keyres res;
|
||||||
int eaten, plen;
|
char commit[Maxutf], preedit[Maxutf];
|
||||||
|
int plen;
|
||||||
|
|
||||||
if(state != WL_KEYBOARD_KEY_STATE_PRESSED || xkbst == nil){
|
if(state != WL_KEYBOARD_KEY_STATE_PRESSED || xkbst == nil){
|
||||||
zwp_virtual_keyboard_v1_key(vk, time, keycode, state);
|
zwp_virtual_keyboard_v1_key(vk, time, keycode, state);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ks = xkb_state_key_get_one_sym(xkbst, keycode + 8);
|
ks = xkb_state_key_get_one_sym(xkbst, keycode + 8);
|
||||||
k = ks >= 0xff00 ? Kspec + (ks - 0xff00) : xkb_keysym_to_utf32(ks);
|
k = kget(ks);
|
||||||
if(k == 0){
|
if(k == 0){
|
||||||
zwp_virtual_keyboard_v1_key(vk, time, keycode, state);
|
zwp_virtual_keyboard_v1_key(vk, time, keycode, state);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mod = mget();
|
mod = mget();
|
||||||
sendkey(k, mod, com, sizeof(com), pre, sizeof(pre), &eaten);
|
sendkey(k, mod, &res);
|
||||||
if(com[0] != '\0'){
|
stoutf(&res.commit, commit, sizeof commit);
|
||||||
zwp_input_method_v2_commit_string(im, com);
|
stoutf(&res.preedit, preedit, sizeof preedit);
|
||||||
|
if(commit[0] != '\0'){
|
||||||
|
zwp_input_method_v2_commit_string(im, commit);
|
||||||
zwp_input_method_v2_commit(im, imserial);
|
zwp_input_method_v2_commit(im, imserial);
|
||||||
}
|
}
|
||||||
plen = strlen(pre);
|
plen = strlen(preedit);
|
||||||
zwp_input_method_v2_set_preedit_string(im, pre, plen, plen);
|
zwp_input_method_v2_set_preedit_string(im, preedit, plen, plen);
|
||||||
zwp_input_method_v2_commit(im, imserial);
|
zwp_input_method_v2_commit(im, imserial);
|
||||||
if(!eaten)
|
if(!res.eaten)
|
||||||
zwp_virtual_keyboard_v1_key(vk, time, keycode, state);
|
zwp_virtual_keyboard_v1_key(vk, time, keycode, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,6 +146,7 @@ kg_keymap(void *data, struct zwp_input_method_keyboard_grab_v2 *g,
|
|||||||
munmap(s, size);
|
munmap(s, size);
|
||||||
}
|
}
|
||||||
zwp_virtual_keyboard_v1_keymap(vk, format, fd, size);
|
zwp_virtual_keyboard_v1_keymap(vk, format, fd, size);
|
||||||
|
close(fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@@ -215,14 +204,6 @@ im_deactivate(void *data, struct zwp_input_method_v2 *m)
|
|||||||
(void)data;
|
(void)data;
|
||||||
(void)m;
|
(void)m;
|
||||||
pending = 0;
|
pending = 0;
|
||||||
if(active){
|
|
||||||
active = 0;
|
|
||||||
sendreset();
|
|
||||||
if(grab != nil){
|
|
||||||
zwp_input_method_keyboard_grab_v2_release(grab);
|
|
||||||
grab = nil;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@@ -266,6 +247,13 @@ im_done(void *data, struct zwp_input_method_v2 *m)
|
|||||||
if(grab != nil)
|
if(grab != nil)
|
||||||
zwp_input_method_keyboard_grab_v2_add_listener(grab,
|
zwp_input_method_keyboard_grab_v2_add_listener(grab,
|
||||||
&grab_listener, nil);
|
&grab_listener, nil);
|
||||||
|
}else if(!pending && active){
|
||||||
|
active = 0;
|
||||||
|
sendreset();
|
||||||
|
if(grab != nil){
|
||||||
|
zwp_input_method_keyboard_grab_v2_release(grab);
|
||||||
|
grab = nil;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -292,9 +280,9 @@ reg_global(void *data, struct wl_registry *r, uint32_t name,
|
|||||||
const char *iface, uint32_t version)
|
const char *iface, uint32_t version)
|
||||||
{
|
{
|
||||||
(void)data;
|
(void)data;
|
||||||
(void)version;
|
|
||||||
if(strcmp(iface, wl_seat_interface.name) == 0)
|
if(strcmp(iface, wl_seat_interface.name) == 0)
|
||||||
seat = wl_registry_bind(r, name, &wl_seat_interface, 5);
|
seat = wl_registry_bind(r, name, &wl_seat_interface,
|
||||||
|
version < 5 ? version : 5);
|
||||||
else if(strcmp(iface, zwp_input_method_manager_v2_interface.name) == 0)
|
else if(strcmp(iface, zwp_input_method_manager_v2_interface.name) == 0)
|
||||||
immgr = wl_registry_bind(r, name,
|
immgr = wl_registry_bind(r, name,
|
||||||
&zwp_input_method_manager_v2_interface, 1);
|
&zwp_input_method_manager_v2_interface, 1);
|
||||||
@@ -345,6 +333,9 @@ waylandthread(void *_)
|
|||||||
im = zwp_input_method_manager_v2_get_input_method(immgr, seat);
|
im = zwp_input_method_manager_v2_get_input_method(immgr, seat);
|
||||||
zwp_input_method_v2_add_listener(im, &im_listener, nil);
|
zwp_input_method_v2_add_listener(im, &im_listener, nil);
|
||||||
vk = zwp_virtual_keyboard_manager_v1_create_virtual_keyboard(vkmgr, seat);
|
vk = zwp_virtual_keyboard_manager_v1_create_virtual_keyboard(vkmgr, seat);
|
||||||
|
replyc = chancreate(sizeof(Keyres), 0);
|
||||||
while(wl_display_dispatch(dpy) != -1)
|
while(wl_display_dispatch(dpy) != -1)
|
||||||
;
|
;
|
||||||
|
chanfree(replyc);
|
||||||
|
replyc = nil;
|
||||||
}
|
}
|
||||||
|
|||||||
13
win.c
13
win.c
@@ -78,17 +78,26 @@ drawstr(u32int *buf, int x, int y, Rune *r, int n, int maxw, int maxh)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
fill(u32int *buf, int n, u32int color)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for(i = 0; i < n; i++)
|
||||||
|
buf[i] = color;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
drawkouho(Drawcmd *dc, int first, int n, int w, int h)
|
drawkouho(Drawcmd *dc, int first, int n, int w, int h)
|
||||||
{
|
{
|
||||||
int sely, y, i;
|
int sely, y, i;
|
||||||
Str *s;
|
Str *s;
|
||||||
|
|
||||||
memset(img, (uchar)Colbg, w * h * sizeof(u32int));
|
fill(img, w * h, Colbg);
|
||||||
drawstr(img, 0, 0, dc->pre.r, dc->pre.n, w, h);
|
drawstr(img, 0, 0, dc->pre.r, dc->pre.n, w, h);
|
||||||
if(dc->sel >= 0){
|
if(dc->sel >= 0){
|
||||||
sely = Fontsz + dc->sel * Fontsz;
|
sely = Fontsz + dc->sel * Fontsz;
|
||||||
memset(img + sely * w, (uchar)Colsel, Fontsz * w * sizeof(u32int));
|
fill(img + sely * w, Fontsz * w, Colsel);
|
||||||
}
|
}
|
||||||
for(i = 0, y = Fontsz; i < n; i++, y += Fontsz){
|
for(i = 0, y = Fontsz; i < n; i++, y += Fontsz){
|
||||||
s = &dc->kouho[first+i];
|
s = &dc->kouho[first+i];
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
CC = cc
|
CC = cc
|
||||||
CFLAGS = -Wall -Wextra -O2 -I.. -Ixcb-imdkit/src
|
XKB_CFLAGS := $(shell pkg-config --cflags xkbcommon)
|
||||||
|
XKB_LIBS := $(shell pkg-config --libs xkbcommon)
|
||||||
|
CFLAGS = -Wall -Wextra -O2 -I.. -Ixcb-imdkit/src $(XKB_CFLAGS)
|
||||||
PROG = strans-xim
|
PROG = strans-xim
|
||||||
|
|
||||||
SRCS = $(wildcard *.c)
|
SRCS = $(wildcard *.c)
|
||||||
@@ -9,8 +11,8 @@ LIBS = xcb-imdkit/libxcb-imdkit.a xcb-util/libxcb-util.a
|
|||||||
|
|
||||||
all: $(PROG)
|
all: $(PROG)
|
||||||
|
|
||||||
$(PROG): $(OBJS) $(LIBS)
|
$(PROG): $(OBJS) $(LIBS) ../ipc.c ../ipc.h
|
||||||
$(CC) -o $@ $(OBJS) $(LIBS) -lxcb
|
$(CC) $(CFLAGS) -o $@ $(OBJS) ../ipc.c $(LIBS) -lxcb $(XKB_LIBS)
|
||||||
|
|
||||||
$(OBJS): ../ipc.h
|
$(OBJS): ../ipc.h
|
||||||
|
|
||||||
|
|||||||
96
xim/main.c
96
xim/main.c
@@ -5,13 +5,14 @@
|
|||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <sys/un.h>
|
#include <sys/un.h>
|
||||||
#include <xcb/xcb.h>
|
#include <xcb/xcb.h>
|
||||||
|
#include <xkbcommon/xkbcommon.h>
|
||||||
#include "imdkit.h"
|
#include "imdkit.h"
|
||||||
#include "encoding.h"
|
#include "encoding.h"
|
||||||
#include "ipc.h"
|
#include "ipc.h"
|
||||||
|
|
||||||
static xcb_connection_t *conn;
|
static xcb_connection_t *conn;
|
||||||
static xcb_im_t *xim;
|
static xcb_im_t *xim;
|
||||||
static int srvfd;
|
static int srvfd = -1;
|
||||||
static xcb_keysym_t *kmap;
|
static xcb_keysym_t *kmap;
|
||||||
static uint8_t minkc, maxkc;
|
static uint8_t minkc, maxkc;
|
||||||
static uint8_t symsper;
|
static uint8_t symsper;
|
||||||
@@ -105,50 +106,74 @@ commit(xcb_im_input_context_t *ic, char *s, int len)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
srvclose(void)
|
||||||
|
{
|
||||||
|
if(srvfd < 0)
|
||||||
|
return;
|
||||||
|
close(srvfd);
|
||||||
|
srvfd = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
srvconnect(void)
|
srvconnect(void)
|
||||||
{
|
{
|
||||||
struct sockaddr_un addr;
|
struct sockaddr_un addr;
|
||||||
|
int fd;
|
||||||
|
|
||||||
srvfd = socket(AF_UNIX, SOCK_STREAM, 0);
|
if(srvfd >= 0)
|
||||||
if(srvfd < 0)
|
return 0;
|
||||||
die("socket failed");
|
fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||||
|
if(fd < 0)
|
||||||
|
return -1;
|
||||||
memset(&addr, 0, sizeof(addr));
|
memset(&addr, 0, sizeof(addr));
|
||||||
addr.sun_family = AF_UNIX;
|
addr.sun_family = AF_UNIX;
|
||||||
snprintf(addr.sun_path, sizeof(addr.sun_path), "/tmp/strans.%d", getuid());
|
snprintf(addr.sun_path, sizeof(addr.sun_path), IPCPATH, getuid());
|
||||||
if(connect(srvfd, (struct sockaddr*)&addr, sizeof(addr)) < 0)
|
if(connect(fd, (struct sockaddr*)&addr, sizeof(addr)) < 0){
|
||||||
die("can't connect to strans");
|
close(fd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
srvfd = fd;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
readresp(xcb_im_input_context_t *ic)
|
readresp(xcb_im_input_context_t *ic)
|
||||||
{
|
{
|
||||||
unsigned char buf[64];
|
char buf[Ipcfieldmax+1];
|
||||||
int n;
|
Ipcresp resp;
|
||||||
|
|
||||||
if(read(srvfd, buf, 2) != 2)
|
if(ipcreadresp(srvfd, 0, buf, sizeof buf, NULL, 0, &resp) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
n = buf[1];
|
if(buf[0] != '\0')
|
||||||
if(n > 0 && (size_t)n < sizeof(buf) && read(srvfd, buf+2, n) == n)
|
commit(ic, buf, strlen(buf));
|
||||||
commit(ic, (char*)(buf+2), n);
|
return resp.eaten;
|
||||||
return buf[0];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
kpress(xcb_im_input_context_t *ic, xcb_key_press_event_t *ev)
|
kpress(xcb_im_input_context_t *ic, xcb_key_press_event_t *ev)
|
||||||
{
|
{
|
||||||
unsigned char buf[4];
|
unsigned char buf[Ipcreqsz];
|
||||||
uint32_t key;
|
uint32_t key, rune;
|
||||||
|
int eaten;
|
||||||
|
|
||||||
key = kget(ev->detail, ev->state);
|
key = kget(ev->detail, ev->state);
|
||||||
if(key >= 0xff00)
|
rune = xkb_keysym_to_utf32(key);
|
||||||
|
if(rune >= ' ' && rune != 0x7f)
|
||||||
|
key = rune;
|
||||||
|
else if(key >= 0xff00 && key <= 0xffff)
|
||||||
key = Kspec + (key - 0xff00);
|
key = Kspec + (key - 0xff00);
|
||||||
buf[0] = 0;
|
else
|
||||||
buf[1] = ev->state;
|
key = rune;
|
||||||
buf[2] = key;
|
ipcpackreq(buf, 0, ev->state, key);
|
||||||
buf[3] = key >> 8;
|
eaten = 0;
|
||||||
if(write(srvfd, buf, 4) != 4)
|
if(srvconnect() == 0){
|
||||||
die("write failed");
|
if(ipcsend(srvfd, buf, sizeof buf) < 0 ||
|
||||||
if(readresp(ic) == 0)
|
(eaten = readresp(ic)) < 0){
|
||||||
|
srvclose();
|
||||||
|
eaten = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(eaten == 0)
|
||||||
xcb_im_forward_event(xim, ic, ev);
|
xcb_im_forward_event(xim, ic, ev);
|
||||||
xcb_flush(conn);
|
xcb_flush(conn);
|
||||||
}
|
}
|
||||||
@@ -156,15 +181,13 @@ kpress(xcb_im_input_context_t *ic, xcb_key_press_event_t *ev)
|
|||||||
static void
|
static void
|
||||||
reset(xcb_im_input_context_t *ic)
|
reset(xcb_im_input_context_t *ic)
|
||||||
{
|
{
|
||||||
unsigned char buf[4];
|
unsigned char buf[Ipcreqsz];
|
||||||
|
|
||||||
buf[0] = 0;
|
ipcpackreq(buf, 0, 0, Kesc);
|
||||||
buf[1] = 0;
|
if(srvconnect() < 0)
|
||||||
buf[2] = Kesc & 0xff;
|
return;
|
||||||
buf[3] = Kesc >> 8;
|
if(ipcsend(srvfd, buf, sizeof buf) < 0 || readresp(ic) < 0)
|
||||||
if(write(srvfd, buf, 4) != 4)
|
srvclose();
|
||||||
die("write failed");
|
|
||||||
readresp(ic);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@@ -173,6 +196,11 @@ callback(xcb_im_t *im, xcb_im_client_t *client, xcb_im_input_context_t *ic,
|
|||||||
{
|
{
|
||||||
xcb_key_press_event_t *ev;
|
xcb_key_press_event_t *ev;
|
||||||
|
|
||||||
|
(void)im;
|
||||||
|
(void)client;
|
||||||
|
(void)frame;
|
||||||
|
(void)user;
|
||||||
|
|
||||||
switch(hdr->major_opcode){
|
switch(hdr->major_opcode){
|
||||||
case XCB_XIM_FORWARD_EVENT:
|
case XCB_XIM_FORWARD_EVENT:
|
||||||
ev = arg;
|
ev = arg;
|
||||||
@@ -220,7 +248,6 @@ main(void)
|
|||||||
{
|
{
|
||||||
xcb_generic_event_t *ev;
|
xcb_generic_event_t *ev;
|
||||||
|
|
||||||
srvconnect();
|
|
||||||
ximinit();
|
ximinit();
|
||||||
for(;;){
|
for(;;){
|
||||||
ev = xcb_wait_for_event(conn);
|
ev = xcb_wait_for_event(conn);
|
||||||
@@ -229,5 +256,6 @@ main(void)
|
|||||||
xcb_im_filter_event(xim, ev);
|
xcb_im_filter_event(xim, ev);
|
||||||
free(ev);
|
free(ev);
|
||||||
}
|
}
|
||||||
|
srvclose();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user