# HG changeset patch # User clanger # Date 1527661199 -7200 # Node ID 668463f93ec02bdcea78411e8b78511b561450b9 # Parent b3a654c6fbccdb9944c8595aae302c164ec242fd 8201429: Support AIX Input Method Editor (IME) for AWT Input Method Framework (IMF) Reviewed-by: prr Contributed-by: takiguc@linux.vnet.ibm.com, christoph.langer@sap.com diff -r b3a654c6fbcc -r 668463f93ec0 make/lib/Awt2dLibraries.gmk --- a/make/lib/Awt2dLibraries.gmk Tue May 29 11:22:21 2018 -0700 +++ b/make/lib/Awt2dLibraries.gmk Wed May 30 08:19:59 2018 +0200 @@ -269,6 +269,7 @@ ifeq ($(ENABLE_HEADLESS_ONLY), false) LIBAWT_XAWT_DIRS := \ + $(wildcard $(TOPDIR)/src/java.desktop/$(OPENJDK_TARGET_OS)/native/libawt_xawt) \ $(TOPDIR)/src/java.desktop/$(OPENJDK_TARGET_OS_TYPE)/native/libawt_xawt \ $(TOPDIR)/src/java.desktop/share/native/common/awt/debug \ $(TOPDIR)/src/java.desktop/share/native/common/awt/utility \ @@ -329,7 +330,7 @@ BUILD_LIBAWT_XAWT_awt_Font.c_CFLAGS := -w # initializing a declared 'extern' BUILD_LIBAWT_XAWT_debug_mem.c_CFLAGS := -w - endif + endif $(eval $(call SetupJdkLibrary, BUILD_LIBAWT_XAWT, \ NAME := awt_xawt, \ diff -r b3a654c6fbcc -r 668463f93ec0 src/java.desktop/aix/classes/sun/awt/X11InputMethod.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.desktop/aix/classes/sun/awt/X11InputMethod.java Wed May 30 08:19:59 2018 +0200 @@ -0,0 +1,490 @@ +/* + * 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. + */ + +package sun.awt; + +import java.awt.AWTException; +import java.awt.EventQueue; +import java.awt.event.InputMethodEvent; +import java.awt.font.TextAttribute; +import java.awt.font.TextHitInfo; +import java.awt.peer.ComponentPeer; +import java.text.AttributedString; + +import sun.util.logging.PlatformLogger; + +/** + * Input Method Adapter for XIM for AIX + * + * @author JavaSoft International + */ +public abstract class X11InputMethod extends X11InputMethodBase { + + // to keep the instance of activating if IM resumed + static protected X11InputMethod activatedInstance = null; + + /** + * Constructs an X11InputMethod instance. It initializes the XIM + * environment if it's not done yet. + * + * @exception AWTException if XOpenIM() failed. + */ + public X11InputMethod() throws AWTException { + super(); + } + + /** + * Reset the composition state to the current composition state. + */ + protected void resetCompositionState() { + if (compositionEnableSupported && haveActiveClient()) { + try { + /* Restore the composition mode to the last saved composition + mode. */ + setCompositionEnabled(savedCompositionState); + } catch (UnsupportedOperationException e) { + compositionEnableSupported = false; + } + } + } + + /** + * Activate input method. + */ + public synchronized void activate() { + activatedInstance = this; + clientComponentWindow = getClientComponentWindow(); + if (clientComponentWindow == null) + return; + + if (lastXICFocussedComponent != null) { + if (log.isLoggable(PlatformLogger.Level.FINE)) { + log.fine("XICFocused {0}, AWTFocused {1}", + lastXICFocussedComponent, awtFocussedComponent); + } + if (lastXICFocussedComponent != awtFocussedComponent) { + ComponentPeer lastXICFocussedComponentPeer = getPeer(lastXICFocussedComponent); + if (lastXICFocussedComponentPeer != null) { + setXICFocus(lastXICFocussedComponentPeer, false, isLastXICActive); + } + } + lastXICFocussedComponent = null; + } + + if (pData == 0) { + if (!createXIC()) { + return; + } + disposed = false; + } + + /* reset input context if necessary and set the XIC focus + */ + resetXICifneeded(); + ComponentPeer awtFocussedComponentPeer = getPeer(awtFocussedComponent); + setStatusAreaVisible(true, pData); + + if (awtFocussedComponentPeer != null) { + setXICFocus(awtFocussedComponentPeer, true, haveActiveClient()); + } + lastXICFocussedComponent = awtFocussedComponent; + isLastXICActive = haveActiveClient(); + isActive = true; + if (savedCompositionState) { + resetCompositionState(); + } + } + + /** + * Deactivate input method. + */ + public synchronized void deactivate(boolean isTemporary) { + boolean isAc = haveActiveClient(); + /* Usually as the client component, let's call it component A, + loses the focus, this method is called. Then when another client + component, let's call it component B, gets the focus, activate is first called on + the previous focused compoent which is A, then endComposition is called on A, + deactivate is called on A again. And finally activate is called on the newly + focused component B. Here is the call sequence. + + A loses focus B gains focus + -------------> deactivate A -------------> activate A -> endComposition A -> + deactivate A -> activate B ----.... + + So in order to carry the composition mode across the components sharing the same + input context, we save it when deactivate is called so that when activate is + called, it can be restored correctly till activate is called on the newly focused + component. (See also sun/awt/im/InputContext and bug 6184471). + Last note, getCompositionState should be called before setXICFocus since + setXICFocus here sets the XIC to 0. + */ + activatedInstance = null; + savedCompositionState = getCompositionState(); + + if (isTemporary) { + //turn the status window off... + turnoffStatusWindow(); + /* Delay resetting the XIC focus until activate is called and the newly + * Focused component has a different peer as the last focused component. + */ + lastXICFocussedComponent = awtFocussedComponent; + } else { + if (awtFocussedComponent != null ) { + ComponentPeer awtFocussedComponentPeer = getPeer(awtFocussedComponent); + if (awtFocussedComponentPeer != null) { + setXICFocus(awtFocussedComponentPeer, false, isAc); + } + } + lastXICFocussedComponent = null; + } + + isLastXICActive = isAc; + isLastTemporary = isTemporary; + isActive = false; + setStatusAreaVisible(false, pData); + } + + // implements java.awt.im.spi.InputMethod.hideWindows + public void hideWindows() { + if (pData != 0) { + setStatusAreaVisible(false, pData); + turnoffStatusWindow(); + } + } + + /** + * Updates composed text with XIM preedit information and + * posts composed text to the awt event queue. The args of + * this method correspond to the XIM preedit callback + * information. The XIM highlight attributes are translated via + * fixed mapping (i.e., independent from any underlying input + * method engine). This method is invoked in the AWT Toolkit + * (X event loop) thread context and thus inside the AWT Lock. + */ + // NOTE: This method may be called by privileged threads. + // This functionality is implemented in a package-private method + // to insure that it cannot be overridden by client subclasses. + // DO NOT INVOKE CLIENT CODE ON THIS THREAD! + void dispatchComposedText(String chgText, + int chgStyles[], + int chgOffset, + int chgLength, + int caretPosition, + long when) { + if (disposed) { + return; + } + + // Workaround for deadlock bug on solaris2.6_zh bug#4170760 + if (chgText == null + && chgStyles == null + && chgOffset == 0 + && chgLength == 0 + && caretPosition == 0 + && composedText == null + && committedText == null) + return; + + // Recalculate chgOffset and chgLength for supplementary char + if (composedText != null) { + int tmpChgOffset=chgOffset; + int tmpChgLength=chgLength; + int index = 0; + for (int i=0;i < tmpChgOffset; i++,index++){ + if (index < composedText.length() + && Character.charCount(composedText.codePointAt(index))==2){ + index++; + chgOffset++; + } + } + // The index keeps value + for (int i=0;i < tmpChgLength; i++,index++){ + if (index < composedText.length() + && Character.charCount(composedText.codePointAt(index))==2){ + index++; + chgLength++; + } + } + } + + // Replace control character with a square box + if (chgText != null) { + StringBuffer newChgText = new StringBuffer(); + for (int i=0; i < chgText.length(); i++){ + char c = chgText.charAt(i); + if (Character.isISOControl(c)){ + c = '\u25A1'; + } + newChgText.append(c); + } + chgText = new String(newChgText); + } + + if (composedText == null) { + // TODO: avoid reallocation of those buffers + composedText = new StringBuffer(INITIAL_SIZE); + rawFeedbacks = new IntBuffer(INITIAL_SIZE); + } + if (chgLength > 0) { + if (chgText == null && chgStyles != null) { + rawFeedbacks.replace(chgOffset, chgStyles); + } else { + if (chgLength == composedText.length()) { + // optimization for the special case to replace the + // entire previous text + composedText = new StringBuffer(INITIAL_SIZE); + rawFeedbacks = new IntBuffer(INITIAL_SIZE); + } else { + if (composedText.length() > 0) { + if (chgOffset+chgLength < composedText.length()) { + String text; + text = composedText.toString().substring(chgOffset+chgLength, + composedText.length()); + composedText.setLength(chgOffset); + composedText.append(text); + } else { + // in case to remove substring from chgOffset + // to the end + composedText.setLength(chgOffset); + } + rawFeedbacks.remove(chgOffset, chgLength); + } + } + } + } + if (chgText != null) { + composedText.insert(chgOffset, chgText); + if (chgStyles != null) { + // Recalculate chgStyles for supplementary char + if (chgText.length() > chgStyles.length){ + int index=0; + int[] newStyles = new int[chgText.length()]; + for (int i=0; i < chgStyles.length; i++, index++){ + newStyles[index]=chgStyles[i]; + if (index < chgText.length() + && Character.charCount(chgText.codePointAt(index))==2){ + newStyles[++index]=chgStyles[i]; + } + } + chgStyles=newStyles; + } + rawFeedbacks.insert(chgOffset, chgStyles); + } + + } + + else if (chgStyles != null) { + // Recalculate chgStyles to support supplementary char + int count=0; + for (int i=0; i < chgStyles.length; i++){ + if (composedText.length() > chgOffset+i+count + && Character.charCount(composedText.codePointAt(chgOffset+i+count))==2){ + count++; + } + } + if (count>0){ + int index=0; + int[] newStyles = new int[chgStyles.length+count]; + for (int i=0; i < chgStyles.length; i++, index++){ + newStyles[index]=chgStyles[i]; + if (composedText.length() > chgOffset+index + && Character.charCount(composedText.codePointAt(chgOffset+index))==2){ + newStyles[++index]=chgStyles[i]; + } + } + chgStyles=newStyles; + } + rawFeedbacks.replace(chgOffset, chgStyles); + } + + if (composedText.length() == 0) { + composedText = null; + rawFeedbacks = null; + + // if there is any outstanding committed text stored by + // dispatchCommittedText(), it has to be sent to the + // client component. + if (committedText != null) { + dispatchCommittedText(committedText, when); + committedText = null; + return; + } + + // otherwise, send null text to delete client's composed + // text. + postInputMethodEvent(InputMethodEvent.INPUT_METHOD_TEXT_CHANGED, + null, + 0, + null, + null, + when); + + return; + } + + // Adjust caretPosition for supplementary char + for (int i=0; i< caretPosition; i++){ + if (i < composedText.length() + && Character.charCount(composedText.codePointAt(i))==2){ + caretPosition++; + i++; + } + } + + // Now sending the composed text to the client + int composedOffset; + AttributedString inputText; + + // if there is any partially committed text, concatenate it to + // the composed text. + if (committedText != null) { + composedOffset = committedText.length(); + inputText = new AttributedString(committedText + composedText); + committedText = null; + } else { + composedOffset = 0; + inputText = new AttributedString(composedText.toString()); + } + + int currentFeedback; + int nextFeedback; + int startOffset = 0; + int currentOffset; + int visiblePosition = 0; + TextHitInfo visiblePositionInfo = null; + + rawFeedbacks.rewind(); + currentFeedback = rawFeedbacks.getNext(); + rawFeedbacks.unget(); + while ((nextFeedback = rawFeedbacks.getNext()) != -1) { + if (visiblePosition == 0) { + visiblePosition = nextFeedback & XIMVisibleMask; + if (visiblePosition != 0) { + int index = rawFeedbacks.getOffset() - 1; + + if (visiblePosition == XIMVisibleToBackward) + visiblePositionInfo = TextHitInfo.leading(index); + else + visiblePositionInfo = TextHitInfo.trailing(index); + } + } + nextFeedback &= ~XIMVisibleMask; + if (currentFeedback != nextFeedback) { + rawFeedbacks.unget(); + currentOffset = rawFeedbacks.getOffset(); + inputText.addAttribute(TextAttribute.INPUT_METHOD_HIGHLIGHT, + convertVisualFeedbackToHighlight(currentFeedback), + composedOffset + startOffset, + composedOffset + currentOffset); + startOffset = currentOffset; + currentFeedback = nextFeedback; + } + } + currentOffset = rawFeedbacks.getOffset(); + if (currentOffset >= 0) { + inputText.addAttribute(TextAttribute.INPUT_METHOD_HIGHLIGHT, + convertVisualFeedbackToHighlight(currentFeedback), + composedOffset + startOffset, + composedOffset + currentOffset); + } + + postInputMethodEvent(InputMethodEvent.INPUT_METHOD_TEXT_CHANGED, + inputText.getIterator(), + composedOffset, + TextHitInfo.leading(caretPosition), + visiblePositionInfo, + when); + } + + /* Some IMs need forced Text clear */ + void clearComposedText(long when) { + composedText = null; + postInputMethodEvent(InputMethodEvent.INPUT_METHOD_TEXT_CHANGED, + null, 0, null, null, + when); + if (committedText != null && committedText.length() > 0) { + dispatchCommittedText(committedText, when); + } + committedText = null; + rawFeedbacks = null; + } + + void clearComposedText() { + if (EventQueue.isDispatchThread()) { + clearComposedText(EventQueue.getMostRecentEventTime()); + } + } + + /* + * Subclasses should override disposeImpl() instead of dispose(). Client + * code should always invoke dispose(), never disposeImpl(). + */ + protected synchronized void disposeImpl() { + disposeXIC(); + awtLock(); + try { + clearComposedText(); + } finally { + // Put awtUnlock into finally block in case an exception is thrown in clearComposedText. + awtUnlock(); + } + awtFocussedComponent = null; + lastXICFocussedComponent = null; + needResetXIC = false; + savedCompositionState = false; + compositionEnableSupported = true; + } + + /** + * @see java.awt.im.spi.InputMethod#setCompositionEnabled(boolean) + */ + public void setCompositionEnabled(boolean enable) { + /* If the composition state is successfully changed, set + the savedCompositionState to 'enable'. Otherwise, simply + return. + setCompositionEnabledNative may throw UnsupportedOperationException. + Don't try to catch it since the method may be called by clients. + Use package private mthod 'resetCompositionState' if you want the + exception to be caught. + */ + boolean pre, post; + pre=getCompositionState(); + + if (setCompositionEnabledNative(enable)) { + savedCompositionState = enable; + } + + post=getCompositionState(); + if (pre != post && post == enable){ + if (enable == false) flushText(); + if (awtFocussedComponent != null && isActive){ + setXICFocus(getPeer(awtFocussedComponent), + true, haveActiveClient()); + } + } + } + + private native void setStatusAreaVisible(boolean value, long data); +} diff -r b3a654c6fbcc -r 668463f93ec0 src/java.desktop/aix/native/libawt_xawt/awt/awt_InputMethod.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.desktop/aix/native/libawt_xawt/awt/awt_InputMethod.c Wed May 30 08:19:59 2018 +0200 @@ -0,0 +1,2269 @@ +/* + * 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)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); + } + } + } + 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; +} diff -r b3a654c6fbcc -r 668463f93ec0 src/java.desktop/unix/classes/sun/awt/X11InputMethod.java --- a/src/java.desktop/unix/classes/sun/awt/X11InputMethod.java Tue May 29 11:22:21 2018 -0700 +++ b/src/java.desktop/unix/classes/sun/awt/X11InputMethod.java Wed May 30 08:19:59 2018 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -25,147 +25,21 @@ package sun.awt; -import java.util.Collections; -import java.util.Locale; -import java.util.Map; -import java.util.HashMap; -import java.awt.AWTEvent; import java.awt.AWTException; -import java.awt.Component; -import java.awt.Container; -import java.awt.EventQueue; -import java.awt.Window; -import java.awt.im.InputMethodHighlight; -import java.awt.im.spi.InputMethodContext; -import sun.awt.im.InputMethodAdapter; import java.awt.event.InputMethodEvent; import java.awt.font.TextAttribute; import java.awt.font.TextHitInfo; import java.awt.peer.ComponentPeer; -import java.lang.Character.Subset; import java.text.AttributedString; -import java.text.AttributedCharacterIterator; -import java.io.File; -import java.io.FileReader; -import java.io.BufferedReader; -import java.io.IOException; -import java.lang.ref.WeakReference; import sun.util.logging.PlatformLogger; -import java.util.StringTokenizer; -import java.util.regex.Pattern; - /** * Input Method Adapter for XIM * * @author JavaSoft International */ -public abstract class X11InputMethod extends InputMethodAdapter { - private static final PlatformLogger log = PlatformLogger.getLogger("sun.awt.X11InputMethod"); - /* - * The following XIM* values must be the same as those defined in - * Xlib.h - */ - private static final int XIMReverse = (1<<0); - private static final int XIMUnderline = (1<<1); - private static final int XIMHighlight = (1<<2); - private static final int XIMPrimary = (1<<5); - private static final int XIMSecondary = (1<<6); - private static final int XIMTertiary = (1<<7); - - /* - * visible position values - */ - private static final int XIMVisibleToForward = (1<<8); - private static final int XIMVisibleToBackward = (1<<9); - private static final int XIMVisibleCenter = (1<<10); - private static final int XIMVisibleMask = (XIMVisibleToForward| - XIMVisibleToBackward| - XIMVisibleCenter); - - private Locale locale; - private static boolean isXIMOpened = false; - protected Container clientComponentWindow = null; - private Component awtFocussedComponent = null; - private Component lastXICFocussedComponent = null; - private boolean isLastXICActive = false; - private boolean isLastTemporary = false; - private boolean isActive = false; - private boolean isActiveClient = false; - private static Map[] highlightStyles; - private boolean disposed = false; - - //reset the XIC if necessary - private boolean needResetXIC = false; - private WeakReference needResetXICClient = new WeakReference<>(null); - - // The use of compositionEnableSupported is to reduce unnecessary - // native calls if set/isCompositionEnabled - // throws UnsupportedOperationException. - // It is set to false if that exception is thrown first time - // either of the two methods are called. - private boolean compositionEnableSupported = true; - // The savedCompositionState indicates the composition mode when - // endComposition or setCompositionEnabled is called. It doesn't always - // reflect the actual composition state because it doesn't get updated - // when the user changes the composition state through direct interaction - // with the input method. It is used to save the composition mode when - // focus is traversed across different client components sharing the - // same java input context. Also if set/isCompositionEnabled are not - // supported, it remains false. - private boolean savedCompositionState = false; - - // variables to keep track of preedit context. - // these variables need to be accessed within AWT_LOCK/UNLOCK - private String committedText = null; - private StringBuffer composedText = null; - private IntBuffer rawFeedbacks; - - // private data (X11InputMethodData structure defined in - // awt_InputMethod.c) for native methods - // this structure needs to be accessed within AWT_LOCK/UNLOCK - private transient long pData = 0; // accessed by native - - // Initialize highlight mapping table - static { - @SuppressWarnings({"unchecked", "rawtypes"}) - Map styles[] = new Map[4]; - HashMap map; - - // UNSELECTED_RAW_TEXT_HIGHLIGHT - map = new HashMap<>(1); - map.put(TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD); - styles[0] = Collections.unmodifiableMap(map); - - // SELECTED_RAW_TEXT_HIGHLIGHT - map = new HashMap<>(1); - map.put(TextAttribute.SWAP_COLORS, TextAttribute.SWAP_COLORS_ON); - styles[1] = Collections.unmodifiableMap(map); - - // UNSELECTED_CONVERTED_TEXT_HIGHLIGHT - map = new HashMap<>(1); - map.put(TextAttribute.INPUT_METHOD_UNDERLINE, - TextAttribute.UNDERLINE_LOW_ONE_PIXEL); - styles[2] = Collections.unmodifiableMap(map); - - // SELECTED_CONVERTED_TEXT_HIGHLIGHT - map = new HashMap<>(1); - map.put(TextAttribute.SWAP_COLORS, TextAttribute.SWAP_COLORS_ON); - styles[3] = Collections.unmodifiableMap(map); - - highlightStyles = styles; - } - - static { - initIDs(); - } - - /** - * Initialize JNI field and method IDs for fields that may be - accessed from C. - */ - private static native void initIDs(); +public abstract class X11InputMethod extends X11InputMethodBase { /** * Constructs an X11InputMethod instance. It initializes the XIM @@ -174,113 +48,13 @@ * @exception AWTException if XOpenIM() failed. */ public X11InputMethod() throws AWTException { - // supports only the locale in which the VM is started - locale = X11InputMethodDescriptor.getSupportedLocale(); - if (initXIM() == false) { - throw new AWTException("Cannot open X Input Method"); - } - } - - @SuppressWarnings("deprecation") - protected void finalize() throws Throwable { - dispose(); - super.finalize(); - } - - /** - * Invokes openIM() that invokes XOpenIM() if it's not opened yet. - * @return true if openXIM() is successful or it's already been opened. - */ - private synchronized boolean initXIM() { - if (isXIMOpened == false) - isXIMOpened = openXIM(); - return isXIMOpened; - } - - protected abstract boolean openXIM(); - - protected boolean isDisposed() { - return disposed; - } - - protected abstract void setXICFocus(ComponentPeer peer, - boolean value, boolean active); - - /** - * Does nothing - this adapter doesn't use the input method context. - * - * @see java.awt.im.spi.InputMethod#setInputMethodContext - */ - public void setInputMethodContext(InputMethodContext context) { - } - - /** - * Set locale to input. If input method doesn't support specified locale, - * false will be returned and its behavior is not changed. - * - * @param lang locale to input - * @return the true is returned when specified locale is supported. - */ - public boolean setLocale(Locale lang) { - if (lang.equals(locale)) { - return true; - } - // special compatibility rule for Japanese and Korean - if (locale.equals(Locale.JAPAN) && lang.equals(Locale.JAPANESE) || - locale.equals(Locale.KOREA) && lang.equals(Locale.KOREAN)) { - return true; - } - return false; - } - - /** - * Returns current input locale. - */ - public Locale getLocale() { - return locale; - } - - /** - * Does nothing - XIM doesn't let you specify which characters you expect. - * - * @see java.awt.im.spi.InputMethod#setCharacterSubsets - */ - public void setCharacterSubsets(Subset[] subsets) { - } - - /** - * Dispatch event to input method. InputContext dispatch event with this - * method. Input method set consume flag if event is consumed in - * input method. - * - * @param e event - */ - public void dispatchEvent(AWTEvent e) { - } - - - protected final void resetXICifneeded(){ - /* needResetXIC is used to indicate whether to call - resetXIC on the active client. resetXIC will always be - called on the passive client when endComposition is called. - */ - if (needResetXIC && haveActiveClient() && - getClientComponent() != needResetXICClient.get()){ - resetXIC(); - - // needs to reset the last xic focussed component. - lastXICFocussedComponent = null; - isLastXICActive = false; - - needResetXICClient.clear(); - needResetXIC = false; - } + super(); } /** * Reset the composition state to the current composition state. */ - private void resetCompositionState() { + protected void resetCompositionState() { if (compositionEnableSupported) { try { /* Restore the composition mode to the last saved composition @@ -293,23 +67,6 @@ } /** - * Query and then return the current composition state. - * @return the composition state if isCompositionEnabled call - * is successful. Otherwise, it returns false. - */ - private boolean getCompositionState() { - boolean compositionState = false; - if (compositionEnableSupported) { - try { - compositionState = isCompositionEnabled(); - } catch (UnsupportedOperationException e) { - compositionEnableSupported = false; - } - } - return compositionState; - } - - /** * Activate input method. */ public synchronized void activate() { @@ -317,7 +74,7 @@ if (clientComponentWindow == null) return; - if (lastXICFocussedComponent != null){ + if (lastXICFocussedComponent != null) { if (log.isLoggable(PlatformLogger.Level.FINE)) { log.fine("XICFocused {0}, AWTFocused {1}", lastXICFocussedComponent, awtFocussedComponent); @@ -360,8 +117,6 @@ isActive = true; } - protected abstract boolean createXIC(); - /** * Deactivate input method. */ @@ -387,198 +142,26 @@ */ savedCompositionState = getCompositionState(); - if (isTemporary){ + if (isTemporary) { //turn the status window off... turnoffStatusWindow(); } /* Delay resetting the XIC focus until activate is called and the newly - focussed component has a different peer as the last focussed component. - */ + * Focused component has a different peer as the last focused component. + */ lastXICFocussedComponent = awtFocussedComponent; isLastXICActive = isAc; isLastTemporary = isTemporary; isActive = false; } - /** - * Explicitly disable the native IME. Native IME is not disabled when - * deactivate is called. - */ - public void disableInputMethod() { - if (lastXICFocussedComponent != null) { - setXICFocus(getPeer(lastXICFocussedComponent), false, isLastXICActive); - lastXICFocussedComponent = null; - isLastXICActive = false; - - resetXIC(); - needResetXICClient.clear(); - needResetXIC = false; - } - } - // implements java.awt.im.spi.InputMethod.hideWindows public void hideWindows() { // ??? need real implementation } /** - * @see java.awt.Toolkit#mapInputMethodHighlight - */ - public static Map mapInputMethodHighlight(InputMethodHighlight highlight) { - int index; - int state = highlight.getState(); - if (state == InputMethodHighlight.RAW_TEXT) { - index = 0; - } else if (state == InputMethodHighlight.CONVERTED_TEXT) { - index = 2; - } else { - return null; - } - if (highlight.isSelected()) { - index += 1; - } - return highlightStyles[index]; - } - - /** - * @see sun.awt.im.InputMethodAdapter#setAWTFocussedComponent - */ - protected void setAWTFocussedComponent(Component component) { - if (component == null) { - return; - } - if (isActive) { - // deactivate/activate are being suppressed during a focus change - - // this may happen when an input method window is made visible - boolean ac = haveActiveClient(); - setXICFocus(getPeer(awtFocussedComponent), false, ac); - setXICFocus(getPeer(component), true, ac); - } - awtFocussedComponent = component; - } - - /** - * @see sun.awt.im.InputMethodAdapter#stopListening - */ - protected void stopListening() { - // It is desirable to disable XIM by calling XSetICValues with - // XNPreeditState == XIMPreeditDisable. But Solaris 2.6 and - // Solaris 7 do not implement this correctly without a patch, - // so just call resetXIC here. Prior endComposition call commits - // the existing composed text. - endComposition(); - // disable the native input method so that the other input - // method could get the input focus. - disableInputMethod(); - if (needResetXIC) { - resetXIC(); - needResetXICClient.clear(); - needResetXIC = false; - } - } - - /** - * Returns the Window instance in which the client component is - * contained. If not found, null is returned. (IS THIS POSSIBLE?) - */ - // NOTE: This method may be called by privileged threads. - // DO NOT INVOKE CLIENT CODE ON THIS THREAD! - private Window getClientComponentWindow() { - Component client = getClientComponent(); - Container container; - - if (client instanceof Container) { - container = (Container) client; - } else { - container = getParent(client); - } - - while (container != null && !(container instanceof java.awt.Window)) { - container = getParent(container); - } - return (Window) container; - } - - protected abstract Container getParent(Component client); - - /** - * Returns peer of the given client component. If the given client component - * doesn't have peer, peer of the native container of the client is returned. - */ - protected abstract ComponentPeer getPeer(Component client); - - /** - * Used to protect preedit data - */ - protected abstract void awtLock(); - protected abstract void awtUnlock(); - - /** - * Creates an input method event from the arguments given - * and posts it on the AWT event queue. For arguments, - * see InputMethodEvent. Called by input method. - * - * @see java.awt.event.InputMethodEvent#InputMethodEvent - */ - private void postInputMethodEvent(int id, - AttributedCharacterIterator text, - int committedCharacterCount, - TextHitInfo caret, - TextHitInfo visiblePosition, - long when) { - Component source = getClientComponent(); - if (source != null) { - InputMethodEvent event = new InputMethodEvent(source, - id, when, text, committedCharacterCount, caret, visiblePosition); - SunToolkit.postEvent(SunToolkit.targetToAppContext(source), (AWTEvent)event); - } - } - - private void postInputMethodEvent(int id, - AttributedCharacterIterator text, - int committedCharacterCount, - TextHitInfo caret, - TextHitInfo visiblePosition) { - postInputMethodEvent(id, text, committedCharacterCount, - caret, visiblePosition, EventQueue.getMostRecentEventTime()); - } - - /** - * Dispatches committed text from XIM to the awt event queue. This - * method is invoked from the event handler in canvas.c in the - * AWT Toolkit thread context and thus inside the AWT Lock. - * @param str committed text - * @param when when - */ - // NOTE: This method may be called by privileged threads. - // This functionality is implemented in a package-private method - // to insure that it cannot be overridden by client subclasses. - // DO NOT INVOKE CLIENT CODE ON THIS THREAD! - void dispatchCommittedText(String str, long when) { - if (str == null) - return; - - if (composedText == null) { - AttributedString attrstr = new AttributedString(str); - postInputMethodEvent(InputMethodEvent.INPUT_METHOD_TEXT_CHANGED, - attrstr.getIterator(), - str.length(), - null, - null, - when); - } else { - // if there is composed text, wait until the preedit - // callback is invoked. - committedText = str; - } - } - - private void dispatchCommittedText(String str) { - dispatchCommittedText(str, EventQueue.getMostRecentEventTime()); - } - - /** * Updates composed text with XIM preedit information and * posts composed text to the awt event queue. The args of * this method correspond to the XIM preedit callback @@ -601,7 +184,7 @@ return; } - //Workaround for deadlock bug on solaris2.6_zh bug#4170760 + // Workaround for deadlock bug on solaris2.6_zh bug#4170760 if (chgText == null && chgStyles == null && chgOffset == 0 @@ -739,34 +322,6 @@ when); } - /** - * Flushes composed and committed text held in this context. - * This method is invoked in the AWT Toolkit (X event loop) thread context - * and thus inside the AWT Lock. - */ - // NOTE: This method may be called by privileged threads. - // This functionality is implemented in a package-private method - // to insure that it cannot be overridden by client subclasses. - // DO NOT INVOKE CLIENT CODE ON THIS THREAD! - void flushText() { - String flush = (committedText != null ? committedText : ""); - if (composedText != null) { - flush += composedText.toString(); - } - - if (!flush.equals("")) { - AttributedString attrstr = new AttributedString(flush); - postInputMethodEvent(InputMethodEvent.INPUT_METHOD_TEXT_CHANGED, - attrstr.getIterator(), - flush.length(), - null, - null, - EventQueue.getMostRecentEventTime()); - composedText = null; - committedText = null; - } - } - /* * Subclasses should override disposeImpl() instead of dispose(). Client * code should always invoke dispose(), never disposeImpl(). @@ -783,43 +338,6 @@ } /** - * Frees all X Window resources associated with this object. - * - * @see java.awt.im.spi.InputMethod#dispose - */ - public final void dispose() { - boolean call_disposeImpl = false; - - if (!disposed) { - synchronized (this) { - if (!disposed) { - disposed = call_disposeImpl = true; - } - } - } - - if (call_disposeImpl) { - disposeImpl(); - } - } - - /** - * Returns null. - * - * @see java.awt.im.spi.InputMethod#getControlObject - */ - public Object getControlObject() { - return null; - } - - /** - * @see java.awt.im.spi.InputMethod#removeNotify - */ - public synchronized void removeNotify() { - dispose(); - } - - /** * @see java.awt.im.spi.InputMethod#setCompositionEnabled(boolean) */ public void setCompositionEnabled(boolean enable) { @@ -835,263 +353,4 @@ savedCompositionState = enable; } } - - /** - * @see java.awt.im.spi.InputMethod#isCompositionEnabled - */ - public boolean isCompositionEnabled() { - /* isCompositionEnabledNative may throw UnsupportedOperationException. - Don't try to catch it since this method may be called by clients. - Use package private method 'getCompositionState' if you want the - exception to be caught. - */ - return isCompositionEnabledNative(); - } - - /** - * Ends any input composition that may currently be going on in this - * context. Depending on the platform and possibly user preferences, - * this may commit or delete uncommitted text. Any changes to the text - * are communicated to the active component using an input method event. - * - *

