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