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"