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