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