300 lines
5.3 KiB
C
300 lines
5.3 KiB
C
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/un.h>
|
|
#include <gtk/gtk.h>
|
|
#include "ipc.h"
|
|
|
|
typedef struct Im Im;
|
|
struct Im
|
|
{
|
|
GtkIMContext parent;
|
|
int fd;
|
|
char pre[Ipcfieldmax+1];
|
|
int prelen;
|
|
};
|
|
|
|
typedef struct ImClass ImClass;
|
|
struct ImClass
|
|
{
|
|
GtkIMContextClass parent;
|
|
};
|
|
|
|
static GType imtype;
|
|
|
|
static void
|
|
srvconnect(Im *im)
|
|
{
|
|
struct sockaddr_un addr;
|
|
|
|
if(im->fd >= 0)
|
|
return;
|
|
im->fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
|
if(im->fd < 0)
|
|
return;
|
|
memset(&addr, 0, sizeof(addr));
|
|
addr.sun_family = AF_UNIX;
|
|
snprintf(addr.sun_path, sizeof(addr.sun_path), IPCPATH, getuid());
|
|
if(connect(im->fd, (struct sockaddr*)&addr, sizeof(addr)) < 0){
|
|
close(im->fd);
|
|
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
|
|
readresp(Im *im, char *buf, int bufsz)
|
|
{
|
|
Ipcresp resp;
|
|
int was;
|
|
|
|
was = im->prelen;
|
|
if(ipcreadresp(im->fd, 1, buf, bufsz, im->pre,
|
|
sizeof(im->pre), &resp) < 0)
|
|
return -1;
|
|
im->prelen = resp.npreedit;
|
|
if(was == 0 && im->prelen > 0)
|
|
g_signal_emit_by_name(im, "preedit-start");
|
|
if(was != 0 || im->prelen != 0)
|
|
g_signal_emit_by_name(im, "preedit-changed");
|
|
if(was > 0 && im->prelen == 0)
|
|
g_signal_emit_by_name(im, "preedit-end");
|
|
return resp.eaten;
|
|
}
|
|
|
|
static uint32_t
|
|
kget(uint32_t gdk)
|
|
{
|
|
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 u;
|
|
}
|
|
|
|
static uint32_t
|
|
mget(uint32_t state)
|
|
{
|
|
uint32_t m;
|
|
|
|
m = 0;
|
|
if(state & GDK_SHIFT_MASK)
|
|
m |= Mshift;
|
|
if(state & GDK_CONTROL_MASK)
|
|
m |= Mctrl;
|
|
if(state & GDK_MOD1_MASK)
|
|
m |= Malt;
|
|
if(state & GDK_SUPER_MASK)
|
|
m |= Msuper;
|
|
return m;
|
|
}
|
|
|
|
static void
|
|
sendreset(Im *im)
|
|
{
|
|
unsigned char buf[Ipcreqsz];
|
|
char resp[Ipcfieldmax+1];
|
|
|
|
if(im->fd < 0){
|
|
srvclose(im);
|
|
return;
|
|
}
|
|
ipcpackreset(buf, 1);
|
|
if(ipcsend(im->fd, buf, sizeof buf) < 0 ||
|
|
readresp(im, resp, sizeof(resp)) < 0)
|
|
srvclose(im);
|
|
}
|
|
|
|
static gboolean
|
|
plaincommit(GtkIMContext *ctx, uint32_t key, uint32_t mod)
|
|
{
|
|
char u[8];
|
|
int n;
|
|
|
|
if(mod & (Mctrl|Malt|Msuper))
|
|
return FALSE;
|
|
if(key < 0x20 || key >= Kspec)
|
|
return FALSE;
|
|
n = g_unichar_to_utf8(key, u);
|
|
u[n] = '\0';
|
|
g_signal_emit_by_name(ctx, "commit", u);
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
kpress(GtkIMContext *ctx, GdkEventKey *ev)
|
|
{
|
|
Im *im;
|
|
unsigned char buf[Ipcreqsz];
|
|
char resp[Ipcfieldmax+1];
|
|
uint32_t key, mod;
|
|
int r;
|
|
GtkInputPurpose purpose;
|
|
|
|
im = (Im*)ctx;
|
|
if(ev->type != GDK_KEY_PRESS)
|
|
return FALSE;
|
|
key = kget(ev->keyval);
|
|
if(key == 0)
|
|
return FALSE;
|
|
mod = mget(ev->state);
|
|
g_object_get(ctx, "input-purpose", &purpose, NULL);
|
|
if(purpose == GTK_INPUT_PURPOSE_PASSWORD || purpose == GTK_INPUT_PURPOSE_PIN)
|
|
return plaincommit(ctx, key, mod);
|
|
srvconnect(im);
|
|
if(im->fd < 0){
|
|
srvclose(im);
|
|
return plaincommit(ctx, key, mod);
|
|
}
|
|
ipcpackreq(buf, 1, mod, key);
|
|
if(ipcsend(im->fd, buf, sizeof buf) < 0){
|
|
srvclose(im);
|
|
return plaincommit(ctx, key, mod);
|
|
}
|
|
r = readresp(im, resp, sizeof(resp));
|
|
if(r < 0){
|
|
srvclose(im);
|
|
return plaincommit(ctx, key, mod);
|
|
}
|
|
if(resp[0] != '\0')
|
|
g_signal_emit_by_name(ctx, "commit", resp);
|
|
if(r != 0)
|
|
return TRUE;
|
|
return plaincommit(ctx, key, mod);
|
|
}
|
|
|
|
static void
|
|
getpreedit(GtkIMContext *ctx, gchar **str, PangoAttrList **attrs,
|
|
gint *cursor_pos)
|
|
{
|
|
Im *im;
|
|
PangoAttribute *u;
|
|
|
|
im = (Im*)ctx;
|
|
if(str)
|
|
*str = g_strdup(im->pre);
|
|
if(attrs){
|
|
*attrs = pango_attr_list_new();
|
|
if(im->prelen > 0){
|
|
u = pango_attr_underline_new(PANGO_UNDERLINE_SINGLE);
|
|
u->start_index = 0;
|
|
u->end_index = im->prelen;
|
|
pango_attr_list_insert(*attrs, u);
|
|
}
|
|
}
|
|
if(cursor_pos)
|
|
*cursor_pos = g_utf8_strlen(im->pre, -1);
|
|
}
|
|
|
|
static void
|
|
reset(GtkIMContext *ctx)
|
|
{
|
|
sendreset((Im*)ctx);
|
|
}
|
|
|
|
static void
|
|
focusout(GtkIMContext *ctx)
|
|
{
|
|
sendreset((Im*)ctx);
|
|
}
|
|
|
|
static void
|
|
finalize(GObject *obj)
|
|
{
|
|
Im *im;
|
|
|
|
im = (Im*)obj;
|
|
if(im->fd >= 0)
|
|
close(im->fd);
|
|
G_OBJECT_CLASS(g_type_class_peek_parent(G_OBJECT_GET_CLASS(obj)))->finalize(obj);
|
|
}
|
|
|
|
static void
|
|
init(Im *im)
|
|
{
|
|
im->fd = -1;
|
|
im->pre[0] = '\0';
|
|
im->prelen = 0;
|
|
}
|
|
|
|
static void
|
|
classinit(ImClass *klass)
|
|
{
|
|
GtkIMContextClass *ic;
|
|
GObjectClass *oc;
|
|
|
|
ic = GTK_IM_CONTEXT_CLASS(klass);
|
|
oc = G_OBJECT_CLASS(klass);
|
|
ic->filter_keypress = kpress;
|
|
ic->get_preedit_string = getpreedit;
|
|
ic->reset = reset;
|
|
ic->focus_out = focusout;
|
|
oc->finalize = finalize;
|
|
}
|
|
|
|
static const GtkIMContextInfo info = {
|
|
"strans",
|
|
"strans",
|
|
"strans",
|
|
"",
|
|
"*",
|
|
};
|
|
|
|
static const GtkIMContextInfo *infolist[] = { &info };
|
|
|
|
G_MODULE_EXPORT void
|
|
im_module_init(GTypeModule *mod)
|
|
{
|
|
static const GTypeInfo ti = {
|
|
sizeof(ImClass),
|
|
NULL, NULL,
|
|
(GClassInitFunc)classinit,
|
|
NULL, NULL,
|
|
sizeof(Im),
|
|
0,
|
|
(GInstanceInitFunc)init,
|
|
};
|
|
imtype = g_type_module_register_type(mod, GTK_TYPE_IM_CONTEXT, "strans-gtk", &ti, 0);
|
|
}
|
|
|
|
G_MODULE_EXPORT void
|
|
im_module_exit(void)
|
|
{
|
|
}
|
|
|
|
G_MODULE_EXPORT void
|
|
im_module_list(const GtkIMContextInfo ***contexts, int *n)
|
|
{
|
|
*contexts = infolist;
|
|
*n = 1;
|
|
}
|
|
|
|
G_MODULE_EXPORT GtkIMContext*
|
|
im_module_create(const char *id)
|
|
{
|
|
if(strcmp(id, "strans") == 0)
|
|
return g_object_new(imtype, NULL);
|
|
return NULL;
|
|
}
|