1 /* 2 * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 #include "com_sun_glass_ui_View.h" 27 #include "glass_window.h" 28 #include "glass_general.h" 29 30 #include <cstring> 31 #include <cstdlib> 32 33 bool WindowContextBase::hasIME() { 34 return xim.enabled; 35 } 36 37 static XKeyPressedEvent convert_event(GdkEventKey *event) { 38 XKeyPressedEvent result; 39 memset(&result, 0, sizeof (result)); 40 41 result.type = (event->type == GDK_KEY_PRESS) ? KeyPress : KeyRelease; 42 result.send_event = event->send_event; 43 result.display = gdk_x11_display_get_xdisplay(gdk_window_get_display(event->window)); 44 result.window = result.subwindow = GDK_WINDOW_XID(event->window); 45 result.root = GDK_WINDOW_XID(gdk_screen_get_root_window(glass_gdk_window_get_screen(event->window))); 46 result.time = event->time; 47 result.state = event->state; 48 result.keycode = event->hardware_keycode; 49 result.same_screen = True; 50 51 return result; 52 } 53 54 bool WindowContextBase::im_filter_keypress(GdkEventKey* event) { 55 static size_t buf_len = 12; 56 static char *buffer = NULL; 57 58 if (buffer == NULL) { 59 buffer = (char*)malloc(buf_len * sizeof (char)); 60 } 61 62 KeySym keysym; 63 Status status; 64 XKeyPressedEvent xevent = convert_event(event); 65 if (XFilterEvent((XEvent*) & xevent, GDK_WINDOW_XID(gdk_window))) { 66 return TRUE; 67 } 68 69 if (event->type == GDK_KEY_RELEASE) { 70 process_key(event); 71 return TRUE; 72 } 73 74 int len = Xutf8LookupString(xim.ic, &xevent, buffer, buf_len - 1, &keysym, &status); 75 if (status == XBufferOverflow) { 76 buf_len = len + 1; 77 buffer = (char*)realloc(buffer, buf_len * sizeof (char)); 78 len = Xutf8LookupString(xim.ic, &xevent, buffer, buf_len - 1, 79 &keysym, &status); 80 } 81 switch (status) { 82 case XLookupKeySym: 83 case XLookupBoth: 84 if (xevent.keycode) { 85 //process it as a normal key 86 process_key(event); 87 break; 88 } 89 // fall-through 90 case XLookupChars: 91 buffer[len] = 0; 92 jstring str = mainEnv->NewStringUTF(buffer); 93 EXCEPTION_OCCURED(mainEnv); 94 jsize slen = mainEnv->GetStringLength(str); 95 mainEnv->CallVoidMethod(jview, 96 jViewNotifyInputMethod, 97 str, 98 NULL, NULL, NULL, 99 slen, 100 slen, 101 0); 102 LOG_EXCEPTION(mainEnv) 103 104 break; 105 } 106 107 return TRUE; 108 } 109 110 bool WindowContextBase::filterIME(GdkEvent * event) { 111 if (!hasIME()) { 112 return false; 113 } 114 115 switch (event->type) { 116 case GDK_KEY_PRESS: 117 case GDK_KEY_RELEASE: 118 return im_filter_keypress(reinterpret_cast<GdkEventKey*> (event)); 119 default: 120 return FALSE; 121 } 122 } 123 124 //Note: this function must return int, despite the fact it doesn't conform to XIMProc type. 125 // This is required in documentation of XIM 126 static int im_preedit_start(XIM im_xim, XPointer client, XPointer call) { 127 (void)im_xim; 128 (void)call; 129 130 mainEnv->CallVoidMethod((jobject) client, jViewNotifyPreeditMode, JNI_TRUE); 131 CHECK_JNI_EXCEPTION_RET(mainEnv, -1); 132 return -1; // No restrictions 133 } 134 135 static void im_preedit_done(XIM im_xim, XPointer client, XPointer call) { 136 (void)im_xim; 137 (void)call; 138 139 mainEnv->CallVoidMethod((jobject) client, jViewNotifyPreeditMode, JNI_FALSE); 140 CHECK_JNI_EXCEPTION(mainEnv); 141 } 142 143 static void im_preedit_draw(XIM im_xim, XPointer client, XPointer call) { 144 (void)im_xim; 145 (void)call; 146 147 XIMPreeditDrawCallbackStruct *data = (XIMPreeditDrawCallbackStruct*) call; 148 jstring text = NULL; 149 jbyteArray attr = NULL; 150 151 if (data->text != NULL) { 152 if (data->text->string.multi_byte) { 153 if (data->text->encoding_is_wchar) { 154 size_t csize = wcstombs(NULL, data->text->string.wide_char, 0); 155 char *ctext = new char[csize + 1]; 156 wcstombs(ctext, data->text->string.wide_char, csize + 1); 157 text = mainEnv->NewStringUTF(ctext); 158 delete[] ctext; 159 CHECK_JNI_EXCEPTION(mainEnv); 160 } else { 161 text = mainEnv->NewStringUTF(data->text->string.multi_byte); 162 CHECK_JNI_EXCEPTION(mainEnv); 163 } 164 } 165 166 if (XIMFeedback* fb = data->text->feedback) { 167 attr = mainEnv->NewByteArray(data->text->length); 168 CHECK_JNI_EXCEPTION(mainEnv) 169 jbyte v[data->text->length]; 170 for (int i = 0; i < data->text->length; i++) { 171 if (fb[i] & XIMReverse) { 172 v[i] = com_sun_glass_ui_View_IME_ATTR_TARGET_NOTCONVERTED; 173 } else if (fb[i] & XIMHighlight) { 174 v[i] = com_sun_glass_ui_View_IME_ATTR_TARGET_CONVERTED; 175 } else if (fb[i] & XIMUnderline) { 176 v[i] = com_sun_glass_ui_View_IME_ATTR_CONVERTED; 177 } else { 178 v[i] = com_sun_glass_ui_View_IME_ATTR_INPUT; 179 } 180 } 181 mainEnv->SetByteArrayRegion(attr, 0, data->text->length, v); 182 CHECK_JNI_EXCEPTION(mainEnv) 183 } 184 } 185 186 mainEnv->CallVoidMethod((jobject)client, jViewNotifyInputMethodDraw, 187 text, data->chg_first, data->chg_length, data->caret, attr); 188 CHECK_JNI_EXCEPTION(mainEnv) 189 } 190 191 static void im_preedit_caret(XIM im_xim, XPointer client, XPointer call) { 192 (void)im_xim; 193 194 XIMPreeditCaretCallbackStruct *data = (XIMPreeditCaretCallbackStruct*) call; 195 mainEnv->CallVoidMethod((jobject)client, jViewNotifyInputMethodCaret, 196 data->position, data->direction, data->style); 197 CHECK_JNI_EXCEPTION(mainEnv) 198 } 199 200 static XIMStyle get_best_supported_style(XIM im_xim) 201 { 202 XIMStyles* styles; 203 int i; 204 XIMStyle result = 0; 205 206 if (XGetIMValues(im_xim, XNQueryInputStyle, &styles, NULL) != NULL) { // NULL means it's OK 207 return 0; 208 } 209 210 for (i = 0; i < styles->count_styles; ++i) { 211 if (styles->supported_styles[i] == (XIMPreeditCallbacks | XIMStatusNothing) 212 || styles->supported_styles[i] == (XIMPreeditNothing | XIMStatusNothing)) { 213 result = styles->supported_styles[i]; 214 break; 215 } 216 } 217 218 XFree(styles); 219 220 return result; 221 } 222 223 void WindowContextBase::enableOrResetIME() { 224 Display *display = gdk_x11_display_get_xdisplay(gdk_window_get_display(gdk_window)); 225 if (xim.im == NULL || xim.ic == NULL) { 226 xim.im = XOpenIM(display, NULL, NULL, NULL); 227 if (xim.im == NULL) { 228 return; 229 } 230 231 XIMStyle styles = get_best_supported_style(xim.im); 232 if (styles == 0) { 233 return; 234 } 235 236 XIMCallback startCallback = {(XPointer) jview, (XIMProc) im_preedit_start}; 237 XIMCallback doneCallback = {(XPointer) jview, im_preedit_done}; 238 XIMCallback drawCallback = {(XPointer) jview, im_preedit_draw}; 239 XIMCallback caretCallback = {(XPointer) jview, im_preedit_caret}; 240 241 XVaNestedList list = XVaCreateNestedList(0, 242 XNPreeditStartCallback, &startCallback, 243 XNPreeditDoneCallback, &doneCallback, 244 XNPreeditDrawCallback, &drawCallback, 245 XNPreeditCaretCallback, &caretCallback, 246 NULL); 247 248 xim.ic = XCreateIC(xim.im, 249 XNInputStyle, styles, 250 XNClientWindow, GDK_WINDOW_XID(gdk_window), 251 XNPreeditAttributes, list, 252 NULL); 253 254 XFree(list); 255 256 if (xim.ic == NULL) { 257 return; 258 } 259 } 260 261 if (xim.enabled) { //called when changed focus to different input 262 XmbResetIC(xim.ic); 263 } 264 265 266 XSetICFocus(xim.ic); 267 268 xim.enabled = TRUE; 269 } 270 271 void WindowContextBase::disableIME() { 272 if (xim.ic != NULL) { 273 XUnsetICFocus(xim.ic); 274 } 275 }