Compare commits

..

6 Commits

Author SHA1 Message Date
795353609b popup tooltip hint 2026-05-28 20:22:27 +09:00
28b48d997c split wayland commit cycles 2026-05-28 20:22:27 +09:00
f4347b70cc default popup off 2026-05-28 19:07:28 +09:00
69e92de010 wait for compositor 2026-05-28 19:04:41 +09:00
293313652f gtk inline preedit 2026-05-28 18:50:42 +09:00
b842c724e2 add popup toggle 2026-05-28 18:10:41 +09:00
6 changed files with 72 additions and 13 deletions

View File

@@ -51,6 +51,7 @@ Switch input modes with Ctrl + key:
T English T English
V Vietnamese (Telex) V Vietnamese (Telex)
E Emoji E Emoji
P Toggle preedit/candidate popup window
Type romanized input. Select candidates with 1-9 or arrow keys. Type romanized input. Select candidates with 1-9 or arrow keys.
Tab or Enter to commit. Tab or Enter to commit.

View File

@@ -12,6 +12,8 @@ struct Im
{ {
GtkIMContext parent; GtkIMContext parent;
int fd; int fd;
char pre[256];
int prelen;
}; };
typedef struct ImClass ImClass; typedef struct ImClass ImClass;
@@ -44,8 +46,8 @@ srvconnect(Im *im)
static int static int
readresp(Im *im, char *buf, int bufsz) readresp(Im *im, char *buf, int bufsz)
{ {
unsigned char hdr[2]; unsigned char hdr[2], pl;
int n; int n, was;
if(read(im->fd, hdr, 2) != 2) if(read(im->fd, hdr, 2) != 2)
return -1; return -1;
@@ -56,6 +58,21 @@ readresp(Im *im, char *buf, int bufsz)
return -1; return -1;
buf[n] = '\0'; buf[n] = '\0';
} }
if(read(im->fd, &pl, 1) != 1)
return -1;
if(pl >= sizeof(im->pre))
return -1;
was = im->prelen;
if(pl > 0 && read(im->fd, im->pre, pl) != pl)
return -1;
im->pre[pl] = '\0';
im->prelen = pl;
if(was == 0 && pl > 0)
g_signal_emit_by_name(im, "preedit-start");
if(was != 0 || pl != 0)
g_signal_emit_by_name(im, "preedit-changed");
if(was > 0 && pl == 0)
g_signal_emit_by_name(im, "preedit-end");
return hdr[0]; return hdr[0];
} }
@@ -92,7 +109,7 @@ sendreset(Im *im)
if(im->fd < 0) if(im->fd < 0)
return; return;
buf[0] = 0; buf[0] = 1;
buf[1] = 0; buf[1] = 0;
buf[2] = Kesc & 0xff; buf[2] = Kesc & 0xff;
buf[3] = Kesc >> 8; buf[3] = Kesc >> 8;
@@ -119,7 +136,7 @@ kpress(GtkIMContext *ctx, GdkEventKey *ev)
if(key == 0) if(key == 0)
return FALSE; return FALSE;
mod = mget(ev->state); mod = mget(ev->state);
buf[0] = 0; buf[0] = 1;
buf[1] = mod; buf[1] = mod;
buf[2] = key & 0xff; buf[2] = key & 0xff;
buf[3] = key >> 8; buf[3] = key >> 8;
@@ -133,6 +150,25 @@ kpress(GtkIMContext *ctx, GdkEventKey *ev)
return r != 0; return r != 0;
} }
static void
getpreedit(GtkIMContext *ctx, gchar **str, PangoAttrList **attrs,
gint *cursor_pos)
{
Im *im;
PangoAttribute *u;
im = (Im*)ctx;
*str = g_strdup(im->pre);
*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);
}
*cursor_pos = g_utf8_strlen(im->pre, -1);
}
static void static void
reset(GtkIMContext *ctx) reset(GtkIMContext *ctx)
{ {
@@ -171,6 +207,7 @@ classinit(ImClass *klass)
ic = GTK_IM_CONTEXT_CLASS(klass); ic = GTK_IM_CONTEXT_CLASS(klass);
oc = G_OBJECT_CLASS(klass); oc = G_OBJECT_CLASS(klass);
ic->filter_keypress = kpress; ic->filter_keypress = kpress;
ic->get_preedit_string = getpreedit;
ic->reset = reset; ic->reset = reset;
ic->focus_out = focusout; ic->focus_out = focusout;
oc->finalize = finalize; oc->finalize = finalize;

6
srv.c
View File

@@ -4,14 +4,14 @@
static char adir[40]; static char adir[40];
static void static void
sendkey(int fd, u32int ks, u32int mod) sendkey(int fd, u32int ks, u32int mod, int want)
{ {
Keyreq kr; Keyreq kr;
kr.fd = fd; kr.fd = fd;
kr.ks = ks; kr.ks = ks;
kr.mod = mod; kr.mod = mod;
kr.want = 0; kr.want = want;
chansend(keyc, &kr); chansend(keyc, &kr);
} }
@@ -24,7 +24,7 @@ clientthread(void *arg)
fd = (int)(uintptr)arg; fd = (int)(uintptr)arg;
threadsetname("client %d", fd); threadsetname("client %d", fd);
while(read(fd, req, 4) == 4) while(read(fd, req, 4) == 4)
sendkey(fd, req[2] | (req[3] << 8), req[1]); sendkey(fd, req[2] | (req[3] << 8), req[1], req[0]);
close(fd); close(fd);
} }

