commit 399ccd26932e3ec495c4eb9abc4c590eef21e416
parent 4a445a044040819bcb503ef5268718eab160ea15
Author: yahei <yahei1423@gmail.com>
Date: Tue, 6 May 2025 16:50:05 +0900
[st][patch][preedit] Added patch
Diffstat:
3 files changed, 404 insertions(+), 0 deletions(-)
diff --git a/st.suckless.org/patches/preedit/index.md b/st.suckless.org/patches/preedit/index.md
@@ -0,0 +1,16 @@
+preedit
+=======
+
+
+
+Description
+-----------
+This patch enables the on-the-spot input style.
+
+Download
+--------
+* [st-preedit-0.9.2.diff](st-preedit-0.9.2.diff)
+
+Author
+------
+* yahei - <yahei1423@gmail.com>
diff --git a/st.suckless.org/patches/preedit/preedit.png b/st.suckless.org/patches/preedit/preedit.png
Binary files differ.
diff --git a/st.suckless.org/patches/preedit/st-preedit-0.9.2.diff b/st.suckless.org/patches/preedit/st-preedit-0.9.2.diff
@@ -0,0 +1,388 @@
+diff --git a/st.c b/st.c
+index b9f66e7..ede6d6b 100644
+--- a/st.c
++++ b/st.c
+@@ -109,6 +109,12 @@ typedef struct {
+ int alt;
+ } Selection;
+
++typedef struct {
++ Glyph *text; /* preedit text */
++ int len; /* text length */
++ PLine pline;
++} Preedit;
++
+ /* Internal representation of the screen */
+ typedef struct {
+ int row; /* nb row */
+@@ -202,6 +208,7 @@ static int32_t tdefcolor(const int *, int *, int);
+ static void tdeftran(char);
+ static void tstrsequence(uchar);
+
++static void pelineupdate(void);
+ static void drawregion(int, int, int, int);
+
+ static void selnormalize(void);
+@@ -221,6 +228,7 @@ static ssize_t xwrite(int, const char *, size_t);
+ /* Globals */
+ static Term term;
+ static Selection sel;
++static Preedit preedit;
+ static CSIEscape csiescseq;
+ static STREscape strescseq;
+ static int iofd = 1;
+@@ -1179,6 +1187,9 @@ tmoveto(int x, int y)
+ term.c.state &= ~CURSOR_WRAPNEXT;
+ term.c.x = LIMIT(x, 0, term.col-1);
+ term.c.y = LIMIT(y, miny, maxy);
++
++ if (preedit.len > 0)
++ pelineupdate();
+ }
+
+ void
+@@ -2627,6 +2638,115 @@ resettitle(void)
+ xsettitle(NULL);
+ }
+
++void
++pereset(void)
++{
++ preedit.len = 0;
++ preedit.pline.width = 0;
++ pelineupdate();
++}
++
++void
++peupdate(int caret, int chg_fst, int chg_len,
++ unsigned short str_len, const ushort *modes, const char *str)
++{
++ int i;
++ int defmode;
++ Glyph *text, *g;
++ int chg_last, len;
++
++ chg_fst = MIN(chg_fst, preedit.len);
++ chg_len = MIN(chg_len, preedit.len - chg_fst);
++ chg_last = chg_fst + chg_len;
++ len = preedit.len - chg_len + (str ? str_len : 0);
++
++ /* default glyph mode */
++ defmode = ATTR_NULL;
++ if (preedit.len > 0)
++ defmode = (chg_fst < preedit.len) ?
++ preedit.text[chg_fst].mode :
++ preedit.text[chg_fst - 1].mode;
++ defmode &= ~ATTR_WIDE;
++
++ /* create new text and copy old glyphs */
++ text = xmalloc(len * sizeof(Glyph));
++ if (preedit.len > 0) {
++ memcpy(text, preedit.text, chg_fst * sizeof(Glyph));
++ memcpy(text + chg_fst + (str ? str_len : 0),
++ preedit.text + chg_last,
++ (preedit.len - chg_last) * sizeof(Glyph));
++ free(preedit.text);
++ }
++ preedit.text = text;
++ preedit.len = len;
++
++ /* new glyphs */
++ if (str) {
++ for (i = 0; i < str_len; i++) {
++ g = text + chg_fst + i;
++ *g = (Glyph){ 0, defmode, defaultfg, defaultbg };
++ str += utf8decode(str, &g->u, UTF_SIZ);
++ if (wcwidth(g->u) > 1)
++ g->mode |= ATTR_WIDE;
++ }
++ }
++
++ /* glyph mode */
++ if (modes) {
++ for (i = 0; i < str_len; i++) {
++ g = text + chg_fst + i;
++ g->mode = modes[i] | (g->mode & ATTR_WIDE);
++ }
++ }
++
++ /* visual width and caret position */
++ preedit.pline.width = 0;
++ preedit.pline.caret = 0;
++ for (i = 0; i < len; i++) {
++ preedit.pline.width += MAX(wcwidth(text[i].u), 1);
++ if (i + 1 == caret)
++ preedit.pline.caret = preedit.pline.width;
++ }
++
++ pelineupdate();
++}
++
++void
++pelineupdate()
++{
++ int i, x;
++
++ free(preedit.pline.line);
++ preedit.pline.line = xmalloc((term.col + 1) * sizeof(Glyph));
++ for (i = 0; i < term.col + 1; i++)
++ preedit.pline.line[i] = (Glyph){ ' ', ATTR_WDUMMY };
++
++ x = term.col / 2 - preedit.pline.caret;
++ x = MIN(x, 0);
++ x = MAX(x, term.col - preedit.pline.width);
++ x = MIN(x, term.c.x);
++ preedit.pline.offset = x;
++
++ for (i = 0; i < preedit.len; i++) {
++ if (term.col < x)
++ break;
++ if (0 <= x)
++ preedit.pline.line[x] = preedit.text[i];
++ x += MAX(wcwidth(preedit.text[i].u), 1);
++ }
++
++ if (preedit.len == 0)
++ term.dirty[term.c.y] = 1;
++
++ if (preedit.pline.l.u == 0) {
++ preedit.pline.l = preedit.pline.r = (Glyph){
++ 0, ATTR_REVERSE, defaultfg, defaultbg
++ };
++ utf8decode("<", &preedit.pline.l.u, UTF_SIZ);
++ utf8decode(">", &preedit.pline.r.u, UTF_SIZ);
++ }
++}
++
+ void
+ drawregion(int x1, int y1, int x2, int y2)
+ {
+@@ -2660,6 +2780,7 @@ draw(void)
+ drawregion(0, 0, term.col, term.row);
+ xdrawcursor(cx, term.c.y, term.line[term.c.y][cx],
+ term.ocx, term.ocy, term.line[term.ocy][term.ocx]);
++ xdrawpreedit(&preedit.pline, term.line[term.c.y], term.c.y, term.col);
+ term.ocx = cx;
+ term.ocy = term.c.y;
+ xfinishdraw();
+diff --git a/st.h b/st.h
+index fd3b0d8..97e1491 100644
+--- a/st.h
++++ b/st.h
+@@ -69,6 +69,14 @@ typedef struct {
+
+ typedef Glyph *Line;
+
++typedef struct {
++ Line line;
++ int offset;
++ int width;
++ int caret;
++ Glyph l,r;
++} PLine;
++
+ typedef union {
+ int i;
+ uint ui;
+@@ -95,6 +103,8 @@ int ttynew(const char *, char *, const char *, char **);
+ size_t ttyread(void);
+ void ttyresize(int, int);
+ void ttywrite(const char *, size_t, int);
++void pereset(void);
++void peupdate(int, int, int, unsigned short, const ushort *, const char *);
+
+ void resettitle(void);
+
+diff --git a/win.h b/win.h
+index 6de960d..fb5a1d5 100644
+--- a/win.h
++++ b/win.h
+@@ -27,6 +27,7 @@ void xbell(void);
+ void xclipcopy(void);
+ void xdrawcursor(int, int, Glyph, int, int, Glyph);
+ void xdrawline(Line, int, int, int);
++void xdrawpreedit(PLine *, Line, int, int);
+ void xfinishdraw(void);
+ void xloadcols(void);
+ int xsetcolorname(int, const char *);
+diff --git a/x.c b/x.c
+index bd23686..fd6308e 100644
+--- a/x.c
++++ b/x.c
+@@ -99,6 +99,7 @@ typedef struct {
+ XIC xic;
+ XPoint spot;
+ XVaNestedList spotlist;
++ XVaNestedList preeditattrs;
+ } ime;
+ Draw draw;
+ Visual *vis;
+@@ -150,6 +151,10 @@ static int ximopen(Display *);
+ static void ximinstantiate(Display *, XPointer, XPointer);
+ static void ximdestroy(XIM, XPointer, XPointer);
+ static int xicdestroy(XIC, XPointer, XPointer);
++static void xpreeditstart(XIM , XPointer, XPointer);
++static void xpreeditdone(XIM, XPointer, XPointer);
++static void xpreeditdraw(XIM, XPointer, XIMPreeditDrawCallbackStruct *);
++static void xpreeditcaret(XIM, XPointer, XIMPreeditCaretCallbackStruct *);
+ static void xinit(int, int);
+ static void cresize(int, int);
+ static void xresize(int, int);
+@@ -1077,6 +1082,16 @@ ximopen(Display *dpy)
+ {
+ XIMCallback imdestroy = { .client_data = NULL, .callback = ximdestroy };
+ XICCallback icdestroy = { .client_data = NULL, .callback = xicdestroy };
++ static XIMCallback pestart = { NULL, xpreeditstart };
++ static XIMCallback pedone = { NULL, xpreeditdone };
++ static XIMCallback pedraw = { NULL, (XIMProc)xpreeditdraw };
++ static XIMCallback pecaret = { NULL, (XIMProc)xpreeditcaret };
++ XIMStyles *styles;
++ XIMStyle candidates[] = {
++ XIMPreeditCallbacks | XIMStatusNothing,
++ XIMPreeditNothing | XIMStatusNothing
++ };
++ int i, j;
+
+ xw.ime.xim = XOpenIM(xw.dpy, NULL, NULL, NULL);
+ if (xw.ime.xim == NULL)
+@@ -1089,12 +1104,38 @@ ximopen(Display *dpy)
+ xw.ime.spotlist = XVaCreateNestedList(0, XNSpotLocation, &xw.ime.spot,
+ NULL);
+
++ if (XGetIMValues(xw.ime.xim, XNQueryInputStyle, &styles, NULL)) {
++ fprintf(stderr, "XGetIMValues:"
++ "Could not get XNQueryInputStyle.\n");
++ return 1;
++ }
++ for (i = 0; i < LEN(candidates); i++)
++ for (j = 0; j < styles->count_styles; j++)
++ if (candidates[i] == styles->supported_styles[j])
++ goto match;
++ fprintf(stderr, "XGetIMValues: "
++ "None of the candidates styles matched.\n");
++ XFree(styles);
++ return 1;
++match:
++ XFree(styles);
++
+ if (xw.ime.xic == NULL) {
+ xw.ime.xic = XCreateIC(xw.ime.xim, XNInputStyle,
+- XIMPreeditNothing | XIMStatusNothing,
++ candidates[i],
+ XNClientWindow, xw.win,
+ XNDestroyCallback, &icdestroy,
+ NULL);
++ if (xw.ime.xic && candidates[i] & XIMPreeditCallbacks) {
++ xw.ime.preeditattrs = XVaCreateNestedList(0,
++ XNPreeditStartCallback, &pestart,
++ XNPreeditDoneCallback, &pedone,
++ XNPreeditDrawCallback, &pedraw,
++ XNPreeditCaretCallback, &pecaret,
++ NULL);
++ XSetICValues(xw.ime.xic, XNPreeditAttributes,
++ xw.ime.preeditattrs, NULL);
++ }
+ }
+ if (xw.ime.xic == NULL)
+ fprintf(stderr, "XCreateIC: Could not create input context.\n");
+@@ -1123,9 +1164,64 @@ int
+ xicdestroy(XIC xim, XPointer client, XPointer call)
+ {
+ xw.ime.xic = NULL;
++ XFree(xw.ime.preeditattrs);
++ xw.ime.preeditattrs = NULL;
+ return 1;
+ }
+
++void
++xpreeditstart(XIM xim, XPointer client, XPointer call)
++{
++ pereset();
++}
++
++void
++xpreeditdone(XIM xim, XPointer client, XPointer call)
++{
++ pereset();
++}
++
++void
++xpreeditdraw(XIM xim, XPointer client, XIMPreeditDrawCallbackStruct *call)
++{
++ const XIMText *text = call->text;
++ ushort *m, *modes = NULL;
++ int i;
++ XIMFeedback fb;
++
++ if (!text) {
++ peupdate(call->caret, call->chg_first, call->chg_length,
++ 0, NULL, NULL);
++ return;
++ }
++
++ if (text->feedback) {
++ modes = xmalloc(text->length * sizeof(ushort));
++ for (i = 0; i < text->length; i++) {
++ m = modes + i;
++ fb = text->feedback[i];
++ *m = ATTR_NULL;
++ *m |= fb & XIMReverse ? ATTR_REVERSE : ATTR_NULL;
++ *m |= fb & XIMUnderline ? ATTR_UNDERLINE : ATTR_NULL;
++ *m |= fb & XIMHighlight ? ATTR_BOLD : ATTR_NULL;
++ *m |= fb & XIMPrimary ? ATTR_ITALIC : ATTR_NULL;
++ *m |= fb & XIMSecondary ? ATTR_FAINT : ATTR_NULL;
++ *m |= fb & XIMTertiary ? ATTR_BOLD_FAINT : ATTR_NULL;
++ }
++ }
++
++ peupdate(call->caret, call->chg_first, call->chg_length,
++ text->length, modes, text->string.multi_byte);
++
++ free(modes);
++}
++
++void
++xpreeditcaret(XIM xim, XPointer client, XIMPreeditCaretCallbackStruct *call)
++{
++ peupdate(call->position, 0, 0, 0, NULL, NULL);
++}
++
+ void
+ xinit(int cols, int rows)
+ {
+@@ -1682,6 +1778,35 @@ xdrawline(Line line, int x1, int y1, int x2)
+ xdrawglyphfontspecs(specs, base, i, ox, y1);
+ }
+
++void
++xdrawpreedit(PLine *pl, Line base, int y, int col)
++{
++ int head, tail;
++ int tcur;
++ const int offc = pl->offset + pl->caret;
++
++ if (pl->width == 0 || !(win.mode & MODE_FOCUSED))
++ return;
++
++ xdrawline(base, 0, y, col);
++
++ head = MAX(pl->offset, 0);
++ tail = MIN(pl->offset + pl->width, col);
++ if (pl->line[head].mode & ATTR_WDUMMY)
++ head++;
++ xdrawline(pl->line, head, y, tail);
++
++ tcur = win.cursor;
++ win.cursor = 6;
++ xdrawcursor(offc, y, pl->line[offc], head, y, pl->line[head]);
++ win.cursor = tcur;
++
++ if (pl->offset < 0)
++ xdrawline(&pl->l, 0, y, 1);
++ if (col < pl->offset + pl->width)
++ xdrawline(&pl->r - (col - 1), col - 1, y, col);
++}
++
+ void
+ xfinishdraw(void)
+ {