- * A text editing component may call this in a variety of situations, - * for example, when the user moves the insertion point within the text - * (but outside the composed text), or when the component's text is - * saved to a file or copied to the clipboard. - * - */ - public void endComposition() { - if (disposed) { - return; - } - - /* Before calling resetXIC, record the current composition mode - so that it can be restored later. */ - savedCompositionState = getCompositionState(); - boolean active = haveActiveClient(); - if (active && composedText == null && committedText == null){ - needResetXIC = true; - needResetXICClient = new WeakReference<>(getClientComponent()); - return; - } - - String text = resetXIC(); - /* needResetXIC is only set to true for active client. So passive - client should not reset the flag to false. */ - if (active) { - needResetXIC = false; - } - - // Remove any existing composed text by posting an InputMethodEvent - // with null composed text. It would be desirable to wait for a - // dispatchComposedText call from X input method engine, but some - // input method does not conform to the XIM specification and does - // not call the preedit callback to erase preedit text on calling - // XmbResetIC. To work around this problem, do it here by ourselves. - awtLock(); - composedText = null; - postInputMethodEvent(InputMethodEvent.INPUT_METHOD_TEXT_CHANGED, - null, - 0, - null, - null); - - if (text != null && text.length() > 0) { - dispatchCommittedText(text); - } - awtUnlock(); - - // Restore the preedit state if it was enabled - if (savedCompositionState) { - resetCompositionState(); - } - } - - /** - * Returns a string with information about the current input method server, or null. - * On both Linux & SunOS, the value of environment variable XMODIFIERS is - * returned if set. Otherwise, on SunOS, $HOME/.dtprofile will be parsed - * to find out the language service engine (atok or wnn) since there is - * no API in Xlib which returns the information of native - * IM server or language service and we want to try our best to return as much - * information as possible. - * - * Note: This method could return null on Linux if XMODIFIERS is not set properly or - * if any IOException is thrown. - * See man page of XSetLocaleModifiers(3X11) for the usgae of XMODIFIERS, - * atok12setup(1) and wnn6setup(1) for the information written to - * $HOME/.dtprofile when you run these two commands. - * - */ - public String getNativeInputMethodInfo() { - String xmodifiers = System.getenv("XMODIFIERS"); - String imInfo = null; - - // If XMODIFIERS is set, return the value - if (xmodifiers != null) { - int imIndex = xmodifiers.indexOf("@im="); - if (imIndex != -1) { - imInfo = xmodifiers.substring(imIndex + 4); - } - } else if (System.getProperty("os.name").startsWith("SunOS")) { - File dtprofile = new File(System.getProperty("user.home") + - "/.dtprofile"); - String languageEngineInfo = null; - try { - BufferedReader br = new BufferedReader(new FileReader(dtprofile)); - String line = null; - - while ( languageEngineInfo == null && (line = br.readLine()) != null) { - if (line.contains("atok") || line.contains("wnn")) { - StringTokenizer tokens = new StringTokenizer(line); - while (tokens.hasMoreTokens()) { - String token = tokens.nextToken(); - if (Pattern.matches("atok.*setup", token) || - Pattern.matches("wnn.*setup", token)){ - languageEngineInfo = token.substring(0, token.indexOf("setup")); - break; - } - } - } - } - - br.close(); - } catch(IOException ioex) { - // Since this method is provided for internal testing only, - // we dump the stack trace for the ease of debugging. - ioex.printStackTrace(); - } - - imInfo = "htt " + languageEngineInfo; - } - - return imInfo; - } - - - /** - * Performs mapping from an XIM visible feedback value to Java IM highlight. - * @return Java input method highlight - */ - private InputMethodHighlight convertVisualFeedbackToHighlight(int feedback) { - InputMethodHighlight highlight; - - switch (feedback) { - case XIMUnderline: - highlight = InputMethodHighlight.UNSELECTED_CONVERTED_TEXT_HIGHLIGHT; - break; - case XIMReverse: - highlight = InputMethodHighlight.SELECTED_CONVERTED_TEXT_HIGHLIGHT; - break; - case XIMHighlight: - highlight = InputMethodHighlight.SELECTED_RAW_TEXT_HIGHLIGHT; - break; - case XIMPrimary: - highlight = InputMethodHighlight.UNSELECTED_CONVERTED_TEXT_HIGHLIGHT; - break; - case XIMSecondary: - highlight = InputMethodHighlight.SELECTED_CONVERTED_TEXT_HIGHLIGHT; - break; - case XIMTertiary: - highlight = InputMethodHighlight.SELECTED_RAW_TEXT_HIGHLIGHT; - break; - default: - highlight = InputMethodHighlight.SELECTED_RAW_TEXT_HIGHLIGHT; - break; - } - return highlight; - } - - // initial capacity size for string buffer, etc. - private static final int INITIAL_SIZE = 64; - - /** - * IntBuffer is an inner class that manipulates an int array and - * provides UNIX file io stream-like programming interfaces to - * access it. (An alternative would be to use ArrayList which may - * be too expensive for the work.) - */ - private final class IntBuffer { - private int[] intArray; - private int size; - private int index; - - IntBuffer(int initialCapacity) { - intArray = new int[initialCapacity]; - size = 0; - index = 0; - } - - void insert(int offset, int[] values) { - int newSize = size + values.length; - if (intArray.length < newSize) { - int[] newIntArray = new int[newSize * 2]; - System.arraycopy(intArray, 0, newIntArray, 0, size); - intArray = newIntArray; - } - System.arraycopy(intArray, offset, intArray, offset+values.length, - size - offset); - System.arraycopy(values, 0, intArray, offset, values.length); - size += values.length; - if (index > offset) - index = offset; - } - - void remove(int offset, int length) { - if (offset + length != size) - System.arraycopy(intArray, offset+length, intArray, offset, - size - offset - length); - size -= length; - if (index > offset) - index = offset; - } - - void replace(int offset, int[] values) { - System.arraycopy(values, 0, intArray, offset, values.length); - } - - void removeAll() { - size = 0; - index = 0; - } - - void rewind() { - index = 0; - } - - int getNext() { - if (index == size) - return -1; - return intArray[index++]; - } - - void unget() { - if (index != 0) - index--; - } - - int getOffset() { - return index; - } - - public String toString() { - StringBuffer s = new StringBuffer(); - for (int i = 0; i < size;) { - s.append(intArray[i++]); - if (i < size) - s.append(","); - } - return s.toString(); - } - } - - /* - * Native methods - */ - private native String resetXIC(); - private native void disposeXIC(); - private native boolean setCompositionEnabledNative(boolean enable); - private native boolean isCompositionEnabledNative(); - private native void turnoffStatusWindow(); } diff -r b3a654c6fbcc -r 668463f93ec0 src/java.desktop/unix/classes/sun/awt/X11InputMethodBase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.desktop/unix/classes/sun/awt/X11InputMethodBase.java Wed May 30 08:19:59 2018 +0200 @@ -0,0 +1,849 @@ +/* + * 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. + */ + +package sun.awt; + +import java.awt.AWTEvent; +import java.awt.AWTException; +import java.awt.Component; +import java.awt.Container; +import java.awt.EventQueue; +import java.awt.Window; +import java.awt.event.InputMethodEvent; +import java.awt.font.TextAttribute; +import java.awt.font.TextHitInfo; +import java.awt.im.InputMethodHighlight; +import java.awt.im.spi.InputMethodContext; +import java.awt.peer.ComponentPeer; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.lang.Character.Subset; +import java.lang.ref.WeakReference; +import java.text.AttributedCharacterIterator; +import java.text.AttributedString; +import java.util.Collections; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import java.util.StringTokenizer; +import java.util.regex.Pattern; + +import sun.awt.im.InputMethodAdapter; +import sun.util.logging.PlatformLogger; + +/** + * Input Method Adapter for XIM + * + * @author JavaSoft International + */ +public abstract class X11InputMethodBase extends InputMethodAdapter { + protected static final PlatformLogger log = PlatformLogger.getLogger("sun.awt.X11InputMethod"); + /* + * The following XIM* values must be the same as those defined in + * Xlib.h + */ + private static final int XIMReverse = (1<<0); + private static final int XIMUnderline = (1<<1); + private static final int XIMHighlight = (1<<2); + private static final int XIMPrimary = (1<<5); + private static final int XIMSecondary = (1<<6); + private static final int XIMTertiary = (1<<7); + + /* + * visible position values + */ + protected static final int XIMVisibleToForward = (1<<8); + protected static final int XIMVisibleToBackward = (1<<9); + protected static final int XIMVisibleCenter = (1<<10); + protected static final int XIMVisibleMask = + (XIMVisibleToForward | XIMVisibleToBackward | XIMVisibleCenter); + + private Locale locale; + private static boolean isXIMOpened = false; + protected Container clientComponentWindow = null; + protected Component awtFocussedComponent = null; + protected Component lastXICFocussedComponent = null; + protected boolean isLastXICActive = false; + protected boolean isLastTemporary = false; + protected boolean isActive = false; + private static Map[] highlightStyles; + protected boolean disposed = false; + + //reset the XIC if necessary + protected boolean needResetXIC = false; + private WeakReference needResetXICClient = new WeakReference<>(null); + + // The use of compositionEnableSupported is to reduce unnecessary + // native calls if set/isCompositionEnabled + // throws UnsupportedOperationException. + // It is set to false if that exception is thrown first time + // either of the two methods are called. + protected boolean compositionEnableSupported = true; + // The savedCompositionState indicates the composition mode when + // endComposition or setCompositionEnabled is called. It doesn't always + // reflect the actual composition state because it doesn't get updated + // when the user changes the composition state through direct interaction + // with the input method. It is used to save the composition mode when + // focus is traversed across different client components sharing the + // same java input context. Also if set/isCompositionEnabled are not + // supported, it remains false. + protected boolean savedCompositionState = false; + + // variables to keep track of preedit context. + // these variables need to be accessed within AWT_LOCK/UNLOCK + protected String committedText = null; + protected StringBuffer composedText = null; + protected IntBuffer rawFeedbacks; + + // private data (X11InputMethodData structure defined in + // awt_InputMethod.c) for native methods + // this structure needs to be accessed within AWT_LOCK/UNLOCK + protected transient long pData = 0; // accessed by native + + // Initialize highlight mapping table + static { + @SuppressWarnings({"unchecked", "rawtypes"}) + Map styles[] = new Map[4]; + HashMap map; + + // UNSELECTED_RAW_TEXT_HIGHLIGHT + map = new HashMap<>(1); + map.put(TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD); + styles[0] = Collections.unmodifiableMap(map); + + // SELECTED_RAW_TEXT_HIGHLIGHT + map = new HashMap<>(1); + map.put(TextAttribute.SWAP_COLORS, TextAttribute.SWAP_COLORS_ON); + styles[1] = Collections.unmodifiableMap(map); + + // UNSELECTED_CONVERTED_TEXT_HIGHLIGHT + map = new HashMap<>(1); + map.put(TextAttribute.INPUT_METHOD_UNDERLINE, + TextAttribute.UNDERLINE_LOW_ONE_PIXEL); + styles[2] = Collections.unmodifiableMap(map); + + // SELECTED_CONVERTED_TEXT_HIGHLIGHT + map = new HashMap<>(1); + map.put(TextAttribute.SWAP_COLORS, TextAttribute.SWAP_COLORS_ON); + styles[3] = Collections.unmodifiableMap(map); + + highlightStyles = styles; + } + + static { + initIDs(); + } + + /** + * Constructs an X11InputMethod instance. It initializes the XIM + * environment if it's not done yet. + * + * @exception AWTException if XOpenIM() failed. + */ + public X11InputMethodBase() throws AWTException { + // supports only the locale in which the VM is started + locale = X11InputMethodDescriptor.getSupportedLocale(); + if (initXIM() == false) { + throw new AWTException("Cannot open X Input Method"); + } + } + + @SuppressWarnings("deprecation") + protected void finalize() throws Throwable { + dispose(); + super.finalize(); + } + + /** + * Invokes openIM() that invokes XOpenIM() if it's not opened yet. + * @return true if openXIM() is successful or it's already been opened. + */ + private synchronized boolean initXIM() { + if (isXIMOpened == false) + isXIMOpened = openXIM(); + return isXIMOpened; + } + + protected abstract boolean openXIM(); + + protected boolean isDisposed() { + return disposed; + } + + protected abstract void setXICFocus(ComponentPeer peer, + boolean value, boolean active); + + /** + * Does nothing - this adapter doesn't use the input method context. + * + * @see java.awt.im.spi.InputMethod#setInputMethodContext + */ + public void setInputMethodContext(InputMethodContext context) { + } + + /** + * Set locale to input. If input method doesn't support specified locale, + * false will be returned and its behavior is not changed. + * + * @param lang locale to input + * @return the true is returned when specified locale is supported. + */ + public boolean setLocale(Locale lang) { + if (lang.equals(locale)) { + return true; + } + // special compatibility rule for Japanese and Korean + if (locale.equals(Locale.JAPAN) && lang.equals(Locale.JAPANESE) || + locale.equals(Locale.KOREA) && lang.equals(Locale.KOREAN)) { + return true; + } + return false; + } + + /** + * Returns current input locale. + */ + public Locale getLocale() { + return locale; + } + + /** + * Does nothing - XIM doesn't let you specify which characters you expect. + * + * @see java.awt.im.spi.InputMethod#setCharacterSubsets + */ + public void setCharacterSubsets(Subset[] subsets) { + } + + /** + * Dispatch event to input method. InputContext dispatch event with this + * method. Input method set consume flag if event is consumed in + * input method. + * + * @param e event + */ + public void dispatchEvent(AWTEvent e) { + } + + protected final void resetXICifneeded(){ + /* needResetXIC is used to indicate whether to call + resetXIC on the active client. resetXIC will always be + called on the passive client when endComposition is called. + */ + if (needResetXIC && haveActiveClient() && + getClientComponent() != needResetXICClient.get()){ + resetXIC(); + + // needs to reset the last xic focussed component. + lastXICFocussedComponent = null; + isLastXICActive = false; + + needResetXICClient.clear(); + needResetXIC = false; + } + } + + /** + * Reset the composition state to the current composition state. + */ + protected abstract void resetCompositionState(); + + /** + * Query and then return the current composition state. + * @return the composition state if isCompositionEnabled call + * is successful. Otherwise, it returns false. + */ + protected boolean getCompositionState() { + boolean compositionState = false; + if (compositionEnableSupported) { + try { + compositionState = isCompositionEnabled(); + } catch (UnsupportedOperationException e) { + compositionEnableSupported = false; + } + } + return compositionState; + } + + /** + * Activate input method. + */ + public abstract void activate(); + + protected abstract boolean createXIC(); + + /** + * Deactivate input method. + */ + public abstract void deactivate(boolean isTemporary); + + /** + * Explicitly disable the native IME. Native IME is not disabled when + * deactivate is called. + */ + public void disableInputMethod() { + if (lastXICFocussedComponent != null) { + setXICFocus(getPeer(lastXICFocussedComponent), false, isLastXICActive); + lastXICFocussedComponent = null; + isLastXICActive = false; + + resetXIC(); + needResetXICClient.clear(); + needResetXIC = false; + } + } + + // implements java.awt.im.spi.InputMethod.hideWindows + public abstract void hideWindows(); + + /** + * @see java.awt.Toolkit#mapInputMethodHighlight + */ + public static Map mapInputMethodHighlight(InputMethodHighlight highlight) { + int index; + int state = highlight.getState(); + if (state == InputMethodHighlight.RAW_TEXT) { + index = 0; + } else if (state == InputMethodHighlight.CONVERTED_TEXT) { + index = 2; + } else { + return null; + } + if (highlight.isSelected()) { + index += 1; + } + return highlightStyles[index]; + } + + /** + * @see sun.awt.im.InputMethodAdapter#setAWTFocussedComponent + */ + protected void setAWTFocussedComponent(Component component) { + if (component == null) { + return; + } + if (isActive) { + // deactivate/activate are being suppressed during a focus change - + // this may happen when an input method window is made visible + boolean ac = haveActiveClient(); + setXICFocus(getPeer(awtFocussedComponent), false, ac); + setXICFocus(getPeer(component), true, ac); + } + awtFocussedComponent = component; + } + + /** + * @see sun.awt.im.InputMethodAdapter#stopListening + */ + protected void stopListening() { + // It is desirable to disable XIM by calling XSetICValues with + // XNPreeditState == XIMPreeditDisable. But Solaris 2.6 and + // Solaris 7 do not implement this correctly without a patch, + // so just call resetXIC here. Prior endComposition call commits + // the existing composed text. + endComposition(); + // disable the native input method so that the other input + // method could get the input focus. + disableInputMethod(); + if (needResetXIC) { + resetXIC(); + needResetXICClient.clear(); + needResetXIC = false; + } + } + + /** + * Returns the Window instance in which the client component is + * contained. If not found, null is returned. (IS THIS POSSIBLE?) + */ + // NOTE: This method may be called by privileged threads. + // DO NOT INVOKE CLIENT CODE ON THIS THREAD! + protected Window getClientComponentWindow() { + Component client = getClientComponent(); + Container container; + + if (client instanceof Container) { + container = (Container) client; + } else { + container = getParent(client); + } + + while (container != null && !(container instanceof java.awt.Window)) { + container = getParent(container); + } + return (Window) container; + } + + protected abstract Container getParent(Component client); + + /** + * Returns peer of the given client component. If the given client component + * doesn't have peer, peer of the native container of the client is returned. + */ + protected abstract ComponentPeer getPeer(Component client); + + /** + * Used to protect preedit data + */ + protected abstract void awtLock(); + protected abstract void awtUnlock(); + + /** + * Creates an input method event from the arguments given + * and posts it on the AWT event queue. For arguments, + * see InputMethodEvent. Called by input method. + * + * @see java.awt.event.InputMethodEvent#InputMethodEvent + */ + protected void postInputMethodEvent(int id, + AttributedCharacterIterator text, + int committedCharacterCount, + TextHitInfo caret, + TextHitInfo visiblePosition, + long when) { + Component source = getClientComponent(); + if (source != null) { + InputMethodEvent event = new InputMethodEvent(source, + id, when, text, committedCharacterCount, caret, visiblePosition); + SunToolkit.postEvent(SunToolkit.targetToAppContext(source), (AWTEvent)event); + } + } + + private void postInputMethodEvent(int id, + AttributedCharacterIterator text, + int committedCharacterCount, + TextHitInfo caret, + TextHitInfo visiblePosition) { + postInputMethodEvent(id, text, committedCharacterCount, + caret, visiblePosition, EventQueue.getMostRecentEventTime()); + } + + /** + * Dispatches committed text from XIM to the awt event queue. This + * method is invoked from the event handler in canvas.c in the + * AWT Toolkit thread context and thus inside the AWT Lock. + * @param str committed text + * @param when when + */ + // NOTE: This method may be called by privileged threads. + // This functionality is implemented in a package-private method + // to insure that it cannot be overridden by client subclasses. + // DO NOT INVOKE CLIENT CODE ON THIS THREAD! + void dispatchCommittedText(String str, long when) { + if (str == null) + return; + + if (composedText == null) { + AttributedString attrstr = new AttributedString(str); + postInputMethodEvent(InputMethodEvent.INPUT_METHOD_TEXT_CHANGED, + attrstr.getIterator(), + str.length(), + null, + null, + when); + } else { + // if there is composed text, wait until the preedit + // callback is invoked. + committedText = str; + } + } + + private void dispatchCommittedText(String str) { + dispatchCommittedText(str, EventQueue.getMostRecentEventTime()); + } + + /** + * Updates composed text with XIM preedit information and + * posts composed text to the awt event queue. The args of + * this method correspond to the XIM preedit callback + * information. The XIM highlight attributes are translated via + * fixed mapping (i.e., independent from any underlying input + * method engine). This method is invoked in the AWT Toolkit + * (X event loop) thread context and thus inside the AWT Lock. + */ + // NOTE: This method may be called by privileged threads. + // This functionality is implemented in a package-private method + // to insure that it cannot be overridden by client subclasses. + // DO NOT INVOKE CLIENT CODE ON THIS THREAD! + abstract void dispatchComposedText(String chgText, + int chgStyles[], + int chgOffset, + int chgLength, + int caretPosition, + long when); + + /** + * Flushes composed and committed text held in this context. + * This method is invoked in the AWT Toolkit (X event loop) thread context + * and thus inside the AWT Lock. + */ + // NOTE: This method may be called by privileged threads. + // This functionality is implemented in a package-private method + // to insure that it cannot be overridden by client subclasses. + // DO NOT INVOKE CLIENT CODE ON THIS THREAD! + void flushText() { + String flush = (committedText != null ? committedText : ""); + if (composedText != null) { + flush += composedText.toString(); + } + + if (!flush.equals("")) { + AttributedString attrstr = new AttributedString(flush); + postInputMethodEvent(InputMethodEvent.INPUT_METHOD_TEXT_CHANGED, + attrstr.getIterator(), + flush.length(), + null, + null, + EventQueue.getMostRecentEventTime()); + composedText = null; + committedText = null; + } + } + + /* + * Subclasses should override disposeImpl() instead of dispose(). Client + * code should always invoke dispose(), never disposeImpl(). + */ + protected abstract void disposeImpl(); + + /** + * Frees all X Window resources associated with this object. + * + * @see java.awt.im.spi.InputMethod#dispose + */ + public final void dispose() { + boolean call_disposeImpl = false; + + if (!disposed) { + synchronized (this) { + if (!disposed) { + disposed = call_disposeImpl = true; + } + } + } + + if (call_disposeImpl) { + disposeImpl(); + } + } + + /** + * Returns null. + * + * @see java.awt.im.spi.InputMethod#getControlObject + */ + public Object getControlObject() { + return null; + } + + /** + * @see java.awt.im.spi.InputMethod#removeNotify + */ + public synchronized void removeNotify() { + dispose(); + } + + /** + * @see java.awt.im.spi.InputMethod#setCompositionEnabled(boolean) + */ + public abstract void setCompositionEnabled(boolean enable); + + /** + * @see java.awt.im.spi.InputMethod#isCompositionEnabled + */ + public boolean isCompositionEnabled() { + /* isCompositionEnabledNative may throw UnsupportedOperationException. + Don't try to catch it since this method may be called by clients. + Use package private method 'getCompositionState' if you want the + exception to be caught. + */ + return isCompositionEnabledNative(); + } + + /** + * Ends any input composition that may currently be going on in this + * context. Depending on the platform and possibly user preferences, + * this may commit or delete uncommitted text. Any changes to the text + * are communicated to the active component using an input method event. + * + *

