diff --git a/modules/juce_gui_basics/native/juce_Windowing_windows.cpp b/modules/juce_gui_basics/native/juce_Windowing_windows.cpp index aee5fb9891..6444b71c4d 100644 --- a/modules/juce_gui_basics/native/juce_Windowing_windows.cpp +++ b/modules/juce_gui_basics/native/juce_Windowing_windows.cpp @@ -2329,49 +2329,68 @@ public: JUCE_DECLARE_NON_COPYABLE (FileDropTarget) }; - static bool offerKeyMessageToJUCEWindow (MSG& m) + static bool offerKeyMessageToJUCEWindow (const MSG& msg) { - auto* peer = getOwnerOfWindow (m.hwnd); + // If this isn't a keyboard message, let the host deal with it. - if (peer == nullptr) + constexpr UINT messages[] { WM_KEYDOWN, WM_SYSKEYDOWN, WM_KEYUP, WM_SYSKEYUP, WM_CHAR, WM_SYSCHAR }; + + if (std::find (std::begin (messages), std::end (messages), msg.message) == std::end (messages)) return false; + auto* peer = getOwnerOfWindow (msg.hwnd); auto* focused = Component::getCurrentlyFocusedComponent(); - if (focused == nullptr || focused->getPeer() != peer) + if (focused == nullptr || peer == nullptr || focused->getPeer() != peer) return false; - if (TranslateMessage (&m)) - return true; + auto* hwnd = static_cast (peer->getNativeHandle()); - constexpr UINT keyMessages[] { WM_KEYDOWN, - WM_KEYUP, - WM_SYSKEYDOWN, - WM_SYSKEYUP, - WM_CHAR }; - - const auto messageTypeMatches = [&] (UINT msg) { return m.message == msg; }; - - if (std::none_of (std::begin (keyMessages), std::end (keyMessages), messageTypeMatches)) + if (hwnd == nullptr) return false; - ScopedThreadDPIAwarenessSetter threadDpiAwarenessSetter { m.hwnd }; + ScopedThreadDPIAwarenessSetter threadDpiAwarenessSetter { hwnd }; - if (m.message == WM_CHAR) - return peer->doKeyChar ((int) m.wParam, m.lParam); + // If we've been sent a text character, process it as text. - switch (m.message) + if (msg.message == WM_CHAR || msg.message == WM_SYSCHAR) + return peer->doKeyChar ((int) msg.wParam, msg.lParam); + + // The event was a keypress, rather than a text character + + if (auto* target = peer->findCurrentTextInputTarget()) { - case WM_KEYDOWN: - case WM_SYSKEYDOWN: - return peer->doKeyDown (m.wParam); + // If there's a focused text input target, we want to attempt "real" text input with an + // IME, and we want to prevent the host from eating keystrokes (spaces etc.). - case WM_KEYUP: - case WM_SYSKEYUP: - return peer->doKeyUp (m.wParam); + TranslateMessage (&msg); + + // TranslateMessage may post WM_CHAR back to the window, so we remove those messages + // from the queue before the host gets to see them. + // This will dispatch pending WM_CHAR messages, so we may end up reentering + // offerKeyMessageToJUCEWindow and hitting the WM_CHAR case above. + // We always return true if WM_CHAR is posted so that the keypress is not forwarded + // to the host. Otherwise, the host may call TranslateMessage again on this message, + // resulting in duplicate WM_CHAR messages being posted. + + MSG peeked{}; + if (PeekMessage (&peeked, hwnd, WM_CHAR, WM_DEADCHAR, PM_REMOVE) + || PeekMessage (&peeked, hwnd, WM_SYSCHAR, WM_SYSDEADCHAR, PM_REMOVE)) + { + return true; + } + + // If TranslateMessage didn't add a WM_CHAR to the queue, fall back to processing the + // event as a plain keypress } - return false; + // There's no text input target, or the key event wasn't translated, so we'll just see if we + // can use the plain keystroke event + + if (msg.message == WM_KEYDOWN || msg.message == WM_SYSKEYDOWN) + return peer->doKeyDown (msg.wParam); + + return peer->doKeyUp (msg.wParam); } double getPlatformScaleFactor() const noexcept override @@ -4350,10 +4369,10 @@ private: case WM_IME_SETCONTEXT: imeHandler.handleSetContext (h, wParam == TRUE); lParam &= ~(LPARAM) ISC_SHOWUICOMPOSITIONWINDOW; - break; + return ImmIsUIMessage (h, message, wParam, lParam); case WM_IME_STARTCOMPOSITION: imeHandler.handleStartComposition (*this); return 0; - case WM_IME_ENDCOMPOSITION: imeHandler.handleEndComposition (*this, h); break; + case WM_IME_ENDCOMPOSITION: imeHandler.handleEndComposition (*this, h); return 0; case WM_IME_COMPOSITION: imeHandler.handleComposition (*this, h, lParam); return 0; case WM_GETDLGCODE: diff --git a/modules/juce_gui_basics/native/juce_WindowsHooks_windows.cpp b/modules/juce_gui_basics/native/juce_WindowsHooks_windows.cpp index 5a3fae24ae..97afe888ee 100644 --- a/modules/juce_gui_basics/native/juce_WindowsHooks_windows.cpp +++ b/modules/juce_gui_basics/native/juce_WindowsHooks_windows.cpp @@ -65,12 +65,11 @@ private: static LRESULT CALLBACK keyboardHookCallback (int nCode, WPARAM wParam, LPARAM lParam) { - MSG& msg = *(MSG*) lParam; + auto& msg = *reinterpret_cast (lParam); - if (nCode == HC_ACTION && wParam == PM_REMOVE - && HWNDComponentPeer::offerKeyMessageToJUCEWindow (msg)) + if (nCode == HC_ACTION && wParam == PM_REMOVE && HWNDComponentPeer::offerKeyMessageToJUCEWindow (msg)) { - zerostruct (msg); + msg = {}; msg.message = WM_USER; return 0; }