/* * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ #ifdef HEADLESS #error This file should not be included in headless library #endif #include "awt.h" #include "awt_p.h" #include #include #include #include #include #include #include #include #include #include #include #include #define THROW_OUT_OF_MEMORY_ERROR() \ JNU_ThrowOutOfMemoryError((JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2), NULL) struct X11InputMethodIDs { jfieldID pData; } x11InputMethodIDs; static int PreeditStartCallback(XIC, XPointer, XPointer); static void PreeditDoneCallback(XIC, XPointer, XPointer); static void PreeditDrawCallback(XIC, XPointer, XIMPreeditDrawCallbackStruct *); static void PreeditCaretCallback(XIC, XPointer, XIMPreeditCaretCallbackStruct *); static void StatusStartCallback(XIC, XPointer, XPointer); static void StatusDoneCallback(XIC, XPointer, XPointer); static void StatusDrawCallback(XIC, XPointer, XIMStatusDrawCallbackStruct *); #define ROOT_WINDOW_STYLES (XIMPreeditNothing | XIMStatusNothing) #define NO_STYLES (XIMPreeditNone | XIMStatusNone) /* added style to allow for in-place composition, such as "dead" keys for accents */ #define IN_PLACE_STYLES (XIMPreeditNothing | XIMStatusNone) #define PreeditStartIndex 0 #define PreeditDoneIndex 1 #define PreeditDrawIndex 2 #define PreeditCaretIndex 3 #define StatusStartIndex 4 #define StatusDoneIndex 5 #define StatusDrawIndex 6 #define NCALLBACKS 7 #define STATUS_BORDER 2 /* Status Border width */ #define CARET_OFFSET 1 /* Offset of caret position (pixel) */ #define BORDER_MARGIN 3 /* BORDER MARGIN width */ #define STATUS_MARGIN 7 /* Margin between the status window and its parent window */ #define PREEDIT_ATTR_MASK (XIMReverse|XIMUnderline) /* Preedit attribute which host adapter can handle */ /* * Callback function pointers: the order has to match the *Index * values above. */ static XIMProc callback_funcs[NCALLBACKS] = { (XIMProc)(void *)&PreeditStartCallback, (XIMProc)PreeditDoneCallback, (XIMProc)PreeditDrawCallback, (XIMProc)PreeditCaretCallback, (XIMProc)StatusStartCallback, (XIMProc)StatusDoneCallback, (XIMProc)StatusDrawCallback, }; #define MAX_STATUS_LEN 100 typedef struct { Window w; /*status window id */ Window root; /*the root window id */ Window parent; /*parent shell window */ Window grandParent; /*window has WM frame */ int x, y; /*parent's upperleft position */ int width, height; /*parent's width, height */ GC lightGC; /*gc for light border */ GC dimGC; /*gc for dim border */ GC bgGC; /*normal painting */ GC fgGC; /*normal painting */ int statusW, statusH; /*status window's w, h */ int rootW, rootH; /*root window's w, h */ int bWidth; /*border width */ wchar_t status[MAX_STATUS_LEN + 1]; /*status text */ XFontSet fontset; /*fontset for drawing */ int off_x, off_y; Bool on; /*if the status window on*/ int fOff; /* font base line(in pixel) from top */ int fBot; /* font bottom line(in pixel) from top */ int peTextW; /* Composition text width in pixel */ wchar_t* peText; /* Composed string (wide char.) */ XIMFeedback* peAttr; /* Composed text attribute */ int peCaret; /* Caret position in number of character */ Bool status_ready; /* Not draw Status at XCreateIC */ } StatusWindow; /* * X11InputMethodData keeps per X11InputMethod instance information. A pointer * to this data structure is kept in an X11InputMethod object (pData). */ typedef struct _X11InputMethodData { XIC current_ic; /* current X Input Context */ XIC ic_active; /* X Input Context for active clients */ XIC ic_passive; /* X Input Context for passive clients */ XIMCallback *callbacks; /* callback parameters */ jobject x11inputmethod; /* global ref to X11InputMethod instance */ /* associated with the XIC */ StatusWindow *statusWindow; /* our own status window */ Bool passiveStatusWindow;/* Passive Client uses StatusWindow */ Bool isActiveClient; /* True:clinet is active */ Bool forceReset; /* True: call resetXIC before UnsetICFocus */ } X11InputMethodData; /* reference to the current X11InputMethod instance, it is always point to the global reference to the X11InputMethodObject since it could be referenced by different threads. */ jobject currentX11InputMethodInstance = NULL; Window currentFocusWindow = 0; /* current window that has focus for input method. (the best place to put this information should be currentX11InputMethodInstance's pData) */ static XIM X11im = NULL; Display * dpy = NULL; #define GetJNIEnv() (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2) static X11InputMethodData * getX11InputMethodData(JNIEnv *, jobject); static void setX11InputMethodData(JNIEnv *, jobject, X11InputMethodData *); static void destroyX11InputMethodData(JNIEnv *, X11InputMethodData *); static void freeX11InputMethodData(JNIEnv *, X11InputMethodData *); /* Prototype for this function is missing in AIX Xlib.h */ extern char *XSetIMValues( #if NeedVarargsPrototypes XIM /* im */, ... #endif ); static int st_wcslen(wchar_t *string); static Bool isPreeditStateActive(XIC ic); static void * buf_insert(void * src, void * insert, int size, int src_len, int ins_len, int offset); static void * handle_buffer(void * source, void * insert, int size, int src_len, int ins_len, int del_len, int offset); static void preedit_draw_passive(X11InputMethodData *pX11IMData, XIMPreeditDrawCallbackStruct *pre_draw); static void resetPassivePreeditText(StatusWindow *statusWindow); static void draw_caret(StatusWindow *statusWindow, GC gc, int pos); static int get_next_attr(int len, unsigned long *attr); static void draw_preedit(StatusWindow *statusWindow); static void align_status(StatusWindow *statusWindow); static void shrink_status(StatusWindow *statusWindow); static GC create_gc(Window win, Bool isReverse); static XFontSet create_fontset(void); static Bool is_text_available(XIMText * text); static Bool isNativeIm(); static Window getGrandParent(Window parent); static void moveStatusWindow(StatusWindow *statusWindow); static void arrange_window_stack(StatusWindow* statusWindow); static Window get_current_focus(XIC ic); /* * This function is stolen from /src/solaris/hpi/src/system_md.c * It is used in setting the time in Java-level InputEvents */ jlong awt_util_nowMillisUTC() { struct timeval t; gettimeofday(&t, NULL); return ((jlong)t.tv_sec) * 1000 + (jlong)(t.tv_usec/1000); } /* * Converts the wchar_t string to a multi-byte string calling wcstombs(). A * buffer is allocated by malloc() to store the multi-byte string. NULL is * returned if the given wchar_t string pointer is NULL or buffer allocation is * failed. */ static char * wcstombsdmp(wchar_t *wcs, int len) { size_t n; char *mbs; if (wcs == NULL) return NULL; n = len*MB_CUR_MAX + 1; mbs = (char *) malloc(n * sizeof(char)); if (mbs == NULL) { THROW_OUT_OF_MEMORY_ERROR(); return NULL; } /* TODO: check return values... Handle invalid characters properly... */ if (wcstombs(mbs, wcs, n) == (size_t)-1) { free(mbs); return NULL; } return mbs; } static X11InputMethodData * getX11InputMethodData(JNIEnv * env, jobject imInstance) { X11InputMethodData *pX11IMData = (X11InputMethodData *)JNU_GetLongFieldAsPtr(env, imInstance, x11InputMethodIDs.pData); /* * In case the XIM server was killed somehow, reset X11InputMethodData. */ if (X11im == NULL && pX11IMData != NULL) { JNU_CallMethodByName(env, NULL, pX11IMData->x11inputmethod, "flushText", "()V"); JNU_CHECK_EXCEPTION_RETURN(env, NULL); /* IMPORTANT: The order of the following calls is critical since "imInstance" may point to the global reference itself, if "freeX11InputMethodData" is called first, the global reference will be destroyed and "setX11InputMethodData" will in fact fail silently. So pX11IMData will not be set to NULL. This could make the original java object refers to a deleted pX11IMData object. */ setX11InputMethodData(env, imInstance, NULL); freeX11InputMethodData(env, pX11IMData); pX11IMData = NULL; } return pX11IMData; } static void setX11InputMethodData(JNIEnv * env, jobject imInstance, X11InputMethodData *pX11IMData) { JNU_SetLongFieldFromPtr(env, imInstance, x11InputMethodIDs.pData, pX11IMData); } /* this function should be called within AWT_LOCK() */ static void destroyX11InputMethodData(JNIEnv *env, X11InputMethodData *pX11IMData) { /* * Destroy XICs */ if (pX11IMData == NULL) { return; } if (pX11IMData->ic_active != (XIC)0) { XUnsetICFocus(pX11IMData->ic_active); XDestroyIC(pX11IMData->ic_active); if (pX11IMData->ic_active != pX11IMData->ic_passive) { if (pX11IMData->ic_passive != (XIC)0) { XUnsetICFocus(pX11IMData->ic_passive); XDestroyIC(pX11IMData->ic_passive); } pX11IMData->ic_passive = (XIC)0; pX11IMData->current_ic = (XIC)0; } } freeX11InputMethodData(env, pX11IMData); } static void freeX11InputMethodData(JNIEnv *env, X11InputMethodData *pX11IMData) { if (pX11IMData->statusWindow != NULL){ StatusWindow *sw = pX11IMData->statusWindow; XFreeGC(awt_display, sw->lightGC); XFreeGC(awt_display, sw->dimGC); XFreeGC(awt_display, sw->bgGC); XFreeGC(awt_display, sw->fgGC); if (sw->fontset != NULL) { XFreeFontSet(awt_display, sw->fontset); } XDestroyWindow(awt_display, sw->w); if (pX11IMData->statusWindow->peText){ free((void *)pX11IMData->statusWindow->peText); pX11IMData->statusWindow->peText = NULL; } if (pX11IMData->statusWindow->peAttr){ free((void *)pX11IMData->statusWindow->peAttr); pX11IMData->statusWindow->peAttr = NULL; } free((void*)sw); } if (pX11IMData->callbacks) free((void *)pX11IMData->callbacks); if (env) { (*env)->DeleteGlobalRef(env, pX11IMData->x11inputmethod); } free((void *)pX11IMData); } /* * Sets or unsets the focus to the given XIC. */ static void setXICFocus(XIC ic, unsigned short req) { if (ic == NULL) { (void)fprintf(stderr, "Couldn't find X Input Context\n"); return; } if (req == 1) XSetICFocus(ic); else XUnsetICFocus(ic); } /* * Sets the focus window to the given XIC. */ static void setXICWindowFocus(XIC ic, Window w) { if (ic == NULL) { (void)fprintf(stderr, "Couldn't find X Input Context\n"); return; } (void) XSetICValues(ic, XNFocusWindow, w, NULL); } /* * Invokes XmbLookupString() to get something from the XIM. It invokes * X11InputMethod.dispatchCommittedText() if XmbLookupString() returns * committed text. This function is called from handleKeyEvent in canvas.c and * it's under the Motif event loop thread context. * * Buffer usage: There is a bug in XFree86-4.3.0 XmbLookupString implementation, * where it never returns XBufferOverflow. We need to allocate the initial lookup buffer * big enough, so that the possibility that user encounters this problem is relatively * small. When this bug gets fixed, we can make the initial buffer size smaller. * Note that XmbLookupString() sometimes produces a non-null-terminated string. * * Returns True when there is a keysym value to be handled. */ #define INITIAL_LOOKUP_BUF_SIZE 512 Boolean awt_x11inputmethod_lookupString(XKeyPressedEvent *event, KeySym *keysymp) { JNIEnv *env = GetJNIEnv(); X11InputMethodData *pX11IMData = NULL; int buf_len = INITIAL_LOOKUP_BUF_SIZE; char mbbuf[INITIAL_LOOKUP_BUF_SIZE]; char *buf; KeySym keysym = NoSymbol; Status status; int mblen; jstring javastr; XIC ic; Boolean result = True; static Boolean composing = False; /* printf("lookupString: entering...\n"); */ pX11IMData = getX11InputMethodData(env, currentX11InputMethodInstance); if (pX11IMData == NULL) { return False; } if ((ic = pX11IMData->current_ic) == (XIC)0){ return False; } buf = mbbuf; mblen = XmbLookupString(ic, event, buf, buf_len - 1, &keysym, &status); /* * In case of overflow, a buffer is allocated and it retries * XmbLookupString(). */ if (status == XBufferOverflow) { buf_len = mblen + 1; buf = (char *)malloc(buf_len); if (buf == NULL) { THROW_OUT_OF_MEMORY_ERROR(); return result; } mblen = XmbLookupString(ic, event, buf, buf_len, &keysym, &status); } buf[mblen] = 0; /* Get keysym without taking modifiers into account first to map * to AWT keyCode table. */ switch (status) { case XLookupBoth: if (!composing) { if (event->keycode != 0) { *keysymp = keysym; result = False; break; } } composing = False; /*FALLTHRU*/ case XLookupChars: /* printf("lookupString: status=XLookupChars, type=%d, state=%x, keycode=%x, keysym=%x\n", event->type, event->state, event->keycode, keysym); */ javastr = JNU_NewStringPlatform(env, (const char *)buf); if (javastr != NULL) { JNU_CallMethodByName(env, NULL, currentX11InputMethodInstance, "dispatchCommittedText", "(Ljava/lang/String;J)V", javastr, event->time); if ((*env)->ExceptionOccurred(env)) { (*env)->ExceptionDescribe(env); (*env)->ExceptionClear(env); } } break; case XLookupKeySym: /* printf("lookupString: status=XLookupKeySym, type=%d, state=%x, keycode=%x, keysym=%x\n", event->type, event->state, event->keycode, keysym); */ if (keysym == XK_Multi_key) composing = True; if (! composing) { *keysymp = keysym; result = False; } break; case XLookupNone: /* printf("lookupString: status=XLookupNone, type=%d, state=%x, keycode=%x, keysym=%x\n", event->type, event->state, event->keycode, keysym); */ break; } if (buf != mbbuf) { free(buf); } return result; } static StatusWindow *createStatusWindow(Window parent) { StatusWindow *statusWindow; XSetWindowAttributes attrib; unsigned long attribmask; Window containerWindow; Window status; Window child; XWindowAttributes xwa; XWindowAttributes xxwa; /* Variable for XCreateFontSet()*/ char **mclr; int mccr = 0; char *dsr; unsigned long bg, fg, light, dim; int x, y, off_x, off_y, xx, yy; unsigned int w, h, bw, depth; XGCValues values; unsigned long valuemask = 0; /*ignore XGCvalue and use defaults*/ int screen = 0; int i; AwtGraphicsConfigDataPtr adata; extern int awt_numScreens; /*hardcode the size right now, should get the size base on font*/ int width=80, height=22; Window rootWindow; Window *ignoreWindowPtr; unsigned int ignoreUnit; Window grandParent; Window target; XFontSet fontset; fontset = create_fontset(); if (NULL == fontset) { return NULL; } XGetGeometry(dpy, parent, &rootWindow, &x, &y, &w, &h, &bw, &depth); attrib.override_redirect = True; attribmask = CWOverrideRedirect; for (i = 0; i < awt_numScreens; i++) { if (RootWindow(dpy, i) == rootWindow) { screen = i; break; } } adata = getDefaultConfig(screen); bg = adata->AwtColorMatch(255, 255, 255, adata); fg = adata->AwtColorMatch(0, 0, 0, adata); light = adata->AwtColorMatch(195, 195, 195, adata); dim = adata->AwtColorMatch(128, 128, 128, adata); grandParent = getGrandParent(parent); target = (grandParent == 0) ? parent : grandParent; XGetWindowAttributes(dpy, target, &xwa); bw = 2; /*xwa.border_width does not have the correct value*/ /*compare the size difference between parent container and shell widget, the diff should be the border frame and title bar height (?)*/ XQueryTree( dpy, target, &rootWindow, &containerWindow, &ignoreWindowPtr, &ignoreUnit); XGetWindowAttributes(dpy, containerWindow, &xxwa); XTranslateCoordinates(dpy, target, xwa.root, 0, 0, &x, &y, &child); if (containerWindow == rootWindow) { off_x = 0; off_y = STATUS_MARGIN; } else { XGetWindowAttributes(dpy, containerWindow, &xxwa); off_x = (xxwa.width - xwa.width) / 2; /* off_y = xxwa.height - xwa.height - off_x;*/ /*it's magic:-) */ { int cx, cy; XTranslateCoordinates(dpy, containerWindow, xxwa.root, 0, 0, &cx, &cy, &child); off_y = (xxwa.height + cy) - (xwa.height + y); } } /*get the size of root window*/ XGetWindowAttributes(dpy, rootWindow, &xxwa); XTranslateCoordinates(dpy, target, xwa.root, xwa.x, xwa.y, &x, &y, &child); xx = x - off_x; yy = y + xwa.height - off_y; if (xx < 0 ){ xx = 0; } if (xx + width > xxwa.width) { xx = xxwa.width - width; } if (yy + height > xxwa.height) { yy = xxwa.height - height; } if ((DefaultVisual(dpy,screen))->class != adata->awt_visInfo.visual->class && adata->awt_visInfo.visual->class == TrueColor) { attrib.colormap = XCreateColormap(dpy, xwa.root, adata->awt_visInfo.visual, AllocNone ); attrib.border_pixel = BlackPixel(dpy, screen) ; attribmask |= CWColormap | CWBorderPixel; } status = XCreateWindow(dpy, xwa.root, xx, yy, width, height, 0, xwa.depth, InputOutput, adata->awt_visInfo.visual, attribmask, &attrib); XSelectInput(dpy, status, ExposureMask | StructureNotifyMask | EnterWindowMask | LeaveWindowMask | VisibilityChangeMask); if (grandParent != 0){ long mask; XGetWindowAttributes(dpy, grandParent, &xwa); mask = xwa.your_event_mask | StructureNotifyMask | VisibilityChangeMask | PropertyChangeMask; XSelectInput(dpy, grandParent,mask); } statusWindow = (StatusWindow*) calloc(1, sizeof(StatusWindow)); if (statusWindow == NULL){ THROW_OUT_OF_MEMORY_ERROR(); return NULL; } statusWindow->w = status; statusWindow->fontset = fontset; statusWindow->parent = parent; statusWindow->grandParent = grandParent; statusWindow->on = False; statusWindow->x = x; statusWindow->y = y; statusWindow->width = xwa.width; statusWindow->height = xwa.height; statusWindow->off_x = off_x; statusWindow->off_y = off_y; statusWindow->bWidth = bw; statusWindow->statusH = height; statusWindow->statusW = width; statusWindow->peTextW = 0; statusWindow->rootH = xxwa.height; statusWindow->rootW = xxwa.width; statusWindow->lightGC = XCreateGC(dpy, status, valuemask, &values); XSetForeground(dpy, statusWindow->lightGC, light); statusWindow->dimGC = XCreateGC(dpy, status, valuemask, &values); XSetForeground(dpy, statusWindow->dimGC, dim); statusWindow->fgGC = create_gc(status, FALSE); XSetForeground(dpy, statusWindow->fgGC, fg); statusWindow->bgGC = create_gc(status, TRUE); XSetForeground(dpy, statusWindow->bgGC, bg); statusWindow->status_ready = False; wcscpy(statusWindow->status, L""); return statusWindow; } /* This method is to turn off or turn on the status window. */ static void onoffStatusWindow(X11InputMethodData* pX11IMData, Window parent, Bool ON){ XWindowAttributes xwa; Window child; int x, y; StatusWindow *statusWindow = NULL; if (NULL == pX11IMData || NULL == (statusWindow = pX11IMData->statusWindow)){ return; } if (ON == False) { XUnmapWindow(dpy, statusWindow->w); return; } if (NULL == currentX11InputMethodInstance){ return; } { JNIEnv *env = GetJNIEnv(); parent = JNU_CallMethodByName(env, NULL, pX11IMData->x11inputmethod, "getCurrentParentWindow", "()J").j; if ((*env)->ExceptionOccurred(env)) { (*env)->ExceptionDescribe(env); (*env)->ExceptionClear(env); } } if (statusWindow->parent != parent) { statusWindow->parent = parent; } if (st_wcslen(statusWindow->status) > 0 || (statusWindow->peText != NULL && st_wcslen(statusWindow->peText) > 0 )) { moveStatusWindow(statusWindow); XMapRaised(dpy, statusWindow->w); } } void paintStatusWindow(StatusWindow *statusWindow){ Window win = statusWindow->w; GC lightgc = statusWindow->lightGC; GC dimgc = statusWindow->dimGC; GC bggc = statusWindow->bgGC; GC fggc = statusWindow->fgGC; int width = statusWindow->statusW; int height = statusWindow->statusH; int bwidth = statusWindow->bWidth; int len; XRectangle logical, ink; if (NULL == statusWindow) return; if ((len = st_wcslen(statusWindow->status)) == 0) { return; } XwcTextExtents(statusWindow->fontset, statusWindow->status, len, &ink, &logical); width = logical.width; height = logical.height; XFillRectangle(dpy, win, bggc, 0, 0, width+2, height+2); XDrawLine(dpy, win, fggc, 0, 0, width+2, 0); XDrawLine(dpy, win, fggc, 0, height+2, width+2, height+2); XDrawLine(dpy, win, fggc, 0, 0, 0, height+2); XDrawLine(dpy, win, fggc, width+2, 0, width+2, height+2); if (statusWindow->fontset) { XwcDrawString(dpy, win, statusWindow->fontset, fggc, -logical.x + 1, -logical.y + 1, statusWindow->status, st_wcslen(statusWindow->status)); } else { /*too bad we failed to create a fontset for this locale*/ XDrawString(dpy, win, fggc, bwidth + 2, height - bwidth - 4, "[InputMethod ON]", strlen("[InputMethod ON]")); } } Bool statusWindowEventHandler(XEvent event) { JNIEnv *env = GetJNIEnv(); X11InputMethodData *pX11IMData = NULL; StatusWindow *statusWindow; if (NULL == currentX11InputMethodInstance || NULL == (pX11IMData = getX11InputMethodData(env, currentX11InputMethodInstance)) || NULL == (statusWindow = pX11IMData->statusWindow)) { return False; } if (statusWindow->w == event.xany.window) { switch (event.type) { case Expose: paintStatusWindow(statusWindow); if (statusWindow->peText) draw_preedit(statusWindow); arrange_window_stack(statusWindow); break; case ConfigureNotify: case VisibilityNotify: arrange_window_stack(statusWindow); break; /* case UnmapNotify: case VisibilityNotify: break; */ default: break; } return True; } else if ((statusWindow->parent == event.xany.window) || (statusWindow->grandParent && statusWindow->grandParent == event.xany.window)) { switch (event.type) { case MapNotify: if (statusWindow->on) { onoffStatusWindow(pX11IMData, statusWindow->parent, True); } break; case UnmapNotify: onoffStatusWindow(pX11IMData, 0, False); break; case VisibilityNotify: if (statusWindow->on) { arrange_window_stack(statusWindow); } break; case ConfigureNotify: if (statusWindow->grandParent && statusWindow->on) { moveStatusWindow(statusWindow); } case PropertyNotify: if (statusWindow->on) { arrange_window_stack(statusWindow); } break; default: break; } } return False; } static void adjustStatusWindow(Window shell) { JNIEnv *env = GetJNIEnv(); X11InputMethodData *pX11IMData = NULL; StatusWindow *statusWindow; if (NULL == currentX11InputMethodInstance || NULL == (pX11IMData = getX11InputMethodData(env,currentX11InputMethodInstance)) || NULL == (statusWindow = pX11IMData->statusWindow) || !statusWindow->on) { return; } moveStatusWindow(statusWindow); } /* * Creates two XICs, one for active clients and the other for passive * clients. All information on those XICs are stored in the * X11InputMethodData given by the pX11IMData parameter. * * For active clients: Try to use preedit callback to support * on-the-spot. If tc is not null, the XIC to be created will * share the Status Area with Motif widgets (TextComponents). If the * preferable styles can't be used, fallback to root-window styles. If * root-window styles failed, fallback to None styles. * * For passive clients: Try to use root-window styles. If failed, * fallback to None styles. */ static Bool createXIC(JNIEnv * env, X11InputMethodData *pX11IMData, Window w) { XVaNestedList preedit = NULL; XVaNestedList status = NULL; XIMStyle on_the_spot_styles = XIMPreeditCallbacks, in_place_styles = 0, active_styles = 0, passive_styles = 0, no_styles = 0; XIMCallback *callbacks; unsigned short i; XIMStyles *im_styles; char *ret = NULL; Bool passiveStatusWindow = False; pX11IMData->statusWindow = NULL; if (X11im == NULL) { return False; } if (!w) { return False; } if (getenv("IBMJAVA_PASSIVE") == NULL) { passiveStatusWindow = False; } else { passiveStatusWindow = True; } if (isNativeIm()) { passiveStatusWindow = True; } ret = XGetIMValues(X11im, XNQueryInputStyle, &im_styles, NULL); if (ret != NULL) { jio_fprintf(stderr,"XGetIMValues: %s\n",ret); return FALSE ; } on_the_spot_styles |= XIMStatusNothing; /*kinput does not support XIMPreeditCallbacks and XIMStatusArea at the same time, so use StatusCallback to draw the status ourself */ for (i = 0; i < im_styles->count_styles; i++) { if (im_styles->supported_styles[i] == (XIMPreeditCallbacks | XIMStatusCallbacks)) { on_the_spot_styles = (XIMPreeditCallbacks | XIMStatusCallbacks); break; } } for (i = 0; i < im_styles->count_styles; i++) { if (im_styles->supported_styles[i] == on_the_spot_styles) active_styles = im_styles->supported_styles[i]; if (im_styles->supported_styles[i] == ROOT_WINDOW_STYLES) passive_styles = im_styles->supported_styles[i]; if (im_styles->supported_styles[i] == IN_PLACE_STYLES) { in_place_styles = im_styles->supported_styles[i]; } if (im_styles->supported_styles[i] == NO_STYLES) { no_styles = im_styles->supported_styles[i]; } } XFree(im_styles); if (active_styles != on_the_spot_styles) { if (passive_styles == ROOT_WINDOW_STYLES) active_styles = passive_styles; else { if (in_place_styles == IN_PLACE_STYLES){ active_styles = passive_styles = IN_PLACE_STYLES; } else { if (no_styles == NO_STYLES) active_styles = passive_styles = NO_STYLES; else active_styles = passive_styles = 0; } } } else { if (!passiveStatusWindow) { if (passive_styles != ROOT_WINDOW_STYLES) { if (no_styles == NO_STYLES) active_styles = passive_styles = NO_STYLES; else active_styles = passive_styles = 0; } } else passive_styles = active_styles; } if (active_styles == on_the_spot_styles) { callbacks = (XIMCallback *)malloc(sizeof(XIMCallback) * NCALLBACKS); if (callbacks == (XIMCallback *)NULL) return False; pX11IMData->callbacks = callbacks; for (i = 0; i < NCALLBACKS; i++, callbacks++) { callbacks->client_data = (XPointer) pX11IMData->x11inputmethod; callbacks->callback = callback_funcs[i]; } callbacks = pX11IMData->callbacks; preedit = (XVaNestedList)XVaCreateNestedList(0, XNPreeditStartCallback, &callbacks[PreeditStartIndex], XNPreeditDoneCallback, &callbacks[PreeditDoneIndex], XNPreeditDrawCallback, &callbacks[PreeditDrawIndex], XNPreeditCaretCallback, &callbacks[PreeditCaretIndex], NULL); if (preedit == (XVaNestedList)NULL) goto err; /*always try XIMStatusCallbacks for active client...*/ { if (on_the_spot_styles & XIMStatusCallbacks) { status = (XVaNestedList)XVaCreateNestedList(0, XNStatusStartCallback, &callbacks[StatusStartIndex], XNStatusDoneCallback, &callbacks[StatusDoneIndex], XNStatusDrawCallback, &callbacks[StatusDrawIndex], NULL); if (status == NULL) goto err; } pX11IMData->statusWindow = createStatusWindow(w); pX11IMData->ic_active = XCreateIC(X11im, XNClientWindow, w, XNFocusWindow, w, XNInputStyle, active_styles, XNPreeditAttributes, preedit, XNStatusAttributes, status, NULL); if (NULL != pX11IMData->statusWindow) { pX11IMData->statusWindow->status_ready = True; } XFree((void *)status); XFree((void *)preedit); } if (passiveStatusWindow) { pX11IMData->ic_passive = pX11IMData->ic_active; } else { pX11IMData->ic_passive = XCreateIC(X11im, XNClientWindow, w, XNFocusWindow, w, XNInputStyle, passive_styles, NULL); } } else { pX11IMData->ic_active = XCreateIC(X11im, XNClientWindow, w, XNFocusWindow, w, XNInputStyle, active_styles, NULL); pX11IMData->ic_passive = pX11IMData->ic_active; } // The code set the IC mode that the preedit state is not initialied // at XmbResetIC. This attribute can be set at XCreateIC. I separately // set the attribute to avoid the failure of XCreateIC at some platform // which does not support the attribute. if (pX11IMData->ic_active != 0) XSetICValues(pX11IMData->ic_active, XNResetState, XIMPreserveState, NULL); if (pX11IMData->ic_passive != 0 && pX11IMData->ic_active != pX11IMData->ic_passive) XSetICValues(pX11IMData->ic_passive, XNResetState, XIMInitialState, NULL); pX11IMData->passiveStatusWindow = passiveStatusWindow; if (pX11IMData->ic_active == (XIC)0 || pX11IMData->ic_passive == (XIC)0) { return False; } /* Unset focus to avoid unexpected IM on */ setXICFocus(pX11IMData->ic_active, False); if (pX11IMData->ic_active != pX11IMData->ic_passive) setXICFocus(pX11IMData->ic_passive, False); return True; err: if (preedit) XFree((void *)preedit); THROW_OUT_OF_MEMORY_ERROR(); return False; } static int PreeditStartCallback(XIC ic, XPointer client_data, XPointer call_data) { JNIEnv *env = GetJNIEnv(); X11InputMethodData *pX11IMData; pX11IMData = getX11InputMethodData(env, (jobject)client_data); if (pX11IMData == NULL || pX11IMData->statusWindow == NULL) { return 0; } resetPassivePreeditText(pX11IMData->statusWindow); return -1; /* unlimited length for preedit text */ } static void PreeditDoneCallback(XIC ic, XPointer client_data, XPointer call_data) { JNIEnv *env = GetJNIEnv(); X11InputMethodData *pX11IMData; pX11IMData = getX11InputMethodData(env, (jobject)client_data); if (pX11IMData == NULL) { return; } if (!pX11IMData->isActiveClient) { resetPassivePreeditText(pX11IMData->statusWindow); shrink_status(pX11IMData->statusWindow); } else{ JNU_CallMethodByName(env, NULL, pX11IMData->x11inputmethod, "clearComposedText", "(J)V", awt_util_nowMillisUTC()); if ((*env)->ExceptionOccurred(env)) { (*env)->ExceptionDescribe(env); (*env)->ExceptionClear(env); } } } /* * Translate the preedit draw callback items to Java values and invoke * X11InputMethod.dispatchComposedText(). * * client_data: X11InputMethod object */ static void PreeditDrawCallback(XIC ic, XPointer client_data, XIMPreeditDrawCallbackStruct *pre_draw) { JNIEnv *env = GetJNIEnv(); X11InputMethodData *pX11IMData = NULL; jmethodID x11imMethodID; XIMText *text; jstring javastr = NULL; jintArray style = NULL; /* printf("Native: PreeditDrawCallback() \n"); */ if (pre_draw == NULL) { return; } AWT_LOCK(); if ((pX11IMData = getX11InputMethodData(env, (jobject)client_data)) == NULL) { goto finally; } if (!pX11IMData->isActiveClient){ if (ic == pX11IMData->ic_passive) { preedit_draw_passive(pX11IMData, pre_draw); } goto finally; } if ((text = pre_draw->text) != NULL) { if (is_text_available(text)) { if (text->string.multi_byte != NULL) { if (pre_draw->text->encoding_is_wchar == False) { javastr = JNU_NewStringPlatform(env, (const char *)text->string.multi_byte); if (javastr == NULL) { goto finally; } } else { char *mbstr = wcstombsdmp(text->string.wide_char, text->length); if (mbstr == NULL) { goto finally; } javastr = JNU_NewStringPlatform(env, (const char *)mbstr); free(mbstr); if (javastr == NULL) { goto finally; } } } } if (text->feedback != NULL) { int cnt; jint *tmpstyle; style = (*env)->NewIntArray(env, text->length); if (JNU_IsNull(env, style)) { (*env)->ExceptionClear(env); THROW_OUT_OF_MEMORY_ERROR(); goto finally; } if (sizeof(XIMFeedback) == sizeof(jint)) { /* * Optimization to avoid copying the array */ (*env)->SetIntArrayRegion(env, style, 0, text->length, (jint *)text->feedback); } else { tmpstyle = (jint *)malloc(sizeof(jint)*(text->length)); if (tmpstyle == (jint *) NULL) { THROW_OUT_OF_MEMORY_ERROR(); goto finally; } for (cnt = 0; cnt < (int)text->length; cnt++) tmpstyle[cnt] = text->feedback[cnt]; (*env)->SetIntArrayRegion(env, style, 0, text->length, (jint *)tmpstyle); free(tmpstyle); } } } JNU_CallMethodByName(env, NULL, pX11IMData->x11inputmethod, "dispatchComposedText", "(Ljava/lang/String;[IIIIJ)V", javastr, style, (jint)pre_draw->chg_first, (jint)pre_draw->chg_length, (jint)pre_draw->caret, awt_util_nowMillisUTC()); if ((*env)->ExceptionOccurred(env)) { (*env)->ExceptionDescribe(env); (*env)->ExceptionClear(env); } finally: AWT_UNLOCK(); return; } static void PreeditCaretCallback(XIC ic, XPointer client_data, XIMPreeditCaretCallbackStruct *pre_caret) { XIMPreeditDrawCallbackStruct pre_draw; if (pre_caret != NULL && pre_caret->direction == XIMAbsolutePosition) { pre_draw.caret = pre_caret->position; pre_draw.chg_first = 0; pre_draw.chg_length = 0; pre_draw.text = NULL; PreeditDrawCallback(ic, client_data, &pre_draw); } } static void StatusStartCallback(XIC ic, XPointer client_data, XPointer call_data) { /*ARGSUSED*/ /*printf("StatusStartCallback:\n"); */ } static void StatusDoneCallback(XIC ic, XPointer client_data, XPointer call_data) { /*ARGSUSED*/ /*printf("StatusDoneCallback:\n"); */ } static void StatusDrawCallback (XIC ic, XPointer client_data, XIMStatusDrawCallbackStruct *status_draw) { /*ARGSUSED*/ /*printf("StatusDrawCallback:\n"); */ JNIEnv *env = GetJNIEnv(); X11InputMethodData *pX11IMData = NULL; StatusWindow *statusWindow; int value_make = CWX|CWWidth|CWHeight; XRectangle logical, ink; XWindowChanges xwc; int len; AWT_LOCK(); if (NULL == (pX11IMData = getX11InputMethodData(env, (jobject)client_data)) || NULL == (statusWindow = pX11IMData->statusWindow)){ goto finally; } if (status_draw->type == XIMTextType) { XIMText *text = (status_draw->data).text; if (text != NULL) { if (text->string.multi_byte != NULL) { if(!strcmp(text->string.multi_byte," ")){ wcscpy(statusWindow->status, L""); onoffStatusWindow(pX11IMData, 0, False); goto finally; } mbstowcs(statusWindow->status, (const char *)text->string.multi_byte, (size_t)MAX_STATUS_LEN); } else { if (0 == st_wcslen(text->string.wide_char)){ wcscpy(statusWindow->status, L""); onoffStatusWindow(pX11IMData, 0, False); goto finally; } wcsncpy(statusWindow->status, text->string.wide_char, MAX_STATUS_LEN); } XwcTextExtents(statusWindow->fontset, statusWindow->status, st_wcslen(statusWindow->status), &ink, &logical); statusWindow->statusW = logical.width + BORDER_MARGIN; statusWindow->statusH = logical.height + BORDER_MARGIN; xwc.x = statusWindow->x - statusWindow->off_x; if (xwc.x < 0 ) xwc.x = 0; xwc.width = statusWindow->statusW; xwc.height = statusWindow->statusH; if (xwc.x + xwc.width > statusWindow->rootW){ xwc.x = statusWindow->rootW - xwc.width; } XConfigureWindow(dpy, statusWindow->w, value_make, &xwc); if (statusWindow->status_ready && statusWindow->on == True){ onoffStatusWindow(pX11IMData, statusWindow->parent, True); } paintStatusWindow(statusWindow); if (statusWindow->peText) draw_preedit(statusWindow); } else { wcscpy(statusWindow->status, L""); /*just turnoff the status window paintStatusWindow(statusWindow); */ onoffStatusWindow(pX11IMData, 0, False); } } finally: AWT_UNLOCK(); } /* return the string length without trailing spaces */ /* work around code for Japanese AIXIM is implemented. */ static int st_wcslen(wchar_t *string) { int len = (int32_t)wcslen(string); if (len == 0) return 0; for (len--;len >= 0; len--) { if (!iswspace((wint_t) string[len])) break; } return len+1; } /* * Checks whether given XIMText contains a string data. */ static Bool is_text_available(XIMText * text) { if (text == NULL || text->length==0) return False; if (text->encoding_is_wchar) { if(text->string.wide_char[0] == L'\0') return False; } else { if (text->string.multi_byte[0] == '\0') return False; } return True; } /* * check if preedit status is active */ static Bool isPreeditStateActive(XIC ic) { XIMPreeditState state = XIMPreeditUnKnown; XVaNestedList pr_atrb; char* nosupportAttr; if (ic == NULL) return False; pr_atrb = XVaCreateNestedList(0,XNPreeditState,&state,NULL); nosupportAttr=XGetICValues(ic,XNPreeditAttributes,pr_atrb,NULL); XFree(pr_atrb); if (nosupportAttr==NULL && state & XIMPreeditDisable) return False; else return True; } static void * buf_insert(void * src, void * insert, int size, int src_len, int ins_len, int offset) { char *temp; temp = realloc(src, size*(src_len+ins_len+1)); if (temp == NULL) { THROW_OUT_OF_MEMORY_ERROR(); return src; } if (offset != src_len) { memmove(&temp[size*(offset+ins_len)], &((char *)temp)[size*offset], size*(src_len-offset)); } memcpy(&temp[size*offset], insert, size*ins_len); return (void *)temp; } static void * handle_buffer(void * source, void * insert, int size,int src_len, int ins_len, int del_len, int offset) { void * temp = source; if (del_len > 0) { if (del_len == ins_len) { memcpy(&((char *)source)[size*offset], insert, size*ins_len); return source; } else if (src_len > offset+del_len) { memmove(&((char *)source)[size*offset], &((char *)source)[size*(offset+del_len)], size*(src_len-offset-del_len)); } } if (ins_len > 0) { temp = buf_insert(source, insert, size, src_len, ins_len, offset); } return temp; } /* * Display the given preedit text to the root window which is ownd by * myself. All of the character is converted to wide char. * this function is used for the passive client. */ static void preedit_draw_passive(X11InputMethodData *pX11IMData, XIMPreeditDrawCallbackStruct *pre_draw) { XIMText *text; wchar_t *tempbuf = NULL; StatusWindow *statusWindow; wchar_t *cur_text; unsigned long *cur_attr; int cur_len = 0; int chg_len = pre_draw->chg_length; int chg_1st = pre_draw->chg_first; if (NULL == (statusWindow = pX11IMData->statusWindow)) return; cur_text = statusWindow->peText; cur_attr = statusWindow->peAttr; if (cur_text == NULL && pre_draw->text == NULL) return; if (cur_text != NULL) cur_len = (int32_t)wcslen(cur_text); text = pre_draw->text; if (text == NULL) { /* delete only */ if (cur_len > chg_1st+chg_len) { memmove(&cur_text[chg_1st], &cur_text[chg_1st+chg_len], sizeof(wchar_t)*(cur_len-chg_1st-chg_len)); memmove(&cur_attr[chg_1st], &cur_attr[chg_1st+chg_len], sizeof(long)*(cur_len-chg_1st-chg_len)); } if ((pre_draw->chg_length <= cur_len ) && (pre_draw->chg_length >0)) cur_text[cur_len-pre_draw->chg_length] =L'\0'; } else { /* insert or replace */ int ins_len = 0; void * ins_text = NULL; /* if invalid offset is specified, do nothing. */ /* this fix is for aixim for eucTW */ if (cur_len < chg_1st) return; if(is_text_available(text)) { /* insert or replace the text */ if (text->encoding_is_wchar == False) { /* convert the text to wide chars. allocate enough size buffer */ tempbuf = (wchar_t *)malloc(sizeof(wchar_t)*(text->length+1)); if (tempbuf == NULL) { THROW_OUT_OF_MEMORY_ERROR(); return; } ins_len = (int32_t)mbstowcs(tempbuf, text->string.multi_byte, text->length); if (ins_len == -1) { free(tempbuf); return; } ins_text = (void *)tempbuf; } else { ins_len = text->length; ins_text = text->string.wide_char; } /* finish prepare the data to be inserted */ statusWindow->peText = handle_buffer(cur_text, ins_text, sizeof(wchar_t), cur_len, ins_len, chg_len, chg_1st); statusWindow->peAttr = handle_buffer(cur_attr, text->feedback, sizeof(long), cur_len, ins_len, chg_len, chg_1st); statusWindow->peText[cur_len-chg_len+ins_len] =L'\0'; if (tempbuf != NULL) free(tempbuf); } /* endof insert or replace text */ else { /* change attribute only */ memcpy(&cur_attr[chg_1st], text->feedback, sizeof(long)*text->length); } } statusWindow->peCaret= pre_draw->caret; draw_preedit(statusWindow); if (statusWindow->on && wcslen(statusWindow->peText) > 0) onoffStatusWindow(pX11IMData, statusWindow->parent, True); else if (wcslen(statusWindow->status) == 0) onoffStatusWindow(pX11IMData, 0, False); } /* * reset predit test of passive mode */ static void resetPassivePreeditText(StatusWindow *statusWindow) { if (NULL == statusWindow) return; if(statusWindow->peText != NULL) { free(statusWindow->peText); statusWindow->peText = NULL; } if(statusWindow->peAttr != NULL) { free(statusWindow->peAttr); statusWindow->peAttr = NULL; } statusWindow->peCaret= 0; } static void draw_caret(StatusWindow *statusWindow, GC gc, int pos) { if (NULL == statusWindow) return; XSetFunction(dpy, gc, GXinvert); XDrawLine(dpy, statusWindow->w, gc, pos, STATUS_BORDER/2, pos, STATUS_BORDER/2+statusWindow->fOff); XSetFunction(dpy, gc, GXcopy); } static int get_next_attr(int len, unsigned long *attr) { int count; for (count = 1; count < len; count++) { if ((attr[count-1] & PREEDIT_ATTR_MASK) != (attr[count] & PREEDIT_ATTR_MASK)) break; } return count; } static void draw_preedit(StatusWindow *statusWindow) { unsigned long *attr; int x_pos,x_caret; unsigned int len; int len_disp, pos; wchar_t *str; GC gc; XRectangle ink, rect, rect_c; Bool caret_done = False; if (NULL == statusWindow) return; align_status(statusWindow); XFillRectangle(dpy, statusWindow->w, statusWindow->bgGC, statusWindow->statusW,0, statusWindow->statusW + statusWindow->peTextW + BORDER_MARGIN, statusWindow->fBot+2); XDrawLine(dpy, statusWindow->w, statusWindow->fgGC, statusWindow->statusW, 0, statusWindow->statusW + statusWindow->peTextW + BORDER_MARGIN, 0); XDrawLine(dpy, statusWindow->w, statusWindow->fgGC, statusWindow->statusW, statusWindow->fBot+2, statusWindow->statusW + statusWindow->peTextW + BORDER_MARGIN, statusWindow->fBot+2); XDrawLine(dpy, statusWindow->w, statusWindow->fgGC, statusWindow->statusW + statusWindow->peTextW + BORDER_MARGIN, 0, statusWindow->statusW + statusWindow->peTextW + BORDER_MARGIN, statusWindow->fBot+2); if (0 == statusWindow->statusW) XDrawLine(dpy, statusWindow->w, statusWindow->fgGC, 0, 0, 0, statusWindow->fBot+2); str = statusWindow->peText; if (str != NULL && (len = (int32_t)wcslen(str)) != 0) { pos = 0; attr = statusWindow->peAttr; x_pos = x_caret = statusWindow->statusW + STATUS_BORDER; while((int)len-1 >= pos) { len_disp = get_next_attr(len - pos, &attr[pos]); if (attr[pos] & XIMReverse) { gc = statusWindow->bgGC; } else { gc = statusWindow->fgGC; } XwcTextExtents(statusWindow->fontset, &str[pos], len_disp, &ink, &rect); XwcDrawImageString(dpy, statusWindow->w, statusWindow->fontset, gc, x_pos, statusWindow->fOff+1, &str[pos], len_disp); if (attr[pos] & XIMUnderline) { XDrawLine(dpy, statusWindow->w, gc, x_pos, statusWindow->fBot, x_pos+rect.width, statusWindow->fBot); } if (!caret_done) { if( statusWindow->peCaret >= pos && statusWindow->peCaret <= pos+len_disp) { if (statusWindow->peCaret == 0) x_caret = x_pos; else if (statusWindow->peCaret == pos+len_disp) x_caret = x_pos+rect.width; else { XwcTextExtents(statusWindow->fontset, &str[pos], statusWindow->peCaret-pos, &ink, &rect_c); x_caret = x_pos+ rect_c.width; } x_caret-=CARET_OFFSET; caret_done = True; } } pos += len_disp; x_pos += rect.width; } if (caret_done) draw_caret(statusWindow, statusWindow->fgGC, x_caret); } } /* calc required status window size and resize the window */ static void align_status(StatusWindow *statusWindow) { int len_st, len_pe = 0; XRectangle rect_st, rect_pe, ink; Dimension cur_w; int value_make = CWX|CWWidth|CWHeight; XWindowChanges xwc; if (NULL == statusWindow) return; if ((len_st = st_wcslen(statusWindow->status)) == 0 && (statusWindow->peText == NULL || st_wcslen(statusWindow->peText) == 0 )) return; rect_pe.x = rect_pe.y = rect_pe.width = rect_pe.height = 0; XwcTextExtents(statusWindow->fontset, statusWindow->status, len_st, &ink, &rect_st); if (statusWindow->peText != NULL && (len_pe = (int32_t)wcslen(statusWindow->peText)) > 0) { XwcTextExtents(statusWindow->fontset, statusWindow->peText, len_pe, &ink, &rect_pe); } statusWindow->fOff = max(-rect_st.y, -rect_pe.y); statusWindow->fBot = max(rect_st.height, rect_pe.height); statusWindow->statusW =rect_st.width; if (rect_st.width > 0) statusWindow->statusW += BORDER_MARGIN; statusWindow->peTextW = rect_pe.width; xwc.x = statusWindow->x - statusWindow->off_x; if (xwc.x < 0 ) xwc.x = 0; if (len_pe > 0) { xwc.width = statusWindow->statusW + statusWindow->peTextW + BORDER_MARGIN + 1; xwc.height = statusWindow->fBot + BORDER_MARGIN; } else { xwc.width = statusWindow->statusW; xwc.height = statusWindow->fBot + BORDER_MARGIN; } if (xwc.x + xwc.width > statusWindow->rootW){ xwc.x = statusWindow->rootW - xwc.width; } XConfigureWindow(dpy, statusWindow->w, value_make, &xwc); } static void shrink_status(StatusWindow *statusWindow) { int value_make = CWX|CWWidth|CWHeight; XWindowChanges xwc; if (NULL == statusWindow) return; xwc.width = statusWindow->statusW; xwc.height = statusWindow->statusH; statusWindow->peTextW = 0; xwc.x = statusWindow->x - statusWindow->off_x; if (xwc.x < 0 ) xwc.x = 0; if (xwc.x + xwc.width > statusWindow->rootW){ xwc.x = statusWindow->rootW - xwc.width; } XConfigureWindow(dpy, statusWindow->w, value_make, &xwc); } static GC create_gc(Window win, Bool isReverse) { XGCValues xgcv; unsigned long mask; AwtScreenDataPtr defaultScreen; defaultScreen = getScreenData(DefaultScreen(dpy)); mask = (GCForeground | GCBackground ); if (isReverse) { xgcv.foreground = defaultScreen->whitepixel; xgcv.background = defaultScreen->blackpixel; } else { xgcv.foreground = defaultScreen->blackpixel; xgcv.background = defaultScreen->whitepixel; } return XCreateGC(dpy, win, mask, &xgcv); } static Bool isNativeIm() { #define XIMMODIFIER "@im=" #define XIM_SERVER_CATEGORY "@server=" char *immodifiers; char *imserver, *imserverPtr; Atom imserverAtom; if (!(immodifiers = getenv("XMODIFIERS"))) return True; if (!(imserver = calloc(1,strlen(immodifiers)+strlen(XIM_SERVER_CATEGORY)+1))) return True; if (!(immodifiers = strstr(immodifiers,XIMMODIFIER))) return True; immodifiers += strlen(XIMMODIFIER); strcpy(imserver,XIM_SERVER_CATEGORY); imserverPtr = imserver + strlen(imserver); while(*immodifiers != '@' && *immodifiers != '\0') { *imserverPtr = *immodifiers; imserverPtr++; immodifiers++; } imserverAtom = XInternAtom(awt_display, imserver, True); free(imserver); if (imserverAtom > 0) return False; else return True; } static Window getGrandParent(Window parent) { Window containerWindow,rootWindow,tmp; Window *ignoreWindowPtr; unsigned int ignoreUnit; Window grandParent=0; XWindowAttributes xwa; Atom WM_STATE; Atom type = None; int32_t format; unsigned long nitems, after; unsigned char * data; if (parent == 0) return grandParent; WM_STATE = XInternAtom(dpy, "WM_STATE", True); if (WM_STATE == None) return grandParent; tmp=parent; while(XQueryTree(dpy, tmp, &rootWindow, &containerWindow, &ignoreWindowPtr, &ignoreUnit)){ XFree(ignoreWindowPtr); if (containerWindow == rootWindow) break; if (XGetWindowProperty(dpy, containerWindow, WM_STATE, 0, 0, False, AnyPropertyType, &type, &format, &nitems, &after, &data) == Success) { XFree(data); if (type) { XGetWindowAttributes(dpy, containerWindow, &xwa); if (FALSE == xwa.override_redirect){ grandParent=containerWindow; } } } tmp=containerWindow; } return grandParent; } static void moveStatusWindow(StatusWindow *statusWindow) { XWindowAttributes xwa; Window child; int x, y, width; Window target; if (NULL == statusWindow) return; if (statusWindow->grandParent) { target = statusWindow->grandParent; } else { target = statusWindow->parent; } XGetWindowAttributes(dpy, target, &xwa); XTranslateCoordinates(dpy, target, xwa.root, 0, 0, &x, &y, &child); if (statusWindow->x != x || statusWindow->y != y || statusWindow->width != xwa.width || statusWindow->height != xwa.height){ statusWindow->x = x; statusWindow->y = y; statusWindow->height = xwa.height; statusWindow->width = xwa.width; x = statusWindow->x - statusWindow->off_x; y = statusWindow->y + statusWindow->height + statusWindow->off_y; if (x < 0 ){ x = 0; } if (statusWindow->peTextW > 0) { width = statusWindow->statusW + statusWindow->peTextW + BORDER_MARGIN + 1; if (x + width > statusWindow->rootW){ x = statusWindow->rootW - width; } } else { if (x + statusWindow->statusW > statusWindow->rootW){ x = statusWindow->rootW - statusWindow->statusW; } } if (y + statusWindow->statusH > statusWindow->rootH){ y = statusWindow->rootH - statusWindow->statusH; } XMoveWindow(dpy, statusWindow->w, x, y); } } static void arrange_window_stack(StatusWindow* statusWindow) { XWindowChanges xwc; int value_make = CWSibling|CWStackMode; Window root, parent, *children; unsigned int nchildren; if (NULL == statusWindow) return; if (XQueryTree(dpy, statusWindow->parent, &root, &parent, &children, &nchildren)){ XFree(children); xwc.sibling = parent; while(XQueryTree(dpy, xwc.sibling, &root, &parent, &children, &nchildren)) { XFree(children); if (root != parent) { xwc.sibling = parent; } else { break; } } xwc.stack_mode = Above; XConfigureWindow(dpy, statusWindow->w, value_make, &xwc); } } static int count_missing_fonts(char **charset_list, int charset_count) { int i,j; if (charset_count > 0) { j=charset_count; for(i=0; i < charset_count; i++) { if ((strstr(charset_list[i], "IBM-udc")) || (strstr(charset_list[i], "IBM-sbd")) || (strstr(charset_list[i], "IBM-ucdTW"))) j--; } return j; } else return 0; } static XFontSet create_fontset_name(char * font_name, Bool force) { XFontSet fontset = NULL; char **charset_list; int charset_count; char *def_string; int missing_fonts; fontset = XCreateFontSet(dpy, font_name, &charset_list, &charset_count, &def_string); if (charset_count > 0) { missing_fonts = count_missing_fonts(charset_list, charset_count); XFreeStringList(charset_list); if (fontset && (missing_fonts > 0)) { if (!force) { XFreeFontSet(dpy, fontset); fontset = NULL; } } } return fontset; } static XFontSet create_fontset() { XFontSet fontset = NULL; int i; static char * fontlist[] = { "-dt-interface user-medium-r-normal-S*-*-*-*-*-*-*-*-*", "-*-*-medium-r-normal-*-14-*-*-*-c-*-*-*", "-*-*-medium-r-normal-*-14-*-*-*-m-*-*-*", "-*-*-medium-r-normal--14-0-0-0-m-*-*-*", "-monotype-sansmonowt-medium-r-normal--14-*-*-*-m-*-*-*", "-*--14-*", "-dt-interface user-medium-r-normal-s*-*-*-*-*-*-*-*-*", "-*--16-*", "-*--17-*", "-*--18-*", "-*--19-*", "-*--20-*", "-*--24-*", NULL}; for (i=0; fontlist[i] != NULL && fontset==NULL; i++) fontset = create_fontset_name(fontlist[i], False); if (!fontset) fprintf(stdout, "Cannot load fonts for IMF.\n"); return fontset; } static Window get_current_focus(XIC ic) { Window w = 0; if (ic != NULL) XGetICValues(ic, XNFocusWindow, &w, NULL); return w; } JNIEXPORT jboolean JNICALL Java_sun_awt_X11_XInputMethod_openXIMNative(JNIEnv *env, jobject this, jlong display) { Bool registered; AWT_LOCK(); dpy = (Display *)jlong_to_ptr(display); if (X11im == NULL) { X11im = XOpenIM(dpy, NULL, NULL, NULL); } AWT_UNLOCK(); return JNI_TRUE; } JNIEXPORT jboolean JNICALL Java_sun_awt_X11_XInputMethod_createXICNative(JNIEnv *env, jobject this, jlong window) { X11InputMethodData *pX11IMData; jobject globalRef; XIC ic; AWT_LOCK(); if (!window) { JNU_ThrowNullPointerException(env, "NullPointerException"); AWT_UNLOCK(); return JNI_FALSE; } pX11IMData = (X11InputMethodData *) calloc(1, sizeof(X11InputMethodData)); if (pX11IMData == NULL) { THROW_OUT_OF_MEMORY_ERROR(); AWT_UNLOCK(); return JNI_FALSE; } globalRef = (*env)->NewGlobalRef(env, this); pX11IMData->x11inputmethod = globalRef; pX11IMData->statusWindow = NULL; setX11InputMethodData(env, this, pX11IMData); if (createXIC(env, pX11IMData, (Window)window) == False) { destroyX11InputMethodData((JNIEnv *) NULL, pX11IMData); pX11IMData = (X11InputMethodData *) NULL; setX11InputMethodData(env, this, pX11IMData); if ((*env)->ExceptionCheck(env)) { goto finally; } } finally: AWT_UNLOCK(); return (pX11IMData != NULL); } JNIEXPORT void JNICALL Java_sun_awt_X11_XInputMethod_setXICFocusNative(JNIEnv *env, jobject this, jlong w, jboolean req, jboolean active) { X11InputMethodData *pX11IMData; AWT_LOCK(); pX11IMData = getX11InputMethodData(env, this); if (pX11IMData == NULL) { AWT_UNLOCK(); return; } if (req) { if (!w) { AWT_UNLOCK(); return; } pX11IMData->isActiveClient = active; pX11IMData->current_ic = active ? pX11IMData->ic_active : pX11IMData->ic_passive; /* * On Solaris2.6, setXICWindowFocus() has to be invoked * before setting focus. */ get_current_focus(pX11IMData->current_ic); /* workaround for kinput2 and SCIM */ if (currentFocusWindow != w) { setXICWindowFocus(pX11IMData->current_ic, w); setXICFocus(pX11IMData->current_ic, req); currentX11InputMethodInstance = pX11IMData->x11inputmethod; currentFocusWindow = w; } else { setXICFocus(pX11IMData->current_ic, req); } if ((active || pX11IMData->passiveStatusWindow) && (pX11IMData->statusWindow && pX11IMData->statusWindow->on)) onoffStatusWindow(pX11IMData, w, True); } else { currentX11InputMethodInstance = NULL; currentFocusWindow = 0; onoffStatusWindow(pX11IMData, 0, False); if (pX11IMData->current_ic != NULL) setXICFocus(pX11IMData->current_ic, req); pX11IMData->current_ic = (XIC)0; } XFlush(dpy); AWT_UNLOCK(); } /* * Class: sun_awt_X11InputMethodBase * Method: initIDs * Signature: ()V * This function gets called from the static initializer for * X11InputMethod.java to initialize the fieldIDs for fields * that may be accessed from C */ JNIEXPORT void JNICALL Java_sun_awt_X11InputMethodBase_initIDs (JNIEnv *env, jclass cls) { x11InputMethodIDs.pData = (*env)->GetFieldID(env, cls, "pData", "J"); } /* * Class: sun_awt_X11InputMethodBase * Method: turnoffStatusWindow * Signature: ()V */ JNIEXPORT void JNICALL Java_sun_awt_X11InputMethodBase_turnoffStatusWindow (JNIEnv *env, jobject this) { X11InputMethodData *pX11IMData; StatusWindow *statusWindow; AWT_LOCK(); if (NULL == currentX11InputMethodInstance || NULL == (pX11IMData = getX11InputMethodData(env,currentX11InputMethodInstance)) || NULL == (statusWindow = pX11IMData->statusWindow) || !statusWindow->on ){ AWT_UNLOCK(); return; } onoffStatusWindow(pX11IMData, 0, False); statusWindow->on = False; AWT_UNLOCK(); } /* * Class: sun_awt_X11InputMethodBase * Method: disposeXIC * Signature: ()V */ JNIEXPORT void JNICALL Java_sun_awt_X11InputMethodBase_disposeXIC (JNIEnv *env, jobject this) { X11InputMethodData *pX11IMData = NULL; AWT_LOCK(); pX11IMData = getX11InputMethodData(env, this); if (pX11IMData == NULL) { AWT_UNLOCK(); return; } setX11InputMethodData(env, this, NULL); if (pX11IMData->x11inputmethod == currentX11InputMethodInstance) { currentX11InputMethodInstance = NULL; currentFocusWindow = 0; } destroyX11InputMethodData(env, pX11IMData); AWT_UNLOCK(); } /* * Class: sun_awt_X11InputMethodBase * Method: resetXIC * Signature: ()Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_sun_awt_X11InputMethodBase_resetXIC (JNIEnv *env, jobject this) { X11InputMethodData *pX11IMData; char *xText = NULL; jstring jText = (jstring)0; AWT_LOCK(); pX11IMData = getX11InputMethodData(env, this); if (pX11IMData == NULL) { AWT_UNLOCK(); return jText; } if (pX11IMData->current_ic) { if (!isPreeditStateActive(pX11IMData->current_ic)) { xText = NULL; } else { if (!(pX11IMData->forceReset)) setXICFocus(pX11IMData->current_ic, FALSE); xText = XmbResetIC(pX11IMData->current_ic); if (!(pX11IMData->forceReset)) setXICFocus(pX11IMData->current_ic, TRUE); } } else { /* * If there is no reference to the current XIC, try to reset both XICs. */ if (!isPreeditStateActive(pX11IMData->ic_active)) xText = NULL; else xText = XmbResetIC(pX11IMData->ic_active); /*it may also means that the real client component does not have focus -- has been deactivated... its xic should not have the focus, bug#4284651 showes reset XIC for htt may bring the focus back, so de-focus it again. */ setXICFocus(pX11IMData->ic_active, FALSE); if (pX11IMData->ic_active != pX11IMData->ic_passive) { char *tmpText; if (!isPreeditStateActive(pX11IMData->ic_passive)) tmpText = NULL; else tmpText = XmbResetIC(pX11IMData->ic_passive); setXICFocus(pX11IMData->ic_passive, FALSE); if (xText == (char *)NULL && tmpText) xText = tmpText; } } if (xText != NULL) { jText = JNU_NewStringPlatform(env, (const char *)xText); XFree((void *)xText); } /* workaround * Some IME do not call PreeditDoneCallback routine even * when XmbResetIC is called. I force to reset the preedit string. */ if (!pX11IMData->isActiveClient) { resetPassivePreeditText(pX11IMData->statusWindow); shrink_status(pX11IMData->statusWindow); } else { JNU_CallMethodByName(env, NULL, pX11IMData->x11inputmethod, "clearComposedText", "()V"); if ((*env)->ExceptionOccurred(env)) { (*env)->ExceptionDescribe(env); (*env)->ExceptionClear(env); } } AWT_UNLOCK(); return jText; } /* * Class: sun_awt_X11InputMethodBase * Method: setCompositionEnabledNative * Signature: (Z)Z * * This method tries to set the XNPreeditState attribute associated with the current * XIC to the passed in 'enable' state. * * Return JNI_TRUE if XNPreeditState attribute is successfully changed to the * 'enable' state; Otherwise, if XSetICValues fails to set this attribute, * java.lang.UnsupportedOperationException will be thrown. JNI_FALSE is returned if this * method fails due to other reasons. */ JNIEXPORT jboolean JNICALL Java_sun_awt_X11InputMethodBase_setCompositionEnabledNative (JNIEnv *env, jobject this, jboolean enable) { X11InputMethodData *pX11IMData; char * ret = NULL; XVaNestedList pr_atrb; AWT_LOCK(); pX11IMData = getX11InputMethodData(env, this); if ((pX11IMData == NULL) || (pX11IMData->current_ic == NULL)) { AWT_UNLOCK(); return JNI_FALSE; } pr_atrb = XVaCreateNestedList(0, XNPreeditState, (enable ? XIMPreeditEnable : XIMPreeditDisable), NULL); ret = XSetICValues(pX11IMData->current_ic, XNPreeditAttributes, pr_atrb, NULL); XFree((void *)pr_atrb); AWT_UNLOCK(); if ((ret != 0) && ((strcmp(ret, XNPreeditAttributes) == 0) || (strcmp(ret, XNPreeditState) == 0))) { JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", ""); } return (jboolean)(ret == 0); } /* * Class: sun_awt_X11InputMethodBase * Method: isCompositionEnabledNative * Signature: ()Z * * This method tries to get the XNPreeditState attribute associated with the current XIC. * * Return JNI_TRUE if the XNPreeditState is successfully retrieved. Otherwise, if * XGetICValues fails to get this attribute, java.lang.UnsupportedOperationException * will be thrown. JNI_FALSE is returned if this method fails due to other reasons. */ JNIEXPORT jboolean JNICALL Java_sun_awt_X11InputMethodBase_isCompositionEnabledNative (JNIEnv *env, jobject this) { X11InputMethodData *pX11IMData = NULL; char * ret = NULL; XIMPreeditState state = XIMPreeditUnKnown; XVaNestedList pr_atrb; AWT_LOCK(); pX11IMData = getX11InputMethodData(env, this); if ((pX11IMData == NULL) || (pX11IMData->current_ic == NULL)) { AWT_UNLOCK(); return JNI_FALSE; } pr_atrb = XVaCreateNestedList(0, XNPreeditState, &state, NULL); ret = XGetICValues(pX11IMData->current_ic, XNPreeditAttributes, pr_atrb, NULL); XFree((void *)pr_atrb); AWT_UNLOCK(); if ((ret != 0) && ((strcmp(ret, XNPreeditAttributes) == 0) || (strcmp(ret, XNPreeditState) == 0))) { JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", ""); return JNI_FALSE; } return (jboolean)(state == XIMPreeditEnable); } JNIEXPORT void JNICALL Java_sun_awt_X11_XInputMethod_adjustStatusWindow (JNIEnv *env, jobject this, jlong window) { } /* * Class: sun_awt_X11InputMethod * Method: setStatusAreaVisible * Signature: (ZJ)V */ JNIEXPORT void JNICALL Java_sun_awt_X11InputMethod_setStatusAreaVisible (JNIEnv *env, jobject this, jboolean value, jlong data) { X11InputMethodData *pX11IMData; pX11IMData = getX11InputMethodData(env, this); if (NULL == pX11IMData) return; if (NULL == pX11IMData->statusWindow) return; if ((int)value){ pX11IMData->statusWindow->on = True; }else{ pX11IMData->statusWindow->on = False; } return; }