+ * A text editing component may call this in a variety of situations, + * for example, when the user moves the insertion point within the text + * (but outside the composed text), or when the component's text is + * saved to a file or copied to the clipboard. + * + */ + public void endComposition() { + if (disposed) { + return; + } + + /* Before calling resetXIC, record the current composition mode + so that it can be restored later. */ + savedCompositionState = getCompositionState(); + boolean active = haveActiveClient(); + if (active && composedText == null && committedText == null){ + needResetXIC = true; + needResetXICClient = new WeakReference<>(getClientComponent()); + return; + } + + String text = resetXIC(); + /* needResetXIC is only set to true for active client. So passive + client should not reset the flag to false. */ + if (active) { + needResetXIC = false; + } + + // Remove any existing composed text by posting an InputMethodEvent + // with null composed text. It would be desirable to wait for a + // dispatchComposedText call from X input method engine, but some + // input method does not conform to the XIM specification and does + // not call the preedit callback to erase preedit text on calling + // XmbResetIC. To work around this problem, do it here by ourselves. + awtLock(); + try { + composedText = null; + postInputMethodEvent(InputMethodEvent.INPUT_METHOD_TEXT_CHANGED, + null, + 0, + null, + null); + + if (text != null && text.length() > 0) { + dispatchCommittedText(text); + } + } finally { + // Put awtUnlock into finally block in case an exception is thrown. + awtUnlock(); + } + + // Restore the preedit state if it was enabled + if (savedCompositionState) { + resetCompositionState(); + } + } + + /** + * Returns a string with information about the current input method server, or null. + * On both Linux & SunOS, the value of environment variable XMODIFIERS is + * returned if set. Otherwise, on SunOS, $HOME/.dtprofile will be parsed + * to find out the language service engine (atok or wnn) since there is + * no API in Xlib which returns the information of native + * IM server or language service and we want to try our best to return as much + * information as possible. + * + * Note: This method could return null on Linux if XMODIFIERS is not set properly or + * if any IOException is thrown. + * See man page of XSetLocaleModifiers(3X11) for the usgae of XMODIFIERS, + * atok12setup(1) and wnn6setup(1) for the information written to + * $HOME/.dtprofile when you run these two commands. + * + */ + public String getNativeInputMethodInfo() { + String xmodifiers = System.getenv("XMODIFIERS"); + String imInfo = null; + + // If XMODIFIERS is set, return the value + if (xmodifiers != null) { + int imIndex = xmodifiers.indexOf("@im="); + if (imIndex != -1) { + imInfo = xmodifiers.substring(imIndex + 4); + } + } else if (System.getProperty("os.name").startsWith("SunOS")) { + File dtprofile = new File(System.getProperty("user.home") + + "/.dtprofile"); + String languageEngineInfo = null; + try { + BufferedReader br = new BufferedReader(new FileReader(dtprofile)); + String line = null; + + while ( languageEngineInfo == null && (line = br.readLine()) != null) { + if (line.contains("atok") || line.contains("wnn")) { + StringTokenizer tokens = new StringTokenizer(line); + while (tokens.hasMoreTokens()) { + String token = tokens.nextToken(); + if (Pattern.matches("atok.*setup", token) || + Pattern.matches("wnn.*setup", token)){ + languageEngineInfo = token.substring(0, token.indexOf("setup")); + break; + } + } + } + } + + br.close(); + } catch(IOException ioex) { + // Since this method is provided for internal testing only, + // we dump the stack trace for the ease of debugging. + ioex.printStackTrace(); + } + + imInfo = "htt " + languageEngineInfo; + } + + return imInfo; + } + + + /** + * Performs mapping from an XIM visible feedback value to Java IM highlight. + * @return Java input method highlight + */ + protected InputMethodHighlight convertVisualFeedbackToHighlight(int feedback) { + InputMethodHighlight highlight; + + switch (feedback) { + case XIMUnderline: + highlight = InputMethodHighlight.UNSELECTED_CONVERTED_TEXT_HIGHLIGHT; + break; + case XIMReverse: + highlight = InputMethodHighlight.SELECTED_CONVERTED_TEXT_HIGHLIGHT; + break; + case XIMHighlight: + highlight = InputMethodHighlight.SELECTED_RAW_TEXT_HIGHLIGHT; + break; + case 0: //None of the values are set by Wnn + case XIMPrimary: + highlight = InputMethodHighlight.UNSELECTED_CONVERTED_TEXT_HIGHLIGHT; + break; + case XIMSecondary: + highlight = InputMethodHighlight.SELECTED_CONVERTED_TEXT_HIGHLIGHT; + break; + case XIMTertiary: + highlight = InputMethodHighlight.SELECTED_RAW_TEXT_HIGHLIGHT; + break; + default: + highlight = InputMethodHighlight.SELECTED_RAW_TEXT_HIGHLIGHT; + break; + } + return highlight; + } + + // initial capacity size for string buffer, etc. + protected static final int INITIAL_SIZE = 64; + + /** + * IntBuffer is an inner class that manipulates an int array and + * provides UNIX file io stream-like programming interfaces to + * access it. (An alternative would be to use ArrayList which may + * be too expensive for the work.) + */ + protected final class IntBuffer { + private int[] intArray; + private int size; + private int index; + + IntBuffer(int initialCapacity) { + intArray = new int[initialCapacity]; + size = 0; + index = 0; + } + + void insert(int offset, int[] values) { + int newSize = size + values.length; + if (intArray.length < newSize) { + int[] newIntArray = new int[newSize * 2]; + System.arraycopy(intArray, 0, newIntArray, 0, size); + intArray = newIntArray; + } + System.arraycopy(intArray, offset, intArray, offset+values.length, + size - offset); + System.arraycopy(values, 0, intArray, offset, values.length); + size += values.length; + if (index > offset) + index = offset; + } + + void remove(int offset, int length) { + if (offset + length != size) + System.arraycopy(intArray, offset+length, intArray, offset, + size - offset - length); + size -= length; + if (index > offset) + index = offset; + } + + void replace(int offset, int[] values) { + System.arraycopy(values, 0, intArray, offset, values.length); + } + + void removeAll() { + size = 0; + index = 0; + } + + void rewind() { + index = 0; + } + + int getNext() { + if (index == size) + return -1; + return intArray[index++]; + } + + void unget() { + if (index != 0) + index--; + } + + int getOffset() { + return index; + } + + public String toString() { + StringBuffer s = new StringBuffer(); + for (int i = 0; i < size;) { + s.append(intArray[i++]); + if (i < size) + s.append(","); + } + return s.toString(); + } + } + + /* + * Native methods + */ + + /** + * Initialize JNI field and method IDs for fields that may be + * accessed from C. + */ + private static native void initIDs(); + + protected native void turnoffStatusWindow(); + + protected native void disposeXIC(); + + private native String resetXIC(); + + protected native boolean setCompositionEnabledNative(boolean enable); + + private native boolean isCompositionEnabledNative(); +} diff -r b3a654c6fbcc -r 668463f93ec0 src/java.desktop/unix/native/libawt_xawt/awt/awt_InputMethod.c --- a/src/java.desktop/unix/native/libawt_xawt/awt/awt_InputMethod.c Tue May 29 11:22:21 2018 -0700 +++ b/src/java.desktop/unix/native/libawt_xawt/awt/awt_InputMethod.c Wed May 30 08:19:59 2018 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -27,18 +27,18 @@ #error This file should not be included in headless library #endif -#include -#include -#include -#include -#include - #include "awt.h" #include "awt_p.h" -#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) @@ -536,10 +536,10 @@ 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); - */ + /* + 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 *)pX11IMData->lookup_buf); if (javastr != NULL) { JNU_CallMethodByName(env, NULL, @@ -552,10 +552,10 @@ break; case XLookupKeySym: - /* - printf("lookupString: status=XLookupKeySym, type=%d, state=%x, keycode=%x, keysym=%x\n", - event->type, event->state, event->keycode, keysym); - */ + /* + 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) { @@ -565,10 +565,10 @@ break; case XLookupNone: - /* - printf("lookupString: status=XLookupNone, type=%d, state=%x, keycode=%x, keysym=%x\n", - event->type, event->state, event->keycode, keysym); - */ + /* + printf("lookupString: status=XLookupNone, type=%d, state=%x, keycode=%x, keysym=%x\n", + event->type, event->state, event->keycode, keysym); + */ break; } @@ -576,8 +576,7 @@ } #if defined(__linux__) || defined(MACOSX) -static StatusWindow *createStatusWindow( - Window parent) { +static StatusWindow *createStatusWindow(Window parent) { StatusWindow *statusWindow; XSetWindowAttributes attrib; unsigned long attribmask; @@ -600,7 +599,7 @@ AwtGraphicsConfigDataPtr adata; extern int awt_numScreens; /*hardcode the size right now, should get the size base on font*/ - int width=80, height=22; + int width=80, height=22; Window rootWindow; Window *ignoreWindowPtr; unsigned int ignoreUnit; @@ -652,10 +651,10 @@ if (xx < 0 ){ xx = 0; } - if (xx + width > xxwa.width){ + if (xx + width > xxwa.width) { xx = xxwa.width - width; } - if (yy + height > xxwa.height){ + if (yy + height > xxwa.height) { yy = xxwa.height - height; } @@ -724,7 +723,7 @@ return; } - if (ON == False){ + if (ON == False) { XUnmapWindow(dpy, statusWindow->w); statusWindow->on = False; return; @@ -732,7 +731,7 @@ parent = JNU_CallMethodByName(GetJNIEnv(), NULL, pX11IMData->x11inputmethod, "getCurrentParentWindow", "()J").j; - if (statusWindow->parent != parent){ + if (statusWindow->parent != parent) { statusWindow->parent = parent; } XGetWindowAttributes(dpy, parent, &xwa); @@ -741,21 +740,22 @@ xwa.x, xwa.y, &x, &y, &child); - if (statusWindow->x != x - || statusWindow->y != y - || statusWindow->height != xwa.height){ + if (statusWindow->x != x || + statusWindow->y != y || + statusWindow->height != xwa.height) + { statusWindow->x = x; statusWindow->y = y; statusWindow->height = xwa.height; x = statusWindow->x - statusWindow->off_x; y = statusWindow->y + statusWindow->height - statusWindow->off_y; - if (x < 0 ){ + if (x < 0 ) { x = 0; } - if (x + statusWindow->statusW > statusWindow->rootW){ + if (x + statusWindow->statusW > statusWindow->rootW) { x = statusWindow->rootW - statusWindow->statusW; } - if (y + statusWindow->statusH > statusWindow->rootH){ + if (y + statusWindow->statusH > statusWindow->rootH) { y = statusWindow->rootH - statusWindow->statusH; } XMoveWindow(dpy, statusWindow->w, x, y); @@ -790,61 +790,19 @@ XDrawLine(dpy, win, dimgc, 2, height-3, width-bwidth-1, height-3); XDrawLine(dpy, win, dimgc, 2, 2, width-bwidth-2, 2); XDrawLine(dpy, win, dimgc, width-bwidth, 2, width-bwidth, height-3); - if (statusWindow->fontset){ + if (statusWindow->fontset) { XmbDrawString(dpy, win, statusWindow->fontset, fggc, bwidth + 2, height - bwidth - 4, statusWindow->status, strlen(statusWindow->status)); - } - else{ + } 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]")); } } -void statusWindowEventHandler(XEvent event){ - JNIEnv *env = GetJNIEnv(); - X11InputMethodData *pX11IMData = NULL; - StatusWindow *statusWindow; - - if (!isX11InputMethodGRefInList(currentX11InputMethodInstance)) { - currentX11InputMethodInstance = NULL; - return; - } - - if (NULL == currentX11InputMethodInstance - || NULL == (pX11IMData = getX11InputMethodData(env, currentX11InputMethodInstance)) - || NULL == (statusWindow = pX11IMData->statusWindow) - || statusWindow->w != event.xany.window){ - return; - } - - switch (event.type){ - case Expose: - paintStatusWindow(statusWindow); - break; - case MapNotify: - case ConfigureNotify: - { - /*need to reset the stackMode...*/ - XWindowChanges xwc; - int value_make = CWStackMode; - xwc.stack_mode = TopIf; - XConfigureWindow(dpy, statusWindow->w, value_make, &xwc); - } - break; - /* - case UnmapNotify: - case VisibilityNotify: - break; - */ - default: - break; - } -} - -static void adjustStatusWindow(Window shell){ +static void adjustStatusWindow(Window shell) { JNIEnv *env = GetJNIEnv(); X11InputMethodData *pX11IMData = NULL; StatusWindow *statusWindow; @@ -853,9 +811,11 @@ || !isX11InputMethodGRefInList(currentX11InputMethodInstance) || NULL == (pX11IMData = getX11InputMethodData(env,currentX11InputMethodInstance)) || NULL == (statusWindow = pX11IMData->statusWindow) - || !statusWindow->on) { + || !statusWindow->on) + { return; } + { XWindowAttributes xwa; int x, y; @@ -875,7 +835,7 @@ x = statusWindow->x - statusWindow->off_x; y = statusWindow->y + statusWindow->height - statusWindow->off_y; - if (x < 0 ){ + if (x < 0 ) { x = 0; } if (x + statusWindow->statusW > statusWindow->rootW){ @@ -889,6 +849,7 @@ } } #endif /* __linux__ || MACOSX */ + /* * Creates two XICs, one for active clients and the other for passive * clients. All information on those XICs are stored in the @@ -931,9 +892,9 @@ return FALSE ; } -#if defined(__linux__) || defined(MACOSX) on_the_spot_styles |= XIMStatusNothing; +#if defined(__linux__) || defined(MACOSX) /*kinput does not support XIMPreeditCallbacks and XIMStatusArea at the same time, so use StatusCallback to draw the status ourself @@ -944,8 +905,6 @@ break; } } -#else /*! __linux__ && !MACOSX */ - on_the_spot_styles |= XIMStatusNothing; #endif /* __linux__ || MACOSX */ for (i = 0; i < im_styles->count_styles; i++) { @@ -1023,12 +982,12 @@ XFree((void *)preedit); } #else /* !__linux__ && !MACOSX */ - pX11IMData->ic_active = XCreateIC(X11im, - XNClientWindow, w, - XNFocusWindow, w, - XNInputStyle, active_styles, - XNPreeditAttributes, preedit, - NULL); + pX11IMData->ic_active = XCreateIC(X11im, + XNClientWindow, w, + XNFocusWindow, w, + XNInputStyle, active_styles, + XNPreeditAttributes, preedit, + NULL); XFree((void *)preedit); #endif /* __linux__ || MACOSX */ } else { @@ -1075,14 +1034,14 @@ PreeditStartCallback(XIC ic, XPointer client_data, XPointer call_data) { /*ARGSUSED*/ - /* printf("Native: PreeditCaretCallback\n"); */ + /* printf("Native: PreeditStartCallback\n"); */ } static void PreeditDoneCallback(XIC ic, XPointer client_data, XPointer call_data) { /*ARGSUSED*/ - /* printf("Native: StatusStartCallback\n"); */ + /* printf("Native: PreeditDoneCallback\n"); */ } /* @@ -1187,7 +1146,6 @@ { /*ARGSUSED*/ /* printf("Native: PreeditCaretCallback\n"); */ - } #if defined(__linux__) || defined(MACOSX) @@ -1196,7 +1154,6 @@ { /*ARGSUSED*/ /*printf("StatusStartCallback:\n"); */ - } static void @@ -1204,7 +1161,6 @@ { /*ARGSUSED*/ /*printf("StatusDoneCallback:\n"); */ - } static void @@ -1230,25 +1186,23 @@ || NULL == (statusWindow = pX11IMData->statusWindow)){ goto finally; } - currentX11InputMethodInstance = (jobject)client_data; + currentX11InputMethodInstance = (jobject)client_data; - if (status_draw->type == XIMTextType){ + if (status_draw->type == XIMTextType) { XIMText *text = (status_draw->data).text; - if (text != NULL){ - if (text->string.multi_byte != NULL) { - strncpy(statusWindow->status, text->string.multi_byte, MAX_STATUS_LEN); - statusWindow->status[MAX_STATUS_LEN - 1] = '\0'; - } - else { - char *mbstr = wcstombsdmp(text->string.wide_char, text->length); - strncpy(statusWindow->status, mbstr, MAX_STATUS_LEN); - statusWindow->status[MAX_STATUS_LEN - 1] = '\0'; - } - statusWindow->on = True; - onoffStatusWindow(pX11IMData, statusWindow->parent, True); - paintStatusWindow(statusWindow); - } - else { + if (text != NULL) { + if (text->string.multi_byte != NULL) { + strncpy(statusWindow->status, text->string.multi_byte, MAX_STATUS_LEN); + statusWindow->status[MAX_STATUS_LEN - 1] = '\0'; + } else { + char *mbstr = wcstombsdmp(text->string.wide_char, text->length); + strncpy(statusWindow->status, mbstr, MAX_STATUS_LEN); + statusWindow->status[MAX_STATUS_LEN - 1] = '\0'; + } + statusWindow->on = True; + onoffStatusWindow(pX11IMData, statusWindow->parent, True); + paintStatusWindow(statusWindow); + } else { statusWindow->on = False; /*just turnoff the status window paintStatusWindow(statusWindow); @@ -1327,26 +1281,10 @@ X11InputMethodData *pX11IMData = getX11InputMethodData(env, currentX11InputMethodInstance); } -/* - * Class: sun_awt_X11InputMethod - * 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_X11InputMethod_initIDs(JNIEnv *env, jclass cls) -{ - x11InputMethodIDs.pData = (*env)->GetFieldID(env, cls, "pData", "J"); -} - - JNIEXPORT jboolean JNICALL Java_sun_awt_X11_XInputMethod_openXIMNative(JNIEnv *env, - jobject this, - jlong display) + jobject this, + jlong display) { Bool registered; @@ -1375,8 +1313,8 @@ JNIEXPORT jboolean JNICALL Java_sun_awt_X11_XInputMethod_createXICNative(JNIEnv *env, - jobject this, - jlong window) + jobject this, + jlong window) { X11InputMethodData *pX11IMData; jobject globalRef; @@ -1423,10 +1361,10 @@ JNIEXPORT void JNICALL Java_sun_awt_X11_XInputMethod_setXICFocusNative(JNIEnv *env, - jobject this, - jlong w, - jboolean req, - jboolean active) + jobject this, + jlong w, + jboolean req, + jboolean active) { X11InputMethodData *pX11IMData; AWT_LOCK(); @@ -1471,9 +1409,27 @@ AWT_UNLOCK(); } -JNIEXPORT void JNICALL -Java_sun_awt_X11InputMethod_turnoffStatusWindow(JNIEnv *env, - jobject this) +/* + * 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) { #if defined(__linux__) || defined(MACOSX) X11InputMethodData *pX11IMData; @@ -1495,9 +1451,13 @@ #endif } -JNIEXPORT void JNICALL -Java_sun_awt_X11InputMethod_disposeXIC(JNIEnv *env, - jobject this) +/* + * Class: sun_awt_X11InputMethodBase + * Method: disposeXIC + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_sun_awt_X11InputMethodBase_disposeXIC + (JNIEnv *env, jobject this) { X11InputMethodData *pX11IMData = NULL; @@ -1518,9 +1478,13 @@ AWT_UNLOCK(); } -JNIEXPORT jstring JNICALL -Java_sun_awt_X11InputMethod_resetXIC(JNIEnv *env, - jobject this) +/* + * 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; @@ -1564,9 +1528,9 @@ } /* - * Class: sun_awt_X11InputMethod + * Class: sun_awt_X11InputMethodBase * Method: setCompositionEnabledNative - * Signature: (ZJ)V + * Signature: (Z)Z * * This method tries to set the XNPreeditState attribute associated with the current * XIC to the passed in 'enable' state. @@ -1575,9 +1539,8 @@ * '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_X11InputMethod_setCompositionEnabledNative +JNIEXPORT jboolean JNICALL Java_sun_awt_X11InputMethodBase_setCompositionEnabledNative (JNIEnv *env, jobject this, jboolean enable) { X11InputMethodData *pX11IMData; @@ -1603,18 +1566,17 @@ } /* - * Class: sun_awt_X11InputMethod + * Class: sun_awt_X11InputMethodBase * Method: isCompositionEnabledNative - * Signature: (J)Z + * 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_X11InputMethod_isCompositionEnabledNative +JNIEXPORT jboolean JNICALL Java_sun_awt_X11InputMethodBase_isCompositionEnabledNative (JNIEnv *env, jobject this) { X11InputMethodData *pX11IMData = NULL; diff -r b3a654c6fbcc -r 668463f93ec0 src/java.desktop/unix/native/libawt_xawt/xawt/XlibWrapper.c --- a/src/java.desktop/unix/native/libawt_xawt/xawt/XlibWrapper.c Tue May 29 11:22:21 2018 -0700 +++ b/src/java.desktop/unix/native/libawt_xawt/xawt/XlibWrapper.c Wed May 30 08:19:59 2018 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 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 @@ -23,31 +23,32 @@ * questions. */ +#include "awt.h" +#include "awt_util.h" +#include "jni.h" +#include "jlong.h" +#include "Region.h" +#include "sizecalc.h" +#include "utility/rect.h" + #include "sun_awt_X11_XlibWrapper.h" -#include -#include -#include -#include -#include + +#include +#include #include #include -#include -#include +#include #include - -#include -#include -#include -#include +#include +#include +#include +#include +#include -#include -#include -#include - -#include -#include "utility/rect.h" - -#include +#if defined(AIX) +#undef X_HAVE_UTF8_STRING +extern Bool statusWindowEventHandler(XEvent event); +#endif // From XWindow.c extern KeySym keycodeToKeysym(Display *display, KeyCode keycode, int index); @@ -288,7 +289,6 @@ return ScreenCount((Display *) jlong_to_ptr(display)); } - /* * Class: XlibWrapper * Method: XCreateWindow @@ -303,7 +303,6 @@ return XCreateWindow((Display *) jlong_to_ptr(display),(Window) window, x, y, w, h, border_width, depth, wclass, (Visual *) jlong_to_ptr(visual), valuemask, (XSetWindowAttributes *) jlong_to_ptr(attributes)); - } /* @@ -320,7 +319,6 @@ (jlong_to_ptr(keysym_uppercase))); } - /* * Class: XlibWrapper * Method: XMapWindow @@ -331,7 +329,6 @@ { AWT_CHECK_HAVE_LOCK(); XMapWindow( (Display *)jlong_to_ptr(display),(Window) window); - } /* @@ -344,7 +341,6 @@ { AWT_CHECK_HAVE_LOCK(); XMapRaised( (Display *)jlong_to_ptr(display),(Window) window); - } /* @@ -355,10 +351,8 @@ JNIEXPORT void JNICALL Java_sun_awt_X11_XlibWrapper_XRaiseWindow (JNIEnv *env, jclass clazz, jlong display, jlong window) { - AWT_CHECK_HAVE_LOCK(); XRaiseWindow( (Display *)jlong_to_ptr(display),(Window) window); - } /* @@ -369,10 +363,8 @@ JNIEXPORT void JNICALL Java_sun_awt_X11_XlibWrapper_XLowerWindow (JNIEnv *env, jclass clazz, jlong display, jlong window) { - AWT_CHECK_HAVE_LOCK(); XLowerWindow( (Display *)jlong_to_ptr(display),(Window) window); - } /* @@ -383,10 +375,8 @@ JNIEXPORT void JNICALL Java_sun_awt_X11_XlibWrapper_XRestackWindows (JNIEnv *env, jclass clazz, jlong display, jlong windows, jint length) { - AWT_CHECK_HAVE_LOCK(); XRestackWindows( (Display *) jlong_to_ptr(display), (Window *) jlong_to_ptr(windows), length); - } /* @@ -411,11 +401,10 @@ JNIEXPORT void JNICALL Java_sun_awt_X11_XlibWrapper_XSetInputFocus (JNIEnv *env, jclass clazz, jlong display, jlong window) { - AWT_CHECK_HAVE_LOCK(); XSetInputFocus( (Display *)jlong_to_ptr(display),(Window) window, RevertToPointerRoot, CurrentTime); +} -} /* * Class: XlibWrapper * Method: XSetInputFocus2 @@ -424,10 +413,8 @@ JNIEXPORT void JNICALL Java_sun_awt_X11_XlibWrapper_XSetInputFocus2 (JNIEnv *env, jclass clazz, jlong display, jlong window, jlong time) { - AWT_CHECK_HAVE_LOCK(); XSetInputFocus( (Display *)jlong_to_ptr(display),(Window) window, RevertToPointerRoot, time); - } /* @@ -438,7 +425,6 @@ JNIEXPORT jlong JNICALL Java_sun_awt_X11_XlibWrapper_XGetInputFocus (JNIEnv *env, jclass clazz, jlong display) { - Window focusOwner; int revert_to; AWT_CHECK_HAVE_LOCK_RETURN(0); @@ -446,7 +432,6 @@ return focusOwner; } - /* * Class: XlibWrapper * Method: XDestroyWindow @@ -525,8 +510,6 @@ } - - JNIEXPORT void JNICALL Java_sun_awt_X11_XlibWrapper_XSelectInput (JNIEnv *env, jclass clazz, jlong display, jlong window, jlong mask) { @@ -543,6 +526,7 @@ (unsigned long)bits_to_change, (unsigned long)values_for_bits); } + JNIEXPORT void JNICALL Java_sun_awt_X11_XlibWrapper_XkbSelectEventDetails (JNIEnv *env, jclass clazz, jlong display, jlong device, jlong event_type, jlong bits_to_change, jlong values_for_bits) @@ -553,6 +537,7 @@ (unsigned long)bits_to_change, (unsigned long)values_for_bits); } + JNIEXPORT jboolean JNICALL Java_sun_awt_X11_XlibWrapper_XkbQueryExtension (JNIEnv *env, jclass clazz, jlong display, jlong opcode_rtrn, jlong event_rtrn, jlong error_rtrn, jlong major_in_out, jlong minor_in_out) @@ -567,6 +552,7 @@ (int *) jlong_to_ptr(minor_in_out)); return status ? JNI_TRUE : JNI_FALSE; } + JNIEXPORT jboolean JNICALL Java_sun_awt_X11_XlibWrapper_XkbLibraryVersion (JNIEnv *env, jclass clazz, jlong lib_major_in_out, jlong lib_minor_in_out) { @@ -587,6 +573,7 @@ (unsigned int) which, (unsigned int) device_spec); } + JNIEXPORT jlong JNICALL Java_sun_awt_X11_XlibWrapper_XkbGetUpdatedMap (JNIEnv *env, jclass clazz, jlong display, jlong which, jlong xkb) { @@ -626,8 +613,6 @@ * Method: XNextEvent * Signature: (JJ)V */ - - JNIEXPORT void JNICALL Java_sun_awt_X11_XlibWrapper_XNextEvent (JNIEnv *env, jclass clazz, jlong display, jlong ptr) { @@ -640,7 +625,6 @@ * Method: XMaskEvent * Signature: (JJJ)V */ - JNIEXPORT void JNICALL Java_sun_awt_X11_XlibWrapper_XMaskEvent (JNIEnv *env, jclass clazz, jlong display, jlong event_mask, jlong event_return) { @@ -653,7 +637,6 @@ * Method: XWindowEvent * Signature: (JJJJ)V */ - JNIEXPORT void JNICALL Java_sun_awt_X11_XlibWrapper_XWindowEvent (JNIEnv *env, jclass clazz, jlong display, jlong window, jlong event_mask, jlong event_return) { @@ -670,6 +653,11 @@ (JNIEnv *env, jclass clazz, jlong ptr, jlong window) { AWT_CHECK_HAVE_LOCK_RETURN(JNI_FALSE); +#if defined(AIX) + if (True == statusWindowEventHandler(*((XEvent *)(uintptr_t)ptr))) { + return (jboolean)True; + } +#endif return (jboolean) XFilterEvent((XEvent *) jlong_to_ptr(ptr), (Window) window); } @@ -727,19 +715,15 @@ XPeekEvent((Display *) jlong_to_ptr(display),jlong_to_ptr(ptr)); } - /* * Class: sun_awt_X11_XlibWrapper * Method: XMoveResizeWindow * Signature: (JJIIII)V */ - JNIEXPORT void JNICALL Java_sun_awt_X11_XlibWrapper_XMoveResizeWindow (JNIEnv *env, jclass clazz, jlong display, jlong window, jint x , jint y , jint width, jint height) { - AWT_CHECK_HAVE_LOCK(); XMoveResizeWindow( (Display *) jlong_to_ptr(display), (Window) window, x, y, width, height); - } /* @@ -747,10 +731,8 @@ * Method: XResizeWindow * Signature: (JJII)V */ - JNIEXPORT void JNICALL Java_sun_awt_X11_XlibWrapper_XResizeWindow -(JNIEnv *env, jclass clazz, jlong display, jlong window, jint width, jint height) -{ +(JNIEnv *env, jclass clazz, jlong display, jlong window, jint width, jint height) { AWT_CHECK_HAVE_LOCK(); XResizeWindow( (Display *) jlong_to_ptr(display),(Window) window,width,height); } @@ -760,10 +742,8 @@ * Method: XMoveWindow * Signature: (JJII)V */ - JNIEXPORT void JNICALL Java_sun_awt_X11_XlibWrapper_XMoveWindow -(JNIEnv *env, jclass clazz, jlong display, jlong window, jint width, jint height) -{ +(JNIEnv *env, jclass clazz, jlong display, jlong window, jint width, jint height) { AWT_CHECK_HAVE_LOCK(); XMoveWindow( (Display *) jlong_to_ptr(display),(Window) window,width,height); } @@ -774,13 +754,10 @@ * Method: XSetWindowBackground * Signature: (JJJ)V */ - JNIEXPORT void JNICALL Java_sun_awt_X11_XlibWrapper_XSetWindowBackground (JNIEnv *env, jclass clazz, jlong display, jlong window, jlong background_pixel) { - AWT_CHECK_HAVE_LOCK(); XSetWindowBackground((Display *) jlong_to_ptr(display),window,background_pixel); - } @@ -803,10 +780,8 @@ */ JNIEXPORT void JNICALL Java_sun_awt_X11_XlibWrapper_XSync (JNIEnv *env, jclass clazz, jlong display, jint discard) { - AWT_CHECK_HAVE_LOCK(); XSync((Display *) jlong_to_ptr(display), discard); - } JNIEXPORT jint JNICALL Java_sun_awt_X11_XlibWrapper_XTranslateCoordinates @@ -855,7 +830,6 @@ cname = ""; } - AWT_CHECK_HAVE_LOCK(); #ifdef X_HAVE_UTF8_STRING @@ -866,7 +840,6 @@ XStdICCTextStyle, &tp); #endif - if (status == Success || status > 0) { XChangeProperty((Display *)jlong_to_ptr(display), window, atom, tp.encoding, tp.format, PropModeReplace, tp.value, tp.nitems); if (tp.value != NULL) { @@ -1005,7 +978,6 @@ return XCreateFontCursor((Display *) jlong_to_ptr(display), (int) shape); } - /* * Class: sun_awt_X11_XlibWrapper * Method: XCreatePixmapCursor @@ -1019,7 +991,6 @@ (XColor *) jlong_to_ptr(fore), (XColor *) jlong_to_ptr(back), x, y); } - /* * Class: sun_awt_X11_XlibWrapper * Method: XQueryBestCursor @@ -1038,7 +1009,6 @@ else return JNI_TRUE; } - /* * Class: sun_awt_X11_XlibWrapper * Method: XFreeCursor @@ -1084,7 +1054,6 @@ (XSetWindowAttributes *) jlong_to_ptr(attributes)); } - /* * Class: sun_awt_X11_XlibWrapper * Method: XSetTransientFor @@ -1181,7 +1150,6 @@ } } - /* * Class: sun_awt_X11_XlibWrapper * Method: getScreenOfWindow @@ -1253,7 +1221,6 @@ return res; } - /* * Class: sun_awt_X11_XlibWrapper * Method: ServerVendor @@ -1265,6 +1232,7 @@ AWT_CHECK_HAVE_LOCK_RETURN(NULL); return JNU_NewStringPlatform(env, ServerVendor((Display*)jlong_to_ptr(display))); } + /* * Class: sun_awt_X11_XlibWrapper * Method: VendorRelease @@ -1276,6 +1244,7 @@ AWT_CHECK_HAVE_LOCK_RETURN(0); return VendorRelease((Display*)jlong_to_ptr(display)); } + /* * Class: sun_awt_X11_XlibWrapper * Method: IsXsunKPBehavior @@ -1309,7 +1278,6 @@ } } - JNIEXPORT jboolean JNICALL Java_sun_awt_X11_XlibWrapper_IsSunKeyboard (JNIEnv *env, jclass clazz, jlong display) { @@ -1407,7 +1375,6 @@ return (*(XErrorHandler)jlong_to_ptr(handler))((Display*) jlong_to_ptr(display), (XErrorEvent*) jlong_to_ptr(event_ptr)); } - /* * Class: sun_awt_X11_XlibWrapper * Method: PrintXErrorEvent @@ -1431,7 +1398,6 @@ } } - /* * Class: sun_awt_X11_XlibWrapper * Method: XInternAtoms @@ -1451,8 +1417,6 @@ return status; } - - /* * Class: sun_awt_X11_XlibWrapper * Method: XGetWindowAttributes @@ -1468,7 +1432,6 @@ return status; } - /* * Class: sun_awt_X11_XlibWrapper * Method: XGetGeometry @@ -1490,7 +1453,6 @@ return status; } - /* * Class: sun_awt_X11_XlibWrapper * Method: XGetWMNormalHints @@ -1546,7 +1508,6 @@ (XEvent*) jlong_to_ptr(event)); } - /* * Class: sun_awt_X11_XlibWrapper * Method: XQueryTree @@ -1564,7 +1525,6 @@ (unsigned int*) jlong_to_ptr(nchildren_return)); } - /* * Class: sun_awt_X11_XlibWrapper * Method: memcpy @@ -1576,7 +1536,6 @@ memcpy(jlong_to_ptr(dest_ptr), jlong_to_ptr(src_ptr), length); } - JNIEXPORT void JNICALL Java_sun_awt_X11_XlibWrapper_XSetMinMaxHints (JNIEnv *env, jclass clazz, jlong display, jlong window, jint x, jint y, jint width, jint height, jlong flags) { XSizeHints * hints; @@ -1595,7 +1554,6 @@ XFree(hints); } - JNIEXPORT jlong JNICALL Java_sun_awt_X11_XlibWrapper_XGetVisualInfo (JNIEnv *env, jclass clazz, jlong display, jlong vinfo_mask, jlong vinfo_template, jlong nitems_return) @@ -1626,7 +1584,6 @@ XBell((Display*)jlong_to_ptr(display), percent); } - /* * Class: sun_awt_X11_XlibWrapper * Method: XAllocColor @@ -1643,7 +1600,6 @@ else return JNI_TRUE; } - /* * Class: sun_awt_X11_XlibWrapper * Method: XCreateBitmapFromData @@ -1657,7 +1613,6 @@ (char *) jlong_to_ptr(data), width, height); } - /* * Class: sun_awt_X11_XlibWrapper * Method: XFreePixmap @@ -1772,6 +1727,7 @@ AWT_CHECK_HAVE_LOCK_RETURN(0); return XCreatePixmap((Display*)jlong_to_ptr(display), (Drawable)drawable, width, height, depth); } + JNIEXPORT jlong JNICALL Java_sun_awt_X11_XlibWrapper_XCreateImage (JNIEnv *env, jclass clazz, jlong display, jlong visual_ptr, @@ -1783,6 +1739,7 @@ depth, format, offset, (char*) jlong_to_ptr(data), width, height, bitmap_pad, bytes_per_line)); } + JNIEXPORT jlong JNICALL Java_sun_awt_X11_XlibWrapper_XCreateGC (JNIEnv *env, jclass clazz, jlong display, jlong drawable, @@ -1806,6 +1763,7 @@ img->data = NULL; XDestroyImage(img); } + JNIEXPORT void JNICALL Java_sun_awt_X11_XlibWrapper_XPutImage(JNIEnv *env, jclass clazz, jlong display, jlong drawable, jlong gc, jlong image, jint src_x, jint src_y, jint dest_x, jint dest_y, jint width, jint height) { @@ -1813,18 +1771,21 @@ XPutImage((Display*)jlong_to_ptr(display), (Drawable)drawable, (GC) jlong_to_ptr(gc), (XImage*) jlong_to_ptr(image), src_x, src_y, dest_x, dest_y, width, height); } + JNIEXPORT void JNICALL Java_sun_awt_X11_XlibWrapper_XFreeGC(JNIEnv *env, jclass clazz, jlong display, jlong gc) { AWT_CHECK_HAVE_LOCK(); XFreeGC((Display*) jlong_to_ptr(display), (GC) jlong_to_ptr(gc)); } + JNIEXPORT void JNICALL Java_sun_awt_X11_XlibWrapper_XSetWindowBackgroundPixmap(JNIEnv *env, jclass clazz, jlong display, jlong window, jlong pixmap) { AWT_CHECK_HAVE_LOCK(); XSetWindowBackgroundPixmap((Display*) jlong_to_ptr(display), (Window)window, (Pixmap)pixmap); } + JNIEXPORT void JNICALL Java_sun_awt_X11_XlibWrapper_XClearWindow(JNIEnv *env, jclass clazz, jlong display, jlong window) { @@ -1922,7 +1883,6 @@ JNIEXPORT void JNICALL Java_sun_awt_X11_XlibWrapper_XQueryKeymap (JNIEnv *env, jclass clazz, jlong display, jlong vector) { - AWT_CHECK_HAVE_LOCK(); XQueryKeymap( (Display *) jlong_to_ptr(display), (char *) jlong_to_ptr(vector)); } @@ -1961,6 +1921,7 @@ // printf("-------------------------------------^^^^\n"); return (jint)(sr.group); } + JNIEXPORT jlong JNICALL Java_sun_awt_X11_XlibWrapper_XkbKeycodeToKeysym(JNIEnv *env, jclass clazz, jlong display, jint keycode, @@ -1989,6 +1950,7 @@ AWT_CHECK_HAVE_LOCK(); XFreeModifiermap((XModifierKeymap*) jlong_to_ptr(keymap)); } + /* * Class: sun_awt_X11_XlibWrapper * Method: XRefreshKeyboardMapping @@ -2034,7 +1996,6 @@ ) ? True : False; } - JNIEXPORT jboolean JNICALL Java_sun_awt_X11_XlibWrapper_XNextSecondaryLoopEvent(JNIEnv *env, jclass clazz, jlong display, jlong ptr) { @@ -2062,7 +2023,6 @@ exitSecondaryLoop = True; AWT_NOTIFY_ALL(); } -/*******************************************************************************/ JNIEXPORT jobjectArray JNICALL Java_sun_awt_X11_XlibWrapper_XTextPropertyToStringList(JNIEnv *env, @@ -2181,7 +2141,6 @@ return ret; } - JNIEXPORT void JNICALL Java_sun_awt_X11_XlibWrapper_XPutBackEvent(JNIEnv *env, jclass clazz,