The engine can reach a Hanja reading back into the text the client already holds, but only if the frontend hands that text over and can take some of it away again. GTK 3 has both: retrieve-surrounding brings the text around the cursor and delete-surrounding removes runes before it, and a widget that answers neither leaves the text empty, so nothing is ever reached into or taken from it. The wire grows a control frame for the text, sent like the caret only when it changes, and one byte in every response for the runes to take back. That byte moves the length fields along, so the version goes to 2: an old daemon and a new module, either way round, fail the handshake and the module falls through to GtkIMContextSimple rather than misread a frame. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
110 lines
3.4 KiB
C
110 lines
3.4 KiB
C
#include <stddef.h>
|
|
#include <stdint.h>
|
|
|
|
/*
|
|
* Key request: [flags, modifiers, key byte 0, ..., key byte 3].
|
|
* The want flag asks for the preedit text: the client then draws it and
|
|
* the popup does not. The reset flag is a lifecycle reset, not Escape.
|
|
* The server identifies the connection as the engine owner; that identity is
|
|
* not sent on the wire.
|
|
* Only Mmask modifier bits are retained.
|
|
* Response: [eaten, take-back, commit-length-low, commit-length-high,
|
|
* commit...], followed, when requested, by
|
|
* [preedit-length-low, preedit-length-high, preedit...].
|
|
* Take-back is the count of runes of the client's own text, just before the
|
|
* cursor, to remove before inserting the commit.
|
|
* Lengths are little-endian byte counts; fields are at most Ipcfieldmax bytes,
|
|
* so callers read them into char[Ipcfieldmax+1].
|
|
*
|
|
* Capability control is six bytes: [0x80|want, version, 0, 0, 0, 0]; the
|
|
* server always answers it eaten, which tells the client that extension
|
|
* frames are understood.
|
|
* Caret control is sixteen bytes: [0x80, version, 1, valid, x, y, h],
|
|
* with the signed coordinates and height encoded as little-endian 32-bit words.
|
|
* Surrounding-text control is six bytes, [0x80, version, 2, 0, length-low,
|
|
* length-high], and then that many bytes of the text just before the cursor.
|
|
* It changes what rides along with the requests that follow and is not
|
|
* answered.
|
|
*/
|
|
|
|
enum
|
|
{
|
|
Ipcreqwant = 1<<0,
|
|
Ipcreqreset = 1<<1,
|
|
Ipcext = 1<<7,
|
|
Ipcversion = 2,
|
|
Ipcopcap = 0,
|
|
Ipcopcaret = 1,
|
|
Ipcopsurround = 2,
|
|
|
|
Kspec = 0x110000,
|
|
Kback = Kspec|0x08,
|
|
Ktab = Kspec|0x09,
|
|
Kret = Kspec|0x0d,
|
|
Kesc = Kspec|0x1b,
|
|
Kup = Kspec|0x52,
|
|
Kdown = Kspec|0x54,
|
|
Kpgup = Kspec|0x55,
|
|
Kpgdown = Kspec|0x56,
|
|
Khangul = Kspec|0x31,
|
|
Khanja = Kspec|0x34,
|
|
Kmuhenkan = Kspec|0x22,
|
|
Khenkan = Kspec|0x23,
|
|
Kkana = Kspec|0x27,
|
|
Kzenkaku = Kspec|0x2a,
|
|
|
|
/* X core modifier bits; GDK and IBus also flag Super at bit 26. */
|
|
Mshift = 1<<0,
|
|
Mctrl = 1<<2,
|
|
Malt = 1<<3,
|
|
Msuper = 1<<6,
|
|
Mmask = Mshift|Mctrl|Malt|Msuper,
|
|
Msupervirt = 1<<26,
|
|
|
|
Ipcreqsz = 6,
|
|
Ipccaretsz = 16,
|
|
Ipclensz = 2,
|
|
Ipcresphdrsz = 2 + Ipclensz,
|
|
Ipcfieldmax = 256,
|
|
Ipcmaxresp = Ipcresphdrsz + Ipcfieldmax + Ipclensz + Ipcfieldmax,
|
|
Ipcwaitms = 250,
|
|
};
|
|
|
|
enum
|
|
{
|
|
Ipcunknown = -1,
|
|
Ipckey,
|
|
Ipccap,
|
|
Ipccaret,
|
|
Ipcsurround,
|
|
};
|
|
|
|
typedef struct Ipcresp Ipcresp;
|
|
struct Ipcresp
|
|
{
|
|
int eaten;
|
|
int del; /* runes of the client's own text to take back first */
|
|
/* Bytes copied to each caller buffer, excluding its trailing NUL. */
|
|
size_t commitlen;
|
|
size_t preeditlen;
|
|
};
|
|
|
|
uint32_t ipckeysym(uint32_t, uint32_t);
|
|
uint32_t ipcmod(uint32_t);
|
|
void ipcpackreq(unsigned char[Ipcreqsz], int, uint32_t, uint32_t);
|
|
void ipcpackreset(unsigned char[Ipcreqsz], int);
|
|
void ipcpackcap(unsigned char[Ipcreqsz], int);
|
|
void ipcpackcaret(unsigned char[Ipccaretsz], int, int32_t, int32_t, int32_t);
|
|
void ipcpacksurround(unsigned char[Ipcreqsz], size_t);
|
|
size_t ipcsurroundlen(const unsigned char[Ipcreqsz]);
|
|
void ipcunpackreq(const unsigned char[Ipcreqsz], int*, uint32_t*, uint32_t*);
|
|
int ipcunpackcaret(const unsigned char[Ipccaretsz], int*, int32_t*, int32_t*, int32_t*);
|
|
int ipcreqtype(const unsigned char[Ipcreqsz]);
|
|
int ipcreqreset(const unsigned char[Ipcreqsz]);
|
|
int ipcpackresp(unsigned char*, size_t, int, 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*, char*, Ipcresp*);
|
|
int ipcpath(char*, size_t);
|
|
int ipcconnect(void);
|