半角/全角, ひらがな/カタカナ, 変換, and 無変換 committed the reading and did nothing, like any function key. They now stand for what a Japanese keyboard means by them: toggling Japanese and English, switching kana modes, converting (or turning Japanese on), and turning it off; the Korean keys became a switch of the same shape. The keypad's arrows and page keys, with NumLock off, fold onto the plain ones as its Enter and Tab already did.
426 lines
7.5 KiB
C
426 lines
7.5 KiB
C
#define _POSIX_C_SOURCE 200809L
|
|
|
|
#include <errno.h>
|
|
#include <poll.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/un.h>
|
|
#include "ipc.h"
|
|
|
|
static int64_t
|
|
nowms(void)
|
|
{
|
|
struct timespec ts;
|
|
|
|
clock_gettime(CLOCK_MONOTONIC, &ts);
|
|
return (int64_t)ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
|
|
}
|
|
|
|
static int64_t
|
|
deadline(void)
|
|
{
|
|
return nowms() + Ipcwaitms;
|
|
}
|
|
|
|
static int
|
|
waitfd(int fd, short events, int64_t until)
|
|
{
|
|
struct pollfd pfd;
|
|
int64_t left;
|
|
int n;
|
|
|
|
pfd.fd = fd;
|
|
pfd.events = events;
|
|
for(;;){
|
|
left = until - nowms();
|
|
if(left <= 0){
|
|
errno = ETIMEDOUT;
|
|
return -1;
|
|
}
|
|
n = poll(&pfd, 1, left);
|
|
if(n < 0 && errno == EINTR)
|
|
continue;
|
|
if(n < 0)
|
|
return -1;
|
|
if(n == 0){
|
|
errno = ETIMEDOUT;
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static int
|
|
readwait(int fd, void *buf, size_t n, int64_t until)
|
|
{
|
|
unsigned char *p;
|
|
ssize_t r;
|
|
|
|
p = buf;
|
|
while(n > 0){
|
|
r = recv(fd, p, n, MSG_DONTWAIT);
|
|
if(r < 0 && errno == EINTR)
|
|
continue;
|
|
if(r < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)){
|
|
if(waitfd(fd, POLLIN, until) < 0)
|
|
return -1;
|
|
continue;
|
|
}
|
|
if(r < 0)
|
|
return -1;
|
|
if(r == 0){
|
|
errno = ECONNRESET;
|
|
return -1;
|
|
}
|
|
p += r;
|
|
n -= r;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
static void
|
|
put32(unsigned char *p, int32_t v)
|
|
{
|
|
uint32_t u;
|
|
|
|
u = v;
|
|
p[0] = u;
|
|
p[1] = u >> 8;
|
|
p[2] = u >> 16;
|
|
p[3] = u >> 24;
|
|
}
|
|
|
|
static int32_t
|
|
get32(const unsigned char *p)
|
|
{
|
|
return (int32_t)((uint32_t)p[0] |
|
|
((uint32_t)p[1] << 8) |
|
|
((uint32_t)p[2] << 16) |
|
|
((uint32_t)p[3] << 24));
|
|
}
|
|
|
|
int
|
|
ipcpath(char *dst, size_t cap)
|
|
{
|
|
const char *dir, *sep;
|
|
int n;
|
|
|
|
dir = getenv("XDG_RUNTIME_DIR");
|
|
if(dir != NULL && dir[0] == '/'){
|
|
sep = dir[strlen(dir)-1] == '/' ? "" : "/";
|
|
n = snprintf(dst, cap, "%s%sstrans.sock", dir, sep);
|
|
}else
|
|
n = snprintf(dst, cap, "/tmp/strans.%lu",
|
|
(unsigned long)getuid());
|
|
if(n < 0 || (size_t)n >= cap)
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* A nonblocking AF_UNIX connect completes at once or fails with EAGAIN
|
|
* when the daemon's backlog is full; the socket stays nonblocking because
|
|
* every transfer below polls with a deadline.
|
|
*/
|
|
int
|
|
ipcconnect(void)
|
|
{
|
|
struct sockaddr_un addr;
|
|
int e, fd;
|
|
|
|
memset(&addr, 0, sizeof addr);
|
|
addr.sun_family = AF_UNIX;
|
|
if(ipcpath(addr.sun_path, sizeof addr.sun_path) < 0){
|
|
errno = ENAMETOOLONG;
|
|
return -1;
|
|
}
|
|
fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
|
|
if(fd < 0)
|
|
return -1;
|
|
if(connect(fd, (struct sockaddr*)&addr, sizeof addr) < 0){
|
|
e = errno;
|
|
close(fd);
|
|
errno = e;
|
|
return -1;
|
|
}
|
|
return fd;
|
|
}
|
|
|
|
/*
|
|
* Engine key for an X keysym and its Unicode value: printable characters
|
|
* as themselves, the function keysyms 0xff00-0xffff as Kspec+offset.
|
|
* The keypad keys and Shift+Tab (ISO_Left_Tab) fold onto their plain
|
|
* counterparts.
|
|
*/
|
|
uint32_t
|
|
ipckeysym(uint32_t sym, uint32_t unicode)
|
|
{
|
|
if(unicode >= ' ' && unicode != 0x7f)
|
|
return unicode;
|
|
switch(sym){
|
|
case 0xfe20:
|
|
case 0xff89:
|
|
return Ktab;
|
|
case 0xff8d:
|
|
return Kret;
|
|
case 0xff97:
|
|
return Kup;
|
|
case 0xff99:
|
|
return Kdown;
|
|
case 0xff9a:
|
|
return Kpgup;
|
|
case 0xff9b:
|
|
return Kpgdown;
|
|
}
|
|
if(sym >= 0xff00 && sym <= 0xffff)
|
|
return Kspec + (sym - 0xff00);
|
|
return unicode;
|
|
}
|
|
|
|
uint32_t
|
|
ipcmod(uint32_t state)
|
|
{
|
|
uint32_t m;
|
|
|
|
m = state & Mmask;
|
|
if(state & Msupervirt)
|
|
m |= Msuper;
|
|
return m;
|
|
}
|
|
|
|
void
|
|
ipcpackreq(unsigned char req[Ipcreqsz], int want, uint32_t mod,
|
|
uint32_t key)
|
|
{
|
|
req[0] = want ? Ipcreqwant : 0;
|
|
req[1] = mod;
|
|
req[2] = key;
|
|
req[3] = key >> 8;
|
|
req[4] = key >> 16;
|
|
req[5] = key >> 24;
|
|
}
|
|
|
|
void
|
|
ipcpackreset(unsigned char req[Ipcreqsz], int want)
|
|
{
|
|
memset(req, 0, Ipcreqsz);
|
|
req[0] = Ipcreqreset | (want ? Ipcreqwant : 0);
|
|
}
|
|
|
|
/* An old server sees this reserved frame as a key-zero request. */
|
|
void
|
|
ipcpackcap(unsigned char req[Ipcreqsz], int want)
|
|
{
|
|
memset(req, 0, Ipcreqsz);
|
|
req[0] = Ipcext | (want ? Ipcreqwant : 0);
|
|
req[1] = Ipcversion;
|
|
req[2] = Ipcopcap;
|
|
}
|
|
|
|
void
|
|
ipcpackcaret(unsigned char req[Ipccaretsz], int valid, int32_t x,
|
|
int32_t y, int32_t h)
|
|
{
|
|
memset(req, 0, Ipccaretsz);
|
|
req[0] = Ipcext;
|
|
req[1] = Ipcversion;
|
|
req[2] = Ipcopcaret;
|
|
req[3] = valid != 0;
|
|
if(valid){
|
|
put32(req + 4, x);
|
|
put32(req + 8, y);
|
|
put32(req + 12, h);
|
|
}
|
|
}
|
|
|
|
void
|
|
ipcunpackreq(const unsigned char req[Ipcreqsz], int *want, uint32_t *mod,
|
|
uint32_t *key)
|
|
{
|
|
*want = (req[0] & Ipcreqwant) != 0;
|
|
*mod = req[1] & Mmask;
|
|
*key = (uint32_t)req[2] |
|
|
((uint32_t)req[3] << 8) |
|
|
((uint32_t)req[4] << 16) |
|
|
((uint32_t)req[5] << 24);
|
|
}
|
|
|
|
int
|
|
ipcreqtype(const unsigned char req[Ipcreqsz])
|
|
{
|
|
if((req[0] & Ipcext) == 0)
|
|
return Ipckey;
|
|
if(req[1] != Ipcversion)
|
|
return Ipcunknown;
|
|
switch(req[2]){
|
|
case Ipcopcap:
|
|
if((req[0] & ~(Ipcext|Ipcreqwant)) != 0 ||
|
|
req[3] != 0 || req[4] != 0 || req[5] != 0)
|
|
return Ipcunknown;
|
|
return Ipccap;
|
|
case Ipcopcaret:
|
|
if(req[0] != Ipcext || req[3] > 1)
|
|
return Ipcunknown;
|
|
return Ipccaret;
|
|
}
|
|
return Ipcunknown;
|
|
}
|
|
|
|
int
|
|
ipcunpackcaret(const unsigned char req[Ipccaretsz], int *valid,
|
|
int32_t *x, int32_t *y, int32_t *h)
|
|
{
|
|
if(ipcreqtype(req) != Ipccaret || get32(req + 12) < 0)
|
|
return -1;
|
|
*valid = req[3];
|
|
*x = *valid ? get32(req + 4) : 0;
|
|
*y = *valid ? get32(req + 8) : 0;
|
|
*h = *valid ? get32(req + 12) : 0;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
ipcreqreset(const unsigned char req[Ipcreqsz])
|
|
{
|
|
return (req[0] & Ipcreqreset) != 0;
|
|
}
|
|
|
|
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;
|
|
n = Ipcresphdrsz + ncommit + (want ? Ipclensz + npreedit : 0);
|
|
if(cap < n)
|
|
return -1;
|
|
dst[0] = eaten != 0;
|
|
putlen(dst + 1, ncommit);
|
|
memcpy(dst + Ipcresphdrsz, commit, ncommit);
|
|
if(want){
|
|
putlen(dst + Ipcresphdrsz + ncommit, npreedit);
|
|
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;
|
|
int64_t until;
|
|
|
|
p = buf;
|
|
until = deadline();
|
|
while(n > 0){
|
|
r = send(fd, p, n, MSG_NOSIGNAL|MSG_DONTWAIT);
|
|
if(r < 0 && errno == EINTR)
|
|
continue;
|
|
if(r < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)){
|
|
if(waitfd(fd, POLLOUT, until) < 0)
|
|
return -1;
|
|
continue;
|
|
}
|
|
if(r < 0)
|
|
return -1;
|
|
if(r == 0){
|
|
errno = EPIPE;
|
|
return -1;
|
|
}
|
|
p += r;
|
|
n -= r;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* Reads a length-prefixed field into dst[Ipcfieldmax+1]. */
|
|
static int
|
|
readfield(int fd, char *dst, int64_t until)
|
|
{
|
|
unsigned char len[Ipclensz];
|
|
size_t n;
|
|
|
|
if(readwait(fd, len, sizeof len, until) < 0)
|
|
return -1;
|
|
n = getlen(len);
|
|
if(n > Ipcfieldmax){
|
|
errno = EPROTO;
|
|
return -1;
|
|
}
|
|
if(readwait(fd, dst, n, until) < 0)
|
|
return -1;
|
|
dst[n] = '\0';
|
|
return n;
|
|
}
|
|
|
|
int
|
|
ipcreadresp(int fd, int want, char *commit, char *preedit, Ipcresp *resp)
|
|
{
|
|
unsigned char eaten;
|
|
int64_t until;
|
|
int n;
|
|
|
|
commit[0] = '\0';
|
|
preedit[0] = '\0';
|
|
memset(resp, 0, sizeof *resp);
|
|
until = deadline();
|
|
if(readwait(fd, &eaten, 1, until) < 0)
|
|
return -1;
|
|
if(eaten > 1){
|
|
errno = EPROTO;
|
|
return -1;
|
|
}
|
|
resp->eaten = eaten;
|
|
n = readfield(fd, commit, until);
|
|
if(n < 0)
|
|
return -1;
|
|
resp->commitlen = n;
|
|
if(!want)
|
|
return 0;
|
|
n = readfield(fd, preedit, until);
|
|
if(n < 0)
|
|
return -1;
|
|
resp->preeditlen = n;
|
|
return 0;
|
|
}
|