goldendict-ng/src/hotkeywrapper.cc

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

850 lines
22 KiB
C++
Raw Normal View History

#include "hotkeywrapper.hh"
#include "gddebug.hh"
#include <QSessionManager>
#include <QTimer>
#include <QWidget>
#ifdef Q_OS_WIN
#include "mainwindow.hh"
#include <windows.h>
#endif
//////////////////////////////////////////////////////////////////////////
QHotkeyApplication::QHotkeyApplication( int & argc, char ** argv ):
QtSingleApplication( argc, argv )
{
connect( this,
&QGuiApplication::commitDataRequest,
this,
&QHotkeyApplication::hotkeyAppCommitData,
Qt::DirectConnection );
connect( this,
&QGuiApplication::saveStateRequest,
this,
&QHotkeyApplication::hotkeyAppSaveState,
Qt::DirectConnection );
2021-11-26 08:42:21 +00:00
#if defined( Q_OS_WIN )
installNativeEventFilter( this );
#endif
}
QHotkeyApplication::QHotkeyApplication( QString const & id, int & argc, char ** argv ):
QtSingleApplication( id, argc, argv )
{
connect( this,
&QGuiApplication::commitDataRequest,
this,
&QHotkeyApplication::hotkeyAppCommitData,
Qt::DirectConnection );
connect( this,
&QGuiApplication::saveStateRequest,
this,
&QHotkeyApplication::hotkeyAppSaveState,
Qt::DirectConnection );
2021-11-26 08:42:21 +00:00
#if defined( Q_OS_WIN )
installNativeEventFilter( this );
#endif
}
void QHotkeyApplication::addDataCommiter( DataCommitter & d )
{
dataCommitters.append( &d );
}
void QHotkeyApplication::removeDataCommiter( DataCommitter & d )
{
dataCommitters.removeAll( &d );
}
2018-03-27 14:46:03 +00:00
void QHotkeyApplication::hotkeyAppCommitData( QSessionManager & mgr )
{
for ( int x = 0; x < dataCommitters.size(); ++x ) {
2018-03-27 14:46:03 +00:00
dataCommitters[ x ]->commitData( mgr );
}
2018-03-27 14:46:03 +00:00
}
2018-03-27 14:46:03 +00:00
void QHotkeyApplication::hotkeyAppSaveState( QSessionManager & mgr )
{
mgr.setRestartHint( QSessionManager::RestartNever );
}
void QHotkeyApplication::registerWrapper( HotkeyWrapper * wrapper )
{
if ( wrapper && !hotkeyWrappers.contains( wrapper ) ) {
hotkeyWrappers.append( wrapper );
}
}
void QHotkeyApplication::unregisterWrapper( HotkeyWrapper * wrapper )
{
if ( wrapper && hotkeyWrappers.contains( wrapper ) ) {
hotkeyWrappers.removeAll( wrapper );
}
}
//////////////////////////////////////////////////////////////////////////
HotkeyStruct::HotkeyStruct( quint32 key_, quint32 key2_, quint32 modifier_, int handle_, int id_ ):
key( key_ ),
key2( key2_ ),
modifier( modifier_ ),
handle( handle_ ),
id( id_ )
#ifdef Q_OS_MAC
,
hkRef( 0 ),
hkRef2( 0 )
#endif
{
}
//////////////////////////////////////////////////////////////////////////
2024-06-24 08:05:41 +00:00
#if !defined( Q_OS_MAC )
HotkeyWrapper::HotkeyWrapper( QObject * parent ):
QThread( parent ),
state2( false )
{
2011-11-17 13:01:42 +00:00
#ifdef Q_OS_WIN
hwnd = (HWND)( ( static_cast< QMainWindow * >( parent ) )->winId() );
gdDebug( "Handle global hotkeys via RegisterHotkey()" );
2011-11-17 13:01:42 +00:00
#else
init();
2011-11-17 13:01:42 +00:00
#endif
( static_cast< QHotkeyApplication * >( qApp ) )->registerWrapper( this );
}
HotkeyWrapper::~HotkeyWrapper()
{
unregister();
}
void HotkeyWrapper::waitKey2()
{
state2 = false;
2013-05-31 05:28:36 +00:00
#ifdef HAVE_X11
if ( keyToUngrab != grabbedKeys.end() ) {
ungrabKey( keyToUngrab );
keyToUngrab = grabbedKeys.end();
}
#endif
}
bool HotkeyWrapper::checkState( quint32 vk, quint32 mod )
{
if ( state2 ) { // wait for 2nd key
waitKey2(); // Cancel the 2nd-key wait stage
if ( state2waiter.key2 == vk && state2waiter.modifier == mod ) {
emit hotkeyActivated( state2waiter.handle );
return true;
}
}
for ( int i = 0; i < hotkeys.count(); i++ ) {
const HotkeyStruct & hs = hotkeys.at( i );
if ( hs.key == vk && hs.modifier == mod ) {
#ifdef Q_OS_WIN32
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
INPUT i[ 10 ];
memset( i, 0, sizeof( i ) );
int nextKeyNom = 0;
short emulateModKeys = 0;
// If modifier keys aren't pressed it looks like emulation
// We then also emulate full sequence
if ( ( mod & MOD_ALT ) != 0 && ( GetAsyncKeyState( VK_MENU ) & 0x8000 ) == 0 ) {
emulateModKeys |= MOD_ALT;
i[ nextKeyNom ].type = INPUT_KEYBOARD;
i[ nextKeyNom ].ki.wVk = VK_MENU;
nextKeyNom += 1;
}
if ( ( mod & MOD_CONTROL ) != 0 && ( GetAsyncKeyState( VK_CONTROL ) & 0x8000 ) == 0 ) {
emulateModKeys |= MOD_CONTROL;
i[ nextKeyNom ].type = INPUT_KEYBOARD;
i[ nextKeyNom ].ki.wVk = VK_CONTROL;
nextKeyNom += 1;
}
if ( ( mod & MOD_SHIFT ) != 0 && ( GetAsyncKeyState( VK_SHIFT ) & 0x8000 ) == 0 ) {
emulateModKeys |= MOD_SHIFT;
i[ nextKeyNom ].type = INPUT_KEYBOARD;
i[ nextKeyNom ].ki.wVk = VK_SHIFT;
nextKeyNom += 1;
}
if ( ( mod & MOD_WIN ) != 0 && ( GetAsyncKeyState( VK_LWIN ) & 0x8000 ) == 0
&& ( GetAsyncKeyState( VK_RWIN ) & 0x8000 ) == 0 ) {
emulateModKeys |= MOD_WIN;
i[ nextKeyNom ].type = INPUT_KEYBOARD;
i[ nextKeyNom ].ki.wVk = VK_LWIN;
nextKeyNom += 1;
}
i[ nextKeyNom ].type = INPUT_KEYBOARD;
i[ nextKeyNom ].ki.wVk = vk;
nextKeyNom += 1;
i[ nextKeyNom ].type = INPUT_KEYBOARD;
i[ nextKeyNom ].ki.wVk = vk;
i[ nextKeyNom ].ki.dwFlags = KEYEVENTF_KEYUP;
nextKeyNom += 1;
if ( emulateModKeys & MOD_WIN ) {
i[ nextKeyNom ].type = INPUT_KEYBOARD;
i[ nextKeyNom ].ki.wVk = VK_LWIN;
i[ nextKeyNom ].ki.dwFlags = KEYEVENTF_KEYUP;
nextKeyNom += 1;
}
if ( emulateModKeys & MOD_SHIFT ) {
i[ nextKeyNom ].type = INPUT_KEYBOARD;
i[ nextKeyNom ].ki.wVk = VK_SHIFT;
i[ nextKeyNom ].ki.dwFlags = KEYEVENTF_KEYUP;
nextKeyNom += 1;
}
if ( emulateModKeys & MOD_CONTROL ) {
i[ nextKeyNom ].type = INPUT_KEYBOARD;
i[ nextKeyNom ].ki.wVk = VK_CONTROL;
i[ nextKeyNom ].ki.dwFlags = KEYEVENTF_KEYUP;
nextKeyNom += 1;
}
if ( emulateModKeys & MOD_ALT ) {
i[ nextKeyNom ].type = INPUT_KEYBOARD;
i[ nextKeyNom ].ki.wVk = VK_MENU;
i[ nextKeyNom ].ki.dwFlags = KEYEVENTF_KEYUP;
nextKeyNom += 1;
}
UnregisterHotKey( hwnd, hs.id );
SendInput( nextKeyNom, i, sizeof( *i ) );
RegisterHotKey( hwnd, hs.id, hs.modifier, hs.key );
}
#endif
if ( hs.key2 == 0 ) {
emit hotkeyActivated( hs.handle );
return true;
}
state2 = true;
state2waiter = hs;
QTimer::singleShot( 500, this, &HotkeyWrapper::waitKey2 );
2013-05-31 05:28:36 +00:00
#ifdef HAVE_X11
// Grab the second key, unless it's grabbed already
// Note that we only grab the clipboard key only if
// the sequence didn't begin with it
if ( ( isCopyToClipboardKey( hs.key, hs.modifier ) || !isCopyToClipboardKey( hs.key2, hs.modifier ) )
&& !isKeyGrabbed( hs.key2, hs.modifier ) )
keyToUngrab = grabKey( hs.key2, hs.modifier );
#endif
return true;
}
}
state2 = false;
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;
2011-11-17 13:01:42 +00:00
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
{
2012-10-31 13:58:35 +00:00
(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 ) {
2022-02-27 05:17:37 +00:00
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:
2022-02-25 15:01:54 +00:00
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;
2022-02-27 05:17:37 +00:00
case Qt::Key_Equal:
return VK_OEM_PLUS;
case Qt::Key_ParenRight:
return 0x30;
case Qt::Key_Exclam:
return 0x31;
2022-02-27 05:17:37 +00:00
case Qt::Key_At:
return 0x32;
2022-02-27 05:17:37 +00:00
case Qt::Key_NumberSign:
return 0x33;
2022-02-27 05:17:37 +00:00
case Qt::Key_Dollar:
return 0x34;
2022-02-27 05:17:37 +00:00
case Qt::Key_Percent:
return 0x35;
2022-02-25 15:08:17 +00:00
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 );
2022-05-28 01:38:10 +00:00
UnregisterHotKey( hwnd, hk.id );
2022-05-28 01:38:10 +00:00
if ( hk.key2 && hk.key2 != hk.key )
UnregisterHotKey( hwnd, hk.id + 1 );
}
2022-05-28 01:38:10 +00:00
( static_cast< QHotkeyApplication * >( qApp ) )->unregisterWrapper( this );
}
2022-02-27 05:17:37 +00:00
#if ( QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) )
2022-05-28 01:38:10 +00:00
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
//////////////////////////////////////////////////////////////////////////
#include <X11/keysym.h>
void HotkeyWrapper::init()
{
keyToUngrab = grabbedKeys.end();
#if QT_VERSION < 0x060000
Display * displayID = QX11Info::display();
#else
QNativeInterface::QX11Application * x11AppInfo = qApp->nativeInterface< QNativeInterface::QX11Application >();
Display * displayID = x11AppInfo->display();
#endif
// We use RECORD extension instead of XGrabKey. That's because XGrabKey
// prevents other clients from getting their input if it's grabbed.
2022-05-28 01:38:10 +00:00
Display * display = displayID;
lShiftCode = XKeysymToKeycode( display, XK_Shift_L );
rShiftCode = XKeysymToKeycode( display, XK_Shift_R );
lCtrlCode = XKeysymToKeycode( display, XK_Control_L );
rCtrlCode = XKeysymToKeycode( display, XK_Control_R );
lAltCode = XKeysymToKeycode( display, XK_Alt_L );
rAltCode = XKeysymToKeycode( display, XK_Alt_R );
lMetaCode = XKeysymToKeycode( display, XK_Super_L );
rMetaCode = XKeysymToKeycode( display, XK_Super_R );
cCode = XKeysymToKeycode( display, XK_c );
insertCode = XKeysymToKeycode( display, XK_Insert );
kpInsertCode = XKeysymToKeycode( display, XK_KP_Insert );
currentModifiers = 0;
// This one will be used to read the recorded content
dataDisplay = XOpenDisplay( 0 );
if ( !dataDisplay )
throw exInit();
recordRange = XRecordAllocRange();
2014-05-11 08:39:55 +00:00
if ( !recordRange ) {
XCloseDisplay( dataDisplay );
throw exInit();
}
recordRange->device_events.first = KeyPress;
recordRange->device_events.last = KeyRelease;
recordClientSpec = XRecordAllClients;
recordContext = XRecordCreateContext( display, 0, &recordClientSpec, 1, &recordRange, 1 );
if ( !recordContext ) {
XFree( recordRange );
XCloseDisplay( dataDisplay );
throw exInit();
}
// This is required to ensure context was indeed created
XSync( display, False );
connect( this, &HotkeyWrapper::keyRecorded, this, &HotkeyWrapper::checkState, Qt::QueuedConnection );
start();
}
void HotkeyWrapper::run() // Runs in a separate thread
{
if ( !XRecordEnableContext( dataDisplay, recordContext, recordEventCallback, (XPointer)this ) )
GD_DPRINTF( "Failed to enable record context\n" );
}
void HotkeyWrapper::recordEventCallback( XPointer ptr, XRecordInterceptData * data )
{
( (HotkeyWrapper *)ptr )->handleRecordEvent( data );
}
void HotkeyWrapper::handleRecordEvent( XRecordInterceptData * data )
{
if ( data->category == XRecordFromServer ) {
xEvent * event = (xEvent *)data->data;
if ( event->u.u.type == KeyPress ) {
KeyCode key = event->u.u.detail;
if ( key == lShiftCode || key == rShiftCode )
currentModifiers |= ShiftMask;
else if ( key == lCtrlCode || key == rCtrlCode )
currentModifiers |= ControlMask;
else if ( key == lAltCode || key == rAltCode )
currentModifiers |= Mod1Mask;
else if ( key == lMetaCode || key == rMetaCode )
currentModifiers |= Mod4Mask;
else {
// Here we employ a kind of hack translating KP_Insert key
// to just Insert. This allows reacting to both Insert keys.
if ( key == kpInsertCode )
key = insertCode;
emit keyRecorded( key, currentModifiers );
}
}
else if ( event->u.u.type == KeyRelease ) {
KeyCode key = event->u.u.detail;
if ( key == lShiftCode || key == rShiftCode )
currentModifiers &= ~ShiftMask;
else if ( key == lCtrlCode || key == rCtrlCode )
currentModifiers &= ~ControlMask;
else if ( key == lAltCode || key == rAltCode )
currentModifiers &= ~Mod1Mask;
else if ( key == lMetaCode || key == rMetaCode )
currentModifiers &= ~Mod4Mask;
}
}
XRecordFreeData( data );
}
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
int vk = nativeKey( key );
int vk2 = key2 ? nativeKey( key2 ) : 0;
quint32 mod = 0;
if ( modifier & Qt::ShiftModifier )
mod |= ShiftMask;
if ( modifier & Qt::ControlModifier )
mod |= ControlMask;
if ( modifier & Qt::AltModifier )
mod |= Mod1Mask;
if ( modifier & Qt::MetaModifier )
mod |= Mod4Mask;
hotkeys.append( HotkeyStruct( vk, vk2, mod, handle, 0 ) );
if ( !isCopyToClipboardKey( vk, mod ) )
grabKey( vk, mod ); // Make sure it doesn't get caught by other apps
return true;
}
bool HotkeyWrapper::isCopyToClipboardKey( quint32 keyCode, quint32 modifiers ) const
{
return modifiers == ControlMask && ( keyCode == cCode || keyCode == insertCode || keyCode == kpInsertCode );
}
bool HotkeyWrapper::isKeyGrabbed( quint32 keyCode, quint32 modifiers ) const
{
GrabbedKeys::const_iterator i = grabbedKeys.find( std::make_pair( keyCode, modifiers ) );
return i != grabbedKeys.end();
}
namespace {
using X11ErrorHandler = int ( * )( Display * display, XErrorEvent * event );
class X11GrabUngrabErrorHandler
{
public:
static bool error;
static int grab_ungrab_error_handler( Display *, XErrorEvent * event )
{
qDebug() << "grab_ungrab_error_handler is invoked";
switch ( event->error_code ) {
case BadAccess:
case BadValue:
case BadWindow:
if ( event->request_code == 33 /* X_GrabKey */ || event->request_code == 34 /* X_UngrabKey */ ) {
error = true;
}
}
return 0;
}
X11GrabUngrabErrorHandler()
{
error = false;
previousErrorHandler_ = XSetErrorHandler( grab_ungrab_error_handler );
}
~X11GrabUngrabErrorHandler()
{
2022-05-28 01:38:10 +00:00
#if QT_VERSION < 0x060000
Display * displayID = QX11Info::display();
#else
QNativeInterface::QX11Application * x11AppInfo = qApp->nativeInterface< QNativeInterface::QX11Application >();
Display * displayID = x11AppInfo->display();
#endif
XFlush( displayID );
(void)XSetErrorHandler( previousErrorHandler_ );
}
bool isError() const
{
2022-05-28 01:38:10 +00:00
#if QT_VERSION < 0x060000
Display * displayID = QX11Info::display();
#else
QNativeInterface::QX11Application * x11AppInfo = qApp->nativeInterface< QNativeInterface::QX11Application >();
Display * displayID = x11AppInfo->display();
#endif
XFlush( displayID );
return error;
}
private:
X11ErrorHandler previousErrorHandler_;
};
bool X11GrabUngrabErrorHandler::error = false;
} // anonymous namespace
HotkeyWrapper::GrabbedKeys::iterator HotkeyWrapper::grabKey( quint32 keyCode, quint32 modifiers )
{
std::pair< GrabbedKeys::iterator, bool > result = grabbedKeys.insert( std::make_pair( keyCode, modifiers ) );
if ( result.second ) {
2022-05-28 01:38:10 +00:00
#if QT_VERSION < 0x060000
Display * displayID = QX11Info::display();
#else
QNativeInterface::QX11Application * x11AppInfo = qApp->nativeInterface< QNativeInterface::QX11Application >();
Display * displayID = x11AppInfo->display();
#endif
X11GrabUngrabErrorHandler errorHandler;
2022-05-28 01:38:10 +00:00
XGrabKey( displayID, keyCode, modifiers, DefaultRootWindow( displayID ), True, GrabModeAsync, GrabModeAsync );
if ( errorHandler.isError() ) {
2017-06-22 15:02:04 +00:00
gdWarning( "Possible hotkeys conflict. Check your hotkeys options." );
ungrabKey( result.first );
}
}
return result.first;
}
void HotkeyWrapper::ungrabKey( GrabbedKeys::iterator i )
{
2022-05-28 01:38:10 +00:00
#if QT_VERSION < 0x060000
Display * displayID = QX11Info::display();
#else
QNativeInterface::QX11Application * x11AppInfo = qApp->nativeInterface< QNativeInterface::QX11Application >();
Display * displayID = x11AppInfo->display();
#endif
X11GrabUngrabErrorHandler errorHandler;
2022-05-28 01:38:10 +00:00
XUngrabKey( displayID, i->first, i->second, XDefaultRootWindow( displayID ) );
grabbedKeys.erase( i );
if ( errorHandler.isError() ) {
2017-06-22 15:02:04 +00:00
gdWarning( "Cannot ungrab the hotkey" );
}
}
quint32 HotkeyWrapper::nativeKey( int key )
{
QString keySymName;
switch ( key ) {
case Qt::Key_Insert:
keySymName = "Insert";
break;
default:
keySymName = QKeySequence( key ).toString();
break;
}
2022-05-28 01:38:10 +00:00
#if QT_VERSION < 0x060000
Display * displayID = QX11Info::display();
#else
QNativeInterface::QX11Application * x11AppInfo = qApp->nativeInterface< QNativeInterface::QX11Application >();
Display * displayID = x11AppInfo->display();
#endif
Display * display = displayID;
return XKeysymToKeycode( display, XStringToKeysym( keySymName.toLatin1().data() ) );
}
void HotkeyWrapper::unregister()
{
2022-05-28 01:38:10 +00:00
#if QT_VERSION < 0x060000
Display * displayID = QX11Info::display();
#else
QNativeInterface::QX11Application * x11AppInfo = qApp->nativeInterface< QNativeInterface::QX11Application >();
Display * displayID = x11AppInfo->display();
#endif
Display * display = displayID;
XRecordDisableContext( display, recordContext );
XSync( display, False );
wait();
XRecordFreeContext( display, recordContext );
XFree( recordRange );
XCloseDisplay( dataDisplay );
while ( grabbedKeys.size() )
ungrabKey( grabbedKeys.begin() );
( static_cast< QHotkeyApplication * >( qApp ) )->unregisterWrapper( this );
}
#endif
#endif