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