1 /* 2 * Copyright (c) 2011, 2014, 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 #include "glass_gtkcompat.h" 30 31 #include <cstring> 32 #include <cstdlib> 33 34 bool WindowContextBase::hasIME() { 35 return xim.enabled; 36 } 37 38 static XKeyPressedEvent convert_event(GdkEventKey *event) { 39 XKeyPressedEvent result; 40 memset(&result, 0, sizeof (result)); 41 42 result.type = (event->type == GDK_KEY_PRESS) ? KeyPress : KeyRelease; 43 result.send_event = event->send_event; 44 result.display = gdk_x11_display_get_xdisplay(glass_gdk_window_get_display(event->window)); 45 result.window = result.subwindow = GDK_WINDOW_XID(event->window); 46 result.root = GDK_WINDOW_XID(gdk_screen_get_root_window(glass_gdk_window_get_screen(event->window))); 47 result.time = event->time; 48 result.state = event->state; 49 result.keycode = event->hardware_keycode; 50 result.same_screen = True; 51 52 return result; 53 } 54 55 bool WindowContextBase::im_filter_keypress(GdkEventKey* event) { 56 static size_t buf_len = 12; 57 static char *buffer = NULL; 58 59 if (buffer == NULL) { 60 buffer = (char*)malloc(buf_len * sizeof (char)); 61 } 62 63 KeySym keysym; 64 Status status; 65 XKeyPressedEvent xevent = convert_event(event); 66 if (XFilterEvent((XEvent*) & xevent, GDK_WINDOW_XID(gdk_window))) { 67 return TRUE; 68 } 69 70 if (event->type == GDK_KEY_RELEASE) { 71 process_key(event); 72 return TRUE; 73 } 74 75 int len = Xutf8LookupString(xim.ic, &xevent, buffer, buf_len - 1, &keysym, &status); 76 if (status == XBufferOverflow) { 77 buf_len = len + 1; 78 buffer = (char*)realloc(buffer, buf_len * sizeof (char)); 79 len = Xutf8LookupString(xim.ic, &xevent, buffer, buf_len - 1, 80 &keysym, &status); 81 } 82 switch (status) { 83 case XLookupKeySym: 84 case XLookupBoth: 85 if (xevent.keycode) { 86 //process it as a normal key 87 process_key(event); 88 break; 89 } 90 // fall-through 91 case XLookupChars: 92 buffer[len] = 0; 93 jstring str = mainEnv->NewStringUTF(buffer); 94 EXCEPTION_OCCURED(mainEnv); 95 jsize slen = mainEnv->GetStringLength(str); 96 mainEnv->CallVoidMethod(jview, 97 jViewNotifyInputMethod, 98 str, 99 NULL, NULL, NULL, 100 slen, 101 slen, 102 0); 103 LOG_EXCEPTION(mainEnv) 104 105 break; 106 } 107 108 return TRUE; 109 } 110 111 bool WindowContextBase::filterIME(GdkEvent * event) { 112 if (!hasIME()) { 113 return false; 114 } 115 116 switch (event->type) { 117 case GDK_KEY_PRESS: 118 case GDK_KEY_RELEASE: 119 return im_filter_keypress(reinterpret_cast<GdkEventKey*> (event)); 120 default: 121 return FALSE; 122 } 123 } 124 125 //Note: this function must return int, despite the fact it doesn't conform to XIMProc type. 126 // This is required in documentation of XIM 127 static int im_preedit_start(XIM im_xim, XPointer client, XPointer call) { 128 (void)im_xim; 129 (void)call; 130 131 mainEnv->CallVoidMethod((jobject) client, jViewNotifyPreeditMode, JNI_TRUE); 132 CHECK_JNI_EXCEPTION_RET(mainEnv, -1); 133 return -1; // No restrictions 134 } 135 136 static void im_preedit_done(XIM im_xim, XPointer client, XPointer call) { 137 (void)im_xim; 138 (void)call; 139 140 mainEnv->CallVoidMethod((jobject) client, jViewNotifyPreeditMode, JNI_FALSE); 141 CHECK_JNI_EXCEPTION(mainEnv); 142 } 143 144 static void im_preedit_draw(XIM im_xim, XPointer client, XPointer call) { 145 (void)im_xim; 146 (void)call; 147 148 XIMPreeditDrawCallbackStruct *data = (XIMPreeditDrawCallbackStruct*) call; 149 jstring text = NULL; 150 jbyteArray attr = NULL; 151 152 if (data->text != NULL) { 153 if (data->text->string.multi_byte) { 154 if (data->text->encoding_is_wchar) { 155 size_t csize = wcstombs(NULL, data->text->string.wide_char, 0); 156 char *ctext = new char[csize + 1]; 157 wcstombs(ctext, data->text->string.wide_char, csize + 1); 158 text = mainEnv->NewStringUTF(ctext); 159 delete[] ctext; 160 CHECK_JNI_EXCEPTION(mainEnv); 161 } else { 162 text = mainEnv->NewStringUTF(data->text->string.multi_byte); 163 CHECK_JNI_EXCEPTION(mainEnv); 164 } 165 } 166 167 if (XIMFeedback* fb = data->text->feedback) { 168 attr = mainEnv->NewByteArray(data->text->length); 169 CHECK_JNI_EXCEPTION(mainEnv) 170 jbyte v[data->text->length]; 171 for (int i = 0; i < data->text->length; i++) { 172 if (fb[i] & XIMReverse) { 173 v[i] = com_sun_glass_ui_View_IME_ATTR_TARGET_NOTCONVERTED; 174 } else if (fb[i] & XIMHighlight) { 175 v[i] = com_sun_glass_ui_View_IME_ATTR_TARGET_CONVERTED; 176 } else if (fb[i] & XIMUnderline) { 177 v[i] = com_sun_glass_ui_View_IME_ATTR_CONVERTED; 178 } else { 179 v[i] = com_sun_glass_ui_View_IME_ATTR_INPUT; 180 } 181 } 182 mainEnv->SetByteArrayRegion(attr, 0, data->text->length, v); 183 CHECK_JNI_EXCEPTION(mainEnv) 184 } 185 } 186 187 mainEnv->CallVoidMethod((jobject)client, jViewNotifyInputMethodDraw, 188 text, data->chg_first, data->chg_length, data->caret, attr); 189 CHECK_JNI_EXCEPTION(mainEnv) 190 } 191 192 static void im_preedit_caret(XIM im_xim, XPointer client, XPointer call) { 193 (void)im_xim; 194 195 XIMPreeditCaretCallbackStruct *data = (XIMPreeditCaretCallbackStruct*) call; 196 mainEnv->CallVoidMethod((jobject)client, jViewNotifyInputMethodCaret, 197 data->position, data->direction, data->style); 198 CHECK_JNI_EXCEPTION(mainEnv) 199 } 200 201 static XIMStyle get_best_supported_style(XIM im_xim) 202 { 203 XIMStyles* styles; 204 int i; 205 XIMStyle result = 0; 206 207 if (XGetIMValues(im_xim, XNQueryInputStyle, &styles, NULL) != NULL) { // NULL means it's OK 208 return 0; 209 } 210 211 for (i = 0; i < styles->count_styles; ++i) { 212 if (styles->supported_styles[i] == (XIMPreeditCallbacks | XIMStatusNothing) 213 || styles->supported_styles[i] == (XIMPreeditNothing | XIMStatusNothing)) { 214 result = styles->supported_styles[i]; 215 break; 216 } 217 } 218 219 XFree(styles); 220 221 return result; 222 } 223 224 void WindowContextBase::enableOrResetIME() { 225 Display *display = gdk_x11_display_get_xdisplay(glass_gdk_window_get_display(gdk_window)); 226 if (xim.im == NULL || xim.ic == NULL) { 227 xim.im = XOpenIM(display, NULL, NULL, NULL); 228 if (xim.im == NULL) { 229 return; 230 } 231 232 XIMStyle styles = get_best_supported_style(xim.im); 233 if (styles == 0) { 234 return; 235 } 236 237 XIMCallback startCallback = {(XPointer) jview, (XIMProc) im_preedit_start}; 238 XIMCallback doneCallback = {(XPointer) jview, im_preedit_done}; 239 XIMCallback drawCallback = {(XPointer) jview, im_preedit_draw}; 240 XIMCallback caretCallback = {(XPointer) jview, im_preedit_caret}; 241 242 XVaNestedList list = XVaCreateNestedList(0, 243 XNPreeditStartCallback, &startCallback, 244 XNPreeditDoneCallback, &doneCallback, 245 XNPreeditDrawCallback, &drawCallback, 246 XNPreeditCaretCallback, &caretCallback, 247 NULL); 248 249 xim.ic = XCreateIC(xim.im, 250 XNInputStyle, styles, 251 XNClientWindow, GDK_WINDOW_XID(gdk_window), 252 XNPreeditAttributes, list, 253 NULL); 254 255 XFree(list); 256 257 if (xim.ic == NULL) { 258 return; 259 } 260 } 261 262 if (xim.enabled) { //called when changed focus to different input 263 XmbResetIC(xim.ic); 264 } 265 266 267 XSetICFocus(xim.ic); 268 269 xim.enabled = TRUE; 270 } 271 272 void WindowContextBase::disableIME() { 273 if (xim.ic != NULL) { 274 XUnsetICFocus(xim.ic); 275 } 276 }