diff --git a/make/modules/java.desktop/lib/Awt2dLibraries.gmk b/make/modules/java.desktop/lib/Awt2dLibraries.gmk index 5f5b8b722961..42f2a3434808 100644 --- a/make/modules/java.desktop/lib/Awt2dLibraries.gmk +++ b/make/modules/java.desktop/lib/Awt2dLibraries.gmk @@ -204,7 +204,7 @@ $(eval $(call SetupJdkLibrary, BUILD_LIBAWT, \ LIBS_windows := kernel32.lib user32.lib gdi32.lib gdiplus.lib winspool.lib \ imm32.lib ole32.lib uuid.lib shell32.lib \ comdlg32.lib winmm.lib comctl32.lib shlwapi.lib \ - delayimp.lib jvm.lib $(WIN_JAVA_LIB) advapi32.lib dwmapi.lib $(A11Y_NVDA_ANNOUNCING_LIBS), \ + delayimp.lib jvm.lib $(WIN_JAVA_LIB) advapi32.lib dwmapi.lib $(A11Y_NVDA_ANNOUNCING_LIBS) oleacc.lib, \ VERSIONINFO_RESOURCE := $(LIBAWT_VERSIONINFO_RESOURCE), \ EXTRA_RCFLAGS := $(LIBAWT_RCFLAGS), \ )) diff --git a/src/java.desktop/windows/classes/sun/awt/windows/AccessibleCaretLocationNotifier.java b/src/java.desktop/windows/classes/sun/awt/windows/AccessibleCaretLocationNotifier.java new file mode 100644 index 000000000000..eaa088155f3a --- /dev/null +++ b/src/java.desktop/windows/classes/sun/awt/windows/AccessibleCaretLocationNotifier.java @@ -0,0 +1,173 @@ +/* + * Copyright 2024 JetBrains s.r.o. + * 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.windows; + +import sun.awt.AWTAccessor; +import sun.java2d.SunGraphicsEnvironment; + +import javax.swing.*; +import javax.swing.event.CaretEvent; +import javax.swing.event.CaretListener; +import javax.swing.text.JTextComponent; +import java.awt.*; +import java.awt.im.InputMethodRequests; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.lang.ref.WeakReference; + +/** + * Provides caret tracking support for assistive tools that don't work with Java Access Bridge. + * Specifically, it's targeted for the built-in Windows Magnifier. + * This class listens to caret change events of a currently focused JTextComponent and forwards them to the native code, + * which in turn sends them as Win32 IAccessible events. + *

+ * A typical high-level scenario of interaction with the magnifier: + *

    + *
  1. Magnifier sends a WM_GETOBJECT window message to get accessible content of the window.
  2. + *
  3. The message is handled in AwtComponent native class (awt_Component.cpp), + * which calls {@link #startCaretNotifier}.
  4. + *
  5. We start listening for keyboard focus change events.
  6. + *
  7. If at some point focus gets to a {@link JTextComponent}, we subscribe to its caret events.
  8. + *
  9. When caret changes, we need to move the magnifier viewport to the new caret location. + * To achieve this, we create a Win32 IAccessible object for the caret (see AccessibleCaret.cpp), + * and send an event that its location was changed (EVENT_OBJECT_LOCATIONCHANGE).
  10. + *
  11. Magnifier receives this event and sends WM_GETOBJECT message with OBJID_CARET argument + * to get the caret object and its location property. After that, it moves the viewport to the returned location. + *
  12. + *
  13. When the {@link JTextComponent} loses focus, we stop listening to caret events + * and release the IAccessible caret object.
  14. + *
+ *

+ *

+ * The feature is enabled by default + * and can be disabled by setting sun.awt.windows.use.native.caret.accessibility.events property to false. + *

+ */ +@SuppressWarnings("unused") // Used from the native through JNI. +class AccessibleCaretLocationNotifier implements PropertyChangeListener, CaretListener { + private static AccessibleCaretLocationNotifier caretNotifier; + private static final boolean nativeCaretEventsEnabled = + Boolean.parseBoolean(System.getProperty("sun.awt.windows.use.native.caret.accessibility.events", "true")); + + private WeakReference currentFocusedComponent; + private long currentHwnd; + + @SuppressWarnings("unused") // Called from the native through JNI. + public static void startCaretNotifier(long hwnd) { + if (nativeCaretEventsEnabled && caretNotifier == null) { + SwingUtilities.invokeLater(() -> { + caretNotifier = new AccessibleCaretLocationNotifier(hwnd); + KeyboardFocusManager cfm = KeyboardFocusManager.getCurrentKeyboardFocusManager(); + cfm.addPropertyChangeListener("focusOwner", caretNotifier); + if (cfm.getFocusOwner() instanceof JTextComponent textComponent) { + caretNotifier.propertyChange(new PropertyChangeEvent(caretNotifier, "focusOwner", null, textComponent)); + } + }); + } + } + + public AccessibleCaretLocationNotifier(long hwnd) { + currentHwnd = hwnd; + } + + private static native void updateNativeCaretLocation(long hwnd, int x, int y, int width, int height); + private static native void releaseNativeCaret(long hwnd); + private static native void notifyFocusedWindowChanged(long hwnd); + + @Override + public void propertyChange(PropertyChangeEvent e) { + Window w = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusedWindow(); + if (w != null) { + WWindowPeer wp = AWTAccessor.getComponentAccessor().getPeer(w); + if (wp != null) { + long hwnd = wp.getHWnd(); + if (currentHwnd != hwnd) { + currentHwnd = hwnd; + notifyFocusedWindowChanged(currentHwnd); + } + } + } + + Object newFocusedComponent = e.getNewValue(); + if (currentFocusedComponent != null) { + JTextComponent currentComponentStrong = currentFocusedComponent.get(); + if (currentComponentStrong != null && newFocusedComponent != currentComponentStrong) { + currentComponentStrong.removeCaretListener(this); + currentFocusedComponent.clear(); + currentFocusedComponent = null; + releaseNativeCaret(currentHwnd); + } + } + + if (newFocusedComponent instanceof JTextComponent textComponent) { + currentFocusedComponent = new WeakReference<>(textComponent); + textComponent.addCaretListener(this); + // Trigger caret event when the text component receives focus to notify about the initial caret location. + caretUpdate(new CaretEvent(textComponent) { + // Dot and mark won't be used, so we can set any values. + @Override + public int getDot() { return 0; } + + @Override + public int getMark() { return 0; } + }); + } + } + + @Override + public void caretUpdate(CaretEvent e) { + if (!(e.getSource() instanceof JTextComponent textComponent)) { + return; + } + + SwingUtilities.invokeLater(() -> { + if (!textComponent.isShowing()) return; + InputMethodRequests imr = textComponent.getInputMethodRequests(); + if (imr == null) return; + Rectangle caretRectangle = imr.getTextLocation(null); + if (caretRectangle == null) return; + caretRectangle.width = 1; + + Container parent = textComponent.getParent(); + if (parent != null && parent.isShowing()) { + // Make sure we don't go outside of parent bounds, which can happen in the case of scrollable components. + Rectangle parentBounds = parent.getBounds(); + parentBounds.setLocation(parent.getLocationOnScreen()); + + if (!parentBounds.contains(caretRectangle)) { + caretRectangle = parentBounds.intersection(caretRectangle); + if (caretRectangle.isEmpty()) return; + } + } + + caretRectangle = SunGraphicsEnvironment.toDeviceSpaceAbs(caretRectangle); + + updateNativeCaretLocation(AccessibleCaretLocationNotifier.this.currentHwnd, + (int) caretRectangle.getX(), (int) caretRectangle.getY(), + (int) caretRectangle.getWidth(), (int) caretRectangle.getHeight()); + }); + } +} diff --git a/src/java.desktop/windows/native/libawt/windows/AccessibleCaret.cpp b/src/java.desktop/windows/native/libawt/windows/AccessibleCaret.cpp new file mode 100644 index 000000000000..f8efce6fb03b --- /dev/null +++ b/src/java.desktop/windows/native/libawt/windows/AccessibleCaret.cpp @@ -0,0 +1,270 @@ +/* +* Copyright (c) 2024, JetBrains s.r.o.. 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. + * + * 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. + */ + +#include "AccessibleCaret.h" + +/** + * This class implements Win32 IAccessible interface in a similar way to the system text caret. +*/ +AccessibleCaret::AccessibleCaret() + : m_refCount(1), m_x(0), m_y(0), m_width(0), m_height(0) { +} + +AccessibleCaret *AccessibleCaret::instance = NULL; + +bool AccessibleCaret::isCaretUsed = false; + +// IUnknown methods +IFACEMETHODIMP_(ULONG) AccessibleCaret::AddRef() { + return InterlockedIncrement(&m_refCount); +} + +IFACEMETHODIMP_(ULONG) AccessibleCaret::Release() { + ULONG count = InterlockedDecrement(&m_refCount); + if (count == 0) { + delete this; + } + return count; +} + +IFACEMETHODIMP AccessibleCaret::QueryInterface(REFIID riid, void **ppInterface) { + if (ppInterface == NULL) { + return E_POINTER; + } + + if (riid == IID_IUnknown || riid == IID_IDispatch || riid == IID_IAccessible) { + *ppInterface = static_cast(this); + AddRef(); + return S_OK; + } + + *ppInterface = NULL; + return E_NOINTERFACE; +} + +// IDispatch methods +IFACEMETHODIMP AccessibleCaret::GetTypeInfoCount(UINT *pctinfo) { + return E_NOTIMPL; +} + +IFACEMETHODIMP AccessibleCaret::GetTypeInfo(UINT itinfo, LCID lcid, ITypeInfo **pptinfo) { + return E_NOTIMPL; +} + +IFACEMETHODIMP AccessibleCaret::GetIDsOfNames(REFIID riid, OLECHAR **rgszNames, UINT cNames, LCID lcid, + DISPID *rgdispid) { + return E_NOTIMPL; +} + +IFACEMETHODIMP AccessibleCaret::Invoke(DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags, + DISPPARAMS *pdispparams, VARIANT *pvarResult, EXCEPINFO *pexcepinfo, + UINT *puArgErr) { + return E_NOTIMPL; +} + +// IAccessible methods +IFACEMETHODIMP AccessibleCaret::get_accParent(IDispatch **ppdispParent) { + if (ppdispParent == NULL) { + return E_POINTER; + } + *ppdispParent = NULL; + return S_OK; +} + +IFACEMETHODIMP AccessibleCaret::get_accChildCount(long *pcountChildren) { + if (pcountChildren == NULL) { + return E_POINTER; + } + *pcountChildren = 0; + return S_OK; +} + +IFACEMETHODIMP AccessibleCaret::get_accChild(VARIANT varChild, IDispatch **ppdispChild) { + *ppdispChild = NULL; + return S_FALSE; +} + +IFACEMETHODIMP AccessibleCaret::get_accName(VARIANT varChild, BSTR *pszName) { + if (pszName == NULL) { + return E_POINTER; + } + *pszName = SysAllocString(L"Edit"); // Same name as the system caret. + return S_OK; +} + +IFACEMETHODIMP AccessibleCaret::get_accValue(VARIANT varChild, BSTR *pszValue) { + return E_NOTIMPL; +} + +IFACEMETHODIMP AccessibleCaret::get_accDescription(VARIANT varChild, BSTR *pszDescription) { + return E_NOTIMPL; +} + +IFACEMETHODIMP AccessibleCaret::get_accRole(VARIANT varChild, VARIANT *pvarRole) { + if (pvarRole == NULL) { + return E_POINTER; + } + + pvarRole->vt = VT_I4; + pvarRole->lVal = ROLE_SYSTEM_CARET; + return S_OK; +} + +IFACEMETHODIMP AccessibleCaret::get_accState(VARIANT varChild, VARIANT *pvarState) { + if (pvarState == NULL) { + return E_POINTER; + } + + pvarState->vt = VT_I4; + pvarState->lVal = 0; // The state without any flags, corresponds to "normal". + return S_OK; +} + +IFACEMETHODIMP AccessibleCaret::get_accHelp(VARIANT varChild, BSTR *pszHelp) { + return E_NOTIMPL; +} + +IFACEMETHODIMP AccessibleCaret::get_accHelpTopic(BSTR *pszHelpFile, VARIANT varChild, long *pidTopic) { + return E_NOTIMPL; +} + +IFACEMETHODIMP AccessibleCaret::get_accKeyboardShortcut(VARIANT varChild, BSTR *pszKeyboardShortcut) { + return E_NOTIMPL; +} + +IFACEMETHODIMP AccessibleCaret::get_accFocus(VARIANT *pvarChild) { + pvarChild->vt = VT_I4; + pvarChild->lVal = CHILDID_SELF; + return S_OK; +} + +IFACEMETHODIMP AccessibleCaret::get_accSelection(VARIANT *pvarChildren) { + return E_NOTIMPL; +} + +IFACEMETHODIMP AccessibleCaret::get_accDefaultAction(VARIANT varChild, BSTR *pszDefaultAction) { + return E_NOTIMPL; +} + +IFACEMETHODIMP AccessibleCaret::accSelect(long flagsSelect, VARIANT varChild) { + return E_NOTIMPL; +} + +IFACEMETHODIMP AccessibleCaret::accLocation(long *pxLeft, long *pyTop, long *pcxWidth, long *pcyHeight, + VARIANT varChild) { + if (pxLeft == NULL || pyTop == NULL || pcxWidth == NULL || pcyHeight == NULL) { + return E_POINTER; + } + isCaretUsed = true; + + *pxLeft = m_x; + *pyTop = m_y; + *pcxWidth = m_width; + *pcyHeight = m_height; + return S_OK; +} + +IFACEMETHODIMP AccessibleCaret::accNavigate(long navDir, VARIANT varStart, VARIANT *pvarEndUpAt) { + return E_NOTIMPL; +} + +IFACEMETHODIMP AccessibleCaret::accHitTest(long xLeft, long yTop, VARIANT *pvarChild) { + return E_NOTIMPL; +} + +IFACEMETHODIMP AccessibleCaret::accDoDefaultAction(VARIANT varChild) { + return E_NOTIMPL; +} + +IFACEMETHODIMP AccessibleCaret::put_accName(VARIANT varChild, BSTR szName) { + return E_NOTIMPL; +} + +IFACEMETHODIMP AccessibleCaret::put_accValue(VARIANT varChild, BSTR szValue) { + return E_NOTIMPL; +} + +void AccessibleCaret::setLocation(long x, long y, long width, long height) { + m_x = x; + m_y = y; + m_width = width; + m_height = height; +} + + +extern "C" { +/* + * Class: sun_awt_windows_AccessibleCaretLocationNotifier + * Method: updateNativeCaretLocation + * Signature: (JIIII)V + */ +JNIEXPORT void JNICALL Java_sun_awt_windows_AccessibleCaretLocationNotifier_updateNativeCaretLocation( + JNIEnv *env, jclass jClass, + jlong jHwnd, jint x, jint y, jint width, jint height) { + HWND hwnd = reinterpret_cast(jHwnd); + if (AccessibleCaret::instance == NULL) { + AccessibleCaret::instance = new AccessibleCaret(); + // Notify with Object ID "OBJID_CARET". + // After that, an assistive tool will send a WM_GETOBJECT message with this ID, + // and we can return the caret instance. + NotifyWinEvent(EVENT_OBJECT_CREATE, hwnd, OBJID_CARET, CHILDID_SELF); + NotifyWinEvent(EVENT_OBJECT_SHOW, hwnd, OBJID_CARET, CHILDID_SELF); + } + AccessibleCaret::instance->setLocation(x, y, width, height); + NotifyWinEvent(EVENT_OBJECT_LOCATIONCHANGE, hwnd, OBJID_CARET, CHILDID_SELF); +} + +/* + * Class: sun_awt_windows_AccessibleCaretLocationNotifier + * Method: releaseNativeCaret + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_sun_awt_windows_AccessibleCaretLocationNotifier_releaseNativeCaret( + JNIEnv *env, jclass jClass, jlong jHwnd) { + if (AccessibleCaret::instance != NULL) { + HWND hwnd = reinterpret_cast(jHwnd); + AccessibleCaret::instance->Release(); + AccessibleCaret::instance = NULL; + NotifyWinEvent(EVENT_OBJECT_HIDE, hwnd, OBJID_CARET, CHILDID_SELF); + NotifyWinEvent(EVENT_OBJECT_DESTROY, hwnd, OBJID_CARET, CHILDID_SELF); + } +} + +/* + * Class: sun_awt_windows_AccessibleCaretLocationNotifier + * Method: notifyFocusedWindowChanged + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_sun_awt_windows_AccessibleCaretLocationNotifier_notifyFocusedWindowChanged( + JNIEnv *env, jclass jClass, jlong jHwnd) { + HWND hwnd = reinterpret_cast(jHwnd); + // This is a workaround to make sure the foreground window is set to the actual focused window. + // Otherwise, in some cases, e.g., when opening a popup, + // the root frame can still stay as the foreground window instead of the popup, + // and Magnifier will be focused on it instead of the popup. + // We only do it if the caret object is actually used to minimize risks. + if (AccessibleCaret::isCaretUsed && ::GetForegroundWindow() != hwnd) { + ::SetForegroundWindow(hwnd); + } +} +} /* extern "C" */ diff --git a/src/java.desktop/windows/native/libawt/windows/AccessibleCaret.h b/src/java.desktop/windows/native/libawt/windows/AccessibleCaret.h new file mode 100644 index 000000000000..e1c2b0a4c135 --- /dev/null +++ b/src/java.desktop/windows/native/libawt/windows/AccessibleCaret.h @@ -0,0 +1,80 @@ +/* +* Copyright (c) 2024, JetBrains s.r.o.. 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. + * + * 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. + */ + +#ifndef ACCESSIBLECARET_H +#define ACCESSIBLECARET_H +#include "jni.h" +#include + +class AccessibleCaret : public IAccessible { +public: + AccessibleCaret(); + + static AccessibleCaret *instance; + static bool isCaretUsed; + + // IUnknown methods. + IFACEMETHODIMP_(ULONG) AddRef(); + IFACEMETHODIMP_(ULONG) Release(); + IFACEMETHODIMP QueryInterface(REFIID riid, void **ppInterface); + + // IDispatch methods. + IFACEMETHODIMP GetTypeInfoCount(UINT *pctinfo); + IFACEMETHODIMP GetTypeInfo(UINT itinfo, LCID lcid, ITypeInfo **pptinfo); + IFACEMETHODIMP GetIDsOfNames(REFIID riid, __in_ecount(cNames) + OLECHAR **rgszNames, UINT cNames, LCID lcid, DISPID *rgdispid); + IFACEMETHODIMP Invoke(DISPID dispidMember, REFIID riid, LCID lcid, + WORD wFlags, DISPPARAMS *pdispparams, VARIANT *pvarResult, + EXCEPINFO *pexcepinfo, UINT *puArgErr); + + // IAccessible methods + IFACEMETHODIMP get_accParent(IDispatch **ppdispParent); + IFACEMETHODIMP get_accChildCount(long *pcountChildren); + IFACEMETHODIMP get_accChild(VARIANT varChild, IDispatch **ppdispChild); + IFACEMETHODIMP get_accName(VARIANT varChild, BSTR *pszName); + IFACEMETHODIMP get_accValue(VARIANT varChild, BSTR *pszValue); + IFACEMETHODIMP get_accDescription(VARIANT varChild, BSTR *pszDescription); + IFACEMETHODIMP get_accRole(VARIANT varChild, VARIANT *pvarRole); + IFACEMETHODIMP get_accState(VARIANT varChild, VARIANT *pvarState); + IFACEMETHODIMP get_accHelp(VARIANT varChild, BSTR *pszHelp); + IFACEMETHODIMP get_accHelpTopic(BSTR *pszHelpFile, VARIANT varChild, long *pidTopic); + IFACEMETHODIMP get_accKeyboardShortcut(VARIANT varChild, BSTR *pszKeyboardShortcut); + IFACEMETHODIMP get_accFocus(VARIANT *pvarChild); + IFACEMETHODIMP get_accSelection(VARIANT *pvarChildren); + IFACEMETHODIMP get_accDefaultAction(VARIANT varChild, BSTR *pszDefaultAction); + IFACEMETHODIMP accSelect(long flagsSelect, VARIANT varChild); + IFACEMETHODIMP accLocation(long *pxLeft, long *pyTop, long *pcxWidth, long *pcyHeight, VARIANT varChild); + IFACEMETHODIMP accNavigate(long navDir, VARIANT varStart, VARIANT *pvarEndUpAt); + IFACEMETHODIMP accHitTest(long xLeft, long yTop, VARIANT *pvarChild); + IFACEMETHODIMP accDoDefaultAction(VARIANT varChild); + IFACEMETHODIMP put_accName(VARIANT varChild, BSTR szName); + IFACEMETHODIMP put_accValue(VARIANT varChild, BSTR szValue); + + void setLocation(long x, long y, long width, long height); + +private: + ULONG m_refCount; + int m_x, m_y, m_width, m_height; +}; + +#endif //ACCESSIBLECARET_H diff --git a/src/java.desktop/windows/native/libawt/windows/awt_Component.cpp b/src/java.desktop/windows/native/libawt/windows/awt_Component.cpp index 4e383d91cc8c..209e3618b0e8 100644 --- a/src/java.desktop/windows/native/libawt/windows/awt_Component.cpp +++ b/src/java.desktop/windows/native/libawt/windows/awt_Component.cpp @@ -49,6 +49,7 @@ #include "Hashtable.h" #include "ComCtl32Util.h" #include "math.h" +#include "AccessibleCaret.h" #include @@ -2071,6 +2072,31 @@ LRESULT AwtComponent::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) if (::IsWindow(AwtWindow::GetModalBlocker(GetHWnd()))) { mr = mrConsume; } + break; + } + case WM_GETOBJECT: + { + // We've got a WM_GETOBJECT message which was likely sent by an assistive tool. + // Therefore, we can start generating native caret accessibility events. + DWORD objId = static_cast(static_cast(lParam)); + if (objId == OBJID_CLIENT) { + JNIEnv *env = (JNIEnv *) JNU_GetEnv(jvm, JNI_VERSION_1_2); + if (env != NULL) { + jclass cls = env->FindClass("sun/awt/windows/AccessibleCaretLocationNotifier"); + if (cls != NULL) { + jmethodID mid = env->GetStaticMethodID(cls, "startCaretNotifier", "(J)V"); + if (mid != NULL) { + env->CallStaticVoidMethod(cls, mid, reinterpret_cast(GetHWnd())); + } + } + } + } else if (objId == OBJID_CARET) { + if (AccessibleCaret::instance != NULL) { + retValue = LresultFromObject(IID_IAccessible, wParam, AccessibleCaret::instance); + mr = mrConsume; + } + } + break; } }