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