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 
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(MACOSX) || 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(MACOSX) || 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(MACOSX) || defined(_ALLBSD_SOURCE)
88     (XIMProc)StatusStartCallback,
89     (XIMProc)StatusDoneCallback,
90     (XIMProc)StatusDrawCallback,
91 #endif
92 };
93 
94 #if defined(__linux__) || defined(MACOSX) || 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(MACOSX) || 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(MACOSX) || defined(_ALLBSD_SOURCE)
179 static Window getParentWindow(Window);
180 #endif
181 
182 #ifdef __solaris__
183 /* Prototype for this function is missing in Solaris X11R6 Xlib.h */
184 extern 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  */
195 jlong
awt_util_nowMillisUTC()196 awt_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  */
209 static char *
wcstombsdmp(wchar_t * wcs,int len)210 wcstombsdmp(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  */
isX11InputMethodGRefInList(jobject imGRef)239 static 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  */
addToX11InputMethodGRefList(jobject newX11InputMethodGRef)259 static 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  */
removeX11InputMethodGRefFromList(jobject x11InputMethodGRef)281 static 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 
getX11InputMethodData(JNIEnv * env,jobject imInstance)316 static 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 
setX11InputMethodData(JNIEnv * env,jobject imInstance,X11InputMethodData * pX11IMData)344 static 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() */
349 static void
destroyX11InputMethodData(JNIEnv * env,X11InputMethodData * pX11IMData)350 destroyX11InputMethodData(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 
375 static void
freeX11InputMethodData(JNIEnv * env,X11InputMethodData * pX11IMData)376 freeX11InputMethodData(JNIEnv *env, X11InputMethodData *pX11IMData)
377 {
378 #if defined(__linux__) || defined(MACOSX) || defined(_ALLBSD_SOURCE)
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  */
414 static void
setXICFocus(XIC ic,unsigned short req)415 setXICFocus(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  */
430 static void
setXICWindowFocus(XIC ic,Window w)431 setXICWindowFocus(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 
456 Boolean
awt_x11inputmethod_lookupString(XKeyPressedEvent * event,KeySym * keysymp)457 awt_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) || defined(_ALLBSD_SOURCE)
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) || defined(_ALLBSD_SOURCE)
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) || defined(_ALLBSD_SOURCE)
createStatusWindow(Window parent)582 static 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. */
onoffStatusWindow(X11InputMethodData * pX11IMData,Window parent,Bool ON)716 static 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 
paintStatusWindow(StatusWindow * statusWindow)771 void 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 
adjustStatusWindow(Window shell)809 static 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 || _ALLBSD_SOURCE */
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  */
871 static Bool
createXIC(JNIEnv * env,X11InputMethodData * pX11IMData,Window w)872 createXIC(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) || defined(_ALLBSD_SOURCE)
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 || _ALLBSD_SOURCE */
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) || defined(_ALLBSD_SOURCE)
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 && !_ALLBSD_SOURCE */
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 || _ALLBSD_SOURCE */
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 
1056 static int
PreeditStartCallback(XIC ic,XPointer client_data,XPointer call_data)1057 PreeditStartCallback(XIC ic, XPointer client_data, XPointer call_data)
1058 {
1059     /*ARGSUSED*/
1060     /* printf("Native: PreeditStartCallback\n"); */
1061     return -1;
1062 }
1063 
1064 static void
PreeditDoneCallback(XIC ic,XPointer client_data,XPointer call_data)1065 PreeditDoneCallback(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  */
1077 static void
PreeditDrawCallback(XIC ic,XPointer client_data,XIMPreeditDrawCallbackStruct * pre_draw)1078 PreeditDrawCallback(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());
1163 finally:
1164     AWT_UNLOCK();
1165     return;
1166 }
1167 
1168 static void
PreeditCaretCallback(XIC ic,XPointer client_data,XIMPreeditCaretCallbackStruct * pre_caret)1169 PreeditCaretCallback(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) || defined(_ALLBSD_SOURCE)
1177 static void
StatusStartCallback(XIC ic,XPointer client_data,XPointer call_data)1178 StatusStartCallback(XIC ic, XPointer client_data, XPointer call_data)
1179 {
1180     /*ARGSUSED*/
1181     /*printf("StatusStartCallback:\n");  */
1182 }
1183 
1184 static void
StatusDoneCallback(XIC ic,XPointer client_data,XPointer call_data)1185 StatusDoneCallback(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 
1214 static void
StatusDrawCallback(XIC ic,XPointer client_data,XIMStatusDrawCallbackStruct * status_draw)1215 StatusDrawCallback(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                 if (mbstr == NULL) {
1248                     goto finally;
1249                 }
1250                 strncpy(statusWindow->status, mbstr, MAX_STATUS_LEN);
1251                 statusWindow->status[MAX_STATUS_LEN - 1] = '\0';
1252                 free(mbstr);
1253             }
1254             statusWindow->on = True;
1255             onoffStatusWindow(pX11IMData, statusWindow->parent, True);
1256             paintStatusWindow(statusWindow);
1257         } else {
1258             statusWindow->on = False;
1259             /*just turnoff the status window
1260             paintStatusWindow(statusWindow);
1261             */
1262             onoffStatusWindow(pX11IMData, 0, False);
1263         }
1264     }
1265 
1266  finally:
1267     AWT_UNLOCK();
1268 }
1269 #endif /* __linux__ || MACOSX || _ALLBSD_SOURCE */
1270 
CommitStringCallback(XIC ic,XPointer client_data,XPointer call_data)1271 static void CommitStringCallback(XIC ic, XPointer client_data, XPointer call_data) {
1272     JNIEnv *env = GetJNIEnv();
1273     XIMText * text = (XIMText *)call_data;
1274     X11InputMethodData *pX11IMData = NULL;
1275     jstring javastr;
1276 
1277     AWT_LOCK();
1278 
1279     if (!isX11InputMethodGRefInList((jobject)client_data)) {
1280         if ((jobject)client_data == currentX11InputMethodInstance) {
1281             currentX11InputMethodInstance = NULL;
1282         }
1283         goto finally;
1284     }
1285 
1286     if ((pX11IMData = getX11InputMethodData(env, (jobject)client_data)) == NULL) {
1287         goto finally;
1288     }
1289     currentX11InputMethodInstance = (jobject)client_data;
1290 
1291     if (text->encoding_is_wchar == False) {
1292         javastr = JNU_NewStringPlatform(env, (const char *)text->string.multi_byte);
1293     } else {
1294         char *mbstr = wcstombsdmp(text->string.wide_char, text->length);
1295         if (mbstr == NULL) {
1296             goto finally;
1297         }
1298         javastr = JNU_NewStringPlatform(env, (const char *)mbstr);
1299         free(mbstr);
1300     }
1301 
1302     if (javastr != NULL) {
1303         JNU_CallMethodByName(env, NULL,
1304                                  pX11IMData->x11inputmethod,
1305                                  "dispatchCommittedText",
1306                                  "(Ljava/lang/String;J)V",
1307                                  javastr,
1308                                  awt_util_nowMillisUTC());
1309     }
1310  finally:
1311     AWT_UNLOCK();
1312 }
1313 
OpenXIMCallback(Display * display,XPointer client_data,XPointer call_data)1314 static void OpenXIMCallback(Display *display, XPointer client_data, XPointer call_data) {
1315     XIMCallback ximCallback;
1316 
1317     X11im = XOpenIM(display, NULL, NULL, NULL);
1318     if (X11im == NULL) {
1319         return;
1320     }
1321 
1322     ximCallback.callback = (XIMProc)DestroyXIMCallback;
1323     ximCallback.client_data = NULL;
1324     XSetIMValues(X11im, XNDestroyCallback, &ximCallback, NULL);
1325 }
1326 
DestroyXIMCallback(XIM im,XPointer client_data,XPointer call_data)1327 static void DestroyXIMCallback(XIM im, XPointer client_data, XPointer call_data) {
1328     /* mark that XIM server was destroyed */
1329     X11im = NULL;
1330     JNIEnv* env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
1331 
1332     AWT_LOCK();
1333     /* free the old pX11IMData and set it to null. this also avoids crashing
1334      * the jvm if the XIM server reappears */
1335     while (x11InputMethodGRefListHead != NULL) {
1336         if (getX11InputMethodData(env,
1337                 x11InputMethodGRefListHead->inputMethodGRef) == NULL) {
1338             /* Clear possible exceptions
1339              */
1340             if ((*env)->ExceptionOccurred(env)) {
1341                 (*env)->ExceptionDescribe(env);
1342                 (*env)->ExceptionClear(env);
1343             }
1344         }
1345     }
1346     AWT_UNLOCK();
1347 }
1348 
1349 JNIEXPORT jboolean JNICALL
Java_sun_awt_X11_XInputMethod_openXIMNative(JNIEnv * env,jobject this,jlong display)1350 Java_sun_awt_X11_XInputMethod_openXIMNative(JNIEnv *env,
1351                                             jobject this,
1352                                             jlong display)
1353 {
1354     Bool registered;
1355 
1356     AWT_LOCK();
1357 
1358     dpy = (Display *)jlong_to_ptr(display);
1359 
1360 /* Use IMInstantiate call back only on Linux, as there is a bug in Solaris
1361    (4768335)
1362 */
1363 #if defined(__linux__) || defined(MACOSX) || defined(_ALLBSD_SOURCE)
1364     registered = XRegisterIMInstantiateCallback(dpy, NULL, NULL,
1365                      NULL, (XIDProc)OpenXIMCallback, NULL);
1366     if (!registered) {
1367         /* directly call openXIM callback */
1368 #endif
1369         OpenXIMCallback(dpy, NULL, NULL);
1370 #if defined(__linux__) || defined(MACOSX) || defined(_ALLBSD_SOURCE)
1371     }
1372 #endif
1373 
1374     AWT_UNLOCK();
1375 
1376     return JNI_TRUE;
1377 }
1378 
1379 JNIEXPORT jboolean JNICALL
Java_sun_awt_X11_XInputMethod_createXICNative(JNIEnv * env,jobject this,jlong window)1380 Java_sun_awt_X11_XInputMethod_createXICNative(JNIEnv *env,
1381                                               jobject this,
1382                                               jlong window)
1383 {
1384     X11InputMethodData *pX11IMData;
1385     jobject globalRef;
1386     XIC ic;
1387 
1388     AWT_LOCK();
1389 
1390     if (!window) {
1391         JNU_ThrowNullPointerException(env, "NullPointerException");
1392         AWT_UNLOCK();
1393         return JNI_FALSE;
1394     }
1395 
1396     pX11IMData = (X11InputMethodData *) calloc(1, sizeof(X11InputMethodData));
1397     if (pX11IMData == NULL) {
1398         THROW_OUT_OF_MEMORY_ERROR();
1399         AWT_UNLOCK();
1400         return JNI_FALSE;
1401     }
1402 
1403     globalRef = (*env)->NewGlobalRef(env, this);
1404     pX11IMData->x11inputmethod = globalRef;
1405 #if defined(__linux__) || defined(MACOSX) || defined(_ALLBSD_SOURCE)
1406     pX11IMData->statusWindow = NULL;
1407 #endif /* __linux__ || MACOSX || _ALLBSD_SOURCE */
1408 
1409     pX11IMData->lookup_buf = 0;
1410     pX11IMData->lookup_buf_len = 0;
1411 
1412     if (createXIC(env, pX11IMData, (Window)window) == False) {
1413         destroyX11InputMethodData((JNIEnv *) NULL, pX11IMData);
1414         pX11IMData = (X11InputMethodData *) NULL;
1415         if ((*env)->ExceptionCheck(env)) {
1416             goto finally;
1417         }
1418     }
1419 
1420     setX11InputMethodData(env, this, pX11IMData);
1421 
1422 finally:
1423     AWT_UNLOCK();
1424     return (pX11IMData != NULL);
1425 }
1426 
1427 JNIEXPORT void JNICALL
Java_sun_awt_X11_XInputMethod_setXICFocusNative(JNIEnv * env,jobject this,jlong w,jboolean req,jboolean active)1428 Java_sun_awt_X11_XInputMethod_setXICFocusNative(JNIEnv *env,
1429                                                 jobject this,
1430                                                 jlong w,
1431                                                 jboolean req,
1432                                                 jboolean active)
1433 {
1434     X11InputMethodData *pX11IMData;
1435     AWT_LOCK();
1436     pX11IMData = getX11InputMethodData(env, this);
1437     if (pX11IMData == NULL) {
1438         AWT_UNLOCK();
1439         return;
1440     }
1441 
1442     if (req) {
1443         if (!w) {
1444             AWT_UNLOCK();
1445             return;
1446         }
1447         pX11IMData->current_ic = active ?
1448                         pX11IMData->ic_active : pX11IMData->ic_passive;
1449         /*
1450          * On Solaris2.6, setXICWindowFocus() has to be invoked
1451          * before setting focus.
1452          */
1453         setXICWindowFocus(pX11IMData->current_ic, w);
1454         setXICFocus(pX11IMData->current_ic, req);
1455         currentX11InputMethodInstance = pX11IMData->x11inputmethod;
1456         currentFocusWindow =  w;
1457 #if defined(__linux__) || defined(MACOSX) || defined(_ALLBSD_SOURCE)
1458         if (active && pX11IMData->statusWindow && pX11IMData->statusWindow->on)
1459             onoffStatusWindow(pX11IMData, w, True);
1460 #endif
1461     } else {
1462         currentX11InputMethodInstance = NULL;
1463         currentFocusWindow = 0;
1464 #if defined(__linux__) || defined(MACOSX) || defined(_ALLBSD_SOURCE)
1465         onoffStatusWindow(pX11IMData, 0, False);
1466         if (pX11IMData->current_ic != NULL)
1467 #endif
1468         setXICFocus(pX11IMData->current_ic, req);
1469 
1470         pX11IMData->current_ic = (XIC)0;
1471     }
1472 
1473     XFlush(dpy);
1474     AWT_UNLOCK();
1475 }
1476 
1477 /*
1478  * Class:     sun_awt_X11InputMethodBase
1479  * Method:    initIDs
1480  * Signature: ()V
1481  * This function gets called from the static initializer for
1482  * X11InputMethod.java to initialize the fieldIDs for fields
1483  * that may be accessed from C
1484  */
Java_sun_awt_X11InputMethodBase_initIDs(JNIEnv * env,jclass cls)1485 JNIEXPORT void JNICALL Java_sun_awt_X11InputMethodBase_initIDs
1486   (JNIEnv *env, jclass cls)
1487 {
1488     x11InputMethodIDs.pData = (*env)->GetFieldID(env, cls, "pData", "J");
1489 }
1490 
1491 /*
1492  * Class:     sun_awt_X11InputMethodBase
1493  * Method:    turnoffStatusWindow
1494  * Signature: ()V
1495  */
Java_sun_awt_X11InputMethodBase_turnoffStatusWindow(JNIEnv * env,jobject this)1496 JNIEXPORT void JNICALL Java_sun_awt_X11InputMethodBase_turnoffStatusWindow
1497   (JNIEnv *env, jobject this)
1498 {
1499 #if defined(__linux__) || defined(MACOSX) || defined(_ALLBSD_SOURCE)
1500     X11InputMethodData *pX11IMData;
1501     StatusWindow *statusWindow;
1502 
1503     AWT_LOCK();
1504 
1505     if (NULL == currentX11InputMethodInstance
1506         || !isX11InputMethodGRefInList(currentX11InputMethodInstance)
1507         || NULL == (pX11IMData = getX11InputMethodData(env,currentX11InputMethodInstance))
1508         || NULL == (statusWindow = pX11IMData->statusWindow)
1509         || !statusWindow->on ){
1510         AWT_UNLOCK();
1511         return;
1512     }
1513     onoffStatusWindow(pX11IMData, 0, False);
1514 
1515     AWT_UNLOCK();
1516 #endif
1517 }
1518 
1519 /*
1520  * Class:     sun_awt_X11InputMethodBase
1521  * Method:    disposeXIC
1522  * Signature: ()V
1523  */
Java_sun_awt_X11InputMethodBase_disposeXIC(JNIEnv * env,jobject this)1524 JNIEXPORT void JNICALL Java_sun_awt_X11InputMethodBase_disposeXIC
1525   (JNIEnv *env, jobject this)
1526 {
1527     X11InputMethodData *pX11IMData = NULL;
1528 
1529     AWT_LOCK();
1530     pX11IMData = getX11InputMethodData(env, this);
1531     if (pX11IMData == NULL) {
1532         AWT_UNLOCK();
1533         return;
1534     }
1535 
1536     setX11InputMethodData(env, this, NULL);
1537 
1538     if (pX11IMData->x11inputmethod == currentX11InputMethodInstance) {
1539         currentX11InputMethodInstance = NULL;
1540         currentFocusWindow = 0;
1541     }
1542     destroyX11InputMethodData(env, pX11IMData);
1543     AWT_UNLOCK();
1544 }
1545 
1546 /*
1547  * Class:     sun_awt_X11InputMethodBase
1548  * Method:    resetXIC
1549  * Signature: ()Ljava/lang/String;
1550  */
Java_sun_awt_X11InputMethodBase_resetXIC(JNIEnv * env,jobject this)1551 JNIEXPORT jstring JNICALL Java_sun_awt_X11InputMethodBase_resetXIC
1552   (JNIEnv *env, jobject this)
1553 {
1554     X11InputMethodData *pX11IMData;
1555     char *xText = NULL;
1556     jstring jText = (jstring)0;
1557 
1558     AWT_LOCK();
1559     pX11IMData = getX11InputMethodData(env, this);
1560     if (pX11IMData == NULL) {
1561         AWT_UNLOCK();
1562         return jText;
1563     }
1564 
1565     if (pX11IMData->current_ic)
1566         xText = XmbResetIC(pX11IMData->current_ic);
1567     else {
1568         /*
1569          * If there is no reference to the current XIC, try to reset both XICs.
1570          */
1571         xText = XmbResetIC(pX11IMData->ic_active);
1572         /*it may also means that the real client component does
1573           not have focus -- has been deactivated... its xic should
1574           not have the focus, bug#4284651 showes reset XIC for htt
1575           may bring the focus back, so de-focus it again.
1576         */
1577         setXICFocus(pX11IMData->ic_active, FALSE);
1578         if (pX11IMData->ic_active != pX11IMData->ic_passive) {
1579             char *tmpText = XmbResetIC(pX11IMData->ic_passive);
1580             setXICFocus(pX11IMData->ic_passive, FALSE);
1581             if (xText == (char *)NULL && tmpText)
1582                 xText = tmpText;
1583         }
1584 
1585     }
1586     if (xText != NULL) {
1587         jText = JNU_NewStringPlatform(env, (const char *)xText);
1588         XFree((void *)xText);
1589     }
1590 
1591     AWT_UNLOCK();
1592     return jText;
1593 }
1594 
1595 /*
1596  * Class:     sun_awt_X11InputMethodBase
1597  * Method:    setCompositionEnabledNative
1598  * Signature: (Z)Z
1599  *
1600  * This method tries to set the XNPreeditState attribute associated with the current
1601  * XIC to the passed in 'enable' state.
1602  *
1603  * Return JNI_TRUE if XNPreeditState attribute is successfully changed to the
1604  * 'enable' state; Otherwise, if XSetICValues fails to set this attribute,
1605  * java.lang.UnsupportedOperationException will be thrown. JNI_FALSE is returned if this
1606  * method fails due to other reasons.
1607  */
Java_sun_awt_X11InputMethodBase_setCompositionEnabledNative(JNIEnv * env,jobject this,jboolean enable)1608 JNIEXPORT jboolean JNICALL Java_sun_awt_X11InputMethodBase_setCompositionEnabledNative
1609   (JNIEnv *env, jobject this, jboolean enable)
1610 {
1611     X11InputMethodData *pX11IMData;
1612     char * ret = NULL;
1613     XVaNestedList   pr_atrb;
1614 #if defined(__linux__) || defined(MACOSX) || defined(_ALLBSD_SOURCE)
1615     Boolean calledXSetICFocus = False;
1616 #endif
1617 
1618     AWT_LOCK();
1619     pX11IMData = getX11InputMethodData(env, this);
1620 
1621     if ((pX11IMData == NULL) || (pX11IMData->current_ic == NULL)) {
1622         AWT_UNLOCK();
1623         return JNI_FALSE;
1624     }
1625 
1626 #if defined(__linux__) || defined(MACOSX) || defined(_ALLBSD_SOURCE)
1627     if (NULL != pX11IMData->statusWindow) {
1628         Window focus = 0;
1629         int revert_to;
1630 #if defined(_LP64) && !defined(_LITTLE_ENDIAN)
1631         // The Window value which is used for XGetICValues must be 32bit on BigEndian XOrg's xlib
1632         unsigned int w = 0;
1633 #else
1634         Window w = 0;
1635 #endif
1636         XGetInputFocus(awt_display, &focus, &revert_to);
1637         XGetICValues(pX11IMData->current_ic, XNFocusWindow, &w, NULL);
1638         if (RevertToPointerRoot == revert_to
1639                 && pX11IMData->ic_active != pX11IMData->ic_passive) {
1640             if (pX11IMData->current_ic == pX11IMData->ic_active) {
1641                 if (getParentWindow(focus) == getParentWindow(w)) {
1642                     XUnsetICFocus(pX11IMData->ic_active);
1643                     calledXSetICFocus = True;
1644                 }
1645             }
1646         }
1647     }
1648 #endif
1649     pr_atrb = XVaCreateNestedList(0,
1650                   XNPreeditState, (enable ? XIMPreeditEnable : XIMPreeditDisable),
1651                   NULL);
1652     ret = XSetICValues(pX11IMData->current_ic, XNPreeditAttributes, pr_atrb, NULL);
1653     XFree((void *)pr_atrb);
1654 #if defined(__linux__) || defined(MACOSX) || defined(_ALLBSD_SOURCE)
1655     if (calledXSetICFocus) {
1656         XSetICFocus(pX11IMData->ic_active);
1657     }
1658 #endif
1659     AWT_UNLOCK();
1660 
1661     if ((ret != 0)
1662             && ((strcmp(ret, XNPreeditAttributes) == 0)
1663             || (strcmp(ret, XNPreeditState) == 0))) {
1664         JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", "");
1665     }
1666 
1667     return (jboolean)(ret == 0);
1668 }
1669 
1670 /*
1671  * Class:     sun_awt_X11InputMethodBase
1672  * Method:    isCompositionEnabledNative
1673  * Signature: ()Z
1674  *
1675  * This method tries to get the XNPreeditState attribute associated with the current XIC.
1676  *
1677  * Return JNI_TRUE if the XNPreeditState is successfully retrieved. Otherwise, if
1678  * XGetICValues fails to get this attribute, java.lang.UnsupportedOperationException
1679  * will be thrown. JNI_FALSE is returned if this method fails due to other reasons.
1680  */
Java_sun_awt_X11InputMethodBase_isCompositionEnabledNative(JNIEnv * env,jobject this)1681 JNIEXPORT jboolean JNICALL Java_sun_awt_X11InputMethodBase_isCompositionEnabledNative
1682   (JNIEnv *env, jobject this)
1683 {
1684     X11InputMethodData *pX11IMData = NULL;
1685     char * ret = NULL;
1686 #if defined(__linux__) && defined(_LP64) && !defined(_LITTLE_ENDIAN)
1687     // XIMPreeditState value which is used for XGetICValues must be 32bit on BigEndian XOrg's xlib
1688     unsigned int state = XIMPreeditUnKnown;
1689 #else
1690     XIMPreeditState state = XIMPreeditUnKnown;
1691 #endif
1692 
1693     XVaNestedList   pr_atrb;
1694 
1695     AWT_LOCK();
1696     pX11IMData = getX11InputMethodData(env, this);
1697 
1698     if ((pX11IMData == NULL) || (pX11IMData->current_ic == NULL)) {
1699         AWT_UNLOCK();
1700         return JNI_FALSE;
1701     }
1702 
1703     pr_atrb = XVaCreateNestedList(0, XNPreeditState, &state, NULL);
1704     ret = XGetICValues(pX11IMData->current_ic, XNPreeditAttributes, pr_atrb, NULL);
1705     XFree((void *)pr_atrb);
1706     AWT_UNLOCK();
1707 
1708     if ((ret != 0)
1709             && ((strcmp(ret, XNPreeditAttributes) == 0)
1710             || (strcmp(ret, XNPreeditState) == 0))) {
1711         JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", "");
1712         return JNI_FALSE;
1713     }
1714 
1715     return (jboolean)(state == XIMPreeditEnable);
1716 }
1717 
Java_sun_awt_X11_XInputMethod_adjustStatusWindow(JNIEnv * env,jobject this,jlong window)1718 JNIEXPORT void JNICALL Java_sun_awt_X11_XInputMethod_adjustStatusWindow
1719   (JNIEnv *env, jobject this, jlong window)
1720 {
1721 #if defined(__linux__) || defined(MACOSX) || defined(_ALLBSD_SOURCE)
1722     AWT_LOCK();
1723     adjustStatusWindow(window);
1724     AWT_UNLOCK();
1725 #endif
1726 }
1727 
1728 #if defined(__linux__) || defined(MACOSX) || defined(_ALLBSD_SOURCE)
getParentWindow(Window w)1729 static Window getParentWindow(Window w)
1730 {
1731     Window root=None, parent=None, *ignore_children=NULL;
1732     unsigned int ignore_uint=0;
1733     Status status = 0;
1734 
1735     if (w == None)
1736         return None;
1737     status = XQueryTree(dpy, w, &root, &parent, &ignore_children, &ignore_uint);
1738     XFree(ignore_children);
1739     if (status == 0)
1740         return None;
1741     return parent;
1742 }
1743 #endif
1744