st-preedit-0.9.2.diff (10180B)
1 diff --git a/st.c b/st.c 2 index b9f66e7..ede6d6b 100644 3 --- a/st.c 4 +++ b/st.c 5 @@ -109,6 +109,12 @@ typedef struct { 6 int alt; 7 } Selection; 8 9 +typedef struct { 10 + Glyph *text; /* preedit text */ 11 + int len; /* text length */ 12 + PLine pline; 13 +} Preedit; 14 + 15 /* Internal representation of the screen */ 16 typedef struct { 17 int row; /* nb row */ 18 @@ -202,6 +208,7 @@ static int32_t tdefcolor(const int *, int *, int); 19 static void tdeftran(char); 20 static void tstrsequence(uchar); 21 22 +static void pelineupdate(void); 23 static void drawregion(int, int, int, int); 24 25 static void selnormalize(void); 26 @@ -221,6 +228,7 @@ static ssize_t xwrite(int, const char *, size_t); 27 /* Globals */ 28 static Term term; 29 static Selection sel; 30 +static Preedit preedit; 31 static CSIEscape csiescseq; 32 static STREscape strescseq; 33 static int iofd = 1; 34 @@ -1179,6 +1187,9 @@ tmoveto(int x, int y) 35 term.c.state &= ~CURSOR_WRAPNEXT; 36 term.c.x = LIMIT(x, 0, term.col-1); 37 term.c.y = LIMIT(y, miny, maxy); 38 + 39 + if (preedit.len > 0) 40 + pelineupdate(); 41 } 42 43 void 44 @@ -2627,6 +2638,115 @@ resettitle(void) 45 xsettitle(NULL); 46 } 47 48 +void 49 +pereset(void) 50 +{ 51 + preedit.len = 0; 52 + preedit.pline.width = 0; 53 + pelineupdate(); 54 +} 55 + 56 +void 57 +peupdate(int caret, int chg_fst, int chg_len, 58 + unsigned short str_len, const ushort *modes, const char *str) 59 +{ 60 + int i; 61 + int defmode; 62 + Glyph *text, *g; 63 + int chg_last, len; 64 + 65 + chg_fst = MIN(chg_fst, preedit.len); 66 + chg_len = MIN(chg_len, preedit.len - chg_fst); 67 + chg_last = chg_fst + chg_len; 68 + len = preedit.len - chg_len + (str ? str_len : 0); 69 + 70 + /* default glyph mode */ 71 + defmode = ATTR_NULL; 72 + if (preedit.len > 0) 73 + defmode = (chg_fst < preedit.len) ? 74 + preedit.text[chg_fst].mode : 75 + preedit.text[chg_fst - 1].mode; 76 + defmode &= ~ATTR_WIDE; 77 + 78 + /* create new text and copy old glyphs */ 79 + text = xmalloc(len * sizeof(Glyph)); 80 + if (preedit.len > 0) { 81 + memcpy(text, preedit.text, chg_fst * sizeof(Glyph)); 82 + memcpy(text + chg_fst + (str ? str_len : 0), 83 + preedit.text + chg_last, 84 + (preedit.len - chg_last) * sizeof(Glyph)); 85 + free(preedit.text); 86 + } 87 + preedit.text = text; 88 + preedit.len = len; 89 + 90 + /* new glyphs */ 91 + if (str) { 92 + for (i = 0; i < str_len; i++) { 93 + g = text + chg_fst + i; 94 + *g = (Glyph){ 0, defmode, defaultfg, defaultbg }; 95 + str += utf8decode(str, &g->u, UTF_SIZ); 96 + if (wcwidth(g->u) > 1) 97 + g->mode |= ATTR_WIDE; 98 + } 99 + } 100 + 101 + /* glyph mode */ 102 + if (modes) { 103 + for (i = 0; i < str_len; i++) { 104 + g = text + chg_fst + i; 105 + g->mode = modes[i] | (g->mode & ATTR_WIDE); 106 + } 107 + } 108 + 109 + /* visual width and caret position */ 110 + preedit.pline.width = 0; 111 + preedit.pline.caret = 0; 112 + for (i = 0; i < len; i++) { 113 + preedit.pline.width += MAX(wcwidth(text[i].u), 1); 114 + if (i + 1 == caret) 115 + preedit.pline.caret = preedit.pline.width; 116 + } 117 + 118 + pelineupdate(); 119 +} 120 + 121 +void 122 +pelineupdate() 123 +{ 124 + int i, x; 125 + 126 + free(preedit.pline.line); 127 + preedit.pline.line = xmalloc((term.col + 1) * sizeof(Glyph)); 128 + for (i = 0; i < term.col + 1; i++) 129 + preedit.pline.line[i] = (Glyph){ ' ', ATTR_WDUMMY }; 130 + 131 + x = term.col / 2 - preedit.pline.caret; 132 + x = MIN(x, 0); 133 + x = MAX(x, term.col - preedit.pline.width); 134 + x = MIN(x, term.c.x); 135 + preedit.pline.offset = x; 136 + 137 + for (i = 0; i < preedit.len; i++) { 138 + if (term.col < x) 139 + break; 140 + if (0 <= x) 141 + preedit.pline.line[x] = preedit.text[i]; 142 + x += MAX(wcwidth(preedit.text[i].u), 1); 143 + } 144 + 145 + if (preedit.len == 0) 146 + term.dirty[term.c.y] = 1; 147 + 148 + if (preedit.pline.l.u == 0) { 149 + preedit.pline.l = preedit.pline.r = (Glyph){ 150 + 0, ATTR_REVERSE, defaultfg, defaultbg 151 + }; 152 + utf8decode("<", &preedit.pline.l.u, UTF_SIZ); 153 + utf8decode(">", &preedit.pline.r.u, UTF_SIZ); 154 + } 155 +} 156 + 157 void 158 drawregion(int x1, int y1, int x2, int y2) 159 { 160 @@ -2660,6 +2780,7 @@ draw(void) 161 drawregion(0, 0, term.col, term.row); 162 xdrawcursor(cx, term.c.y, term.line[term.c.y][cx], 163 term.ocx, term.ocy, term.line[term.ocy][term.ocx]); 164 + xdrawpreedit(&preedit.pline, term.line[term.c.y], term.c.y, term.col); 165 term.ocx = cx; 166 term.ocy = term.c.y; 167 xfinishdraw(); 168 diff --git a/st.h b/st.h 169 index fd3b0d8..97e1491 100644 170 --- a/st.h 171 +++ b/st.h 172 @@ -69,6 +69,14 @@ typedef struct { 173 174 typedef Glyph *Line; 175 176 +typedef struct { 177 + Line line; 178 + int offset; 179 + int width; 180 + int caret; 181 + Glyph l,r; 182 +} PLine; 183 + 184 typedef union { 185 int i; 186 uint ui; 187 @@ -95,6 +103,8 @@ int ttynew(const char *, char *, const char *, char **); 188 size_t ttyread(void); 189 void ttyresize(int, int); 190 void ttywrite(const char *, size_t, int); 191 +void pereset(void); 192 +void peupdate(int, int, int, unsigned short, const ushort *, const char *); 193 194 void resettitle(void); 195 196 diff --git a/win.h b/win.h 197 index 6de960d..fb5a1d5 100644 198 --- a/win.h 199 +++ b/win.h 200 @@ -27,6 +27,7 @@ void xbell(void); 201 void xclipcopy(void); 202 void xdrawcursor(int, int, Glyph, int, int, Glyph); 203 void xdrawline(Line, int, int, int); 204 +void xdrawpreedit(PLine *, Line, int, int); 205 void xfinishdraw(void); 206 void xloadcols(void); 207 int xsetcolorname(int, const char *); 208 diff --git a/x.c b/x.c 209 index bd23686..fd6308e 100644 210 --- a/x.c 211 +++ b/x.c 212 @@ -99,6 +99,7 @@ typedef struct { 213 XIC xic; 214 XPoint spot; 215 XVaNestedList spotlist; 216 + XVaNestedList preeditattrs; 217 } ime; 218 Draw draw; 219 Visual *vis; 220 @@ -150,6 +151,10 @@ static int ximopen(Display *); 221 static void ximinstantiate(Display *, XPointer, XPointer); 222 static void ximdestroy(XIM, XPointer, XPointer); 223 static int xicdestroy(XIC, XPointer, XPointer); 224 +static void xpreeditstart(XIM , XPointer, XPointer); 225 +static void xpreeditdone(XIM, XPointer, XPointer); 226 +static void xpreeditdraw(XIM, XPointer, XIMPreeditDrawCallbackStruct *); 227 +static void xpreeditcaret(XIM, XPointer, XIMPreeditCaretCallbackStruct *); 228 static void xinit(int, int); 229 static void cresize(int, int); 230 static void xresize(int, int); 231 @@ -1077,6 +1082,16 @@ ximopen(Display *dpy) 232 { 233 XIMCallback imdestroy = { .client_data = NULL, .callback = ximdestroy }; 234 XICCallback icdestroy = { .client_data = NULL, .callback = xicdestroy }; 235 + static XIMCallback pestart = { NULL, xpreeditstart }; 236 + static XIMCallback pedone = { NULL, xpreeditdone }; 237 + static XIMCallback pedraw = { NULL, (XIMProc)xpreeditdraw }; 238 + static XIMCallback pecaret = { NULL, (XIMProc)xpreeditcaret }; 239 + XIMStyles *styles; 240 + XIMStyle candidates[] = { 241 + XIMPreeditCallbacks | XIMStatusNothing, 242 + XIMPreeditNothing | XIMStatusNothing 243 + }; 244 + int i, j; 245 246 xw.ime.xim = XOpenIM(xw.dpy, NULL, NULL, NULL); 247 if (xw.ime.xim == NULL) 248 @@ -1089,12 +1104,38 @@ ximopen(Display *dpy) 249 xw.ime.spotlist = XVaCreateNestedList(0, XNSpotLocation, &xw.ime.spot, 250 NULL); 251 252 + if (XGetIMValues(xw.ime.xim, XNQueryInputStyle, &styles, NULL)) { 253 + fprintf(stderr, "XGetIMValues:" 254 + "Could not get XNQueryInputStyle.\n"); 255 + return 1; 256 + } 257 + for (i = 0; i < LEN(candidates); i++) 258 + for (j = 0; j < styles->count_styles; j++) 259 + if (candidates[i] == styles->supported_styles[j]) 260 + goto match; 261 + fprintf(stderr, "XGetIMValues: " 262 + "None of the candidates styles matched.\n"); 263 + XFree(styles); 264 + return 1; 265 +match: 266 + XFree(styles); 267 + 268 if (xw.ime.xic == NULL) { 269 xw.ime.xic = XCreateIC(xw.ime.xim, XNInputStyle, 270 - XIMPreeditNothing | XIMStatusNothing, 271 + candidates[i], 272 XNClientWindow, xw.win, 273 XNDestroyCallback, &icdestroy, 274 NULL); 275 + if (xw.ime.xic && candidates[i] & XIMPreeditCallbacks) { 276 + xw.ime.preeditattrs = XVaCreateNestedList(0, 277 + XNPreeditStartCallback, &pestart, 278 + XNPreeditDoneCallback, &pedone, 279 + XNPreeditDrawCallback, &pedraw, 280 + XNPreeditCaretCallback, &pecaret, 281 + NULL); 282 + XSetICValues(xw.ime.xic, XNPreeditAttributes, 283 + xw.ime.preeditattrs, NULL); 284 + } 285 } 286 if (xw.ime.xic == NULL) 287 fprintf(stderr, "XCreateIC: Could not create input context.\n"); 288 @@ -1123,9 +1164,64 @@ int 289 xicdestroy(XIC xim, XPointer client, XPointer call) 290 { 291 xw.ime.xic = NULL; 292 + XFree(xw.ime.preeditattrs); 293 + xw.ime.preeditattrs = NULL; 294 return 1; 295 } 296 297 +void 298 +xpreeditstart(XIM xim, XPointer client, XPointer call) 299 +{ 300 + pereset(); 301 +} 302 + 303 +void 304 +xpreeditdone(XIM xim, XPointer client, XPointer call) 305 +{ 306 + pereset(); 307 +} 308 + 309 +void 310 +xpreeditdraw(XIM xim, XPointer client, XIMPreeditDrawCallbackStruct *call) 311 +{ 312 + const XIMText *text = call->text; 313 + ushort *m, *modes = NULL; 314 + int i; 315 + XIMFeedback fb; 316 + 317 + if (!text) { 318 + peupdate(call->caret, call->chg_first, call->chg_length, 319 + 0, NULL, NULL); 320 + return; 321 + } 322 + 323 + if (text->feedback) { 324 + modes = xmalloc(text->length * sizeof(ushort)); 325 + for (i = 0; i < text->length; i++) { 326 + m = modes + i; 327 + fb = text->feedback[i]; 328 + *m = ATTR_NULL; 329 + *m |= fb & XIMReverse ? ATTR_REVERSE : ATTR_NULL; 330 + *m |= fb & XIMUnderline ? ATTR_UNDERLINE : ATTR_NULL; 331 + *m |= fb & XIMHighlight ? ATTR_BOLD : ATTR_NULL; 332 + *m |= fb & XIMPrimary ? ATTR_ITALIC : ATTR_NULL; 333 + *m |= fb & XIMSecondary ? ATTR_FAINT : ATTR_NULL; 334 + *m |= fb & XIMTertiary ? ATTR_BOLD_FAINT : ATTR_NULL; 335 + } 336 + } 337 + 338 + peupdate(call->caret, call->chg_first, call->chg_length, 339 + text->length, modes, text->string.multi_byte); 340 + 341 + free(modes); 342 +} 343 + 344 +void 345 +xpreeditcaret(XIM xim, XPointer client, XIMPreeditCaretCallbackStruct *call) 346 +{ 347 + peupdate(call->position, 0, 0, 0, NULL, NULL); 348 +} 349 + 350 void 351 xinit(int cols, int rows) 352 { 353 @@ -1682,6 +1778,35 @@ xdrawline(Line line, int x1, int y1, int x2) 354 xdrawglyphfontspecs(specs, base, i, ox, y1); 355 } 356 357 +void 358 +xdrawpreedit(PLine *pl, Line base, int y, int col) 359 +{ 360 + int head, tail; 361 + int tcur; 362 + const int offc = pl->offset + pl->caret; 363 + 364 + if (pl->width == 0 || !(win.mode & MODE_FOCUSED)) 365 + return; 366 + 367 + xdrawline(base, 0, y, col); 368 + 369 + head = MAX(pl->offset, 0); 370 + tail = MIN(pl->offset + pl->width, col); 371 + if (pl->line[head].mode & ATTR_WDUMMY) 372 + head++; 373 + xdrawline(pl->line, head, y, tail); 374 + 375 + tcur = win.cursor; 376 + win.cursor = 6; 377 + xdrawcursor(offc, y, pl->line[offc], head, y, pl->line[head]); 378 + win.cursor = tcur; 379 + 380 + if (pl->offset < 0) 381 + xdrawline(&pl->l, 0, y, 1); 382 + if (col < pl->offset + pl->width) 383 + xdrawline(&pl->r - (col - 1), col - 1, y, col); 384 +} 385 + 386 void 387 xfinishdraw(void) 388 {