diff --git a/src/hotkeywrapper.cc b/src/hotkeywrapper.cc index 0bc9927d..c46c8c23 100644 --- a/src/hotkeywrapper.cc +++ b/src/hotkeywrapper.cc @@ -1,12 +1,9 @@ #include "hotkeywrapper.hh" #include "gddebug.hh" - -#include #include +#include #include - #ifdef Q_OS_WIN - #include "mainwindow.hh" #include #endif @@ -96,11 +93,6 @@ HotkeyStruct::HotkeyStruct( quint32 key_, quint32 key2_, quint32 modifier_, int modifier( modifier_ ), handle( handle_ ), id( id_ ) -#ifdef Q_OS_MAC - , - hkRef( 0 ), - hkRef2( 0 ) -#endif { } @@ -111,14 +103,7 @@ HotkeyWrapper::HotkeyWrapper( QObject * parent ): QThread( parent ), state2( false ) { - #ifdef Q_OS_WIN - hwnd = (HWND)( ( static_cast< QMainWindow * >( parent ) )->winId() ); - - gdDebug( "Handle global hotkeys via RegisterHotkey()" ); - - #else init(); - #endif ( static_cast< QHotkeyApplication * >( qApp ) )->registerWrapper( this ); } @@ -160,7 +145,8 @@ bool HotkeyWrapper::checkState( quint32 vk, quint32 mod ) if ( hs.key == vk && hs.modifier == mod ) { #ifdef Q_OS_WIN32 - + // If that was a copy-to-clipboard shortcut, re-emit it back so it could + // reach its original destination so it could be acted upon. if ( hs.key2 != 0 || ( mod == MOD_CONTROL && ( vk == VK_INSERT || vk == 'c' || vk == 'C' ) ) ) { // Pass-through first part of compound hotkey or clipdoard copy command @@ -266,264 +252,8 @@ bool HotkeyWrapper::checkState( quint32 vk, quint32 mod ) return false; } -////////////////////////////////////////////////////////////////////////// - #ifdef Q_OS_WIN - -void HotkeyWrapper::init() -{ - QWidget * root = qApp->topLevelWidgets().value( 0 ); - hwnd = (HWND)root->winId(); -} - -bool HotkeyWrapper::setGlobalKey( QKeySequence const & seq, int handle ) -{ - Config::HotKey hk( seq ); - return setGlobalKey( hk.key1, hk.key2, hk.modifiers, handle ); -} - -bool HotkeyWrapper::setGlobalKey( int key, int key2, Qt::KeyboardModifiers modifier, int handle ) -{ - if ( !key ) - return false; // We don't monitor empty combinations - - static int id = 0; - if ( id > 0xBFFF - 1 ) - id = 0; - - quint32 mod = 0; - if ( modifier & Qt::CTRL ) - mod |= MOD_CONTROL; - if ( modifier & Qt::ALT ) - mod |= MOD_ALT; - if ( modifier & Qt::SHIFT ) - mod |= MOD_SHIFT; - if ( modifier & Qt::META ) - mod |= MOD_WIN; - - quint32 vk = nativeKey( key ); - quint32 vk2 = key2 ? nativeKey( key2 ) : 0; - - hotkeys.append( HotkeyStruct( vk, vk2, mod, handle, id ) ); - - if ( !RegisterHotKey( hwnd, id++, mod, vk ) ) - return false; - - if ( key2 && key2 != key ) - return RegisterHotKey( hwnd, id++, mod, vk2 ); - - return true; -} - - #if ( QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) ) -bool HotkeyWrapper::winEvent( MSG * message, long * result ) - #else -bool HotkeyWrapper::winEvent( MSG * message, qintptr * result ) - #endif -{ - (void)result; - if ( message->message == WM_HOTKEY ) - return checkState( ( message->lParam >> 16 ), ( message->lParam & 0xffff ) ); - - return false; -} - -/// Ref: https://learn.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes -quint32 HotkeyWrapper::nativeKey( int key ) -{ - // Qt's 0-9 & A-Z overlaps with Windows's VK - if ( key >= Qt::Key_0 && key <= Qt::Key_9 || key >= Qt::Key_A && key <= Qt::Key_Z ) { - return key; - } - - switch ( key ) { - case Qt::Key_Space: - return VK_SPACE; - case Qt::Key_Asterisk: - return VK_MULTIPLY; - case Qt::Key_Plus: - return VK_ADD; - case Qt::Key_Comma: - return VK_SEPARATOR; - case Qt::Key_Minus: - return VK_SUBTRACT; - case Qt::Key_Slash: - return VK_DIVIDE; - case Qt::Key_Tab: - case Qt::Key_Backtab: - return VK_TAB; - case Qt::Key_Backspace: - return VK_BACK; - case Qt::Key_Return: - case Qt::Key_Escape: - return VK_ESCAPE; - case Qt::Key_Enter: - return VK_RETURN; - case Qt::Key_Insert: - return VK_INSERT; - case Qt::Key_Delete: - return VK_DELETE; - case Qt::Key_Pause: - return VK_PAUSE; - case Qt::Key_Print: - return VK_PRINT; - case Qt::Key_Clear: - return VK_CLEAR; - case Qt::Key_Home: - return VK_HOME; - case Qt::Key_End: - return VK_END; - case Qt::Key_Up: - return VK_UP; - case Qt::Key_Down: - return VK_DOWN; - case Qt::Key_Left: - return VK_LEFT; - case Qt::Key_Right: - return VK_RIGHT; - case Qt::Key_PageUp: - return VK_PRIOR; - case Qt::Key_PageDown: - return VK_NEXT; - case Qt::Key_F1: - return VK_F1; - case Qt::Key_F2: - return VK_F2; - case Qt::Key_F3: - return VK_F3; - case Qt::Key_F4: - return VK_F4; - case Qt::Key_F5: - return VK_F5; - case Qt::Key_F6: - return VK_F6; - case Qt::Key_F7: - return VK_F7; - case Qt::Key_F8: - return VK_F8; - case Qt::Key_F9: - return VK_F9; - case Qt::Key_F10: - return VK_F10; - case Qt::Key_F11: - return VK_F11; - case Qt::Key_F12: - return VK_F12; - case Qt::Key_F13: - return VK_F13; - case Qt::Key_F14: - return VK_F14; - case Qt::Key_F15: - return VK_F15; - case Qt::Key_F16: - return VK_F16; - case Qt::Key_F17: - return VK_F17; - case Qt::Key_F18: - return VK_F18; - case Qt::Key_F19: - return VK_F19; - case Qt::Key_F20: - return VK_F20; - case Qt::Key_F21: - return VK_F21; - case Qt::Key_F22: - return VK_F22; - case Qt::Key_F23: - return VK_F23; - case Qt::Key_F24: - return VK_F24; - case Qt::Key_Colon: - case Qt::Key_Semicolon: - return VK_OEM_1; - case Qt::Key_Question: - return VK_OEM_2; - case Qt::Key_AsciiTilde: - case Qt::Key_QuoteLeft: - return VK_OEM_3; - case Qt::Key_BraceLeft: - case Qt::Key_BracketLeft: - return VK_OEM_4; - case Qt::Key_Bar: - case Qt::Key_Backslash: - return VK_OEM_5; - case Qt::Key_BraceRight: - case Qt::Key_BracketRight: - return VK_OEM_6; - case Qt::Key_QuoteDbl: - case Qt::Key_Apostrophe: - return VK_OEM_7; - case Qt::Key_Less: - return VK_OEM_COMMA; - case Qt::Key_Greater: - return VK_OEM_PERIOD; - case Qt::Key_Equal: - return VK_OEM_PLUS; - case Qt::Key_ParenRight: - return 0x30; - case Qt::Key_Exclam: - return 0x31; - case Qt::Key_At: - return 0x32; - case Qt::Key_NumberSign: - return 0x33; - case Qt::Key_Dollar: - return 0x34; - case Qt::Key_Percent: - return 0x35; - case Qt::Key_AsciiCircum: - return 0x36; - case Qt::Key_Ampersand: - return 0x37; - case Qt::Key_copyright: - return 0x38; - case Qt::Key_ParenLeft: - return 0x39; - case Qt::Key_Underscore: - return VK_OEM_MINUS; - case Qt::Key_Meta: - return VK_LWIN; - default:; - } - - return key; -} - -void HotkeyWrapper::unregister() -{ - for ( int i = 0; i < hotkeys.count(); i++ ) { - HotkeyStruct const & hk = hotkeys.at( i ); - - UnregisterHotKey( hwnd, hk.id ); - - if ( hk.key2 && hk.key2 != hk.key ) - UnregisterHotKey( hwnd, hk.id + 1 ); - } - - ( static_cast< QHotkeyApplication * >( qApp ) )->unregisterWrapper( this ); -} - - #if ( QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) ) -bool QHotkeyApplication::nativeEventFilter( const QByteArray & /*eventType*/, void * message, long * result ) - #else -bool QHotkeyApplication::nativeEventFilter( const QByteArray & /*eventType*/, void * message, qintptr * result ) - #endif -{ - MSG * msg = reinterpret_cast< MSG * >( message ); - - if ( msg->message == WM_HOTKEY ) { - for ( int i = 0; i < hotkeyWrappers.size(); i++ ) { - if ( hotkeyWrappers.at( i )->winEvent( msg, result ) ) - return true; - } - } - - return false; -} - - ////////////////////////////////////////////////////////////////////////// - - #else + #ifndef Q_OS_WIN ////////////////////////////////////////////////////////////////////////// diff --git a/src/hotkeywrapper.hh b/src/hotkeywrapper.hh index ca233f42..d9f88c16 100644 --- a/src/hotkeywrapper.hh +++ b/src/hotkeywrapper.hh @@ -1,5 +1,12 @@ #pragma once +/// @file +/// Handling global hotkeys and some tricks +/// Part of this header are implmented in +/// + `winhotkeywrapper.cc` +/// + `machotkeywrapper.hh` +/// + #include #include @@ -37,7 +44,7 @@ struct HotkeyStruct { - HotkeyStruct() {} + HotkeyStruct() = default; HotkeyStruct( quint32 key, quint32 key2, quint32 modifier, int handle, int id ); quint32 key, key2; @@ -45,7 +52,8 @@ struct HotkeyStruct int handle; int id; #ifdef Q_OS_MAC - EventHotKeyRef hkRef, hkRef2; + EventHotKeyRef hkRef = 0; + EventHotKeyRef hkRef2 = 0; #endif }; @@ -98,15 +106,11 @@ private: HotkeyStruct state2waiter; #ifdef Q_OS_WIN32 - - #if ( QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) ) - virtual bool winEvent( MSG * message, long * result ); - #else virtual bool winEvent( MSG * message, qintptr * result ); - #endif HWND hwnd; +#endif -#elif defined( Q_OS_MAC ) +#ifdef Q_OS_MAC public: void activated( int hkId ); @@ -117,9 +121,9 @@ private: static EventHandlerUPP hotKeyFunction; quint32 keyC; EventHandlerRef handlerRef; +#endif -#else - +#ifdef HAVE_X11 static void recordEventCallback( XPointer, XRecordInterceptData * ); /// Called by recordEventCallback() @@ -205,16 +209,10 @@ protected: void registerWrapper( HotkeyWrapper * wrapper ); void unregisterWrapper( HotkeyWrapper * wrapper ); -#ifdef Q_OS_WIN32 - - #if ( QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) ) - virtual bool nativeEventFilter( const QByteArray & eventType, void * message, long * result ); - #else +#ifdef Q_OS_WIN virtual bool nativeEventFilter( const QByteArray & eventType, void * message, qintptr * result ); - #endif +#endif -protected: -#endif // Q_OS_WIN32 QList< HotkeyWrapper * > hotkeyWrappers; }; diff --git a/src/windows/winhotkeywrapper.cc b/src/windows/winhotkeywrapper.cc new file mode 100644 index 00000000..8f1f0696 --- /dev/null +++ b/src/windows/winhotkeywrapper.cc @@ -0,0 +1,256 @@ +#include +#ifdef Q_OS_WIN + #include "hotkeywrapper.hh" + #include + #include + +/// Implementation is pretty much using RegisterHotKey & UnregisterHotKey +/// https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-registerhotkey + +void HotkeyWrapper::init() +{ + hwnd = (HWND)( ( static_cast< QWidget * >( this->parent() ) )->winId() ); +} + +bool HotkeyWrapper::setGlobalKey( QKeySequence const & seq, int handle ) +{ + Config::HotKey hk( seq ); + return setGlobalKey( hk.key1, hk.key2, hk.modifiers, handle ); +} + +bool HotkeyWrapper::setGlobalKey( int key, int key2, Qt::KeyboardModifiers modifier, int handle ) +{ + if ( !key ) + return false; // We don't monitor empty combinations + + static int id = 0; + if ( id > 0xBFFF - 1 ) + id = 0; + + quint32 mod = 0; + if ( modifier & Qt::CTRL ) + mod |= MOD_CONTROL; + if ( modifier & Qt::ALT ) + mod |= MOD_ALT; + if ( modifier & Qt::SHIFT ) + mod |= MOD_SHIFT; + if ( modifier & Qt::META ) + mod |= MOD_WIN; + + quint32 vk = nativeKey( key ); + quint32 vk2 = key2 ? nativeKey( key2 ) : 0; + + hotkeys.append( HotkeyStruct( vk, vk2, mod, handle, id ) ); + + if ( !RegisterHotKey( hwnd, id++, mod, vk ) ) + return false; + + if ( key2 && key2 != key ) + return RegisterHotKey( hwnd, id++, mod, vk2 ); + + return true; +} + + +bool HotkeyWrapper::winEvent( MSG * message, qintptr * result ) +{ + Q_UNUSED( result ); + if ( message->message == WM_HOTKEY ) + return checkState( ( message->lParam >> 16 ), ( message->lParam & 0xffff ) ); + + return false; +} + +void HotkeyWrapper::unregister() +{ + for ( int i = 0; i < hotkeys.count(); i++ ) { + HotkeyStruct const & hk = hotkeys.at( i ); + + UnregisterHotKey( hwnd, hk.id ); + + if ( hk.key2 && hk.key2 != hk.key ) + UnregisterHotKey( hwnd, hk.id + 1 ); + } + + ( static_cast< QHotkeyApplication * >( qApp ) )->unregisterWrapper( this ); +} + +bool QHotkeyApplication::nativeEventFilter( const QByteArray & /*eventType*/, void * message, qintptr * result ) +{ + MSG * msg = reinterpret_cast< MSG * >( message ); + + if ( msg->message == WM_HOTKEY ) { + for ( int i = 0; i < hotkeyWrappers.size(); i++ ) { + if ( hotkeyWrappers.at( i )->winEvent( msg, result ) ) + return true; + } + } + + return false; +} + + +/// References: +/// https://learn.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes +/// https://doc.qt.io/qt-6/qt.html#Key-enum +quint32 HotkeyWrapper::nativeKey( int key ) +{ + // Qt's 0-9 & A-Z overlaps with Windows's VK + if ( key >= Qt::Key_0 && key <= Qt::Key_9 || key >= Qt::Key_A && key <= Qt::Key_Z ) { + return key; + } + + switch ( key ) { + case Qt::Key_Space: + return VK_SPACE; + case Qt::Key_Asterisk: + return VK_MULTIPLY; + case Qt::Key_Plus: + return VK_ADD; + case Qt::Key_Comma: + return VK_SEPARATOR; + case Qt::Key_Minus: + return VK_SUBTRACT; + case Qt::Key_Slash: + return VK_DIVIDE; + case Qt::Key_Tab: + case Qt::Key_Backtab: + return VK_TAB; + case Qt::Key_Backspace: + return VK_BACK; + case Qt::Key_Return: + case Qt::Key_Escape: + return VK_ESCAPE; + case Qt::Key_Enter: + return VK_RETURN; + case Qt::Key_Insert: + return VK_INSERT; + case Qt::Key_Delete: + return VK_DELETE; + case Qt::Key_Pause: + return VK_PAUSE; + case Qt::Key_Print: + return VK_PRINT; + case Qt::Key_Clear: + return VK_CLEAR; + case Qt::Key_Home: + return VK_HOME; + case Qt::Key_End: + return VK_END; + case Qt::Key_Up: + return VK_UP; + case Qt::Key_Down: + return VK_DOWN; + case Qt::Key_Left: + return VK_LEFT; + case Qt::Key_Right: + return VK_RIGHT; + case Qt::Key_PageUp: + return VK_PRIOR; + case Qt::Key_PageDown: + return VK_NEXT; + case Qt::Key_F1: + return VK_F1; + case Qt::Key_F2: + return VK_F2; + case Qt::Key_F3: + return VK_F3; + case Qt::Key_F4: + return VK_F4; + case Qt::Key_F5: + return VK_F5; + case Qt::Key_F6: + return VK_F6; + case Qt::Key_F7: + return VK_F7; + case Qt::Key_F8: + return VK_F8; + case Qt::Key_F9: + return VK_F9; + case Qt::Key_F10: + return VK_F10; + case Qt::Key_F11: + return VK_F11; + case Qt::Key_F12: + return VK_F12; + case Qt::Key_F13: + return VK_F13; + case Qt::Key_F14: + return VK_F14; + case Qt::Key_F15: + return VK_F15; + case Qt::Key_F16: + return VK_F16; + case Qt::Key_F17: + return VK_F17; + case Qt::Key_F18: + return VK_F18; + case Qt::Key_F19: + return VK_F19; + case Qt::Key_F20: + return VK_F20; + case Qt::Key_F21: + return VK_F21; + case Qt::Key_F22: + return VK_F22; + case Qt::Key_F23: + return VK_F23; + case Qt::Key_F24: + return VK_F24; + case Qt::Key_Colon: + case Qt::Key_Semicolon: + return VK_OEM_1; + case Qt::Key_Question: + return VK_OEM_2; + case Qt::Key_AsciiTilde: + case Qt::Key_QuoteLeft: + return VK_OEM_3; + case Qt::Key_BraceLeft: + case Qt::Key_BracketLeft: + return VK_OEM_4; + case Qt::Key_Bar: + case Qt::Key_Backslash: + return VK_OEM_5; + case Qt::Key_BraceRight: + case Qt::Key_BracketRight: + return VK_OEM_6; + case Qt::Key_QuoteDbl: + case Qt::Key_Apostrophe: + return VK_OEM_7; + case Qt::Key_Less: + return VK_OEM_COMMA; + case Qt::Key_Greater: + return VK_OEM_PERIOD; + case Qt::Key_Equal: + return VK_OEM_PLUS; + case Qt::Key_ParenRight: + return 0x30; + case Qt::Key_Exclam: + return 0x31; + case Qt::Key_At: + return 0x32; + case Qt::Key_NumberSign: + return 0x33; + case Qt::Key_Dollar: + return 0x34; + case Qt::Key_Percent: + return 0x35; + case Qt::Key_AsciiCircum: + return 0x36; + case Qt::Key_Ampersand: + return 0x37; + case Qt::Key_copyright: + return 0x38; + case Qt::Key_ParenLeft: + return 0x39; + case Qt::Key_Underscore: + return VK_OEM_MINUS; + case Qt::Key_Meta: + return VK_LWIN; + default:; + } + + return key; +} + +#endif