View File

@@ -2,6 +2,7 @@
#include "fn.h" #include "fn.h"
static Im im; static Im im;
static int popup = 0;
static void dictqmap(Im*); static void dictqmap(Im*);
static void static void
@@ -45,6 +46,8 @@ show(void)
Drawcmd dc; Drawcmd dc;
int i, first, n; int i, first, n;
if(!popup)
return;
sclear(&dc.pre); sclear(&dc.pre);
if(!mapget(im.l->map, &im.pre, &dc.pre)) if(!mapget(im.l->map, &im.pre, &dc.pre))
dc.pre = im.pre; dc.pre = im.pre;
@@ -256,6 +259,10 @@ keystroke(u32int ks, u32int mod, Str *com)
reset(); reset();
if(ks >= 'a' && ks <= 'z') if(ks >= 'a' && ks <= 'z')
ks -= 'a' - 1; ks -= 'a' - 1;
if(ks == 0x10){
popup = !popup;
return 1;
}
if(setlang(ks)) if(setlang(ks))
return 1; return 1;
return 0; return 0;

View File

@@ -5,6 +5,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#include <poll.h>
#include <sys/mman.h> #include <sys/mman.h>
#include <wayland-client.h> #include <wayland-client.h>
#include <xkbcommon/xkbcommon.h> #include <xkbcommon/xkbcommon.h>
@@ -118,8 +119,10 @@ kpress(uint32_t time, uint32_t keycode, uint32_t state)
} }
mod = mget(); mod = mget();
sendkey(k, mod, com, sizeof(com), pre, sizeof(pre), &eaten); sendkey(k, mod, com, sizeof(com), pre, sizeof(pre), &eaten);
if(com[0] != '\0') if(com[0] != '\0'){
zwp_input_method_v2_commit_string(im, com); zwp_input_method_v2_commit_string(im, com);
zwp_input_method_v2_commit(im, imserial);
}
plen = strlen(pre); plen = strlen(pre);
zwp_input_method_v2_set_preedit_string(im, pre, plen, plen); zwp_input_method_v2_set_preedit_string(im, pre, plen, plen);
zwp_input_method_v2_commit(im, imserial); zwp_input_method_v2_commit(im, imserial);
@@ -321,11 +324,8 @@ waylandthread(void *_)
(void)_; (void)_;
threadsetname("wayland"); threadsetname("wayland");
dpy = wl_display_connect(nil); while((dpy = wl_display_connect(nil)) == nil)
if(dpy == nil){ poll(nil, 0, 1000);
fprint(2, "strans: wayland: cannot connect to display\n");
return;
}
xkb = xkb_context_new(XKB_CONTEXT_NO_FLAGS); xkb = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
if(xkb == nil){ if(xkb == nil){
wl_display_disconnect(dpy); wl_display_disconnect(dpy);

14
win.c
View File

@@ -31,6 +31,8 @@ wininit(void)
{ {
int n; int n;
u32int mask, vals[4]; u32int mask, vals[4];
xcb_intern_atom_cookie_t c1, c2;
xcb_intern_atom_reply_t *r1, *r2;
conn = xcb_connect(nil, &n); conn = xcb_connect(nil, &n);
if(conn == nil || xcb_connection_has_error(conn)) if(conn == nil || xcb_connection_has_error(conn))
@@ -49,6 +51,18 @@ wininit(void)
xcb_create_window(conn, XCB_COPY_FROM_PARENT, win, scr->root, xcb_create_window(conn, XCB_COPY_FROM_PARENT, win, scr->root,
0, 0, 1, 1, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, 0, 0, 1, 1, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT,
scr->root_visual, mask, vals); scr->root_visual, mask, vals);
c1 = xcb_intern_atom(conn, 0,
strlen("_NET_WM_WINDOW_TYPE"), "_NET_WM_WINDOW_TYPE");
c2 = xcb_intern_atom(conn, 0,
strlen("_NET_WM_WINDOW_TYPE_TOOLTIP"),
"_NET_WM_WINDOW_TYPE_TOOLTIP");
r1 = xcb_intern_atom_reply(conn, c1, nil);
r2 = xcb_intern_atom_reply(conn, c2, nil);
if(r1 != nil && r2 != nil)
xcb_change_property(conn, XCB_PROP_MODE_REPLACE,
win, r1->atom, XCB_ATOM_ATOM, 32, 1, &r2->atom);
free(r1);
free(r2);
gc = xcb_generate_id(conn); gc = xcb_generate_id(conn);
xcb_create_gc(conn, gc, win, 0, nil); xcb_create_gc(conn, gc, win, 0, nil);
img = emalloc(Imgw * Imgh * sizeof(img[0])); img = emalloc(Imgw * Imgh * sizeof(img[0]));