1 /*
   2  * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 #include "common.h"
  27 #include <UIAutomation.h>
  28 
  29 #include "GlassApplication.h"
  30 #include "ViewContainer.h"
  31 #include "GlassView.h"
  32 #include "KeyTable.h"
  33 #include "Utils.h"
  34 #include "GlassDnD.h"
  35 #include "GlassInputTextInfo.h"
  36 #include "ManipulationEvents.h"
  37 #include "BaseWnd.h"
  38 
  39 #include "com_sun_glass_events_ViewEvent.h"
  40 #include "com_sun_glass_events_KeyEvent.h"
  41 #include "com_sun_glass_events_MouseEvent.h"
  42 #include "com_sun_glass_events_DndEvent.h"
  43 #include "com_sun_glass_events_TouchEvent.h"
  44 
  45 static UINT LangToCodePage(LANGID idLang)
  46 {
  47     WCHAR strCodePage[8];
  48     // use the LANGID to create a LCID
  49     LCID idLocale = MAKELCID(idLang, SORT_DEFAULT);
  50     // get the ANSI code page associated with this locale
  51     if (::GetLocaleInfo(idLocale, LOCALE_IDEFAULTANSICODEPAGE,
  52                         strCodePage, sizeof(strCodePage) / sizeof(WCHAR)) > 0)
  53     {
  54         return _wtoi(strCodePage);
  55     } else {
  56         return ::GetACP();
  57     }
  58 }
  59 
  60 namespace {
  61 
  62 bool IsTouchEvent()
  63 {
  64     // Read this link if you wonder why we need to hard code the mask and signature:
  65     // http://msdn.microsoft.com/en-us/library/windows/desktop/ms703320(v=vs.85).aspx
  66     //"The lower 8 bits returned from GetMessageExtraInfo are variable.
  67     // Of those bits, 7 (the lower 7, masked by 0x7F) are used to represent the cursor ID,
  68     // zero for the mouse or a variable value for the pen ID.
  69     // Additionally, in Windows Vista, the eighth bit, masked by 0x80, is used to
  70     // differentiate touch input from pen input (0 = pen, 1 = touch)."
  71     UINT SIGNATURE = 0xFF515780;
  72     UINT MASK = 0xFFFFFF80;
  73 
  74     UINT v = (UINT) GetMessageExtraInfo();
  75 
  76     return ((v & MASK) == SIGNATURE);
  77 }
  78 
  79 } // namespace
  80 
  81 ViewContainer::ViewContainer() :
  82     m_view(NULL),
  83     m_bTrackingMouse(FALSE),
  84     m_manipProc(NULL),
  85     m_inertiaProc(NULL),
  86     m_manipEventSink(NULL),
  87     m_gestureSupportCls(NULL),
  88     m_lastMouseMovePosition(-1),
  89     m_mouseButtonDownCounter(0),
  90     m_deadKeyWParam(0)
  91 {
  92     m_kbLayout = ::GetKeyboardLayout(0);
  93     m_idLang = LOWORD(m_kbLayout);
  94     m_codePage = LangToCodePage(m_idLang);
  95 }
  96 
  97 jobject ViewContainer::GetView()
  98 {
  99     return GetGlassView() != NULL ? GetGlassView()->GetView() : NULL;
 100 }
 101 
 102 void ViewContainer::InitDropTarget(HWND hwnd)
 103 {
 104     if (!hwnd) {
 105         return;
 106     }
 107 
 108     m_spDropTarget = 
 109         std::auto_ptr<IDropTarget>(new GlassDropTarget(this, hwnd));
 110 }
 111 
 112 void ViewContainer::ReleaseDropTarget()
 113 {
 114     m_spDropTarget = std::auto_ptr<IDropTarget>();
 115 }
 116 
 117 void ViewContainer::InitManipProcessor(HWND hwnd)
 118 {
 119     if (IS_WIN7) {
 120         ::RegisterTouchWindow(hwnd, TWF_WANTPALM);
 121 
 122         HRESULT hr = ::CoCreateInstance(CLSID_ManipulationProcessor,
 123                                         NULL,
 124                                         CLSCTX_INPROC_SERVER,
 125                                         IID_IUnknown,
 126                                         (VOID**)(&m_manipProc)
 127                                         );
 128 
 129         if (SUCCEEDED(hr)) {
 130             ::CoCreateInstance(CLSID_InertiaProcessor,
 131                                NULL,
 132                                CLSCTX_INPROC_SERVER,
 133                                IID_IUnknown,
 134                                (VOID**)(&m_inertiaProc)
 135                                );
 136         
 137             m_manipEventSink = 
 138                 new ManipulationEventSinkWithInertia(m_manipProc, m_inertiaProc, this, hwnd);
 139         }
 140 
 141         const DWORD_PTR dwHwndTabletProperty =
 142              TABLET_DISABLE_PENTAPFEEDBACK |
 143              TABLET_DISABLE_PENBARRELFEEDBACK |
 144              TABLET_DISABLE_FLICKS;
 145         ::SetProp(hwnd, MICROSOFT_TABLETPENSERVICE_PROPERTY, reinterpret_cast<HANDLE>(dwHwndTabletProperty));
 146 
 147         if (!m_gestureSupportCls) {
 148             JNIEnv *env = GetEnv();
 149             const jclass cls = GlassApplication::ClassForName(env,
 150                     "com.sun.glass.ui.win.WinGestureSupport");
 151 
 152             m_gestureSupportCls = (jclass)env->NewGlobalRef(cls);
 153             env->DeleteLocalRef(cls);
 154             ASSERT(m_gestureSupportCls);
 155         }
 156     }
 157 }
 158 
 159 void ViewContainer::ReleaseManipProcessor()
 160 {
 161     if (IS_WIN7) {
 162         if (m_manipProc) {
 163             m_manipProc->Release();
 164             m_manipProc = NULL;
 165         }
 166         if (m_inertiaProc) {
 167             m_inertiaProc->Release();
 168             m_inertiaProc = NULL;
 169         }
 170         if (m_manipEventSink) {
 171             m_manipEventSink->Release();
 172             m_manipEventSink = NULL;
 173         }
 174     }
 175     
 176     if (m_gestureSupportCls) {
 177         JNIEnv *env = GetEnv();
 178         env->DeleteGlobalRef(m_gestureSupportCls);
 179         m_gestureSupportCls = 0;
 180     }
 181 }
 182 
 183 void ViewContainer::HandleViewInputLangChange(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
 184 {
 185     if (!GetGlassView()) {
 186         return;
 187     }
 188 
 189     m_kbLayout = reinterpret_cast<HKL>(lParam);
 190     m_idLang = LOWORD(m_kbLayout);
 191     m_codePage = LangToCodePage(m_idLang);
 192 
 193     m_deadKeyWParam = 0;
 194 }
 195 
 196 void ViewContainer::NotifyViewSize(HWND hwnd)
 197 {
 198     if (!hwnd || !GetGlassView()) {
 199         return;
 200     }
 201 
 202     RECT r;
 203     if (::GetClientRect(hwnd, &r)) {
 204         JNIEnv* env = GetEnv();
 205         env->CallVoidMethod(GetView(), javaIDs.View.notifyResize,
 206                             r.right-r.left, r.bottom - r.top);
 207         CheckAndClearException(env);
 208     }
 209 }
 210 
 211 void ViewContainer::HandleViewPaintEvent(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
 212 {
 213     if (!GetGlassView()) {
 214         return;
 215     }
 216 
 217     RECT r;
 218     if (!::GetUpdateRect(hwnd, &r, FALSE)) {
 219         return;
 220     }
 221 
 222     JNIEnv* env = GetEnv();
 223     env->CallVoidMethod(GetView(), javaIDs.View.notifyRepaint,
 224             r.left, r.top, r.right-r.left, r.bottom-r.top);
 225     CheckAndClearException(env);
 226 }
 227 
 228 
 229 LRESULT ViewContainer::HandleViewGetAccessible(HWND hwnd, WPARAM wParam, LPARAM lParam)
 230 {
 231     if (!GetGlassView()) {
 232         return NULL;
 233     }
 234 
 235     /* WM_GETOBJECT  is sent to request different object types,
 236      * always test the type to avoid unnecessary work.
 237      */
 238     LRESULT lr = NULL;
 239     if (static_cast<long>(lParam) == static_cast<long>(UiaRootObjectId)) {
 240 
 241         /* The client is requesting UI Automation. */
 242         JNIEnv* env = GetEnv();
 243         if (!env) return NULL;
 244         jlong pProvider = env->CallLongMethod(GetView(), javaIDs.View.getAccessible);
 245         CheckAndClearException(env);
 246 
 247         /* It is possible WM_GETOBJECT is sent before the toolkit is ready to
 248          * create the accessible object (getAccessible returns NULL).
 249          * On Windows 7, calling UiaReturnRawElementProvider() with a NULL provider
 250          * returns an invalid LRESULT which stops further WM_GETOBJECT to be sent,
 251          * effectively disabling accessibility for the window.
 252          */
 253         if (pProvider) {
 254             lr = UiaReturnRawElementProvider(hwnd, wParam, lParam, reinterpret_cast<IRawElementProviderSimple*>(pProvider));
 255         }
 256     } else if (static_cast<long>(lParam) == static_cast<long>(OBJID_CLIENT)) {
 257 
 258         /* By default JAWS does not send WM_GETOBJECT with UiaRootObjectId till
 259          * a focus event is raised by UiaRaiseAutomationEvent().
 260          * In some systems (i.e. touch monitors), OBJID_CLIENT are sent when
 261          * no screen reader is active. Test for SPI_GETSCREENREADER and
 262          * UiaClientsAreListening() to avoid initializing accessibility
 263          * unnecessarily.
 264          */
 265         UINT screenReader = 0;
 266         ::SystemParametersInfo(SPI_GETSCREENREADER, 0, &screenReader, 0);
 267         if (screenReader && UiaClientsAreListening()) {
 268             JNIEnv* env = GetEnv();
 269             if (env) {
 270 
 271                 /* Calling getAccessible() initializes accessibility which
 272                  * eventually raises the focus events required to indicate to
 273                  * JAWS to use UIA for this window.
 274                  *
 275                  * Note: do not return the accessible object for OBJID_CLIENT,
 276                  * that would create an UIA-MSAA bridge. That problem with the
 277                  * bridge is that it does not respect
 278                  * ProviderOptions_UseComThreading.
 279                  */
 280                 env->CallLongMethod(GetView(), javaIDs.View.getAccessible);
 281                 CheckAndClearException(env);
 282             }
 283         }
 284     }
 285     return lr;
 286 }
 287 
 288 
 289 void ViewContainer::HandleViewSizeEvent(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
 290 {
 291     if (wParam == SIZE_MINIMIZED) {
 292         return;
 293     }
 294     NotifyViewSize(hwnd);
 295 }
 296 
 297 void ViewContainer::HandleViewMenuEvent(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
 298 {
 299     if (!GetGlassView()) {
 300         return;
 301     }
 302     if ((HWND)wParam != hwnd) {
 303         return;
 304     }
 305     jboolean isKeyboardTrigger = lParam == (LPARAM)-1; 
 306     if (isKeyboardTrigger) {
 307         lParam = ::GetMessagePos ();
 308     }
 309     POINT pt;
 310     int absX = pt.x = GET_X_LPARAM(lParam);
 311     int absY = pt.y = GET_Y_LPARAM(lParam);
 312     ::ScreenToClient (hwnd, &pt);
 313     if (!isKeyboardTrigger) {
 314         RECT rect;
 315         ::GetClientRect(hwnd, &rect);
 316         if (!::PtInRect(&rect, pt)) {
 317             return;
 318         }
 319     }
 320     // unmirror the x coordinate
 321     LONG style = ::GetWindowLong(hwnd, GWL_EXSTYLE);
 322     if (style & WS_EX_LAYOUTRTL) {
 323         RECT rect = {0};
 324         ::GetClientRect(hwnd, &rect);
 325         pt.x = max(0, rect.right - rect.left) - pt.x;
 326     }
 327     JNIEnv* env = GetEnv();
 328     env->CallVoidMethod(GetView(), javaIDs.View.notifyMenu, pt.x, pt.y, absX, absY, isKeyboardTrigger);
 329     CheckAndClearException(env);
 330 }
 331 
 332 void ViewContainer::HandleViewKeyEvent(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
 333 {
 334     if (!GetGlassView()) {
 335         return;
 336     }
 337 
 338     static const BYTE KEY_STATE_DOWN = 0x80;
 339 
 340     UINT wKey = static_cast<UINT>(wParam);
 341     UINT flags = HIWORD(lParam);
 342 
 343     jint jKeyCode = WindowsKeyToJavaKey(wKey);
 344     if (flags & (1 << 8)) {
 345         // this is an extended key (e.g. Right ALT == AltGr)
 346         switch (jKeyCode) {
 347             case com_sun_glass_events_KeyEvent_VK_ALT:
 348                 jKeyCode = com_sun_glass_events_KeyEvent_VK_ALT_GRAPH;
 349                 break;
 350         }
 351     }
 352 
 353     BYTE kbState[256];
 354     if (!::GetKeyboardState(kbState)) {
 355         return;
 356     }
 357 
 358     jint jModifiers = GetModifiers();
 359 
 360     if (jModifiers & com_sun_glass_events_KeyEvent_MODIFIER_CONTROL) {
 361         kbState[VK_CONTROL] &= ~KEY_STATE_DOWN;
 362     }
 363 
 364     WORD mbChar;
 365     UINT scancode = ::MapVirtualKeyEx(wKey, 0, m_kbLayout);
 366 
 367     // Depress modifiers to map a Unicode char to a key code
 368     kbState[VK_CONTROL] &= ~0x80;
 369     kbState[VK_SHIFT]   &= ~0x80;
 370     kbState[VK_MENU]    &= ~0x80;
 371 
 372     int converted = ::ToAsciiEx(wKey, scancode, kbState,
 373                                 &mbChar, 0, m_kbLayout);
 374 
 375     wchar_t wChar[4] = {0};
 376     int unicodeConverted = ::ToUnicodeEx(wKey, scancode, kbState,
 377                                 wChar, 4, 0, m_kbLayout);
 378     
 379     // Some virtual codes require special handling
 380     switch (wKey) {
 381         case 0x00BA:// VK_OEM_1
 382         case 0x00BB:// VK_OEM_PLUS
 383         case 0x00BC:// VK_OEM_COMMA
 384         case 0x00BD:// VK_OEM_MINUS
 385         case 0x00BE:// VK_OEM_PERIOD
 386         case 0x00BF:// VK_OEM_2
 387         case 0x00C0:// VK_OEM_3
 388         case 0x00DB:// VK_OEM_4
 389         case 0x00DC:// VK_OEM_5
 390         case 0x00DD:// VK_OEM_6
 391         case 0x00DE:// VK_OEM_7
 392         case 0x00DF:// VK_OEM_8
 393         case 0x00E2:// VK_OEM_102
 394             if (unicodeConverted < 0) {
 395                 // Dead key
 396                 switch (wChar[0]) {
 397                     case L'`':   jKeyCode = com_sun_glass_events_KeyEvent_VK_DEAD_GRAVE; break;
 398                     case L'\'':  jKeyCode = com_sun_glass_events_KeyEvent_VK_DEAD_ACUTE; break;
 399                     case 0x00B4: jKeyCode = com_sun_glass_events_KeyEvent_VK_DEAD_ACUTE; break;
 400                     case L'^':   jKeyCode = com_sun_glass_events_KeyEvent_VK_DEAD_CIRCUMFLEX; break;
 401                     case L'~':   jKeyCode = com_sun_glass_events_KeyEvent_VK_DEAD_TILDE; break;
 402                     case 0x02DC: jKeyCode = com_sun_glass_events_KeyEvent_VK_DEAD_TILDE; break;
 403                     case 0x00AF: jKeyCode = com_sun_glass_events_KeyEvent_VK_DEAD_MACRON; break;
 404                     case 0x02D8: jKeyCode = com_sun_glass_events_KeyEvent_VK_DEAD_BREVE; break;
 405                     case 0x02D9: jKeyCode = com_sun_glass_events_KeyEvent_VK_DEAD_ABOVEDOT; break;
 406                     case L'"':   jKeyCode = com_sun_glass_events_KeyEvent_VK_DEAD_DIAERESIS; break;
 407                     case 0x00A8: jKeyCode = com_sun_glass_events_KeyEvent_VK_DEAD_DIAERESIS; break;
 408                     case 0x02DA: jKeyCode = com_sun_glass_events_KeyEvent_VK_DEAD_ABOVERING; break;
 409                     case 0x02DD: jKeyCode = com_sun_glass_events_KeyEvent_VK_DEAD_DOUBLEACUTE; break;
 410                     case 0x02C7: jKeyCode = com_sun_glass_events_KeyEvent_VK_DEAD_CARON; break;            // aka hacek
 411                     case L',':   jKeyCode = com_sun_glass_events_KeyEvent_VK_DEAD_CEDILLA; break;
 412                     case 0x00B8: jKeyCode = com_sun_glass_events_KeyEvent_VK_DEAD_CEDILLA; break;
 413                     case 0x02DB: jKeyCode = com_sun_glass_events_KeyEvent_VK_DEAD_OGONEK; break;
 414                     case 0x037A: jKeyCode = com_sun_glass_events_KeyEvent_VK_DEAD_IOTA; break;             // ASCII ???
 415                     case 0x309B: jKeyCode = com_sun_glass_events_KeyEvent_VK_DEAD_VOICED_SOUND; break;
 416                     case 0x309C: jKeyCode = com_sun_glass_events_KeyEvent_VK_DEAD_SEMIVOICED_SOUND; break;
 417 
 418                     default:     jKeyCode = com_sun_glass_events_KeyEvent_VK_UNDEFINED; break;
 419                 };
 420             } else if (unicodeConverted == 1) {
 421                 switch (wChar[0]) {
 422                     case L'!':   jKeyCode = com_sun_glass_events_KeyEvent_VK_EXCLAMATION; break;
 423                     case L'"':   jKeyCode = com_sun_glass_events_KeyEvent_VK_DOUBLE_QUOTE; break;
 424                     case L'#':   jKeyCode = com_sun_glass_events_KeyEvent_VK_NUMBER_SIGN; break;
 425                     case L'$':   jKeyCode = com_sun_glass_events_KeyEvent_VK_DOLLAR; break;
 426                     case L'&':   jKeyCode = com_sun_glass_events_KeyEvent_VK_AMPERSAND; break;
 427                     case L'\'':  jKeyCode = com_sun_glass_events_KeyEvent_VK_QUOTE; break;
 428                     case L'(':   jKeyCode = com_sun_glass_events_KeyEvent_VK_LEFT_PARENTHESIS; break;
 429                     case L')':   jKeyCode = com_sun_glass_events_KeyEvent_VK_RIGHT_PARENTHESIS; break;
 430                     case L'*':   jKeyCode = com_sun_glass_events_KeyEvent_VK_ASTERISK; break;
 431                     case L'+':   jKeyCode = com_sun_glass_events_KeyEvent_VK_PLUS; break;
 432                     case L',':   jKeyCode = com_sun_glass_events_KeyEvent_VK_COMMA; break;
 433                     case L'-':   jKeyCode = com_sun_glass_events_KeyEvent_VK_MINUS; break;
 434                     case L'.':   jKeyCode = com_sun_glass_events_KeyEvent_VK_PERIOD; break;
 435                     case L'/':   jKeyCode = com_sun_glass_events_KeyEvent_VK_SLASH; break;
 436                     case L':':   jKeyCode = com_sun_glass_events_KeyEvent_VK_COLON; break;
 437                     case L';':   jKeyCode = com_sun_glass_events_KeyEvent_VK_SEMICOLON; break;
 438                     case L'<':   jKeyCode = com_sun_glass_events_KeyEvent_VK_LESS; break;
 439                     case L'=':   jKeyCode = com_sun_glass_events_KeyEvent_VK_EQUALS; break;
 440                     case L'>':   jKeyCode = com_sun_glass_events_KeyEvent_VK_GREATER; break;
 441                     case L'@':   jKeyCode = com_sun_glass_events_KeyEvent_VK_AT; break;
 442                     case L'[':   jKeyCode = com_sun_glass_events_KeyEvent_VK_OPEN_BRACKET; break;
 443                     case L'\\':  jKeyCode = com_sun_glass_events_KeyEvent_VK_BACK_SLASH; break;
 444                     case L']':   jKeyCode = com_sun_glass_events_KeyEvent_VK_CLOSE_BRACKET; break;
 445                     case L'^':   jKeyCode = com_sun_glass_events_KeyEvent_VK_CIRCUMFLEX; break;
 446                     case L'_':   jKeyCode = com_sun_glass_events_KeyEvent_VK_UNDERSCORE; break;
 447                     case L'`':   jKeyCode = com_sun_glass_events_KeyEvent_VK_BACK_QUOTE; break;
 448                     case L'{':   jKeyCode = com_sun_glass_events_KeyEvent_VK_BRACELEFT; break;
 449                     case L'}':   jKeyCode = com_sun_glass_events_KeyEvent_VK_BRACERIGHT; break;
 450                     case 0x00A1: jKeyCode = com_sun_glass_events_KeyEvent_VK_INV_EXCLAMATION; break;
 451                     case 0x20A0: jKeyCode = com_sun_glass_events_KeyEvent_VK_EURO_SIGN; break;
 452 
 453                     default:     jKeyCode = com_sun_glass_events_KeyEvent_VK_UNDEFINED; break;
 454                 }
 455             } else if (unicodeConverted == 0 || unicodeConverted > 1) {
 456                 jKeyCode = com_sun_glass_events_KeyEvent_VK_UNDEFINED;
 457             }
 458             break;
 459     };
 460 
 461 
 462     int keyCharCount = 0;
 463     jchar keyChars[4];
 464     const bool isAutoRepeat = (msg == WM_KEYDOWN || msg == WM_SYSKEYDOWN)
 465         && (lParam & (1 << 30));
 466 
 467     if (converted < 0) {
 468         // Dead key
 469         return;
 470     } else if (converted == 0) {
 471         // No translation available
 472         keyCharCount = 0;
 473         // This includes SHIFT, CONTROL, ALT, etc.
 474         // RT-17062: suppress auto-repeated events for modifier keys
 475         if (isAutoRepeat) {
 476             switch (jKeyCode) {
 477                 case com_sun_glass_events_KeyEvent_VK_SHIFT:
 478                 case com_sun_glass_events_KeyEvent_VK_CONTROL:
 479                 case com_sun_glass_events_KeyEvent_VK_ALT:
 480                 case com_sun_glass_events_KeyEvent_VK_ALT_GRAPH:
 481                 case com_sun_glass_events_KeyEvent_VK_WINDOWS:
 482                     return;
 483             }
 484         }
 485     } else {
 486         // Handle some special cases
 487         if ((wKey == VK_BACK) ||
 488             (wKey == VK_ESCAPE))
 489         {
 490             keyCharCount = 0;
 491         } else {
 492             keyCharCount = ::MultiByteToWideChar(m_codePage, MB_PRECOMPOSED,
 493                                                  (LPCSTR)&mbChar, 2, (LPWSTR)keyChars,
 494                                                  4 * sizeof(jchar)) - 1;
 495             if (keyCharCount <= 0) {
 496                 return;
 497             }
 498         }
 499     }
 500 
 501     JNIEnv* env = GetEnv();
 502 
 503     jcharArray jKeyChars = env->NewCharArray(keyCharCount);
 504     if (jKeyChars) {
 505         if (keyCharCount) {
 506             env->SetCharArrayRegion(jKeyChars, 0, keyCharCount, keyChars);
 507             CheckAndClearException(env);
 508         }
 509 
 510         if (jKeyCode == com_sun_glass_events_KeyEvent_VK_PRINTSCREEN &&
 511                 (msg == WM_KEYUP || msg == WM_SYSKEYUP))
 512         {
 513             // MS Windows doesn't send WM_KEYDOWN for the PrintScreen key,
 514             // so we synthesize one
 515             env->CallVoidMethod(GetView(), javaIDs.View.notifyKey,
 516                     com_sun_glass_events_KeyEvent_PRESS,
 517                     jKeyCode, jKeyChars, jModifiers);
 518             CheckAndClearException(env);
 519         }
 520 
 521         if (GetGlassView()) {
 522             env->CallVoidMethod(GetView(), javaIDs.View.notifyKey,
 523                     (msg == WM_KEYDOWN || msg == WM_SYSKEYDOWN) ?
 524                     com_sun_glass_events_KeyEvent_PRESS : com_sun_glass_events_KeyEvent_RELEASE,
 525                     jKeyCode, jKeyChars, jModifiers);
 526             CheckAndClearException(env);
 527         }
 528 
 529         // MS Windows doesn't send WM_CHAR for the Delete key,
 530         // so we synthesize one
 531         if (jKeyCode == com_sun_glass_events_KeyEvent_VK_DELETE &&
 532                 (msg == WM_KEYDOWN || msg == WM_SYSKEYDOWN) &&
 533                 GetGlassView())
 534         {
 535             // 0x7F == U+007F - a Unicode character for DELETE
 536             SendViewTypedEvent(1, (jchar)0x7F);
 537         }
 538 
 539         env->DeleteLocalRef(jKeyChars);
 540     }
 541 }
 542 
 543 void ViewContainer::SendViewTypedEvent(int repCount, jchar wChar) 
 544 {
 545     if (!GetGlassView()) {
 546         return;
 547     }
 548 
 549     JNIEnv* env = GetEnv();
 550     jcharArray jKeyChars = env->NewCharArray(repCount);
 551     if (jKeyChars) {
 552         jchar* nKeyChars = env->GetCharArrayElements(jKeyChars, NULL);
 553         if (nKeyChars) {
 554             for (int i = 0; i < repCount; i++) {
 555                 nKeyChars[i] = wChar;
 556             }
 557             env->ReleaseCharArrayElements(jKeyChars, nKeyChars, 0);
 558 
 559             env->CallVoidMethod(GetView(), javaIDs.View.notifyKey,
 560                                 com_sun_glass_events_KeyEvent_TYPED,
 561                                 com_sun_glass_events_KeyEvent_VK_UNDEFINED, jKeyChars, 
 562                                 GetModifiers());
 563             CheckAndClearException(env);
 564         }
 565         env->DeleteLocalRef(jKeyChars);
 566     }
 567 }
 568 
 569 void ViewContainer::HandleViewDeadKeyEvent(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) 
 570 {
 571     if (!GetGlassView()) {
 572         return;
 573     }
 574 
 575     if (!m_deadKeyWParam) {
 576         // HandleViewKeyEvent() calls ::ToAsciiEx and ::ToUnicodeEx which clear
 577         // the dead key status from the keyboard layout. We store the current dead
 578         // key here to use it when processing WM_CHAR in order to get the
 579         // actual character typed.
 580 
 581         m_deadKeyWParam = wParam;
 582     } else {
 583         // There already was another dead key pressed previously. Clear it
 584         // and send two separate TYPED events instead to emulate native behavior.
 585 
 586         SendViewTypedEvent(1, (jchar)m_deadKeyWParam);
 587         SendViewTypedEvent(1, (jchar)wParam);
 588 
 589         m_deadKeyWParam = 0;
 590     }
 591 
 592     // Since we handle dead keys ourselves, reset the keyboard dead key status (if any)
 593     static BYTE kbState[256];
 594     ::GetKeyboardState(kbState);
 595     WORD ignored;
 596     ::ToAsciiEx(VK_SPACE, ::MapVirtualKey(VK_SPACE, 0),
 597             kbState, &ignored, 0, m_kbLayout);
 598 }
 599 
 600 void ViewContainer::HandleViewTypedEvent(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) 
 601 {
 602     if (!GetGlassView()) {
 603         return;
 604     }
 605 
 606     int repCount = LOWORD(lParam);
 607     jchar wChar;
 608     
 609     if (!m_deadKeyWParam) {
 610         wChar = (jchar)wParam;
 611     } else {
 612         // The character is composed together with the dead key, which
 613         // may be translated into one or more combining characters.
 614         const size_t COMP_SIZE = 5;
 615         wchar_t comp[COMP_SIZE] = { (wchar_t)wParam };
 616 
 617         // Some dead keys need additional translation:
 618         // http://www.fileformat.info/info/unicode/block/combining_diacritical_marks/images.htm
 619         // Also see awt_Component.cpp for the original dead keys table
 620         if (LOBYTE(m_idLang) == LANG_GREEK) {
 621             switch (m_deadKeyWParam) {
 622                 case L']':   comp[1] = 0x300; break; // varia
 623                 case L';':   comp[1] = 0x301; break; // oxia (wrong? generates tonos, not oxia)
 624                 case L'-':   comp[1] = 0x304; break; // macron
 625                 case L'_':   comp[1] = 0x306; break; // vrachy
 626                 case L':':   comp[1] = 0x308; break; // dialytika
 627                 case L'"':   comp[1] = 0x314; break; // dasia
 628                 case 0x0384: comp[1] = 0x341; break; // tonos
 629                 case L'[':   comp[1] = 0x342; break; // perispomeni
 630                 case L'\'':  comp[1] = 0x343; break; // psili
 631                 case L'~':   comp[1] = 0x344; break; // dialytika oxia
 632                 case L'{':   comp[1] = 0x345; break; // ypogegrammeni
 633 
 634                 case L'`':   comp[1] = 0x308; comp[2] = 0x300; break; // dialytika varia
 635                 case L'\\':  comp[1] = 0x313; comp[2] = 0x300; break; // psili varia
 636                 case L'/':   comp[1] = 0x313; comp[2] = 0x301; break; // psili oxia 
 637                 case L'=':   comp[1] = 0x313; comp[2] = 0x342; break; // psili perispomeni
 638                 case L'|':   comp[1] = 0x314; comp[2] = 0x300; break; // dasia varia
 639                 case L'?':   comp[1] = 0x314; comp[2] = 0x301; break; // dasia oxia 
 640                 case L'+':   comp[1] = 0x314; comp[2] = 0x342; break; // dasia perispomeni
 641 
 642                 // AltGr dead chars don't work. Maybe kbd isn't reset properly?
 643                 // case 0x1fc1: comp[1] = 0x308; comp[2] = 0x342; break; // dialytika perispomeni
 644                 // case 0x1fde: comp[1] = 0x314; comp[2] = 0x301; comp[3] = 0x345; break; // dasia oxia ypogegrammeni
 645 
 646                 default:     comp[1] = static_cast<wchar_t>(m_deadKeyWParam); break;
 647             }
 648         } else if (HIWORD(m_kbLayout) == 0xF0B1 && LOBYTE(m_idLang) == LANG_LATVIAN) {
 649             // The Latvian (Standard) keyboard, available in Win 8.1 and later.
 650             switch (m_deadKeyWParam) {
 651                 case L'\'':
 652                 case L'"':
 653                     // Note: " is Shift-' and automatically capitalizes the typed
 654                     // character in native Win 8.1 apps. We don't do this, so the user
 655                     // needs to keep the Shift key down. This is probably the common use
 656                     // case anyway.
 657                     switch (wParam) {
 658                         case L'A': case L'a':
 659                         case L'E': case L'e':
 660                         case L'I': case L'i':
 661                         case L'U': case L'u':
 662                              comp[1] = 0x304; break; // macron
 663                         case L'C': case L'c':
 664                         case L'S': case L's':
 665                         case L'Z': case L'z':
 666                              comp[1] = 0x30c; break; // caron
 667                         case L'G': case L'g':
 668                         case L'K': case L'k':
 669                         case L'L': case L'l':
 670                         case L'N': case L'n':
 671                              comp[1] = 0x327; break; // cedilla
 672                         default:
 673                              comp[1] = static_cast<wchar_t>(m_deadKeyWParam); break;
 674                     } break;
 675                 default:     comp[1] = static_cast<wchar_t>(m_deadKeyWParam); break;
 676             }
 677         } else {
 678             switch (m_deadKeyWParam) {
 679                 case L'`':   comp[1] = 0x300; break;
 680                 case L'\'':  comp[1] = 0x301; break;
 681                 case 0x00B4: comp[1] = 0x301; break;
 682                 case L'^':   comp[1] = 0x302; break;
 683                 case L'~':   comp[1] = 0x303; break;
 684                 case 0x02DC: comp[1] = 0x303; break;
 685                 case 0x00AF: comp[1] = 0x304; break;
 686                 case 0x02D8: comp[1] = 0x306; break;
 687                 case 0x02D9: comp[1] = 0x307; break;
 688                 case L'"':   comp[1] = 0x308; break;
 689                 case 0x00A8: comp[1] = 0x308; break;
 690                 case 0x00B0: comp[1] = 0x30A; break;
 691                 case 0x02DA: comp[1] = 0x30A; break;
 692                 case 0x02DD: comp[1] = 0x30B; break;
 693                 case 0x02C7: comp[1] = 0x30C; break;
 694                 case L',':   comp[1] = 0x327; break;
 695                 case 0x00B8: comp[1] = 0x327; break;
 696                 case 0x02DB: comp[1] = 0x328; break;
 697                 default:     comp[1] = static_cast<wchar_t>(m_deadKeyWParam); break;
 698             }
 699         }
 700 
 701         int compSize = 3;
 702         for (int i = 1; i < COMP_SIZE; i++) {
 703             if (comp[i] == L'\0') {
 704                 compSize = i + 1;
 705                 break;
 706             }
 707         }
 708         wchar_t out[3];
 709         int res = ::FoldString(MAP_PRECOMPOSED, (LPWSTR)comp, compSize, (LPWSTR)out, 3);
 710         
 711         if (res > 0) {
 712             wChar = (jchar)out[0];
 713 
 714             if (res == 3) {
 715                 // The character cannot be accented, so we send a TYPED event
 716                 // for the dead key itself first.
 717                 SendViewTypedEvent(1, (jchar)m_deadKeyWParam);
 718             }
 719         } else {
 720             // Folding failed. Use the untranslated original character then
 721             wChar = (jchar)wParam;
 722         }
 723 
 724         // Clear the dead key
 725         m_deadKeyWParam = 0;
 726     }
 727 
 728     SendViewTypedEvent(repCount, wChar);
 729 }
 730 
 731 BOOL ViewContainer::HandleViewMouseEvent(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
 732 {
 733     if (!GetGlassView()) {
 734         return FALSE;
 735     }
 736 
 737     int type = 0;
 738     int button = com_sun_glass_events_MouseEvent_BUTTON_NONE;
 739     POINT pt;   // client coords
 740     jdouble wheelRotation = 0.0;
 741 
 742     if (msg == WM_MOUSELEAVE) {
 743         type = com_sun_glass_events_MouseEvent_EXIT;
 744         // get the coords (the message does not contain them)
 745         lParam = ::GetMessagePos();
 746         pt.x = GET_X_LPARAM(lParam);
 747         pt.y = GET_Y_LPARAM(lParam);
 748         // this is screen coords, convert to client
 749         ::ScreenToClient(hwnd, &pt);
 750 
 751         // Windows has finished tracking mouse pointer already
 752         m_bTrackingMouse = FALSE;
 753 
 754         m_lastMouseMovePosition = -1;
 755     } else {
 756         // for all other messages lParam contains cursor coords
 757         pt.x = GET_X_LPARAM(lParam);
 758         pt.y = GET_Y_LPARAM(lParam);
 759 
 760         switch (msg) {
 761             case WM_MOUSEMOVE:
 762                 if (lParam == m_lastMouseMovePosition) {
 763                     // Avoid sending synthetic MOVE/DRAG events if
 764                     // the pointer hasn't moved actually.
 765                     // Just consume the messages.
 766                     return TRUE;
 767                 } else {
 768                     m_lastMouseMovePosition = lParam;
 769                 }
 770                 // See RT-11305 regarding the GetCapture() check
 771                 if ((wParam & (MK_LBUTTON | MK_RBUTTON | MK_MBUTTON)) != 0 && ::GetCapture() == hwnd) {
 772                     type = com_sun_glass_events_MouseEvent_DRAG;
 773                 } else {
 774                     type = com_sun_glass_events_MouseEvent_MOVE;
 775                 }
 776                 // Due to RT-11305 we should report the pressed button for both
 777                 // MOVE and DRAG. This also enables one to filter out these
 778                 // events in client code in case they're undesired.
 779                 if (wParam & MK_RBUTTON) {
 780                     button = com_sun_glass_events_MouseEvent_BUTTON_RIGHT;
 781                 } else if (wParam & MK_LBUTTON) {
 782                     button = com_sun_glass_events_MouseEvent_BUTTON_LEFT;
 783                 } else if (wParam & MK_MBUTTON) {
 784                     button = com_sun_glass_events_MouseEvent_BUTTON_OTHER;
 785                 }
 786                 break;
 787             case WM_LBUTTONDOWN:
 788                 type = com_sun_glass_events_MouseEvent_DOWN;
 789                 button = com_sun_glass_events_MouseEvent_BUTTON_LEFT;
 790                 break;
 791             case WM_LBUTTONUP:
 792                 type = com_sun_glass_events_MouseEvent_UP;
 793                 button = com_sun_glass_events_MouseEvent_BUTTON_LEFT;
 794                 break;
 795             case WM_RBUTTONDOWN:
 796                 type = com_sun_glass_events_MouseEvent_DOWN;
 797                 button = com_sun_glass_events_MouseEvent_BUTTON_RIGHT;
 798                 break;
 799             case WM_RBUTTONUP:
 800                 type = com_sun_glass_events_MouseEvent_UP;
 801                 button = com_sun_glass_events_MouseEvent_BUTTON_RIGHT;
 802                 break;
 803             case WM_MBUTTONDOWN:
 804                 type = com_sun_glass_events_MouseEvent_DOWN;
 805                 button = com_sun_glass_events_MouseEvent_BUTTON_OTHER;
 806                 break;
 807             case WM_MBUTTONUP:
 808                 type = com_sun_glass_events_MouseEvent_UP;
 809                 button = com_sun_glass_events_MouseEvent_BUTTON_OTHER;
 810                 break;
 811             case WM_MOUSEWHEEL:
 812             case WM_MOUSEHWHEEL:
 813                 {
 814                     // MS Windows always sends WHEEL events to the focused window.
 815                     // Redirect the message to a Glass window under the mouse
 816                     // cursor instead to match Mac behavior
 817                     HWND hwndUnderCursor = ::ChildWindowFromPointEx(
 818                             ::GetDesktopWindow(), pt,
 819                             CWP_SKIPDISABLED | CWP_SKIPINVISIBLE);
 820 
 821                     if (hwndUnderCursor && hwndUnderCursor != hwnd)
 822                     {
 823                         DWORD hWndUnderCursorProcess;
 824                         ::GetWindowThreadProcessId(hwndUnderCursor, &hWndUnderCursorProcess);
 825                         if (::GetCurrentProcessId() == hWndUnderCursorProcess) {
 826                             return (BOOL)::SendMessage(hwndUnderCursor, msg, wParam, lParam);
 827                         }
 828                     }
 829 
 830                     // if there's none, proceed as usual
 831                     type = com_sun_glass_events_MouseEvent_WHEEL;
 832                     wheelRotation = (jdouble)GET_WHEEL_DELTA_WPARAM(wParam) / WHEEL_DELTA;
 833                 }
 834                 break;
 835         }
 836     }
 837 
 838     switch (type) {
 839         case 0:
 840             // not handled
 841             return FALSE;
 842         case com_sun_glass_events_MouseEvent_DOWN:
 843             m_mouseButtonDownCounter++;
 844             if (::GetCapture() != hwnd) {
 845                 ::SetCapture(hwnd);
 846             }
 847             break;
 848         case com_sun_glass_events_MouseEvent_UP:
 849             if (m_mouseButtonDownCounter) {
 850                 m_mouseButtonDownCounter--;
 851             } //else { internal inconsistency; quite unimportant though }
 852             if (::GetCapture() == hwnd && !m_mouseButtonDownCounter) {
 853                 ::ReleaseCapture();
 854             }
 855             break;
 856     }
 857 
 858     // get screen coords
 859     POINT ptAbs = pt;
 860     if (type == com_sun_glass_events_MouseEvent_WHEEL) {
 861         ::ScreenToClient(hwnd, &pt);
 862     } else {
 863         ::ClientToScreen(hwnd, &ptAbs);
 864     }
 865 
 866     // unmirror the x coordinate
 867     LONG style = ::GetWindowLong(hwnd, GWL_EXSTYLE);
 868     if (style & WS_EX_LAYOUTRTL) {
 869         RECT rect = {0};
 870         ::GetClientRect(hwnd, &rect);
 871         pt.x = max(0, rect.right - rect.left) - pt.x;
 872     }
 873 
 874     jint jModifiers = GetModifiers();
 875     
 876     const jboolean isSynthesized = jboolean(IsTouchEvent());
 877 
 878     JNIEnv *env = GetEnv();
 879 
 880     if (!m_bTrackingMouse && type != com_sun_glass_events_MouseEvent_EXIT) {
 881         TRACKMOUSEEVENT trackData;
 882         trackData.cbSize = sizeof(trackData);
 883         trackData.dwFlags = TME_LEAVE;
 884         trackData.hwndTrack = hwnd;
 885         trackData.dwHoverTime = HOVER_DEFAULT;
 886         if (::TrackMouseEvent(&trackData)) {
 887             // Mouse tracking will be canceled automatically upon receiving WM_MOUSELEAVE
 888             m_bTrackingMouse = TRUE;
 889         }
 890 
 891         // Note that (ViewContainer*)this != (BaseWnd*)this. We could use
 892         // dynamic_case<>() instead, but it would fail later if 'this' is
 893         // already deleted. So we use FromHandle() which is safe.
 894         const BaseWnd *origWnd = BaseWnd::FromHandle(hwnd);
 895 
 896         env->CallVoidMethod(GetView(), javaIDs.View.notifyMouse,
 897                 com_sun_glass_events_MouseEvent_ENTER,
 898                 com_sun_glass_events_MouseEvent_BUTTON_NONE,
 899                 pt.x, pt.y, ptAbs.x, ptAbs.y,
 900                 jModifiers, JNI_FALSE, isSynthesized);
 901         CheckAndClearException(env);
 902 
 903         // At this point 'this' might have already been deleted if the app
 904         // closed the window while processing the ENTER event. Hence the check:
 905         if (!::IsWindow(hwnd) || BaseWnd::FromHandle(hwnd) != origWnd ||
 906                 !GetGlassView())
 907         {
 908             return TRUE;
 909         }
 910     }
 911 
 912     switch (type) {
 913         case com_sun_glass_events_MouseEvent_DOWN:
 914             GlassDropSource::SetDragButton(button);
 915             break;
 916         case com_sun_glass_events_MouseEvent_UP:
 917             GlassDropSource::SetDragButton(0);
 918             break;
 919     }
 920 
 921     if (type == com_sun_glass_events_MouseEvent_WHEEL) {
 922         jdouble dx, dy;
 923         if (msg == WM_MOUSEHWHEEL) { // native horizontal scroll
 924             // Negate the value to be more "natural"
 925             dx = -wheelRotation;
 926             dy = 0.0;
 927         } else if (msg == WM_MOUSEWHEEL && LOWORD(wParam) & MK_SHIFT) {
 928             // Do not negate the emulated horizontal scroll amount
 929             dx = wheelRotation;
 930             dy = 0.0;
 931         } else { // vertical scroll
 932             dx = 0.0;
 933             dy = wheelRotation;
 934         }
 935 
 936         jint ls, cs;
 937 
 938         UINT val = 0;
 939         ::SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &val, 0);
 940         ls = (jint)val;
 941 
 942         val = 0;
 943         ::SystemParametersInfo(SPI_GETWHEELSCROLLCHARS, 0, &val, 0);
 944         cs = (jint)val;
 945 
 946         env->CallVoidMethod(GetView(), javaIDs.View.notifyScroll,
 947                 pt.x, pt.y, ptAbs.x, ptAbs.y,
 948                 dx, dy, jModifiers, ls, cs, 3, 3, (jdouble)40.0, (jdouble)40.0);
 949     } else {
 950         env->CallVoidMethod(GetView(), javaIDs.View.notifyMouse,
 951                 type, button, pt.x, pt.y, ptAbs.x, ptAbs.y,
 952                 jModifiers,
 953                 type == com_sun_glass_events_MouseEvent_UP && button == com_sun_glass_events_MouseEvent_BUTTON_RIGHT,
 954                 isSynthesized);
 955     }
 956     CheckAndClearException(env);
 957 
 958     return TRUE;
 959 }
 960 
 961 void ViewContainer::NotifyCaptureChanged(HWND hwnd, HWND to)
 962 {
 963     m_mouseButtonDownCounter = 0;
 964 }
 965 
 966 void ViewContainer::ResetMouseTracking(HWND hwnd)
 967 {
 968     if (!m_bTrackingMouse) {
 969         return;
 970     }
 971 
 972     // We don't expect WM_MOUSELEAVE anymore, so we cancel mouse tracking manually
 973     TRACKMOUSEEVENT trackData;
 974     trackData.cbSize = sizeof(trackData);
 975     trackData.dwFlags = TME_LEAVE | TME_CANCEL;
 976     trackData.hwndTrack = hwnd;
 977     trackData.dwHoverTime = HOVER_DEFAULT;
 978     ::TrackMouseEvent(&trackData);
 979 
 980     m_bTrackingMouse = FALSE;
 981 
 982     if (!GetGlassView()) {
 983         return;
 984     }
 985 
 986     POINT ptAbs;
 987     ::GetCursorPos(&ptAbs);
 988 
 989     POINT pt = ptAbs;
 990     ::ScreenToClient(hwnd, &pt);
 991 
 992     // unmirror the x coordinate
 993     LONG style = ::GetWindowLong(hwnd, GWL_EXSTYLE);
 994     if (style & WS_EX_LAYOUTRTL) {
 995         RECT rect = {0};
 996         ::GetClientRect(hwnd, &rect);
 997         pt.x = max(0, rect.right - rect.left) - pt.x;
 998     }
 999 
1000     JNIEnv *env = GetEnv();
1001     env->CallVoidMethod(GetView(), javaIDs.View.notifyMouse,
1002             com_sun_glass_events_MouseEvent_EXIT, 0, pt.x, pt.y, ptAbs.x, ptAbs.y,
1003             GetModifiers(),
1004             JNI_FALSE,
1005             JNI_FALSE);
1006     CheckAndClearException(env);
1007 }
1008 
1009 BOOL ViewContainer::HandleViewInputMethodEvent(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
1010 {
1011     GlassView* gv = GetGlassView();
1012 
1013     if (!gv) {
1014         return FALSE;
1015     }
1016 
1017     switch (msg) {
1018     case WM_IME_ENDCOMPOSITION:
1019         SendInputMethodEvent(NULL, 0, NULL, 0, NULL, NULL, 0, 0, 0);
1020     case WM_IME_STARTCOMPOSITION:
1021         return gv->IsInputMethodEventEnabled();
1022 
1023     case WM_IME_COMPOSITION:
1024         if (gv->IsInputMethodEventEnabled()) {
1025             WmImeComposition(hwnd, wParam, lParam);
1026             return TRUE;
1027         }
1028         break;
1029 
1030     case WM_IME_NOTIFY:
1031         if (gv->IsInputMethodEventEnabled()) {
1032             WmImeNotify(hwnd, wParam, lParam);
1033         }
1034         break;
1035 
1036     default:
1037         return FALSE;
1038     }
1039 
1040     return FALSE;
1041 }
1042 
1043 void ViewContainer::WmImeComposition(HWND hwnd, WPARAM wParam, LPARAM lParam) 
1044 {
1045     BOOL ret = FALSE;
1046 
1047     JNIEnv *env = GetEnv();
1048 
1049     int*      bndClauseW = NULL;
1050     int*      bndAttrW = NULL;
1051     BYTE*     valAttrW = NULL;
1052     int       cClauseW = 0;
1053     GlassInputTextInfo textInfo = GlassInputTextInfo(this);
1054     HIMC hIMC = ImmGetContext(hwnd);
1055     ASSERT(hIMC!=0);
1056 
1057     try {
1058         textInfo.GetContextData(hIMC, lParam);
1059 
1060         jstring jtextString = textInfo.GetText();
1061         if ((lParam & GCS_RESULTSTR && jtextString != NULL) ||
1062             (lParam & GCS_COMPSTR)) {
1063             int       cursorPosW = textInfo.GetCursorPosition();
1064             int       cAttrW = textInfo.GetAttributeInfo(bndAttrW, valAttrW);
1065             cClauseW = textInfo.GetClauseInfo(bndClauseW);
1066 
1067             SendInputMethodEvent(jtextString,
1068                                  cClauseW, bndClauseW,
1069                                  cAttrW, bndAttrW, valAttrW,
1070                                  textInfo.GetCommittedTextLength(),
1071                                  cursorPosW, cursorPosW);
1072 
1073         }
1074         ImmReleaseContext(hwnd, hIMC);
1075     } catch (...) {
1076         // since GetClauseInfo and GetAttributeInfo could throw exception, we have to release
1077         // the pointer here.
1078         delete [] bndClauseW;
1079         delete [] bndAttrW;
1080         delete [] valAttrW;
1081         ImmReleaseContext(hwnd, hIMC);
1082         throw;
1083     }
1084 
1085     /* Free the storage allocated. Since jtextString won't be passed from threads
1086      *  to threads, we just use the local ref and it will be deleted within the destructor
1087      *  of GlassInputTextInfo object.
1088      */
1089 
1090     delete [] bndClauseW;
1091     delete [] bndAttrW;
1092     delete [] valAttrW;
1093     CheckAndClearException(env);
1094 }
1095 
1096 void ViewContainer::WmImeNotify(HWND hwnd, WPARAM wParam, LPARAM lParam) 
1097 {
1098     if (wParam == IMN_OPENCANDIDATE || wParam == IMN_CHANGECANDIDATE) {
1099         JNIEnv *env = GetEnv();
1100         POINT curPos;
1101         UINT bits = 1;
1102         HIMC hIMC = ImmGetContext(hwnd);
1103         CANDIDATEFORM cf;
1104 
1105         GetCandidatePos(&curPos);
1106         ::ScreenToClient(hwnd, &curPos);
1107 
1108         for (int iCandType=0; iCandType<32; iCandType++, bits<<=1) {
1109             if (lParam & bits) {
1110                 cf.dwIndex = iCandType;
1111                 cf.dwStyle = CFS_CANDIDATEPOS;
1112                 // The constant offset is needed because Windows is moving the IM window
1113                 cf.ptCurrentPos.x = curPos.x - 6;
1114                 cf.ptCurrentPos.y = curPos.y - 15;
1115                 ::ImmSetCandidateWindow(hIMC, &cf);
1116             }
1117         }
1118         ImmReleaseContext(hwnd, hIMC);
1119     }
1120 }
1121 
1122 //
1123 // generate and post InputMethodEvent
1124 //
1125 void ViewContainer::SendInputMethodEvent(jstring text,
1126     int cClause, int* rgClauseBoundary,
1127     int cAttrBlock, int* rgAttrBoundary, BYTE *rgAttrValue,
1128     int commitedTextLength, int caretPos, int visiblePos)
1129 {
1130     JNIEnv *env = GetEnv();
1131 
1132     // assumption for array type casting
1133     ASSERT(sizeof(int)==sizeof(jint));
1134     ASSERT(sizeof(BYTE)==sizeof(jbyte));
1135 
1136     // caluse information
1137     jintArray clauseBoundary = NULL;
1138     if (cClause && rgClauseBoundary) {
1139         // convert clause boundary offset array to java array
1140         clauseBoundary = env->NewIntArray(cClause+1);
1141         if (clauseBoundary) {
1142             env->SetIntArrayRegion(clauseBoundary, 0, cClause+1, (jint *)rgClauseBoundary);
1143             CheckAndClearException(env);
1144         }
1145     }
1146 
1147     // attribute information
1148     jintArray attrBoundary = NULL;
1149     jbyteArray attrValue = NULL;
1150     if (cAttrBlock && rgAttrBoundary && rgAttrValue) {
1151         // convert attribute boundary offset array to java array
1152         attrBoundary = env->NewIntArray(cAttrBlock+1);
1153         if (attrBoundary) {
1154             env->SetIntArrayRegion(attrBoundary, 0, cAttrBlock+1, (jint *)rgAttrBoundary);
1155             CheckAndClearException(env);
1156         }
1157         // convert attribute value byte array to java array
1158         attrValue = env->NewByteArray(cAttrBlock);
1159         if (attrValue) {
1160             env->SetByteArrayRegion(attrValue, 0, cAttrBlock, (jbyte *)rgAttrValue);
1161             CheckAndClearException(env);
1162         }
1163     }
1164 
1165     env->CallBooleanMethod(GetView(), javaIDs.View.notifyInputMethod,
1166                         text, clauseBoundary, attrBoundary,
1167                         attrValue, commitedTextLength, caretPos, visiblePos);
1168     CheckAndClearException(env);
1169 
1170     if (clauseBoundary) {
1171         env->DeleteLocalRef(clauseBoundary);
1172     }
1173     if (attrBoundary) {
1174         env->DeleteLocalRef(attrBoundary);
1175     }
1176     if (attrValue) {
1177         env->DeleteLocalRef(attrValue);
1178     }
1179 }
1180 
1181 // Gets the candidate position
1182 void ViewContainer::GetCandidatePos(LPPOINT curPos)
1183 {
1184     JNIEnv *env = GetEnv();
1185     double* nativePos; 
1186 
1187     jdoubleArray pos = (jdoubleArray)env->CallObjectMethod(GetView(), 
1188                         javaIDs.View.notifyInputMethodCandidatePosRequest,
1189                         0);
1190     nativePos = env->GetDoubleArrayElements(pos, NULL);
1191     if (nativePos) {
1192         curPos->x = (int)nativePos[0];
1193         curPos->y  = (int)nativePos[1];
1194 
1195         env->ReleaseDoubleArrayElements(pos, nativePos, 0);
1196     }
1197 }
1198 
1199 namespace {
1200 
1201 class AutoTouchInputHandle {
1202     HTOUCHINPUT m_h;
1203 private:
1204     AutoTouchInputHandle(const AutoTouchInputHandle&);
1205     AutoTouchInputHandle& operator=(const AutoTouchInputHandle&);
1206 public:
1207     explicit AutoTouchInputHandle(LPARAM lParam): m_h((HTOUCHINPUT)lParam) {
1208     }
1209     ~AutoTouchInputHandle() {
1210         if (m_h) {
1211             ::CloseTouchInputHandle(m_h);
1212         }
1213     }
1214     operator HTOUCHINPUT() const {
1215         return m_h;
1216     }
1217 };
1218 
1219 void NotifyTouchInput(
1220         HWND hWnd, jobject view, jclass gestureSupportCls, 
1221         const TOUCHINPUT* ti, unsigned count) 
1222 {
1223 
1224     JNIEnv *env = GetEnv();
1225     
1226     // TBD: set to 'true' if source device is a touch screen 
1227     // and to 'false' if source device is a touch pad.
1228     // So far assume source device on Windows is always a touch screen.
1229     const bool isDirect = true;
1230 
1231     jint modifiers = GetModifiers();
1232     env->CallStaticObjectMethod(gestureSupportCls, 
1233                                 javaIDs.Gestures.notifyBeginTouchEventMID,
1234                                 view, modifiers, jboolean(isDirect), 
1235                                 jint(count));
1236     CheckAndClearException(env);
1237 
1238     for (; count; --count, ++ti) {
1239         jlong touchID = jlong(ti->dwID);
1240 
1241         jint eventID = 0;
1242         if (ti->dwFlags & TOUCHEVENTF_MOVE) {
1243             eventID = com_sun_glass_events_TouchEvent_TOUCH_MOVED;
1244         }
1245         if (ti->dwFlags & TOUCHEVENTF_DOWN) {
1246             eventID = com_sun_glass_events_TouchEvent_TOUCH_PRESSED;
1247         }
1248         if (ti->dwFlags & TOUCHEVENTF_UP) {
1249             eventID = com_sun_glass_events_TouchEvent_TOUCH_RELEASED;
1250         }
1251 
1252         POINT screen;
1253         POINT client;
1254         client.x = screen.x = LONG(ti->x / 100);
1255         client.y = screen.y = LONG(ti->y / 100);
1256         ScreenToClient(hWnd, &client);
1257 
1258         // unmirror the x coordinate
1259         LONG style = ::GetWindowLong(hWnd, GWL_EXSTYLE);
1260         if (style & WS_EX_LAYOUTRTL) {
1261             RECT rect = {0};
1262             ::GetClientRect(hWnd, &rect);
1263             client.x = max(0, rect.right - rect.left) - client.x;
1264         }
1265         
1266         env->CallStaticObjectMethod(gestureSupportCls, 
1267                                     javaIDs.Gestures.notifyNextTouchEventMID,
1268                                     view, eventID, touchID, 
1269                                     jint(client.x), jint(client.y),
1270                                     jint(screen.x), jint(screen.y));
1271         CheckAndClearException(env);
1272     }
1273 
1274     env->CallStaticObjectMethod(
1275             gestureSupportCls, javaIDs.Gestures.notifyEndTouchEventMID, view);
1276     CheckAndClearException(env);
1277 }
1278 
1279 void NotifyManipulationProcessor(
1280         IManipulationProcessor& manipProc, 
1281         const TOUCHINPUT* ti, unsigned count) 
1282 {
1283     for (; count; --count, ++ti) {
1284         if (ti->dwFlags & TOUCHEVENTF_DOWN) {
1285             manipProc.ProcessDownWithTime(ti->dwID, FLOAT(ti->x), FLOAT(ti->y), ti->dwTime);
1286         }
1287         if (ti->dwFlags & TOUCHEVENTF_MOVE) {
1288             manipProc.ProcessMoveWithTime(ti->dwID, FLOAT(ti->x), FLOAT(ti->y), ti->dwTime);
1289         }
1290         if (ti->dwFlags & TOUCHEVENTF_UP) {
1291             manipProc.ProcessUpWithTime(ti->dwID, FLOAT(ti->x), FLOAT(ti->y), ti->dwTime);
1292         }
1293     }
1294 }
1295 
1296 } // namespace
1297 
1298 void ViewContainer::HandleViewTouchEvent(
1299         HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
1300 {
1301     // Preallocated arrays. One element per finger. Though so far
1302     // Win 7 can handle at most four touch points simultaneously, 
1303     // so the buffer is excessive.
1304     TOUCHINPUT staticTouchInputBuf[10];
1305 
1306     std::vector<TOUCHINPUT> dynamicTouchInputBuf;
1307     PTOUCHINPUT pInputs = staticTouchInputBuf;
1308 
1309     const UINT cInputs = static_cast<UINT>(LOWORD(wParam));
1310     if (ARRAYSIZE(staticTouchInputBuf) < cInputs) {
1311         dynamicTouchInputBuf.resize(cInputs);
1312         pInputs = &*dynamicTouchInputBuf.begin();
1313     }
1314 
1315     do {
1316         AutoTouchInputHandle inputInfo(lParam);
1317         if (!::GetTouchInputInfo(inputInfo, cInputs, 
1318                                  pInputs, sizeof(TOUCHINPUT))) {
1319             return;
1320         }
1321     } while(0); // scope for 'inputInfo'
1322 
1323     NotifyTouchInput(hWnd, GetView(), m_gestureSupportCls, pInputs, cInputs);
1324 
1325     if (m_manipProc) {
1326         NotifyManipulationProcessor(*m_manipProc, pInputs, cInputs);
1327     }
1328 }
1329 
1330 void ViewContainer::HandleViewTimerEvent(HWND hwnd, UINT_PTR timerID)
1331 {
1332     if (IDT_GLASS_INERTIAPROCESSOR == timerID) {
1333         BOOL completed = FALSE;
1334         HRESULT hr = m_inertiaProc->Process(&completed);
1335         if (SUCCEEDED(hr) && completed) {
1336             StopTouchInputInertia(hwnd);
1337             
1338             JNIEnv *env = GetEnv();
1339             env->CallStaticVoidMethod(m_gestureSupportCls, 
1340                     javaIDs.Gestures.inertiaGestureFinishedMID, GetView());
1341             CheckAndClearException(env);
1342         }
1343     }
1344 }
1345 
1346 void ViewContainer::NotifyGesturePerformed(HWND hWnd, 
1347         bool isDirect, bool isInertia,
1348         FLOAT x, FLOAT y, FLOAT deltaX, FLOAT deltaY,
1349         FLOAT scaleDelta, FLOAT expansionDelta, FLOAT rotationDelta,
1350         FLOAT cumulativeDeltaX, FLOAT cumulativeDeltaY,
1351         FLOAT cumulativeScale, FLOAT cumulativeExpansion, 
1352         FLOAT cumulativeRotation) 
1353 {
1354     JNIEnv *env = GetEnv();
1355 
1356     POINT screen;
1357     screen.x = LONG((x + 0.5) / 100);
1358     screen.y = LONG((y + 0.5) / 100);
1359 
1360     POINT client;
1361     client.x = screen.x;
1362     client.y = screen.y;
1363     ScreenToClient(hWnd, &client);
1364 
1365     // unmirror the x coordinate
1366     LONG style = ::GetWindowLong(hWnd, GWL_EXSTYLE);
1367     if (style & WS_EX_LAYOUTRTL) {
1368         RECT rect = {0};
1369         ::GetClientRect(hWnd, &rect);
1370         client.x = max(0, rect.right - rect.left) - client.x;
1371     }
1372 
1373     jint modifiers = GetModifiers();
1374     env->CallStaticVoidMethod(m_gestureSupportCls, 
1375                               javaIDs.Gestures.gesturePerformedMID,
1376                               GetView(), modifiers, 
1377                               jboolean(isDirect), jboolean(isInertia),
1378                               jint(client.x), jint(client.y),
1379                               jint(screen.x), jint(screen.y),
1380                               deltaX / 100, deltaY / 100,
1381                               cumulativeDeltaX / 100, cumulativeDeltaY / 100,
1382                               cumulativeScale, cumulativeExpansion / 100, 
1383                               cumulativeRotation);
1384     CheckAndClearException(env);
1385 }
1386 
1387 void ViewContainer::StartTouchInputInertia(HWND hwnd)
1388 {
1389     // TBD: check errors
1390 
1391     //
1392     // Collect initial inertia data 
1393     //
1394 
1395     FLOAT vX, vY;
1396     m_manipProc->GetVelocityX(&vX);
1397     m_manipProc->GetVelocityY(&vY);
1398     
1399     const FLOAT VELOCITY_THRESHOLD = 10.0f;
1400 
1401     if (fabs(vX) < VELOCITY_THRESHOLD && fabs(vY) < VELOCITY_THRESHOLD) {
1402         return;
1403     }
1404 
1405     // TBD: check errors
1406     POINT origin;
1407     GetCursorPos(&origin);
1408 
1409     //
1410     // Setup inertia.
1411     //
1412     
1413     m_inertiaProc->Reset();
1414 
1415     m_inertiaProc->put_DesiredDeceleration(0.23f);
1416 
1417     // Set initial origins.
1418     m_inertiaProc->put_InitialOriginX(origin.x * 100.0f);
1419     m_inertiaProc->put_InitialOriginY(origin.y * 100.0f);
1420     
1421     // Set initial velocities.    
1422     m_inertiaProc->put_InitialVelocityX(vX);
1423     m_inertiaProc->put_InitialVelocityY(vY);
1424     
1425     // TBD: check errors
1426     ::SetTimer(hwnd, IDT_GLASS_INERTIAPROCESSOR, 16, NULL);
1427 }
1428 
1429 void ViewContainer::StopTouchInputInertia(HWND hwnd)
1430 {
1431     // TBD: check errors
1432     ::KillTimer(hwnd, IDT_GLASS_INERTIAPROCESSOR);
1433 }
1434 
1435 
1436 extern "C" {
1437 JNIEXPORT void JNICALL Java_com_sun_glass_ui_win_WinGestureSupport__1initIDs(
1438         JNIEnv *env, jclass cls) 
1439 {
1440     javaIDs.Gestures.gesturePerformedMID = 
1441         env->GetStaticMethodID(cls, "gesturePerformed",
1442                                 "(Lcom/sun/glass/ui/View;IZZIIIIFFFFFFF)V");
1443     CheckAndClearException(env);
1444     
1445     javaIDs.Gestures.inertiaGestureFinishedMID = 
1446         env->GetStaticMethodID(cls, "inertiaGestureFinished",
1447                                 "(Lcom/sun/glass/ui/View;)V");
1448     CheckAndClearException(env);
1449 
1450     javaIDs.Gestures.notifyBeginTouchEventMID = 
1451         env->GetStaticMethodID(cls, "notifyBeginTouchEvent", 
1452                                 "(Lcom/sun/glass/ui/View;IZI)V");
1453     CheckAndClearException(env);
1454 
1455     javaIDs.Gestures.notifyNextTouchEventMID = 
1456         env->GetStaticMethodID(cls, "notifyNextTouchEvent", 
1457                                 "(Lcom/sun/glass/ui/View;IJIIII)V");
1458     CheckAndClearException(env);
1459 
1460     javaIDs.Gestures.notifyEndTouchEventMID = 
1461         env->GetStaticMethodID(cls, "notifyEndTouchEvent", 
1462                                 "(Lcom/sun/glass/ui/View;)V");
1463     CheckAndClearException(env);
1464 }
1465 
1466 } // extern "C"