diff --git a/gtk/Dockerfile b/gtk/Dockerfile new file mode 100644 index 0000000..019620b --- /dev/null +++ b/gtk/Dockerfile @@ -0,0 +1,3 @@ +FROM debian:stable-slim +RUN apt-get update && apt-get install -y gcc libgtk-3-dev make +WORKDIR /src diff --git a/gtk/Makefile b/gtk/Makefile new file mode 100644 index 0000000..2d01b80 --- /dev/null +++ b/gtk/Makefile @@ -0,0 +1,21 @@ +CFLAGS = -Wall -O2 -I.. `pkg-config --cflags gtk+-3.0` +LDFLAGS = `pkg-config --libs gtk+-3.0` +DSTDIR = /usr/lib64/gtk-3.0/3.0.0/immodules + +im-strans.so: main.c + $(CC) -shared -fPIC $(CFLAGS) -o $@ $< $(LDFLAGS) + +docker: main.c Dockerfile + docker build -t strans-gtk . + docker run --rm -v $(CURDIR)/..:/src -w /src/gtk strans-gtk make im-strans.so + +install: + cp im-strans.so $(DSTDIR)/ + gtk-query-immodules-3.0 --update-cache + +uninstall: + rm -f $(DSTDIR)/im-strans.so + gtk-query-immodules-3.0 --update-cache + +clean: + rm -f im-strans.so diff --git a/gtk/main.c b/gtk/main.c new file mode 100644 index 0000000..0b8367d --- /dev/null +++ b/gtk/main.c @@ -0,0 +1,222 @@ +#include +#include +#include +#include +#include +#include +#include +#include "ipc.h" + +typedef struct Im Im; +struct Im +{ + GtkIMContext parent; + int fd; +}; + +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), "/tmp/strans.%d", getuid()); + if(connect(im->fd, (struct sockaddr*)&addr, sizeof(addr)) < 0){ + close(im->fd); + im->fd = -1; + } +} + +static int +readresp(Im *im, char *buf, int bufsz) +{ + unsigned char hdr[2]; + int n; + + if(read(im->fd, hdr, 2) != 2) + return -1; + n = hdr[1]; + buf[0] = '\0'; + if(n > 0 && n < bufsz){ + if(read(im->fd, buf, n) != n) + return -1; + buf[n] = '\0'; + } + return hdr[0]; +} + +static uint32_t +kget(uint32_t gdk) +{ + if(gdk >= 0xff00) + return Kspec + (gdk - 0xff00); + return gdk_keyval_to_unicode(gdk); +} + +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[4]; + char resp[64]; + + if(im->fd < 0) + return; + buf[0] = 0; + buf[1] = 0; + buf[2] = Kesc & 0xff; + buf[3] = Kesc >> 8; + if(write(im->fd, buf, 4) == 4) + readresp(im, resp, sizeof(resp)); +} + +static gboolean +kpress(GtkIMContext *ctx, GdkEventKey *ev) +{ + Im *im; + unsigned char buf[4]; + char resp[64]; + uint32_t key, mod; + int r; + + im = (Im*)ctx; + if(ev->type != GDK_KEY_PRESS) + return FALSE; + srvconnect(im); + if(im->fd < 0) + return FALSE; + key = kget(ev->keyval); + if(key == 0) + return FALSE; + mod = mget(ev->state); + buf[0] = 0; + buf[1] = mod; + buf[2] = key & 0xff; + buf[3] = key >> 8; + if(write(im->fd, buf, 4) != 4) + return FALSE; + r = readresp(im, resp, sizeof(resp)); + if(r < 0) + return FALSE; + if(r != 0 && resp[0] != '\0') + g_signal_emit_by_name(ctx, "commit", resp); + return r != 0; +} + +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; +} + +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->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; +}