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