1 /*
2 * Copyright (c) 1997, 2020, 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 #ifdef HEADLESS
27 #error This file should not be included in headless library
28 #endif
29
30 #include "awt.h"
31 #include "awt_p.h"
32
33 #include <sun_awt_X11InputMethodBase.h>
34 #include <sun_awt_X11_XInputMethod.h>
35
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <sys/time.h>
39 #include <X11/keysym.h>
40 #include <X11/Xlib.h>
41
42 #define THROW_OUT_OF_MEMORY_ERROR() \
43 JNU_ThrowOutOfMemoryError((JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2), NULL)
44
45 struct X11InputMethodIDs {
46 jfieldID pData;
47 } x11InputMethodIDs;
48
49 static int PreeditStartCallback(XIC, XPointer, XPointer);
50 static void PreeditDoneCallback(XIC, XPointer, XPointer);
51 static void PreeditDrawCallback(XIC, XPointer,
52 XIMPreeditDrawCallbackStruct *);
53 static void PreeditCaretCallback(XIC, XPointer,
54 XIMPreeditCaretCallbackStruct *);
55 #if defined(__linux__) || defined(_ALLBSD_SOURCE)
56 static void StatusStartCallback(XIC, XPointer, XPointer);
57 static void StatusDoneCallback(XIC, XPointer, XPointer);
58 static void StatusDrawCallback(XIC, XPointer,
59 XIMStatusDrawCallbackStruct *);
60 #endif
61
62 #define ROOT_WINDOW_STYLES (XIMPreeditNothing | XIMStatusNothing)
63 #define NO_STYLES (XIMPreeditNone | XIMStatusNone)
64
65 #define PreeditStartIndex 0
66 #define PreeditDoneIndex 1
67 #define PreeditDrawIndex 2
68 #define PreeditCaretIndex 3
69 #if defined(__linux__) || defined(_ALLBSD_SOURCE)
70 #define StatusStartIndex 4
71 #define StatusDoneIndex 5
72 #define StatusDrawIndex 6
73 #define NCALLBACKS 7
74 #else
75 #define NCALLBACKS 4
76 #endif
77
78 /*
79 * Callback function pointers: the order has to match the *Index
80 * values above.
81 */
82 static XIMProc callback_funcs[NCALLBACKS] = {
83 (XIMProc)(void *)&PreeditStartCallback,
84 (XIMProc)PreeditDoneCallback,
85 (XIMProc)PreeditDrawCallback,
86 (XIMProc)PreeditCaretCallback,
87 #if defined(__linux__) || defined(_ALLBSD_SOURCE)
88 (XIMProc)StatusStartCallback,
89 (XIMProc)StatusDoneCallback,
90 (XIMProc)StatusDrawCallback,
91 #endif
92 };
93
94 #if defined(__linux__) || defined(_ALLBSD_SOURCE)
95 #define MAX_STATUS_LEN 100
96 typedef struct {
97 Window w; /*status window id */
98 Window root; /*the root window id */
99 Window parent; /*parent shell window */
100 int x, y; /*parent's upperleft position */
101 int width, height; /*parent's width, height */
102 GC lightGC; /*gc for light border */
103 GC dimGC; /*gc for dim border */
104 GC bgGC; /*normal painting */
105 GC fgGC; /*normal painting */
106 int statusW, statusH; /*status window's w, h */
107 int rootW, rootH; /*root window's w, h */
108 int bWidth; /*border width */
109 char status[MAX_STATUS_LEN]; /*status text */
110 XFontSet fontset; /*fontset for drawing */
111 int off_x, off_y;
112 Bool on; /*if the status window on*/
113 } StatusWindow;
114 #endif
115
116 /*
117 * X11InputMethodData keeps per X11InputMethod instance information. A pointer
118 * to this data structure is kept in an X11InputMethod object (pData).
119 */
120 typedef struct _X11InputMethodData {
121 XIC current_ic; /* current X Input Context */
122 XIC ic_active; /* X Input Context for active clients */
123 XIC ic_passive; /* X Input Context for passive clients */
124 XIMCallback *callbacks; /* callback parameters */
125 jobject x11inputmethod; /* global ref to X11InputMethod instance */
126 /* associated with the XIC */
127 #if defined(__linux__) || defined(_ALLBSD_SOURCE)
128 StatusWindow *statusWindow; /* our own status window */
129 #endif
130 char *lookup_buf; /* buffer used for XmbLookupString */
131 int lookup_buf_len; /* lookup buffer size in bytes */
132 } X11InputMethodData;
133
134 /*
135 * When XIC is created, a global reference is created for
136 * sun.awt.X11InputMethod object so that it could be used by the XIM callback
137 * functions. This could be a dangerous thing to do when the original
138 * X11InputMethod object is garbage collected and as a result,
139 * destroyX11InputMethodData is called to delete the global reference.
140 * If any XIM callback function still holds and uses the "already deleted"
141 * global reference, disaster is going to happen. So we have to maintain
142 * a list for these global references which is consulted first when the
143 * callback functions or any function tries to use "currentX11InputMethodObject"
144 * which always refers to the global reference try to use it.
145 *
146 */
147 typedef struct _X11InputMethodGRefNode {
148 jobject inputMethodGRef;
149 struct _X11InputMethodGRefNode* next;
150 } X11InputMethodGRefNode;
151
152 X11InputMethodGRefNode *x11InputMethodGRefListHead = NULL;
153
154 /* reference to the current X11InputMethod instance, it is always
155 point to the global reference to the X11InputMethodObject since
156 it could be referenced by different threads. */
157 jobject currentX11InputMethodInstance = NULL;
158
159 Window currentFocusWindow = 0; /* current window that has focus for input
160 method. (the best place to put this
161 information should be
162 currentX11InputMethodInstance's pData) */
163 static XIM X11im = NULL;
164 Display * dpy = NULL;
165
166 #define GetJNIEnv() (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2)
167
168 static void DestroyXIMCallback(XIM, XPointer, XPointer);
169 static void OpenXIMCallback(Display *, XPointer, XPointer);
170 /* Solaris XIM Extention */
171 #define XNCommitStringCallback "commitStringCallback"
172 static void CommitStringCallback(XIC, XPointer, XPointer);
173
174 static X11InputMethodData * getX11InputMethodData(JNIEnv *, jobject);
175 static void setX11InputMethodData(JNIEnv *, jobject, X11InputMethodData *);
176 static void destroyX11InputMethodData(JNIEnv *, X11InputMethodData *);
177 static void freeX11InputMethodData(JNIEnv *, X11InputMethodData *);
178 #if defined(__linux__) || defined(_ALLBSD_SOURCE)
179 static Window getParentWindow(Window);
180 #endif
181
182 /*
183 * This function is stolen from /src/solaris/hpi/src/system_md.c
184 * It is used in setting the time in Java-level InputEvents
185 */
186 jlong
awt_util_nowMillisUTC()187 awt_util_nowMillisUTC()
188 {
189 struct timeval t;
190 gettimeofday(&t, NULL);
191 return ((jlong)t.tv_sec) * 1000 + (jlong)(t.tv_usec/1000);
192 }
193
194 /*
195 * Converts the wchar_t string to a multi-byte string calling wcstombs(). A
196 * buffer is allocated by malloc() to store the multi-byte string. NULL is
197 * returned if the given wchar_t string pointer is NULL or buffer allocation is
198 * failed.
199 */
200 static char *
wcstombsdmp(wchar_t * wcs,int len)201 wcstombsdmp(wchar_t *wcs, int len)
202 {
203 size_t n;
204 char *mbs;
205
206 if (wcs == NULL)
207 return NULL;
208
209 n = len*MB_CUR_MAX + 1;
210
211 mbs = (char *) malloc(n * sizeof(char));
212 if (mbs == NULL) {
213 THROW_OUT_OF_MEMORY_ERROR();
214 return NULL;
215 }
216
217 /* TODO: check return values... Handle invalid characters properly... */
218 if (wcstombs(mbs, wcs, n) == (size_t)-1) {
219 free(mbs);
220 return NULL;
221 }
222
223 return mbs;
224 }
225
226 /*
227 * Returns True if the global reference is still in the list,
228 * otherwise False.
229 */
isX11InputMethodGRefInList(jobject imGRef)230 static Bool isX11InputMethodGRefInList(jobject imGRef) {
231 X11InputMethodGRefNode *pX11InputMethodGRef = x11InputMethodGRefListHead;
232
233 if (imGRef == NULL) {
234 return False;
235 }
236
237 while (pX11InputMethodGRef != NULL) {
238 if (pX11InputMethodGRef->inputMethodGRef == imGRef) {
239 return True;
240 }
241 pX11InputMethodGRef = pX11InputMethodGRef->next;
242 }
243
244 return False;
245 }
246
247 /*
248 * Add the new created global reference to the list.
249 */
addToX11InputMethodGRefList(jobject newX11InputMethodGRef)250 static void addToX11InputMethodGRefList(jobject newX11InputMethodGRef) {
251 X11InputMethodGRefNode *newNode = NULL;
252
253 if (newX11InputMethodGRef == NULL ||
254 isX11InputMethodGRefInList(newX11InputMethodGRef)) {
255 return;
256 }
257
258 newNode = (X11InputMethodGRefNode *)malloc(sizeof(X11InputMethodGRefNode));
259
260 if (newNode == NULL) {
261 return;
262 } else {
263 newNode->inputMethodGRef = newX11InputMethodGRef;
264 newNode->next = x11InputMethodGRefListHead;
265 x11InputMethodGRefListHead = newNode;
266 }
267 }
268
269 /*
270 * Remove the global reference from the list.
271 */
removeX11InputMethodGRefFromList(jobject x11InputMethodGRef)272 static void removeX11InputMethodGRefFromList(jobject x11InputMethodGRef) {
273 X11InputMethodGRefNode *pX11InputMethodGRef = NULL;
274 X11InputMethodGRefNode *cX11InputMethodGRef = x11InputMethodGRefListHead;
275
276 if (x11InputMethodGRefListHead == NULL ||
277 x11InputMethodGRef == NULL) {
278 return;
279 }
280
281 /* cX11InputMethodGRef always refers to the current node while
282 pX11InputMethodGRef refers to the previous node.
283 */
284 while (cX11InputMethodGRef != NULL) {
285 if (cX11InputMethodGRef->inputMethodGRef == x11InputMethodGRef) {
286 break;
287 }
288 pX11InputMethodGRef = cX11InputMethodGRef;
289 cX11InputMethodGRef = cX11InputMethodGRef->next;
290 }
291
292 if (cX11InputMethodGRef == NULL) {
293 return; /* Not found. */
294 }
295
296 if (cX11InputMethodGRef == x11InputMethodGRefListHead) {
297 x11InputMethodGRefListHead = x11InputMethodGRefListHead->next;
298 } else {
299 pX11InputMethodGRef->next = cX11InputMethodGRef->next;
300 }
301 free(cX11InputMethodGRef);
302
303 return;
304 }
305
306
getX11InputMethodData(JNIEnv * env,jobject imInstance)307 static X11InputMethodData * getX11InputMethodData(JNIEnv * env, jobject imInstance) {
308 X11InputMethodData *pX11IMData =
309 (X11InputMethodData *)JNU_GetLongFieldAsPtr(env, imInstance, x11InputMethodIDs.pData);
310
311 /*
312 * In case the XIM server was killed somehow, reset X11InputMethodData.
313 */
314 if (X11im == NULL && pX11IMData != NULL) {
315 JNU_CallMethodByName(env, NULL, pX11IMData->x11inputmethod,
316 "flushText",
317 "()V");
318 JNU_CHECK_EXCEPTION_RETURN(env, NULL);
319 /* IMPORTANT:
320 The order of the following calls is critical since "imInstance" may
321 point to the global reference itself, if "freeX11InputMethodData" is called
322 first, the global reference will be destroyed and "setX11InputMethodData"
323 will in fact fail silently. So pX11IMData will not be set to NULL.
324 This could make the original java object refers to a deleted pX11IMData
325 object.
326 */
327 setX11InputMethodData(env, imInstance, NULL);
328 freeX11InputMethodData(env, pX11IMData);
329 pX11IMData = NULL;
330 }
331
332 return pX11IMData;
333 }
334
setX11InputMethodData(JNIEnv * env,jobject imInstance,X11InputMethodData * pX11IMData)335 static void setX11InputMethodData(JNIEnv * env, jobject imInstance, X11InputMethodData *pX11IMData) {
336 JNU_SetLongFieldFromPtr(env, imInstance, x11InputMethodIDs.pData, pX11IMData);
337 }
338
339 /* this function should be called within AWT_LOCK() */
340 static void
destroyX11InputMethodData(JNIEnv * env,X11InputMethodData * pX11IMData)341 destroyX11InputMethodData(JNIEnv *env, X11InputMethodData *pX11IMData)
342 {
343 /*
344 * Destroy XICs
345 */
346 if (pX11IMData == NULL) {
347 return;
348 }
349
350 if (pX11IMData->ic_active != (XIC)0) {
351 XUnsetICFocus(pX11IMData->ic_active);
352 XDestroyIC(pX11IMData->ic_active);
353 if (pX11IMData->ic_active != pX11IMData->ic_passive) {
354 if (pX11IMData->ic_passive != (XIC)0) {
355 XUnsetICFocus(pX11IMData->ic_passive);
356 XDestroyIC(pX11IMData->ic_passive);
357 }
358 pX11IMData->ic_passive = (XIC)0;
359 pX11IMData->current_ic = (XIC)0;
360 }
361 }
362
363 freeX11InputMethodData(env, pX11IMData);
364 }
365
366 static void
freeX11InputMethodData(JNIEnv * env,X11InputMethodData * pX11IMData)367 freeX11InputMethodData(JNIEnv *env, X11InputMethodData *pX11IMData)
368 {
369 #if defined(__linux__) || defined(_ALLBSD_SOURCE)
370 if (pX11IMData->statusWindow != NULL){
371 StatusWindow *sw = pX11IMData->statusWindow;
372 XFreeGC(awt_display, sw->lightGC);
373 XFreeGC(awt_display, sw->dimGC);
374 XFreeGC(awt_display, sw->bgGC);
375 XFreeGC(awt_display, sw->fgGC);
376 if (sw->fontset != NULL) {
377 XFreeFontSet(awt_display, sw->fontset);
378 }
379 XDestroyWindow(awt_display, sw->w);
380 free((void*)sw);
381 }
382 #endif
383
384 if (pX11IMData->callbacks)
385 free((void *)pX11IMData->callbacks);
386
387 if (env) {
388 /* Remove the global reference from the list, so that
389 the callback function or whoever refers to it could know.
390 */
391 removeX11InputMethodGRefFromList(pX11IMData->x11inputmethod);
392 (*env)->DeleteGlobalRef(env, pX11IMData->x11inputmethod);
393 }
394
395 if (pX11IMData->lookup_buf) {
396 free((void *)pX11IMData->lookup_buf);
397 }
398
399 free((void *)pX11IMData);
400 }
401
402 /*
403 * Sets or unsets the focus to the given XIC.
404 */
405 static void
setXICFocus(XIC ic,unsigned short req)406 setXICFocus(XIC ic, unsigned short req)
407 {
408 if (ic == NULL) {
409 (void)fprintf(stderr, "Couldn't find X Input Context\n");
410 return;
411 }
412 if (req == 1)
413 XSetICFocus(ic);
414 else
415 XUnsetICFocus(ic);
416 }
417
418 /*
419 * Sets the focus window to the given XIC.
420 */
421 static void
setXICWindowFocus(XIC ic,Window w)422 setXICWindowFocus(XIC ic, Window w)
423 {
424 if (ic == NULL) {
425 (void)fprintf(stderr, "Couldn't find X Input Context\n");
426 return;
427 }
428 (void) XSetICValues(ic, XNFocusWindow, w, NULL);
429 }
430
431 /*
432 * Invokes XmbLookupString() to get something from the XIM. It invokes
433 * X11InputMethod.dispatchCommittedText() if XmbLookupString() returns
434 * committed text. This function is called from handleKeyEvent in canvas.c and
435 * it's under the Motif event loop thread context.
436 *
437 * Buffer usage: There is a bug in XFree86-4.3.0 XmbLookupString implementation,
438 * where it never returns XBufferOverflow. We need to allocate the initial lookup buffer
439 * big enough, so that the possibility that user encounters this problem is relatively
440 * small. When this bug gets fixed, we can make the initial buffer size smaller.
441 * Note that XmbLookupString() sometimes produces a non-null-terminated string.
442 *
443 * Returns True when there is a keysym value to be handled.
444 */
445 #define INITIAL_LOOKUP_BUF_SIZE 512
446
447 Boolean
awt_x11inputmethod_lookupString(XKeyPressedEvent * event,KeySym * keysymp)448 awt_x11inputmethod_lookupString(XKeyPressedEvent *event, KeySym *keysymp)
449 {
450 JNIEnv *env = GetJNIEnv();
451 X11InputMethodData *pX11IMData = NULL;
452 KeySym keysym = NoSymbol;
453 Status status;
454 int mblen;
455 jstring javastr;
456 XIC ic;
457 Boolean result = True;
458 static Boolean composing = False;
459
460 /*
461 printf("lookupString: entering...\n");
462 */
463
464 if (!isX11InputMethodGRefInList(currentX11InputMethodInstance)) {
465 currentX11InputMethodInstance = NULL;
466 return False;
467 }
468
469 pX11IMData = getX11InputMethodData(env, currentX11InputMethodInstance);
470
471 if (pX11IMData == NULL) {
472 #if defined(__linux__) || defined(_ALLBSD_SOURCE)
473 return False;
474 #else
475 return result;
476 #endif
477 }
478
479 if ((ic = pX11IMData->current_ic) == (XIC)0){
480 #if defined(__linux__) || defined(_ALLBSD_SOURCE)
481 return False;
482 #else
483 return result;
484 #endif
485 }
486
487 /* allocate the lookup buffer at the first invocation */
488 if (pX11IMData->lookup_buf_len == 0) {
489 pX11IMData->lookup_buf = (char *)malloc(INITIAL_LOOKUP_BUF_SIZE);
490 if (pX11IMData->lookup_buf == NULL) {
491 THROW_OUT_OF_MEMORY_ERROR();
492 return result;
493 }
494 pX11IMData->lookup_buf_len = INITIAL_LOOKUP_BUF_SIZE;
495 }
496
497 mblen = XmbLookupString(ic, event, pX11IMData->lookup_buf,
498 pX11IMData->lookup_buf_len - 1, &keysym, &status);
499
500 /*
501 * In case of overflow, a buffer is allocated and it retries
502 * XmbLookupString().
503 */
504 if (status == XBufferOverflow) {
505 free((void *)pX11IMData->lookup_buf);
506 pX11IMData->lookup_buf_len = 0;
507 pX11IMData->lookup_buf = (char *)malloc(mblen + 1);
508 if (pX11IMData->lookup_buf == NULL) {
509 THROW_OUT_OF_MEMORY_ERROR();
510 return result;
511 }
512 pX11IMData->lookup_buf_len = mblen + 1;
513 mblen = XmbLookupString(ic, event, pX11IMData->lookup_buf,
514 pX11IMData->lookup_buf_len - 1, &keysym, &status);
515 }
516 pX11IMData->lookup_buf[mblen] = 0;
517
518 /* Get keysym without taking modifiers into account first to map
519 * to AWT keyCode table.
520 */
521 switch (status) {
522 case XLookupBoth:
523 if (!composing) {
524 if (event->keycode != 0) {
525 *keysymp = keysym;
526 result = False;
527 break;
528 }
529 }
530 composing = False;
531 /*FALLTHRU*/
532 case XLookupChars:
533 /*
534 printf("lookupString: status=XLookupChars, type=%d, state=%x, keycode=%x, keysym=%x\n",
535 event->type, event->state, event->keycode, keysym);
536 */
537 javastr = JNU_NewStringPlatform(env, (const char *)pX11IMData->lookup_buf);
538 if (javastr != NULL) {
539 JNU_CallMethodByName(env, NULL,
540 currentX11InputMethodInstance,
541 "dispatchCommittedText",
542 "(Ljava/lang/String;J)V",
543 javastr,
544 event->time);
545 }
546 break;
547
548 case XLookupKeySym:
549 /*
550 printf("lookupString: status=XLookupKeySym, type=%d, state=%x, keycode=%x, keysym=%x\n",
551 event->type, event->state, event->keycode, keysym);
552 */
553 if (keysym == XK_Multi_key)
554 composing = True;
555 if (! composing) {
556 *keysymp = keysym;
557 result = False;
558 }
559 break;
560
561 case XLookupNone:
562 /*
563 printf("lookupString: status=XLookupNone, type=%d, state=%x, keycode=%x, keysym=%x\n",
564 event->type, event->state, event->keycode, keysym);
565 */
566 break;
567 }
568
569 return result;
570 }
571
572 #if defined(__linux__) || defined(_ALLBSD_SOURCE)
createStatusWindow(Window parent)573 static StatusWindow *createStatusWindow(Window parent) {
574 StatusWindow *statusWindow;
575 XSetWindowAttributes attrib;
576 unsigned long attribmask;
577 Window containerWindow;
578 Window status;
579 Window child;
580 XWindowAttributes xwa;
581 XWindowAttributes xxwa;
582 /* Variable for XCreateFontSet()*/
583 char **mclr;
584 int mccr = 0;
585 char *dsr;
586 unsigned long bg, fg, light, dim;
587 int x, y, off_x, off_y, xx, yy;
588 unsigned int w, h, bw, depth;
589 XGCValues values;
590 unsigned long valuemask = 0; /*ignore XGCvalue and use defaults*/
591 int screen = 0;
592 int i;
593 AwtGraphicsConfigDataPtr adata;
594 extern int awt_numScreens;
595 /*hardcode the size right now, should get the size base on font*/
596 int width=80, height=22;
597 Window rootWindow;
598 Window *ignoreWindowPtr;
599 unsigned int ignoreUnit;
600
601 XGetGeometry(dpy, parent, &rootWindow, &x, &y, &w, &h, &bw, &depth);
602
603 attrib.override_redirect = True;
604 attribmask = CWOverrideRedirect;
605 for (i = 0; i < awt_numScreens; i++) {
606 if (RootWindow(dpy, i) == rootWindow) {
607 screen = i;
608 break;
609 }
610 }
611 adata = getDefaultConfig(screen);
612 bg = adata->AwtColorMatch(255, 255, 255, adata);
613 fg = adata->AwtColorMatch(0, 0, 0, adata);
614 light = adata->AwtColorMatch(195, 195, 195, adata);
615 dim = adata->AwtColorMatch(128, 128, 128, adata);
616
617 XGetWindowAttributes(dpy, parent, &xwa);
618 bw = 2; /*xwa.border_width does not have the correct value*/
619
620 /*compare the size difference between parent container
621 and shell widget, the diff should be the border frame
622 and title bar height (?)*/
623
624 XQueryTree( dpy,
625 parent,
626 &rootWindow,
627 &containerWindow,
628 &ignoreWindowPtr,
629 &ignoreUnit);
630 XGetWindowAttributes(dpy, containerWindow, &xxwa);
631
632 off_x = (xxwa.width - xwa.width) / 2;
633 off_y = xxwa.height - xwa.height - off_x; /*it's magic:-) */
634
635 /*get the size of root window*/
636 XGetWindowAttributes(dpy, rootWindow, &xxwa);
637
638 XTranslateCoordinates(dpy,
639 parent, xwa.root,
640 xwa.x, xwa.y,
641 &x, &y,
642 &child);
643 xx = x - off_x;
644 yy = y + xwa.height - off_y;
645 if (xx < 0 ){
646 xx = 0;
647 }
648 if (xx + width > xxwa.width) {
649 xx = xxwa.width - width;
650 }
651 if (yy + height > xxwa.height) {
652 yy = xxwa.height - height;
653 }
654
655 status = XCreateWindow(dpy,
656 xwa.root,
657 xx, yy,
658 width, height,
659 0,
660 xwa.depth,
661 InputOutput,
662 adata->awt_visInfo.visual,
663 attribmask, &attrib);
664 XSelectInput(dpy, status,
665 ExposureMask | StructureNotifyMask | EnterWindowMask |
666 LeaveWindowMask | VisibilityChangeMask);
667 statusWindow = (StatusWindow*) calloc(1, sizeof(StatusWindow));
668 if (statusWindow == NULL){
669 THROW_OUT_OF_MEMORY_ERROR();
670 return NULL;
671 }
672 statusWindow->w = status;
673 //12, 13-point fonts
674 statusWindow->fontset = XCreateFontSet(dpy,
675 "-*-*-medium-r-normal-*-*-120-*-*-*-*," \
676 "-*-*-medium-r-normal-*-*-130-*-*-*-*",
677 &mclr, &mccr, &dsr);
678 /* In case we didn't find the font set, release the list of missing characters */
679 if (mccr > 0) {
680 XFreeStringList(mclr);
681 }
682 statusWindow->parent = parent;
683 statusWindow->on = False;
684 statusWindow->x = x;
685 statusWindow->y = y;
686 statusWindow->width = xwa.width;
687 statusWindow->height = xwa.height;
688 statusWindow->off_x = off_x;
689 statusWindow->off_y = off_y;
690 statusWindow->bWidth = bw;
691 statusWindow->statusH = height;
692 statusWindow->statusW = width;
693 statusWindow->rootH = xxwa.height;
694 statusWindow->rootW = xxwa.width;
695 statusWindow->lightGC = XCreateGC(dpy, status, valuemask, &values);
696 XSetForeground(dpy, statusWindow->lightGC, light);
697 statusWindow->dimGC = XCreateGC(dpy, status, valuemask, &values);
698 XSetForeground(dpy, statusWindow->dimGC, dim);
699 statusWindow->fgGC = XCreateGC(dpy, status, valuemask, &values);
700 XSetForeground(dpy, statusWindow->fgGC, fg);
701 statusWindow->bgGC = XCreateGC(dpy, status, valuemask, &values);
702 XSetForeground(dpy, statusWindow->bgGC, bg);
703 return statusWindow;
704 }
705
706 /* This method is to turn off or turn on the status window. */
onoffStatusWindow(X11InputMethodData * pX11IMData,Window parent,Bool ON)707 static void onoffStatusWindow(X11InputMethodData* pX11IMData,
708 Window parent,
709 Bool ON){
710 XWindowAttributes xwa;
711 Window child;
712 int x, y;
713 StatusWindow *statusWindow = NULL;
714
715 if (NULL == currentX11InputMethodInstance ||
716 NULL == pX11IMData ||
717 NULL == (statusWindow = pX11IMData->statusWindow)){
718 return;
719 }
720
721 if (ON == False) {
722 XUnmapWindow(dpy, statusWindow->w);
723 statusWindow->on = False;
724 return;
725 }
726 parent = JNU_CallMethodByName(GetJNIEnv(), NULL, pX11IMData->x11inputmethod,
727 "getCurrentParentWindow",
728 "()J").j;
729 if (statusWindow->parent != parent) {
730 statusWindow->parent = parent;
731 }
732 XGetWindowAttributes(dpy, parent, &xwa);
733 XTranslateCoordinates(dpy,
734 parent, xwa.root,
735 xwa.x, xwa.y,
736 &x, &y,
737 &child);
738 if (statusWindow->x != x ||
739 statusWindow->y != y ||
740 statusWindow->height != xwa.height)
741 {
742 statusWindow->x = x;
743 statusWindow->y = y;
744 statusWindow->height = xwa.height;
745 x = statusWindow->x - statusWindow->off_x;
746 y = statusWindow->y + statusWindow->height - statusWindow->off_y;
747 if (x < 0 ) {
748 x = 0;
749 }
750 if (x + statusWindow->statusW > statusWindow->rootW) {
751 x = statusWindow->rootW - statusWindow->statusW;
752 }
753 if (y + statusWindow->statusH > statusWindow->rootH) {
754 y = statusWindow->rootH - statusWindow->statusH;
755 }
756 XMoveWindow(dpy, statusWindow->w, x, y);
757 }
758 statusWindow->on = True;
759 XMapWindow(dpy, statusWindow->w);
760 }
761
paintStatusWindow(StatusWindow * statusWindow)762 void paintStatusWindow(StatusWindow *statusWindow){
763 Window win = statusWindow->w;
764 GC lightgc = statusWindow->lightGC;
765 GC dimgc = statusWindow->dimGC;
766 GC bggc = statusWindow->bgGC;
767 GC fggc = statusWindow->fgGC;
768
769 int width = statusWindow->statusW;
770 int height = statusWindow->statusH;
771 int bwidth = statusWindow->bWidth;
772 XFillRectangle(dpy, win, bggc, 0, 0, width, height);
773 /* draw border */
774 XDrawLine(dpy, win, fggc, 0, 0, width, 0);
775 XDrawLine(dpy, win, fggc, 0, height-1, width-1, height-1);
776 XDrawLine(dpy, win, fggc, 0, 0, 0, height-1);
777 XDrawLine(dpy, win, fggc, width-1, 0, width-1, height-1);
778
779 XDrawLine(dpy, win, lightgc, 1, 1, width-bwidth, 1);
780 XDrawLine(dpy, win, lightgc, 1, 1, 1, height-2);
781 XDrawLine(dpy, win, lightgc, 1, height-2, width-bwidth, height-2);
782 XDrawLine(dpy, win, lightgc, width-bwidth-1, 1, width-bwidth-1, height-2);
783
784 XDrawLine(dpy, win, dimgc, 2, 2, 2, height-3);
785 XDrawLine(dpy, win, dimgc, 2, height-3, width-bwidth-1, height-3);
786 XDrawLine(dpy, win, dimgc, 2, 2, width-bwidth-2, 2);
787 XDrawLine(dpy, win, dimgc, width-bwidth, 2, width-bwidth, height-3);
788 if (statusWindow->fontset) {
789 XmbDrawString(dpy, win, statusWindow->fontset, fggc,
790 bwidth + 2, height - bwidth - 4,
791 statusWindow->status,
792 strlen(statusWindow->status));
793 } else {
794 /*too bad we failed to create a fontset for this locale*/
795 XDrawString(dpy, win, fggc, bwidth + 2, height - bwidth - 4,
796 "[InputMethod ON]", strlen("[InputMethod ON]"));
797 }
798 }
799
adjustStatusWindow(Window shell)800 static void adjustStatusWindow(Window shell) {
801 JNIEnv *env = GetJNIEnv();
802 X11InputMethodData *pX11IMData = NULL;
803 StatusWindow *statusWindow;
804
805 if (NULL == currentX11InputMethodInstance
806 || !isX11InputMethodGRefInList(currentX11InputMethodInstance)
807 || NULL == (pX11IMData = getX11InputMethodData(env,currentX11InputMethodInstance))
808 || NULL == (statusWindow = pX11IMData->statusWindow)
809 || !statusWindow->on)
810 {
811 return;
812 }
813
814 {
815 XWindowAttributes xwa;
816 int x, y;
817 Window child;
818 XGetWindowAttributes(dpy, shell, &xwa);
819 XTranslateCoordinates(dpy,
820 shell, xwa.root,
821 xwa.x, xwa.y,
822 &x, &y,
823 &child);
824 if (statusWindow->x != x
825 || statusWindow->y != y
826 || statusWindow->height != xwa.height){
827 statusWindow->x = x;
828 statusWindow->y = y;
829 statusWindow->height = xwa.height;
830
831 x = statusWindow->x - statusWindow->off_x;
832 y = statusWindow->y + statusWindow->height - statusWindow->off_y;
833 if (x < 0 ) {
834 x = 0;
835 }
836 if (x + statusWindow->statusW > statusWindow->rootW){
837 x = statusWindow->rootW - statusWindow->statusW;
838 }
839 if (y + statusWindow->statusH > statusWindow->rootH){
840 y = statusWindow->rootH - statusWindow->statusH;
841 }
842 XMoveWindow(dpy, statusWindow->w, x, y);
843 }
844 }
845 }
846 #endif /* __linux__ || _ALLBSD_SOURCE */
847
848 /*
849 * Creates two XICs, one for active clients and the other for passive
850 * clients. All information on those XICs are stored in the
851 * X11InputMethodData given by the pX11IMData parameter.
852 *
853 * For active clients: Try to use preedit callback to support
854 * on-the-spot. If tc is not null, the XIC to be created will
855 * share the Status Area with Motif widgets (TextComponents). If the
856 * preferable styles can't be used, fallback to root-window styles. If
857 * root-window styles failed, fallback to None styles.
858 *
859 * For passive clients: Try to use root-window styles. If failed,
860 * fallback to None styles.
861 */
862 static Bool
createXIC(JNIEnv * env,X11InputMethodData * pX11IMData,Window w)863 createXIC(JNIEnv * env, X11InputMethodData *pX11IMData, Window w)
864 {
865 XVaNestedList preedit = NULL;
866 XVaNestedList status = NULL;
867 XIMStyle on_the_spot_styles = XIMPreeditCallbacks,
868 active_styles = 0,
869 passive_styles = 0,
870 no_styles = 0;
871 XIMCallback *callbacks;
872 unsigned short i;
873 XIMStyles *im_styles;
874 char *ret = NULL;
875
876 if (X11im == NULL) {
877 return False;
878 }
879 if (!w) {
880 return False;
881 }
882
883 ret = XGetIMValues(X11im, XNQueryInputStyle, &im_styles, NULL);
884
885 if (ret != NULL) {
886 jio_fprintf(stderr,"XGetIMValues: %s\n",ret);
887 return FALSE ;
888 }
889
890 on_the_spot_styles |= XIMStatusNothing;
891
892 #if defined(__linux__) || defined(_ALLBSD_SOURCE)
893 /*kinput does not support XIMPreeditCallbacks and XIMStatusArea
894 at the same time, so use StatusCallback to draw the status
895 ourself
896 */
897 for (i = 0; i < im_styles->count_styles; i++) {
898 if (im_styles->supported_styles[i] == (XIMPreeditCallbacks | XIMStatusCallbacks)) {
899 on_the_spot_styles = (XIMPreeditCallbacks | XIMStatusCallbacks);
900 break;
901 }
902 }
903 #endif /* __linux__ || _ALLBSD_SOURCE */
904
905 for (i = 0; i < im_styles->count_styles; i++) {
906 active_styles |= im_styles->supported_styles[i] & on_the_spot_styles;
907 passive_styles |= im_styles->supported_styles[i] & ROOT_WINDOW_STYLES;
908 no_styles |= im_styles->supported_styles[i] & NO_STYLES;
909 }
910
911 XFree(im_styles);
912
913 if (active_styles != on_the_spot_styles) {
914 if (passive_styles == ROOT_WINDOW_STYLES)
915 active_styles = passive_styles;
916 else {
917 if (no_styles == NO_STYLES)
918 active_styles = passive_styles = NO_STYLES;
919 else
920 active_styles = passive_styles = 0;
921 }
922 } else {
923 if (passive_styles != ROOT_WINDOW_STYLES) {
924 if (no_styles == NO_STYLES)
925 active_styles = passive_styles = NO_STYLES;
926 else
927 active_styles = passive_styles = 0;
928 }
929 }
930
931 if (active_styles == on_the_spot_styles) {
932 pX11IMData->ic_passive = XCreateIC(X11im,
933 XNClientWindow, w,
934 XNFocusWindow, w,
935 XNInputStyle, passive_styles,
936 NULL);
937
938 callbacks = (XIMCallback *)malloc(sizeof(XIMCallback) * NCALLBACKS);
939 if (callbacks == (XIMCallback *)NULL)
940 return False;
941 pX11IMData->callbacks = callbacks;
942
943 for (i = 0; i < NCALLBACKS; i++, callbacks++) {
944 callbacks->client_data = (XPointer) pX11IMData->x11inputmethod;
945 callbacks->callback = callback_funcs[i];
946 }
947
948 callbacks = pX11IMData->callbacks;
949 preedit = (XVaNestedList)XVaCreateNestedList(0,
950 XNPreeditStartCallback, &callbacks[PreeditStartIndex],
951 XNPreeditDoneCallback, &callbacks[PreeditDoneIndex],
952 XNPreeditDrawCallback, &callbacks[PreeditDrawIndex],
953 XNPreeditCaretCallback, &callbacks[PreeditCaretIndex],
954 NULL);
955 if (preedit == (XVaNestedList)NULL)
956 goto err;
957 #if defined(__linux__) || defined(_ALLBSD_SOURCE)
958 /*always try XIMStatusCallbacks for active client...*/
959 {
960 status = (XVaNestedList)XVaCreateNestedList(0,
961 XNStatusStartCallback, &callbacks[StatusStartIndex],
962 XNStatusDoneCallback, &callbacks[StatusDoneIndex],
963 XNStatusDrawCallback, &callbacks[StatusDrawIndex],
964 NULL);
965
966 if (status == NULL)
967 goto err;
968 pX11IMData->statusWindow = createStatusWindow(w);
969 pX11IMData->ic_active = XCreateIC(X11im,
970 XNClientWindow, w,
971 XNFocusWindow, w,
972 XNInputStyle, active_styles,
973 XNPreeditAttributes, preedit,
974 XNStatusAttributes, status,
975 NULL);
976 XFree((void *)status);
977 XFree((void *)preedit);
978 }
979 #else /* !__linux__ && !_ALLBSD_SOURCE */
980 pX11IMData->ic_active = XCreateIC(X11im,
981 XNClientWindow, w,
982 XNFocusWindow, w,
983 XNInputStyle, active_styles,
984 XNPreeditAttributes, preedit,
985 NULL);
986 XFree((void *)preedit);
987 #endif /* __linux__ || _ALLBSD_SOURCE */
988 } else {
989 pX11IMData->ic_active = XCreateIC(X11im,
990 XNClientWindow, w,
991 XNFocusWindow, w,
992 XNInputStyle, active_styles,
993 NULL);
994 pX11IMData->ic_passive = pX11IMData->ic_active;
995 }
996
997 if (pX11IMData->ic_active == (XIC)0
998 || pX11IMData->ic_passive == (XIC)0) {
999 return False;
1000 }
1001
1002 /*
1003 * Use commit string call back if possible.
1004 * This will ensure the correct order of preedit text and commit text
1005 */
1006 {
1007 XIMCallback cb;
1008 cb.client_data = (XPointer) pX11IMData->x11inputmethod;
1009 cb.callback = (XIMProc) CommitStringCallback;
1010 XSetICValues (pX11IMData->ic_active, XNCommitStringCallback, &cb, NULL);
1011 if (pX11IMData->ic_active != pX11IMData->ic_passive) {
1012 XSetICValues (pX11IMData->ic_passive, XNCommitStringCallback, &cb, NULL);
1013 }
1014 }
1015
1016 // The code set the IC mode that the preedit state is not initialied
1017 // at XmbResetIC. This attribute can be set at XCreateIC. I separately
1018 // set the attribute to avoid the failure of XCreateIC at some platform
1019 // which does not support the attribute.
1020 if (pX11IMData->ic_active != 0)
1021 XSetICValues(pX11IMData->ic_active,
1022 XNResetState, XIMInitialState,
1023 NULL);
1024 if (pX11IMData->ic_passive != 0
1025 && pX11IMData->ic_active != pX11IMData->ic_passive)
1026 XSetICValues(pX11IMData->ic_passive,
1027 XNResetState, XIMInitialState,
1028 NULL);
1029
1030 /* Add the global reference object to X11InputMethod to the list. */
1031 addToX11InputMethodGRefList(pX11IMData->x11inputmethod);
1032
1033 /* Unset focus to avoid unexpected IM on */
1034 setXICFocus(pX11IMData->ic_active, False);
1035 if (pX11IMData->ic_active != pX11IMData->ic_passive)
1036 setXICFocus(pX11IMData->ic_passive, False);
1037
1038 return True;
1039
1040 err:
1041 if (preedit)
1042 XFree((void *)preedit);
1043 THROW_OUT_OF_MEMORY_ERROR();
1044 return False;
1045 }
1046
1047 static int
PreeditStartCallback(XIC ic,XPointer client_data,XPointer call_data)1048 PreeditStartCallback(XIC ic, XPointer client_data, XPointer call_data)
1049 {
1050 /*ARGSUSED*/
1051 /* printf("Native: PreeditStartCallback\n"); */
1052 return -1;
1053 }
1054
1055 static void
PreeditDoneCallback(XIC ic,XPointer client_data,XPointer call_data)1056 PreeditDoneCallback(XIC ic, XPointer client_data, XPointer call_data)
1057 {
1058 /*ARGSUSED*/
1059 /* printf("Native: PreeditDoneCallback\n"); */
1060 }
1061
1062 /*
1063 * Translate the preedit draw callback items to Java values and invoke
1064 * X11InputMethod.dispatchComposedText().
1065 *
1066 * client_data: X11InputMethod object
1067 */
1068 static void
PreeditDrawCallback(XIC ic,XPointer client_data,XIMPreeditDrawCallbackStruct * pre_draw)1069 PreeditDrawCallback(XIC ic, XPointer client_data,
1070 XIMPreeditDrawCallbackStruct *pre_draw)
1071 {
1072 JNIEnv *env = GetJNIEnv();
1073 X11InputMethodData *pX11IMData = NULL;
1074 jmethodID x11imMethodID;
1075
1076 XIMText *text;
1077 jstring javastr = NULL;
1078 jintArray style = NULL;
1079
1080 /* printf("Native: PreeditDrawCallback() \n"); */
1081 if (pre_draw == NULL) {
1082 return;
1083 }
1084 AWT_LOCK();
1085 if (!isX11InputMethodGRefInList((jobject)client_data)) {
1086 if ((jobject)client_data == currentX11InputMethodInstance) {
1087 currentX11InputMethodInstance = NULL;
1088 }
1089 goto finally;
1090 }
1091 if ((pX11IMData = getX11InputMethodData(env, (jobject)client_data)) == NULL) {
1092 goto finally;
1093 }
1094
1095 if ((text = pre_draw->text) != NULL) {
1096 if (text->string.multi_byte != NULL) {
1097 if (pre_draw->text->encoding_is_wchar == False) {
1098 javastr = JNU_NewStringPlatform(env, (const char *)text->string.multi_byte);
1099 if (javastr == NULL) {
1100 goto finally;
1101 }
1102 } else {
1103 char *mbstr = wcstombsdmp(text->string.wide_char, text->length);
1104 if (mbstr == NULL) {
1105 goto finally;
1106 }
1107 javastr = JNU_NewStringPlatform(env, (const char *)mbstr);
1108 free(mbstr);
1109 if (javastr == NULL) {
1110 goto finally;
1111 }
1112 }
1113 }
1114 if (text->feedback != NULL) {
1115 int cnt;
1116 jint *tmpstyle;
1117
1118 style = (*env)->NewIntArray(env, text->length);
1119 if (JNU_IsNull(env, style)) {
1120 (*env)->ExceptionClear(env);
1121 THROW_OUT_OF_MEMORY_ERROR();
1122 goto finally;
1123 }
1124
1125 if (sizeof(XIMFeedback) == sizeof(jint)) {
1126 /*
1127 * Optimization to avoid copying the array
1128 */
1129 (*env)->SetIntArrayRegion(env, style, 0,
1130 text->length, (jint *)text->feedback);
1131 } else {
1132 tmpstyle = (jint *)malloc(sizeof(jint)*(text->length));
1133 if (tmpstyle == (jint *) NULL) {
1134 THROW_OUT_OF_MEMORY_ERROR();
1135 goto finally;
1136 }
1137 for (cnt = 0; cnt < (int)text->length; cnt++)
1138 tmpstyle[cnt] = text->feedback[cnt];
1139 (*env)->SetIntArrayRegion(env, style, 0,
1140 text->length, (jint *)tmpstyle);
1141 free(tmpstyle);
1142 }
1143 }
1144 }
1145 JNU_CallMethodByName(env, NULL, pX11IMData->x11inputmethod,
1146 "dispatchComposedText",
1147 "(Ljava/lang/String;[IIIIJ)V",
1148 javastr,
1149 style,
1150 (jint)pre_draw->chg_first,
1151 (jint)pre_draw->chg_length,
1152 (jint)pre_draw->caret,
1153 awt_util_nowMillisUTC());
1154 finally:
1155 AWT_UNLOCK();
1156 return;
1157 }
1158
1159 static void
PreeditCaretCallback(XIC ic,XPointer client_data,XIMPreeditCaretCallbackStruct * pre_caret)1160 PreeditCaretCallback(XIC ic, XPointer client_data,
1161 XIMPreeditCaretCallbackStruct *pre_caret)
1162 {
1163 /*ARGSUSED*/
1164 /* printf("Native: PreeditCaretCallback\n"); */
1165 }
1166
1167 #if defined(__linux__) || defined(_ALLBSD_SOURCE)
1168 static void
StatusStartCallback(XIC ic,XPointer client_data,XPointer call_data)1169 StatusStartCallback(XIC ic, XPointer client_data, XPointer call_data)
1170 {
1171 /*ARGSUSED*/
1172 /*printf("StatusStartCallback:\n"); */
1173 }
1174
1175 static void
StatusDoneCallback(XIC ic,XPointer client_data,XPointer call_data)1176 StatusDoneCallback(XIC ic, XPointer client_data, XPointer call_data)
1177 {
1178 /*ARGSUSED*/
1179 /*printf("StatusDoneCallback:\n"); */
1180 JNIEnv *env = GetJNIEnv();
1181 X11InputMethodData *pX11IMData = NULL;
1182 StatusWindow *statusWindow;
1183
1184 AWT_LOCK();
1185
1186 if (!isX11InputMethodGRefInList((jobject)client_data)) {
1187 if ((jobject)client_data == currentX11InputMethodInstance) {
1188 currentX11InputMethodInstance = NULL;
1189 }
1190 goto finally;
1191 }
1192
1193 if (NULL == (pX11IMData = getX11InputMethodData(env, (jobject)client_data))
1194 || NULL == (statusWindow = pX11IMData->statusWindow)){
1195 goto finally;
1196 }
1197 currentX11InputMethodInstance = (jobject)client_data;
1198
1199 onoffStatusWindow(pX11IMData, 0, False);
1200
1201 finally:
1202 AWT_UNLOCK();
1203 }
1204
1205 static void
StatusDrawCallback(XIC ic,XPointer client_data,XIMStatusDrawCallbackStruct * status_draw)1206 StatusDrawCallback(XIC ic, XPointer client_data,
1207 XIMStatusDrawCallbackStruct *status_draw)
1208 {
1209 /*ARGSUSED*/
1210 /*printf("StatusDrawCallback:\n"); */
1211 JNIEnv *env = GetJNIEnv();
1212 X11InputMethodData *pX11IMData = NULL;
1213 StatusWindow *statusWindow;
1214
1215 AWT_LOCK();
1216
1217 if (!isX11InputMethodGRefInList((jobject)client_data)) {
1218 if ((jobject)client_data == currentX11InputMethodInstance) {
1219 currentX11InputMethodInstance = NULL;
1220 }
1221 goto finally;
1222 }
1223
1224 if (NULL == (pX11IMData = getX11InputMethodData(env, (jobject)client_data))
1225 || NULL == (statusWindow = pX11IMData->statusWindow)){
1226 goto finally;
1227 }
1228 currentX11InputMethodInstance = (jobject)client_data;
1229
1230 if (status_draw->type == XIMTextType) {
1231 XIMText *text = (status_draw->data).text;
1232 if (text != NULL) {
1233 if (text->string.multi_byte != NULL) {
1234 strncpy(statusWindow->status, text->string.multi_byte, MAX_STATUS_LEN);
1235 statusWindow->status[MAX_STATUS_LEN - 1] = '\0';
1236 } else {
1237 char *mbstr = wcstombsdmp(text->string.wide_char, text->length);
1238 strncpy(statusWindow->status, mbstr, MAX_STATUS_LEN);
1239 statusWindow->status[MAX_STATUS_LEN - 1] = '\0';
1240 }
1241 statusWindow->on = True;
1242 onoffStatusWindow(pX11IMData, statusWindow->parent, True);
1243 paintStatusWindow(statusWindow);
1244 } else {
1245 statusWindow->on = False;
1246 /*just turnoff the status window
1247 paintStatusWindow(statusWindow);
1248 */
1249 onoffStatusWindow(pX11IMData, 0, False);
1250 }
1251 }
1252
1253 finally:
1254 AWT_UNLOCK();
1255 }
1256 #endif /* __linux__ || _ALLBSD_SOURCE */
1257
CommitStringCallback(XIC ic,XPointer client_data,XPointer call_data)1258 static void CommitStringCallback(XIC ic, XPointer client_data, XPointer call_data) {
1259 JNIEnv *env = GetJNIEnv();
1260 XIMText * text = (XIMText *)call_data;
1261 X11InputMethodData *pX11IMData = NULL;
1262 jstring javastr;
1263
1264 AWT_LOCK();
1265
1266 if (!isX11InputMethodGRefInList((jobject)client_data)) {
1267 if ((jobject)client_data == currentX11InputMethodInstance) {
1268 currentX11InputMethodInstance = NULL;
1269 }
1270 goto finally;
1271 }
1272
1273 if ((pX11IMData = getX11InputMethodData(env, (jobject)client_data)) == NULL) {
1274 goto finally;
1275 }
1276 currentX11InputMethodInstance = (jobject)client_data;
1277
1278 if (text->encoding_is_wchar == False) {
1279 javastr = JNU_NewStringPlatform(env, (const char *)text->string.multi_byte);
1280 } else {
1281 char *mbstr = wcstombsdmp(text->string.wide_char, text->length);
1282 if (mbstr == NULL) {
1283 goto finally;
1284 }
1285 javastr = JNU_NewStringPlatform(env, (const char *)mbstr);
1286 free(mbstr);
1287 }
1288
1289 if (javastr != NULL) {
1290 JNU_CallMethodByName(env, NULL,
1291 pX11IMData->x11inputmethod,
1292 "dispatchCommittedText",
1293 "(Ljava/lang/String;J)V",
1294 javastr,
1295 awt_util_nowMillisUTC());
1296 }
1297 finally:
1298 AWT_UNLOCK();
1299 }
1300
OpenXIMCallback(Display * display,XPointer client_data,XPointer call_data)1301 static void OpenXIMCallback(Display *display, XPointer client_data, XPointer call_data) {
1302 XIMCallback ximCallback;
1303
1304 X11im = XOpenIM(display, NULL, NULL, NULL);
1305 if (X11im == NULL) {
1306 return;
1307 }
1308
1309 ximCallback.callback = (XIMProc)DestroyXIMCallback;
1310 ximCallback.client_data = NULL;
1311 XSetIMValues(X11im, XNDestroyCallback, &ximCallback, NULL);
1312 }
1313
DestroyXIMCallback(XIM im,XPointer client_data,XPointer call_data)1314 static void DestroyXIMCallback(XIM im, XPointer client_data, XPointer call_data) {
1315 /* mark that XIM server was destroyed */
1316 X11im = NULL;
1317 JNIEnv* env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
1318
1319 AWT_LOCK();
1320 /* free the old pX11IMData and set it to null. this also avoids crashing
1321 * the jvm if the XIM server reappears */
1322 while (x11InputMethodGRefListHead != NULL) {
1323 if (getX11InputMethodData(env,
1324 x11InputMethodGRefListHead->inputMethodGRef) == NULL) {
1325 /* Clear possible exceptions
1326 */
1327 if ((*env)->ExceptionOccurred(env)) {
1328 (*env)->ExceptionDescribe(env);
1329 (*env)->ExceptionClear(env);
1330 }
1331 }
1332 }
1333 AWT_UNLOCK();
1334 }
1335
1336 JNIEXPORT jboolean JNICALL
Java_sun_awt_X11_XInputMethod_openXIMNative(JNIEnv * env,jobject this,jlong display)1337 Java_sun_awt_X11_XInputMethod_openXIMNative(JNIEnv *env,
1338 jobject this,
1339 jlong display)
1340 {
1341 Bool registered;
1342
1343 AWT_LOCK();
1344
1345 dpy = (Display *)jlong_to_ptr(display);
1346
1347 /* Use IMInstantiate call back only on Linux, as there is a bug in Solaris
1348 (4768335)
1349 */
1350 #if defined(__linux__) || defined(_ALLBSD_SOURCE)
1351 registered = XRegisterIMInstantiateCallback(dpy, NULL, NULL,
1352 NULL, (XIDProc)OpenXIMCallback, NULL);
1353 if (!registered) {
1354 /* directly call openXIM callback */
1355 #endif
1356 OpenXIMCallback(dpy, NULL, NULL);
1357 #if defined(__linux__) || defined(_ALLBSD_SOURCE)
1358 }
1359 #endif
1360
1361 AWT_UNLOCK();
1362
1363 return JNI_TRUE;
1364 }
1365
1366 JNIEXPORT jboolean JNICALL
Java_sun_awt_X11_XInputMethod_createXICNative(JNIEnv * env,jobject this,jlong window)1367 Java_sun_awt_X11_XInputMethod_createXICNative(JNIEnv *env,
1368 jobject this,
1369 jlong window)
1370 {
1371 X11InputMethodData *pX11IMData;
1372 jobject globalRef;
1373 XIC ic;
1374
1375 AWT_LOCK();
1376
1377 if (!window) {
1378 JNU_ThrowNullPointerException(env, "NullPointerException");
1379 AWT_UNLOCK();
1380 return JNI_FALSE;
1381 }
1382
1383 pX11IMData = (X11InputMethodData *) calloc(1, sizeof(X11InputMethodData));
1384 if (pX11IMData == NULL) {
1385 THROW_OUT_OF_MEMORY_ERROR();
1386 AWT_UNLOCK();
1387 return JNI_FALSE;
1388 }
1389
1390 globalRef = (*env)->NewGlobalRef(env, this);
1391 pX11IMData->x11inputmethod = globalRef;
1392 #if defined(__linux__) || defined(_ALLBSD_SOURCE)
1393 pX11IMData->statusWindow = NULL;
1394 #endif /* __linux__ || _ALLBSD_SOURCE */
1395
1396 pX11IMData->lookup_buf = 0;
1397 pX11IMData->lookup_buf_len = 0;
1398
1399 if (createXIC(env, pX11IMData, (Window)window) == False) {
1400 destroyX11InputMethodData((JNIEnv *) NULL, pX11IMData);
1401 pX11IMData = (X11InputMethodData *) NULL;
1402 if ((*env)->ExceptionCheck(env)) {
1403 goto finally;
1404 }
1405 }
1406
1407 setX11InputMethodData(env, this, pX11IMData);
1408
1409 finally:
1410 AWT_UNLOCK();
1411 return (pX11IMData != NULL);
1412 }
1413
1414 JNIEXPORT void JNICALL
Java_sun_awt_X11_XInputMethod_setXICFocusNative(JNIEnv * env,jobject this,jlong w,jboolean req,jboolean active)1415 Java_sun_awt_X11_XInputMethod_setXICFocusNative(JNIEnv *env,
1416 jobject this,
1417 jlong w,
1418 jboolean req,
1419 jboolean active)
1420 {
1421 X11InputMethodData *pX11IMData;
1422 AWT_LOCK();
1423 pX11IMData = getX11InputMethodData(env, this);
1424 if (pX11IMData == NULL) {
1425 AWT_UNLOCK();
1426 return;
1427 }
1428
1429 if (req) {
1430 if (!w) {
1431 AWT_UNLOCK();
1432 return;
1433 }
1434 pX11IMData->current_ic = active ?
1435 pX11IMData->ic_active : pX11IMData->ic_passive;
1436 /*
1437 * On Solaris2.6, setXICWindowFocus() has to be invoked
1438 * before setting focus.
1439 */
1440 setXICWindowFocus(pX11IMData->current_ic, w);
1441 setXICFocus(pX11IMData->current_ic, req);
1442 currentX11InputMethodInstance = pX11IMData->x11inputmethod;
1443 currentFocusWindow = w;
1444 #if defined(__linux__) || defined(_ALLBSD_SOURCE)
1445 if (active && pX11IMData->statusWindow && pX11IMData->statusWindow->on)
1446 onoffStatusWindow(pX11IMData, w, True);
1447 #endif
1448 } else {
1449 currentX11InputMethodInstance = NULL;
1450 currentFocusWindow = 0;
1451 #if defined(__linux__) || defined(_ALLBSD_SOURCE)
1452 onoffStatusWindow(pX11IMData, 0, False);
1453 if (pX11IMData->current_ic != NULL)
1454 #endif
1455 setXICFocus(pX11IMData->current_ic, req);
1456
1457 pX11IMData->current_ic = (XIC)0;
1458 }
1459
1460 XFlush(dpy);
1461 AWT_UNLOCK();
1462 }
1463
1464 /*
1465 * Class: sun_awt_X11InputMethodBase
1466 * Method: initIDs
1467 * Signature: ()V
1468 * This function gets called from the static initializer for
1469 * X11InputMethod.java to initialize the fieldIDs for fields
1470 * that may be accessed from C
1471 */
Java_sun_awt_X11InputMethodBase_initIDs(JNIEnv * env,jclass cls)1472 JNIEXPORT void JNICALL Java_sun_awt_X11InputMethodBase_initIDs
1473 (JNIEnv *env, jclass cls)
1474 {
1475 x11InputMethodIDs.pData = (*env)->GetFieldID(env, cls, "pData", "J");
1476 }
1477
1478 /*
1479 * Class: sun_awt_X11InputMethodBase
1480 * Method: turnoffStatusWindow
1481 * Signature: ()V
1482 */
Java_sun_awt_X11InputMethodBase_turnoffStatusWindow(JNIEnv * env,jobject this)1483 JNIEXPORT void JNICALL Java_sun_awt_X11InputMethodBase_turnoffStatusWindow
1484 (JNIEnv *env, jobject this)
1485 {
1486 #if defined(__linux__) || defined(_ALLBSD_SOURCE)
1487 X11InputMethodData *pX11IMData;
1488 StatusWindow *statusWindow;
1489
1490 AWT_LOCK();
1491
1492 if (NULL == currentX11InputMethodInstance
1493 || !isX11InputMethodGRefInList(currentX11InputMethodInstance)
1494 || NULL == (pX11IMData = getX11InputMethodData(env,currentX11InputMethodInstance))
1495 || NULL == (statusWindow = pX11IMData->statusWindow)
1496 || !statusWindow->on ){
1497 AWT_UNLOCK();
1498 return;
1499 }
1500 onoffStatusWindow(pX11IMData, 0, False);
1501
1502 AWT_UNLOCK();
1503 #endif
1504 }
1505
1506 /*
1507 * Class: sun_awt_X11InputMethodBase
1508 * Method: disposeXIC
1509 * Signature: ()V
1510 */
Java_sun_awt_X11InputMethodBase_disposeXIC(JNIEnv * env,jobject this)1511 JNIEXPORT void JNICALL Java_sun_awt_X11InputMethodBase_disposeXIC
1512 (JNIEnv *env, jobject this)
1513 {
1514 X11InputMethodData *pX11IMData = NULL;
1515
1516 AWT_LOCK();
1517 pX11IMData = getX11InputMethodData(env, this);
1518 if (pX11IMData == NULL) {
1519 AWT_UNLOCK();
1520 return;
1521 }
1522
1523 setX11InputMethodData(env, this, NULL);
1524
1525 if (pX11IMData->x11inputmethod == currentX11InputMethodInstance) {
1526 currentX11InputMethodInstance = NULL;
1527 currentFocusWindow = 0;
1528 }
1529 destroyX11InputMethodData(env, pX11IMData);
1530 AWT_UNLOCK();
1531 }
1532
1533 /*
1534 * Class: sun_awt_X11InputMethodBase
1535 * Method: resetXIC
1536 * Signature: ()Ljava/lang/String;
1537 */
Java_sun_awt_X11InputMethodBase_resetXIC(JNIEnv * env,jobject this)1538 JNIEXPORT jstring JNICALL Java_sun_awt_X11InputMethodBase_resetXIC
1539 (JNIEnv *env, jobject this)
1540 {
1541 X11InputMethodData *pX11IMData;
1542 char *xText = NULL;
1543 jstring jText = (jstring)0;
1544
1545 AWT_LOCK();
1546 pX11IMData = getX11InputMethodData(env, this);
1547 if (pX11IMData == NULL) {
1548 AWT_UNLOCK();
1549 return jText;
1550 }
1551
1552 if (pX11IMData->current_ic)
1553 xText = XmbResetIC(pX11IMData->current_ic);
1554 else {
1555 /*
1556 * If there is no reference to the current XIC, try to reset both XICs.
1557 */
1558 xText = XmbResetIC(pX11IMData->ic_active);
1559 /*it may also means that the real client component does
1560 not have focus -- has been deactivated... its xic should
1561 not have the focus, bug#4284651 showes reset XIC for htt
1562 may bring the focus back, so de-focus it again.
1563 */
1564 setXICFocus(pX11IMData->ic_active, FALSE);
1565 if (pX11IMData->ic_active != pX11IMData->ic_passive) {
1566 char *tmpText = XmbResetIC(pX11IMData->ic_passive);
1567 setXICFocus(pX11IMData->ic_passive, FALSE);
1568 if (xText == (char *)NULL && tmpText)
1569 xText = tmpText;
1570 }
1571
1572 }
1573 if (xText != NULL) {
1574 jText = JNU_NewStringPlatform(env, (const char *)xText);
1575 XFree((void *)xText);
1576 }
1577
1578 AWT_UNLOCK();
1579 return jText;
1580 }
1581
1582 /*
1583 * Class: sun_awt_X11InputMethodBase
1584 * Method: setCompositionEnabledNative
1585 * Signature: (Z)Z
1586 *
1587 * This method tries to set the XNPreeditState attribute associated with the current
1588 * XIC to the passed in 'enable' state.
1589 *
1590 * Return JNI_TRUE if XNPreeditState attribute is successfully changed to the
1591 * 'enable' state; Otherwise, if XSetICValues fails to set this attribute,
1592 * java.lang.UnsupportedOperationException will be thrown. JNI_FALSE is returned if this
1593 * method fails due to other reasons.
1594 */
Java_sun_awt_X11InputMethodBase_setCompositionEnabledNative(JNIEnv * env,jobject this,jboolean enable)1595 JNIEXPORT jboolean JNICALL Java_sun_awt_X11InputMethodBase_setCompositionEnabledNative
1596 (JNIEnv *env, jobject this, jboolean enable)
1597 {
1598 X11InputMethodData *pX11IMData;
1599 char * ret = NULL;
1600 XVaNestedList pr_atrb;
1601 #if defined(__linux__) || defined(_ALLBSD_SOURCE)
1602 Boolean calledXSetICFocus = False;
1603 #endif
1604
1605 AWT_LOCK();
1606 pX11IMData = getX11InputMethodData(env, this);
1607
1608 if ((pX11IMData == NULL) || (pX11IMData->current_ic == NULL)) {
1609 AWT_UNLOCK();
1610 return JNI_FALSE;
1611 }
1612
1613 #if defined(__linux__) || defined(_ALLBSD_SOURCE)
1614 if (NULL != pX11IMData->statusWindow) {
1615 Window focus = 0;
1616 int revert_to;
1617 #if defined(_LP64) && !defined(_LITTLE_ENDIAN)
1618 // The Window value which is used for XGetICValues must be 32bit on BigEndian XOrg's xlib
1619 unsigned int w = 0;
1620 #else
1621 Window w = 0;
1622 #endif
1623 XGetInputFocus(awt_display, &focus, &revert_to);
1624 XGetICValues(pX11IMData->current_ic, XNFocusWindow, &w, NULL);
1625 if (RevertToPointerRoot == revert_to
1626 && pX11IMData->ic_active != pX11IMData->ic_passive) {
1627 if (pX11IMData->current_ic == pX11IMData->ic_active) {
1628 if (getParentWindow(focus) == getParentWindow(w)) {
1629 XUnsetICFocus(pX11IMData->ic_active);
1630 calledXSetICFocus = True;
1631 }
1632 }
1633 }
1634 }
1635 #endif
1636 pr_atrb = XVaCreateNestedList(0,
1637 XNPreeditState, (enable ? XIMPreeditEnable : XIMPreeditDisable),
1638 NULL);
1639 ret = XSetICValues(pX11IMData->current_ic, XNPreeditAttributes, pr_atrb, NULL);
1640 XFree((void *)pr_atrb);
1641 #if defined(__linux__) || defined(_ALLBSD_SOURCE)
1642 if (calledXSetICFocus) {
1643 XSetICFocus(pX11IMData->ic_active);
1644 }
1645 #endif
1646 AWT_UNLOCK();
1647
1648 if ((ret != 0)
1649 && ((strcmp(ret, XNPreeditAttributes) == 0)
1650 || (strcmp(ret, XNPreeditState) == 0))) {
1651 JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", "");
1652 }
1653
1654 return (jboolean)(ret == 0);
1655 }
1656
1657 /*
1658 * Class: sun_awt_X11InputMethodBase
1659 * Method: isCompositionEnabledNative
1660 * Signature: ()Z
1661 *
1662 * This method tries to get the XNPreeditState attribute associated with the current XIC.
1663 *
1664 * Return JNI_TRUE if the XNPreeditState is successfully retrieved. Otherwise, if
1665 * XGetICValues fails to get this attribute, java.lang.UnsupportedOperationException
1666 * will be thrown. JNI_FALSE is returned if this method fails due to other reasons.
1667 */
Java_sun_awt_X11InputMethodBase_isCompositionEnabledNative(JNIEnv * env,jobject this)1668 JNIEXPORT jboolean JNICALL Java_sun_awt_X11InputMethodBase_isCompositionEnabledNative
1669 (JNIEnv *env, jobject this)
1670 {
1671 X11InputMethodData *pX11IMData = NULL;
1672 char * ret = NULL;
1673 #if (defined(__linux__) || defined(_ALLBSD_SOURCE)) && defined(_LP64) && !defined(_LITTLE_ENDIAN)
1674 // XIMPreeditState value which is used for XGetICValues must be 32bit on BigEndian XOrg's xlib
1675 unsigned int state = XIMPreeditUnKnown;
1676 #else
1677 XIMPreeditState state = XIMPreeditUnKnown;
1678 #endif
1679
1680 XVaNestedList pr_atrb;
1681
1682 AWT_LOCK();
1683 pX11IMData = getX11InputMethodData(env, this);
1684
1685 if ((pX11IMData == NULL) || (pX11IMData->current_ic == NULL)) {
1686 AWT_UNLOCK();
1687 return JNI_FALSE;
1688 }
1689
1690 pr_atrb = XVaCreateNestedList(0, XNPreeditState, &state, NULL);
1691 ret = XGetICValues(pX11IMData->current_ic, XNPreeditAttributes, pr_atrb, NULL);
1692 XFree((void *)pr_atrb);
1693 AWT_UNLOCK();
1694
1695 if ((ret != 0)
1696 && ((strcmp(ret, XNPreeditAttributes) == 0)
1697 || (strcmp(ret, XNPreeditState) == 0))) {
1698 JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", "");
1699 return JNI_FALSE;
1700 }
1701
1702 return (jboolean)(state == XIMPreeditEnable);
1703 }
1704
Java_sun_awt_X11_XInputMethod_adjustStatusWindow(JNIEnv * env,jobject this,jlong window)1705 JNIEXPORT void JNICALL Java_sun_awt_X11_XInputMethod_adjustStatusWindow
1706 (JNIEnv *env, jobject this, jlong window)
1707 {
1708 #if defined(__linux__) || defined(_ALLBSD_SOURCE)
1709 AWT_LOCK();
1710 adjustStatusWindow(window);
1711 AWT_UNLOCK();
1712 #endif
1713 }
1714
1715 #if defined(__linux__) || defined(_ALLBSD_SOURCE)
getParentWindow(Window w)1716 static Window getParentWindow(Window w)
1717 {
1718 Window root=None, parent=None, *ignore_children=NULL;
1719 unsigned int ignore_uint=0;
1720 Status status = 0;
1721
1722 if (w == None)
1723 return None;
1724 status = XQueryTree(dpy, w, &root, &parent, &ignore_children, &ignore_uint);
1725 XFree(ignore_children);
1726 if (status == 0)
1727 return None;
1728 return parent;
1729 }
1730 #endif
1731