inputmethod.c (5133B)
1 static size_t nextrunetext(const char *text, size_t position, int inc) 2 { 3 ssize_t n; 4 5 /* return location of next utf8 rune in the given direction (+1 or -1) */ 6 for (n = position + inc; n + inc >= 0 && (text[n] & 0xc0) == 0x80; n += inc) 7 ; 8 return n; 9 } 10 11 /* return bytes from beginning of text to nth utf8 rune to the right */ 12 static size_t runebytes(const char *text, size_t n) 13 { 14 size_t ret; 15 16 ret = 0; 17 while (n-- > 0) 18 ret += nextrunetext(text + ret, 0, 1); 19 return ret; 20 } 21 22 /* return number of characters from beginning of text to nth byte to the right 23 */ 24 static size_t runechars(const char *text, size_t n) 25 { 26 size_t ret, i; 27 28 ret = i = 0; 29 while (i < n) { 30 i += nextrunetext(text + i, 0, 1); 31 ret++; 32 } 33 return ret; 34 } 35 36 /* move caret on pre-edit text */ 37 static void preeditcaret(XIC xic, XPointer clientdata, XPointer calldata) 38 { 39 XIMPreeditCaretCallbackStruct *pcaret; 40 41 (void)xic; 42 pcaret = (XIMPreeditCaretCallbackStruct *)calldata; 43 if (!pcaret) 44 return; 45 switch (pcaret->direction) { 46 case XIMForwardChar: 47 cursor = nextrune(+1); 48 break; 49 case XIMBackwardChar: 50 cursor = nextrune(-1); 51 break; 52 case XIMForwardWord: 53 movewordedge(+1); 54 break; 55 case XIMBackwardWord: 56 movewordedge(-1); 57 break; 58 case XIMLineStart: 59 cursor = 0; 60 break; 61 case XIMLineEnd: 62 if (preview[cursor] != '\0') 63 cursor = strlen(preview); 64 break; 65 case XIMAbsolutePosition: 66 cursor = runebytes(text, pcaret->position); 67 break; 68 case XIMDontChange: 69 /* do nothing */ 70 break; 71 case XIMCaretUp: 72 case XIMCaretDown: 73 case XIMNextLine: 74 case XIMPreviousLine: 75 /* not implemented */ 76 break; 77 } 78 pcaret->position = runechars(preview, cursor); 79 } 80 81 /* start input method pre-editing */ 82 static int preeditstart(XIC xic, XPointer clientdata, XPointer calldata) 83 { 84 (void)xic; 85 (void)calldata; 86 (void)clientdata; 87 composing = 1; 88 printf("PREEDIT\n"); 89 return BUFSIZ; 90 } 91 92 /* end input method pre-editing */ 93 static void preeditdone(XIC xic, XPointer clientdata, XPointer calldata) 94 { 95 (void)xic; 96 (void)clientdata; 97 (void)calldata; 98 printf("DONE\n"); 99 100 composing = 0; 101 } 102 103 /* draw input method pre-edit text */ 104 static void preeditdraw(XIC xic, XPointer clientdata, XPointer calldata) 105 { 106 XIMPreeditDrawCallbackStruct *pdraw; 107 size_t beg, dellen, inslen, endlen; 108 109 printf("DRAW\n"); 110 111 (void)xic; 112 pdraw = (XIMPreeditDrawCallbackStruct *)calldata; 113 if (!pdraw) 114 return; 115 116 /* we do not support wide characters */ 117 if (pdraw->text && pdraw->text->encoding_is_wchar == True) { 118 fputs("warning: wchar is not supportecd; use utf8", stderr); 119 return; 120 } 121 122 beg = runebytes(text, pdraw->chg_first); 123 dellen = runebytes(preview + beg, pdraw->chg_length); 124 inslen = pdraw->text ? runebytes(pdraw->text->string.multi_byte, pdraw->text->length) : 0; 125 endlen = 0; 126 if (beg + dellen < strlen(preview)) 127 endlen = strlen(preview + beg + dellen); 128 129 /* we cannot change text past the end of our pre-edit string */ 130 131 if (beg + dellen >= BUFSIZ || beg + inslen >= BUFSIZ) 132 return; 133 134 /* get space for text to be copied, and copy it */ 135 memmove(preview + beg + inslen, preview + beg + dellen, endlen + 1); 136 if (pdraw->text && pdraw->text->length) 137 memcpy(preview + beg, pdraw->text->string.multi_byte, inslen); 138 (preview + beg + inslen + endlen)[0] = '\0'; 139 140 /* get caret position */ 141 cursor = runebytes(text, pdraw->caret); 142 } 143 144 static void init_input_method(XIM xim) 145 { 146 XVaNestedList preedit = NULL; 147 XICCallback start, done, draw, caret; 148 XIMStyle preeditstyle; 149 XIMStyle statusstyle; 150 XIMStyles *imstyles; 151 int i; 152 153 /* get styles supported by input method */ 154 if (XGetIMValues(xim, XNQueryInputStyle, &imstyles, NULL) != NULL) 155 fputs("XGetIMValues: could not obtain input method values", stderr); 156 157 /* check whether input method support on-the-spot pre-editing */ 158 preeditstyle = XIMPreeditNothing; 159 statusstyle = XIMStatusNothing; 160 for (i = 0; i < imstyles->count_styles; i++) { 161 if (imstyles->supported_styles[i] & XIMPreeditCallbacks) { 162 preeditstyle = XIMPreeditCallbacks; 163 break; 164 } 165 } 166 167 /* create callbacks for the input context */ 168 start.client_data = NULL; 169 done.client_data = NULL; 170 draw.client_data = (XPointer)text; 171 caret.client_data = (XPointer)text; 172 start.callback = (XICProc)preeditstart; 173 done.callback = (XICProc)preeditdone; 174 draw.callback = (XICProc)preeditdraw; 175 caret.callback = (XICProc)preeditcaret; 176 177 /* create list of values for input context */ 178 preedit = XVaCreateNestedList(0, XNPreeditStartCallback, &start, XNPreeditDoneCallback, 179 &done, XNPreeditDrawCallback, &draw, XNPreeditCaretCallback, 180 &caret, NULL); 181 if (preedit == NULL) 182 fputs("XVaCreateNestedList: could not create nested list", stderr); 183 184 xic = XCreateIC(xim, XNInputStyle, preeditstyle | statusstyle, XNPreeditAttributes, preedit, 185 XNClientWindow, win, XNFocusWindow, win, NULL); 186 XFree(preedit); 187 188 long eventmask; 189 /* get events the input method is interested in */ 190 if (XGetICValues(xic, XNFilterEvents, &eventmask, NULL)) 191 fputs("XGetICValues: could not obtain input context values", stderr); 192 193 XSelectInput(dpy, win, 194 ExposureMask | KeyPressMask | VisibilityChangeMask | ButtonPressMask | 195 PointerMotionMask | eventmask); 196 }