Files
strans/dat.h
Hojun-Cho f7277f54a3 engine: Ctrl+H reaches back into the text the client already has
Korean commits a syllable as the next one begins, so by the time the
한자 key is pressed only the last syllable is still ours: typing 한자 and
then Ctrl+H asked about 자 alone and answered 子, leaving 한子 in the
document -- the mixed Hangul and Hanja that was refused as dictionary
data, made by the interaction instead.  To get 漢字 the key had to be
pressed before typing, which no other Korean input method asks for.

A request now carries the client's own text just before the cursor, and
the reading reaches back through it as far as the dictionary still knows
the whole of it: 한 joins 자 and 대한민 joins 국.  Nothing but the
dictionary says where to stop, because a reading is syllables, so a key
holding a space or an already converted Hanja leads nowhere and the
reach ends there.  A pick answers with the count of runes to take back
first; Escape gives back only what was pending, and a query with no
candidate types only the part the client lacks.

A frontend that sends no surrounding text reaches back by nothing and
behaves exactly as before, which is what XIM will keep doing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-18 14:17:08 +09:00

231 lines
3.9 KiB
C

#include <u.h>
#include <libc.h>
#include <thread.h>
#include <bio.h>
#include "ipc.h"
#define min(a, b) ((a) < (b) ? (a) : (b))
#define max(a, b) ((a) > (b) ? (a) : (b))
/*
* plan9port runs the note handlers for a note it goes on to ignore, and
* the process lives through it, so what strans announced must outlive
* such a note. A daemon that forks nothing gets only the one.
*/
#define notefatal(note) (strcmp(note, "sys: write on closed pipe") != 0)
/* A language id is the control code of the Ctrl key that selects it. */
enum
{
LangEN = 0x14,
LangJP = 0x0e,
LangJPK = 0x0b,
LangKO = 0x13,
LangHANJA = 0x08,
LangEMOJI = 0x05,
LangVI = 0x16,
Maxrunes = 64,
Maxutf = Maxrunes * UTFmax + 1,
};
enum
{
Maxclients = 64,
Ownerpoll = 200, /* ms between a frontend's owner checks while its preedit shows */
Maxkouho = 128,
Maxdisp = 9,
/* Content purposes whose keys the engine never sees; IBus and
* text-input-v3 number them alike, both after GTK. */
Purposepassword = 8,
Purposepin = 9,
Colfg = 0x000000,
Colbg = 0xffffff,
Colsep = 0xd0d0d0,
Colsel = 0x333333,
Colselfg = 0xffffff,
};
/* Each frontend composes on its own: they run in different procs. */
enum
{
Composeibus,
Composexim,
Composewl,
Ncompose,
};
/* Popup metrics in pixels; popupscale is GDK_SCALE, for HiDPI. */
extern int popupscale;
#define Fontsz (32*popupscale)
#define PopupPad (4*popupscale)
#define PopupSep popupscale
#define PopupBorder popupscale
#define PopupNumw Fontsz
#define PopupTextw (12*Fontsz)
#define PopupBasew (2*PopupPad + PopupNumw + PopupTextw)
typedef struct Str Str;
struct Str
{
Rune r[Maxrunes];
int n;
};
/*
* What one key does to a composition: text to commit, the new pending
* text, and the new raw state (Telex key history, or pending Japanese
* romaji), given the old ones in Im.
*/
typedef struct Emit Emit;
struct Emit
{
int eat;
Str s;
Str next;
Str raw;
};
typedef struct Tnode Tnode;
struct Tnode
{
char *val;
int vlen;
int child;
int sibling;
Rune c;
};
typedef struct Trie Trie;
struct Trie
{
Tnode *nodes;
int n;
int cap;
/* the last key put and its path, so a sorted file walks it once */
Str last;
int path[Maxrunes];
};
enum
{
TrieMiss,
TriePrefix,
TrieExact,
};
typedef struct Lang Lang;
typedef struct Im Im;
struct Lang
{
int lang;
char *mapname;
char *dictname;
Emit (*trans)(Im*, Rune);
void (*back)(Im*);
Trie *map;
Trie *dict;
};
struct Im
{
Lang *l;
Str pre;
/* Telex history, or the short pending romaji in a Japanese mode. */
Str raw;
Str kouho[Maxkouho];
int nkouho;
int sel;
};
typedef struct Drawcmd Drawcmd;
typedef struct Caret Caret;
typedef struct Area Area;
struct Area
{
int x;
int y;
int w;
int h;
};
struct Caret
{
int valid;
int x;
int y;
int h;
};
struct Drawcmd
{
Str pre;
Str kouho[Maxdisp];
int nkouho;
int sel;
int first;
int total;
Caret caret;
};
/*
* Popup geometry from popuplayout: n rows from row0 of the candidates,
* each Fontsz tall; a section's y is -1 when it is absent. Numbers
* start at PopupPad, text at PopupPad+PopupNumw.
*/
typedef struct Popup Popup;
struct Popup
{
int w;
int h;
int n;
int row0;
int prey;
int sepy;
int rowsy;
int textw;
int marky;
int markx;
int markw;
Str mark;
int sely;
int selw;
};
typedef struct Keyreq Keyreq;
typedef struct Keyres Keyres;
enum
{
Keypress,
Keyreset,
Keyrelease,
Keycaret,
Keycap,
};
struct Keyres
{
int eaten; /* Keycap: the caller is the owner and clientpre took. */
int del; /* runes of the client's own text to take back first */
Str commit;
Str preedit;
};
struct Keyreq
{
void *owner; /* stable until the context's release is acknowledged */
int clientpre; /* the client draws the preedit; the popup does not */
int op;
u32int ks;
u32int mod;
Caret caret;
Str surround; /* the client's own text just before the cursor */
Channel *reply;
};
extern Lang langs[];
extern int nlang;
extern Channel *drawc;
extern Channel *keyc;