#include #include #include #include #include #include #include #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; } ipcpackreq(buf, 1, 0, Kesc); 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; }