2009-04-19 21:32:18 +00:00
|
|
|
#include "hotkeywrapper.hh"
|
2013-11-16 18:34:09 +00:00
|
|
|
#include "gddebug.hh"
|
2023-05-30 06:31:07 +00:00
|
|
|
#include <QTimer>
|
2024-11-14 11:12:17 +00:00
|
|
|
#include <QSessionManager>
|
2013-05-30 13:24:21 +00:00
|
|
|
#include <QWidget>
|
2019-01-31 14:59:24 +00:00
|
|
|
#ifdef Q_OS_WIN
|
2024-11-14 09:06:23 +00:00
|
|
|
#include <windows.h>
|
2014-03-17 14:53:47 +00:00
|
|
|
#endif
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2009-04-19 21:32:18 +00:00
|
|
|
//////////////////////////////////////////////////////////////////////////
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2023-05-11 23:59:56 +00:00
|
|
|
QHotkeyApplication::QHotkeyApplication( int & argc, char ** argv ):
|
2023-04-28 16:45:03 +00:00
|
|
|
QtSingleApplication( argc, argv )
|
2010-11-20 13:43:55 +00:00
|
|
|
{
|
2022-12-26 02:08:17 +00:00
|
|
|
connect( this,
|
|
|
|
&QGuiApplication::commitDataRequest,
|
|
|
|
this,
|
|
|
|
&QHotkeyApplication::hotkeyAppCommitData,
|
|
|
|
Qt::DirectConnection );
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2022-12-26 02:08:17 +00:00
|
|
|
connect( this,
|
|
|
|
&QGuiApplication::saveStateRequest,
|
|
|
|
this,
|
|
|
|
&QHotkeyApplication::hotkeyAppSaveState,
|
|
|
|
Qt::DirectConnection );
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2021-11-26 08:42:21 +00:00
|
|
|
#if defined( Q_OS_WIN )
|
2013-05-30 13:24:21 +00:00
|
|
|
installNativeEventFilter( this );
|
|
|
|
#endif
|
2010-11-20 13:43:55 +00:00
|
|
|
}
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2010-11-20 13:43:55 +00:00
|
|
|
QHotkeyApplication::QHotkeyApplication( QString const & id, int & argc, char ** argv ):
|
2023-04-28 16:45:03 +00:00
|
|
|
QtSingleApplication( id, argc, argv )
|
2009-04-19 21:32:18 +00:00
|
|
|
{
|
2022-12-26 02:08:17 +00:00
|
|
|
connect( this,
|
|
|
|
&QGuiApplication::commitDataRequest,
|
|
|
|
this,
|
|
|
|
&QHotkeyApplication::hotkeyAppCommitData,
|
|
|
|
Qt::DirectConnection );
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2022-12-26 02:08:17 +00:00
|
|
|
connect( this,
|
|
|
|
&QGuiApplication::saveStateRequest,
|
|
|
|
this,
|
|
|
|
&QHotkeyApplication::hotkeyAppSaveState,
|
|
|
|
Qt::DirectConnection );
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2021-11-26 08:42:21 +00:00
|
|
|
#if defined( Q_OS_WIN )
|
2013-06-08 14:36:05 +00:00
|
|
|
installNativeEventFilter( this );
|
|
|
|
#endif
|
2009-04-19 21:32:18 +00:00
|
|
|
}
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2010-06-28 15:14:07 +00:00
|
|
|
void QHotkeyApplication::addDataCommiter( DataCommitter & d )
|
|
|
|
{
|
|
|
|
dataCommitters.append( &d );
|
|
|
|
}
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2010-06-28 15:14:07 +00:00
|
|
|
void QHotkeyApplication::removeDataCommiter( DataCommitter & d )
|
|
|
|
{
|
|
|
|
dataCommitters.removeAll( &d );
|
|
|
|
}
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2018-03-27 14:46:03 +00:00
|
|
|
void QHotkeyApplication::hotkeyAppCommitData( QSessionManager & mgr )
|
2010-06-28 15:14:07 +00:00
|
|
|
{
|
|
|
|
for ( int x = 0; x < dataCommitters.size(); ++x ) {
|
2018-03-27 14:46:03 +00:00
|
|
|
dataCommitters[ x ]->commitData( mgr );
|
2024-10-10 07:13:23 +00:00
|
|
|
}
|
2018-03-27 14:46:03 +00:00
|
|
|
}
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2018-03-27 14:46:03 +00:00
|
|
|
void QHotkeyApplication::hotkeyAppSaveState( QSessionManager & mgr )
|
|
|
|
{
|
|
|
|
mgr.setRestartHint( QSessionManager::RestartNever );
|
2010-06-28 15:14:07 +00:00
|
|
|
}
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2009-04-19 21:32:18 +00:00
|
|
|
void QHotkeyApplication::registerWrapper( HotkeyWrapper * wrapper )
|
|
|
|
{
|
|
|
|
if ( wrapper && !hotkeyWrappers.contains( wrapper ) ) {
|
|
|
|
hotkeyWrappers.append( wrapper );
|
2024-10-10 07:13:23 +00:00
|
|
|
}
|
2009-04-19 21:32:18 +00:00
|
|
|
}
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2009-04-19 21:32:18 +00:00
|
|
|
void QHotkeyApplication::unregisterWrapper( HotkeyWrapper * wrapper )
|
|
|
|
{
|
|
|
|
if ( wrapper && hotkeyWrappers.contains( wrapper ) ) {
|
|
|
|
hotkeyWrappers.removeAll( wrapper );
|
2024-10-10 07:13:23 +00:00
|
|
|
}
|
2009-04-19 21:32:18 +00:00
|
|
|
}
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2009-04-19 21:32:18 +00:00
|
|
|
//////////////////////////////////////////////////////////////////////////
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2009-04-21 18:27:26 +00:00
|
|
|
HotkeyStruct::HotkeyStruct( quint32 key_, quint32 key2_, quint32 modifier_, int handle_, int id_ ):
|
|
|
|
key( key_ ),
|
|
|
|
key2( key2_ ),
|
|
|
|
modifier( modifier_ ),
|
2009-04-21 21:07:15 +00:00
|
|
|
handle( handle_ ),
|
|
|
|
id( id_ )
|
2009-04-19 21:32:18 +00:00
|
|
|
{
|
|
|
|
}
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2009-04-19 21:32:18 +00:00
|
|
|
//////////////////////////////////////////////////////////////////////////
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2024-06-24 08:05:41 +00:00
|
|
|
#if !defined( Q_OS_MAC )
|
2009-04-21 18:27:26 +00:00
|
|
|
HotkeyWrapper::HotkeyWrapper( QObject * parent ):
|
|
|
|
QThread( parent ),
|
2009-04-19 21:32:18 +00:00
|
|
|
state2( false )
|
|
|
|
{
|
|
|
|
init();
|
|
|
|
( static_cast< QHotkeyApplication * >( qApp ) )->registerWrapper( this );
|
|
|
|
}
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2009-04-21 21:07:15 +00:00
|
|
|
HotkeyWrapper::~HotkeyWrapper()
|
|
|
|
{
|
|
|
|
unregister();
|
|
|
|
}
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2009-04-19 21:32:18 +00:00
|
|
|
void HotkeyWrapper::waitKey2()
|
|
|
|
{
|
|
|
|
state2 = false;
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2013-05-31 05:28:36 +00:00
|
|
|
#ifdef HAVE_X11
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2009-04-22 15:29:28 +00:00
|
|
|
if ( keyToUngrab != grabbedKeys.end() ) {
|
|
|
|
ungrabKey( keyToUngrab );
|
|
|
|
keyToUngrab = grabbedKeys.end();
|
|
|
|
}
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2009-04-22 15:29:28 +00:00
|
|
|
#endif
|
2009-04-19 21:32:18 +00:00
|
|
|
}
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2009-04-19 21:32:18 +00:00
|
|
|
bool HotkeyWrapper::checkState( quint32 vk, quint32 mod )
|
|
|
|
{
|
2009-04-22 15:29:28 +00:00
|
|
|
if ( state2 ) { // wait for 2nd key
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2009-04-22 15:29:28 +00:00
|
|
|
waitKey2(); // Cancel the 2nd-key wait stage
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2009-04-22 15:29:28 +00:00
|
|
|
if ( state2waiter.key2 == vk && state2waiter.modifier == mod ) {
|
|
|
|
emit hotkeyActivated( state2waiter.handle );
|
|
|
|
return true;
|
2023-07-20 08:02:22 +00:00
|
|
|
}
|
2009-04-19 21:32:18 +00:00
|
|
|
}
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2009-04-19 21:32:18 +00:00
|
|
|
for ( int i = 0; i < hotkeys.count(); i++ ) {
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2009-04-19 21:32:18 +00:00
|
|
|
const HotkeyStruct & hs = hotkeys.at( i );
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2009-04-19 21:32:18 +00:00
|
|
|
if ( hs.key == vk && hs.modifier == mod ) {
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2013-05-31 04:28:29 +00:00
|
|
|
#ifdef Q_OS_WIN32
|
2024-11-14 11:12:17 +00:00
|
|
|
// 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.
|
2022-09-11 01:36:39 +00:00
|
|
|
if ( hs.key2 != 0 || ( mod == MOD_CONTROL && ( vk == VK_INSERT || vk == 'c' || vk == 'C' ) ) ) {
|
2013-06-10 13:49:42 +00:00
|
|
|
// Pass-through first part of compound hotkey or clipdoard copy command
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2013-06-10 13:49:42 +00:00
|
|
|
INPUT i[ 10 ];
|
2009-04-22 20:06:31 +00:00
|
|
|
memset( i, 0, sizeof( i ) );
|
2013-06-10 13:49:42 +00:00
|
|
|
int nextKeyNom = 0;
|
|
|
|
short emulateModKeys = 0;
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2013-06-10 13:49:42 +00:00
|
|
|
// If modifier keys aren't pressed it looks like emulation
|
|
|
|
// We then also emulate full sequence
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2013-06-10 13:49:42 +00:00
|
|
|
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;
|
2013-04-29 19:35:26 +00:00
|
|
|
}
|
2013-06-10 13:49:42 +00:00
|
|
|
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;
|
2013-04-29 19:35:26 +00:00
|
|
|
}
|
2013-06-10 13:49:42 +00:00
|
|
|
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;
|
2023-07-20 08:02:22 +00:00
|
|
|
}
|
|
|
|
|
2013-06-10 13:49:42 +00:00
|
|
|
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;
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2013-06-10 13:49:42 +00:00
|
|
|
if ( emulateModKeys & MOD_WIN ) {
|
|
|
|
i[ nextKeyNom ].type = INPUT_KEYBOARD;
|
|
|
|
i[ nextKeyNom ].ki.wVk = VK_LWIN;
|
|
|
|
i[ nextKeyNom ].ki.dwFlags = KEYEVENTF_KEYUP;
|
|
|
|
nextKeyNom += 1;
|
2023-07-20 08:02:22 +00:00
|
|
|
}
|
2013-06-10 13:49:42 +00:00
|
|
|
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;
|
|
|
|
}
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2013-06-10 13:49:42 +00:00
|
|
|
UnregisterHotKey( hwnd, hs.id );
|
|
|
|
SendInput( nextKeyNom, i, sizeof( *i ) );
|
|
|
|
RegisterHotKey( hwnd, hs.id, hs.modifier, hs.key );
|
2023-07-20 08:02:22 +00:00
|
|
|
}
|
2013-06-10 13:49:42 +00:00
|
|
|
#endif
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2013-06-10 13:49:42 +00:00
|
|
|
if ( hs.key2 == 0 ) {
|
2009-04-22 20:06:31 +00:00
|
|
|
emit hotkeyActivated( hs.handle );
|
|
|
|
return true;
|
2023-07-20 08:02:22 +00:00
|
|
|
}
|
|
|
|
|
2009-04-19 21:32:18 +00:00
|
|
|
state2 = true;
|
2009-04-22 20:06:31 +00:00
|
|
|
state2waiter = hs;
|
2013-06-10 13:49:42 +00:00
|
|
|
QTimer::singleShot( 500, this, &HotkeyWrapper::waitKey2 );
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2013-05-31 05:28:36 +00:00
|
|
|
#ifdef HAVE_X11
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2013-06-10 13:49:42 +00:00
|
|
|
// 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
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2009-04-22 20:06:31 +00:00
|
|
|
if ( ( isCopyToClipboardKey( hs.key, hs.modifier ) || !isCopyToClipboardKey( hs.key2, hs.modifier ) )
|
2009-04-21 18:27:26 +00:00
|
|
|
&& !isKeyGrabbed( hs.key2, hs.modifier ) )
|
|
|
|
keyToUngrab = grabKey( hs.key2, hs.modifier );
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2009-04-21 18:27:26 +00:00
|
|
|
#endif
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2009-04-19 21:32:18 +00:00
|
|
|
return true;
|
|
|
|
}
|
2023-07-20 08:02:22 +00:00
|
|
|
}
|
|
|
|
|
2009-04-19 21:32:18 +00:00
|
|
|
state2 = false;
|
2022-12-26 02:08:17 +00:00
|
|
|
return false;
|
2023-07-20 08:02:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2024-11-14 11:12:17 +00:00
|
|
|
#ifndef Q_OS_WIN
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2009-04-19 21:32:18 +00:00
|
|
|
//////////////////////////////////////////////////////////////////////////
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2009-04-25 10:45:42 +00:00
|
|
|
#include <X11/keysym.h>
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2009-04-21 18:27:26 +00:00
|
|
|
void HotkeyWrapper::init()
|
2023-07-20 08:02:22 +00:00
|
|
|
{
|
2009-04-21 18:27:26 +00:00
|
|
|
keyToUngrab = grabbedKeys.end();
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2009-04-21 18:27:26 +00:00
|
|
|
#if QT_VERSION < 0x060000
|
|
|
|
Display * displayID = QX11Info::display();
|
|
|
|
#else
|
|
|
|
QNativeInterface::QX11Application * x11AppInfo = qApp->nativeInterface< QNativeInterface::QX11Application >();
|
|
|
|
Display * displayID = x11AppInfo->display();
|
2023-07-20 08:02:22 +00:00
|
|
|
#endif
|
|
|
|
|
2009-04-21 18:27:26 +00:00
|
|
|
// We use RECORD extension instead of XGrabKey. That's because XGrabKey
|
|
|
|
// prevents other clients from getting their input if it's grabbed.
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2022-05-28 01:38:10 +00:00
|
|
|
Display * display = displayID;
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2009-04-21 18:27:26 +00:00
|
|
|
lShiftCode = XKeysymToKeycode( display, XK_Shift_L );
|
|
|
|
rShiftCode = XKeysymToKeycode( display, XK_Shift_R );
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2009-04-21 18:27:26 +00:00
|
|
|
lCtrlCode = XKeysymToKeycode( display, XK_Control_L );
|
|
|
|
rCtrlCode = XKeysymToKeycode( display, XK_Control_R );
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2009-04-21 18:27:26 +00:00
|
|
|
lAltCode = XKeysymToKeycode( display, XK_Alt_L );
|
2022-12-26 02:08:17 +00:00
|
|
|
rAltCode = XKeysymToKeycode( display, XK_Alt_R );
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2022-12-26 02:08:17 +00:00
|
|
|
lMetaCode = XKeysymToKeycode( display, XK_Super_L );
|
|
|
|
rMetaCode = XKeysymToKeycode( display, XK_Super_R );
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2022-12-26 02:08:17 +00:00
|
|
|
cCode = XKeysymToKeycode( display, XK_c );
|
2009-04-22 15:29:28 +00:00
|
|
|
insertCode = XKeysymToKeycode( display, XK_Insert );
|
|
|
|
kpInsertCode = XKeysymToKeycode( display, XK_KP_Insert );
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2009-04-21 18:27:26 +00:00
|
|
|
currentModifiers = 0;
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2022-12-26 02:08:17 +00:00
|
|
|
// This one will be used to read the recorded content
|
2009-04-21 18:27:26 +00:00
|
|
|
dataDisplay = XOpenDisplay( 0 );
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2009-04-21 18:27:26 +00:00
|
|
|
if ( !dataDisplay )
|
|
|
|
throw exInit();
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2009-04-21 18:27:26 +00:00
|
|
|
recordRange = XRecordAllocRange();
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2014-05-11 08:39:55 +00:00
|
|
|
if ( !recordRange ) {
|
2009-04-21 18:27:26 +00:00
|
|
|
XCloseDisplay( dataDisplay );
|
|
|
|
throw exInit();
|
2023-07-20 08:02:22 +00:00
|
|
|
}
|
|
|
|
|
2009-04-21 18:27:26 +00:00
|
|
|
recordRange->device_events.first = KeyPress;
|
|
|
|
recordRange->device_events.last = KeyRelease;
|
|
|
|
recordClientSpec = XRecordAllClients;
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2009-04-21 18:27:26 +00:00
|
|
|
recordContext = XRecordCreateContext( display, 0, &recordClientSpec, 1, &recordRange, 1 );
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2009-04-21 18:27:26 +00:00
|
|
|
if ( !recordContext ) {
|
|
|
|
XFree( recordRange );
|
|
|
|
XCloseDisplay( dataDisplay );
|
|
|
|
throw exInit();
|
2023-07-20 08:02:22 +00:00
|
|
|
}
|
|
|
|
|
2009-04-21 18:27:26 +00:00
|
|
|
// This is required to ensure context was indeed created
|
|
|
|
XSync( display, False );
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2009-04-25 10:45:42 +00:00
|
|
|
connect( this, &HotkeyWrapper::keyRecorded, this, &HotkeyWrapper::checkState, Qt::QueuedConnection );
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2009-04-25 10:45:42 +00:00
|
|
|
start();
|
2023-07-20 08:02:22 +00:00
|
|
|
}
|
|
|
|
|
2009-04-25 10:45:42 +00:00
|
|
|
void HotkeyWrapper::run() // Runs in a separate thread
|
2023-07-20 08:02:22 +00:00
|
|
|
{
|
2009-04-21 18:27:26 +00:00
|
|
|
if ( !XRecordEnableContext( dataDisplay, recordContext, recordEventCallback, (XPointer)this ) )
|
|
|
|
GD_DPRINTF( "Failed to enable record context\n" );
|
2023-07-20 08:02:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-04-21 18:27:26 +00:00
|
|
|
void HotkeyWrapper::recordEventCallback( XPointer ptr, XRecordInterceptData * data )
|
2023-07-20 08:02:22 +00:00
|
|
|
{
|
2009-04-21 18:27:26 +00:00
|
|
|
( (HotkeyWrapper *)ptr )->handleRecordEvent( data );
|
2009-04-25 10:45:42 +00:00
|
|
|
}
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2009-04-21 18:27:26 +00:00
|
|
|
void HotkeyWrapper::handleRecordEvent( XRecordInterceptData * data )
|
2023-07-20 08:02:22 +00:00
|
|
|
{
|
2009-04-21 18:27:26 +00:00
|
|
|
if ( data->category == XRecordFromServer ) {
|
|
|
|
xEvent * event = (xEvent *)data->data;
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2009-04-21 18:27:26 +00:00
|
|
|
if ( event->u.u.type == KeyPress ) {
|
|
|
|
KeyCode key = event->u.u.detail;
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2009-04-21 18:27:26 +00:00
|
|
|
if ( key == lShiftCode || key == rShiftCode )
|
|
|
|
currentModifiers |= ShiftMask;
|
|
|
|
else if ( key == lCtrlCode || key == rCtrlCode )
|
|
|
|
currentModifiers |= ControlMask;
|
|
|
|
else if ( key == lAltCode || key == rAltCode )
|
|
|
|
currentModifiers |= Mod1Mask;
|
2013-09-24 13:55:58 +00:00
|
|
|
else if ( key == lMetaCode || key == rMetaCode )
|
|
|
|
currentModifiers |= Mod4Mask;
|
2023-07-20 08:02:22 +00:00
|
|
|
else {
|
2009-04-25 10:45:42 +00:00
|
|
|
// Here we employ a kind of hack translating KP_Insert key
|
2013-09-24 13:55:58 +00:00
|
|
|
// to just Insert. This allows reacting to both Insert keys.
|
2009-04-25 10:45:42 +00:00
|
|
|
if ( key == kpInsertCode )
|
|
|
|
key = insertCode;
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2013-09-24 13:55:58 +00:00
|
|
|
emit keyRecorded( key, currentModifiers );
|
2023-07-20 08:02:22 +00:00
|
|
|
}
|
|
|
|
}
|
2009-04-21 18:27:26 +00:00
|
|
|
else if ( event->u.u.type == KeyRelease ) {
|
2013-09-24 13:55:58 +00:00
|
|
|
KeyCode key = event->u.u.detail;
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2013-09-24 13:55:58 +00:00
|
|
|
if ( key == lShiftCode || key == rShiftCode )
|
|
|
|
currentModifiers &= ~ShiftMask;
|
2022-12-30 02:09:37 +00:00
|
|
|
else if ( key == lCtrlCode || key == rCtrlCode )
|
|
|
|
currentModifiers &= ~ControlMask;
|
|
|
|
else if ( key == lAltCode || key == rAltCode )
|
|
|
|
currentModifiers &= ~Mod1Mask;
|
|
|
|
else if ( key == lMetaCode || key == rMetaCode )
|
2013-09-24 13:55:58 +00:00
|
|
|
currentModifiers &= ~Mod4Mask;
|
2022-12-30 02:09:37 +00:00
|
|
|
}
|
2023-07-20 08:02:22 +00:00
|
|
|
}
|
|
|
|
|
2009-04-21 18:27:26 +00:00
|
|
|
XRecordFreeData( data );
|
2023-07-20 08:02:22 +00:00
|
|
|
}
|
|
|
|
|
2009-04-21 18:27:26 +00:00
|
|
|
bool HotkeyWrapper::setGlobalKey( QKeySequence const & seq, int handle )
|
2023-07-20 08:02:22 +00:00
|
|
|
{
|
2009-04-21 18:27:26 +00:00
|
|
|
Config::HotKey hk( seq );
|
|
|
|
return setGlobalKey( hk.key1, hk.key2, hk.modifiers, handle );
|
2023-07-20 08:02:22 +00:00
|
|
|
}
|
|
|
|
|
2009-04-19 21:32:18 +00:00
|
|
|
bool HotkeyWrapper::setGlobalKey( int key, int key2, Qt::KeyboardModifiers modifier, int handle )
|
2023-07-20 08:02:22 +00:00
|
|
|
{
|
2009-04-19 21:32:18 +00:00
|
|
|
if ( !key )
|
|
|
|
return false; // We don't monitor empty combinations
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2009-04-21 18:27:26 +00:00
|
|
|
int vk = nativeKey( key );
|
|
|
|
int vk2 = key2 ? nativeKey( key2 ) : 0;
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2009-04-19 21:32:18 +00:00
|
|
|
quint32 mod = 0;
|
|
|
|
if ( modifier & Qt::ShiftModifier )
|
|
|
|
mod |= ShiftMask;
|
2013-09-24 13:55:58 +00:00
|
|
|
if ( modifier & Qt::ControlModifier )
|
|
|
|
mod |= ControlMask;
|
2009-04-22 15:29:28 +00:00
|
|
|
if ( modifier & Qt::AltModifier )
|
|
|
|
mod |= Mod1Mask;
|
|
|
|
if ( modifier & Qt::MetaModifier )
|
2013-09-24 13:55:58 +00:00
|
|
|
mod |= Mod4Mask;
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2009-04-22 15:29:28 +00:00
|
|
|
hotkeys.append( HotkeyStruct( vk, vk2, mod, handle, 0 ) );
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2009-04-22 15:29:28 +00:00
|
|
|
if ( !isCopyToClipboardKey( vk, mod ) )
|
|
|
|
grabKey( vk, mod ); // Make sure it doesn't get caught by other apps
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2009-04-22 15:29:28 +00:00
|
|
|
return true;
|
|
|
|
}
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2009-04-22 15:29:28 +00:00
|
|
|
bool HotkeyWrapper::isCopyToClipboardKey( quint32 keyCode, quint32 modifiers ) const
|
|
|
|
{
|
|
|
|
return modifiers == ControlMask && ( keyCode == cCode || keyCode == insertCode || keyCode == kpInsertCode );
|
2023-07-20 08:02:22 +00:00
|
|
|
}
|
|
|
|
|
2009-04-22 15:29:28 +00:00
|
|
|
bool HotkeyWrapper::isKeyGrabbed( quint32 keyCode, quint32 modifiers ) const
|
2023-07-20 08:02:22 +00:00
|
|
|
{
|
2009-04-22 15:29:28 +00:00
|
|
|
GrabbedKeys::const_iterator i = grabbedKeys.find( std::make_pair( keyCode, modifiers ) );
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2013-07-19 13:22:40 +00:00
|
|
|
return i != grabbedKeys.end();
|
2023-07-20 08:02:22 +00:00
|
|
|
}
|
|
|
|
|
2013-07-19 13:22:40 +00:00
|
|
|
namespace {
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2013-07-19 13:22:40 +00:00
|
|
|
using X11ErrorHandler = int ( * )( Display * display, XErrorEvent * event );
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2013-07-19 13:22:40 +00:00
|
|
|
class X11GrabUngrabErrorHandler
|
2023-07-20 08:02:22 +00:00
|
|
|
{
|
|
|
|
public:
|
2013-07-19 13:22:40 +00:00
|
|
|
static bool error;
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2013-07-19 13:22:40 +00:00
|
|
|
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;
|
|
|
|
}
|
2023-07-20 08:02:22 +00:00
|
|
|
}
|
2013-07-19 13:22:40 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2013-07-19 13:22:40 +00:00
|
|
|
X11GrabUngrabErrorHandler()
|
|
|
|
{
|
|
|
|
error = false;
|
|
|
|
previousErrorHandler_ = XSetErrorHandler( grab_ungrab_error_handler );
|
|
|
|
}
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2013-07-19 13:22:40 +00:00
|
|
|
~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 );
|
2013-07-19 13:22:40 +00:00
|
|
|
(void)XSetErrorHandler( previousErrorHandler_ );
|
|
|
|
}
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2013-07-19 13:22:40 +00:00
|
|
|
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 );
|
2013-07-19 13:22:40 +00:00
|
|
|
return error;
|
|
|
|
}
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2013-07-19 13:22:40 +00:00
|
|
|
private:
|
|
|
|
X11ErrorHandler previousErrorHandler_;
|
|
|
|
};
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2013-07-19 13:22:40 +00:00
|
|
|
bool X11GrabUngrabErrorHandler::error = false;
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2013-07-19 13:22:40 +00:00
|
|
|
} // anonymous namespace
|
2023-07-20 08:02:22 +00:00
|
|
|
|
|
|
|
|
2009-04-22 15:29:28 +00:00
|
|
|
HotkeyWrapper::GrabbedKeys::iterator HotkeyWrapper::grabKey( quint32 keyCode, quint32 modifiers )
|
|
|
|
{
|
|
|
|
std::pair< GrabbedKeys::iterator, bool > result = grabbedKeys.insert( std::make_pair( keyCode, modifiers ) );
|
2023-07-20 08:02:22 +00:00
|
|
|
|
|
|
|
|
2009-04-22 15:29:28 +00:00
|
|
|
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
|
2013-07-19 13:22:40 +00:00
|
|
|
X11GrabUngrabErrorHandler errorHandler;
|
2022-05-28 01:38:10 +00:00
|
|
|
XGrabKey( displayID, keyCode, modifiers, DefaultRootWindow( displayID ), True, GrabModeAsync, GrabModeAsync );
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2013-07-19 13:22:40 +00:00
|
|
|
if ( errorHandler.isError() ) {
|
2017-06-22 15:02:04 +00:00
|
|
|
gdWarning( "Possible hotkeys conflict. Check your hotkeys options." );
|
2013-07-19 13:22:40 +00:00
|
|
|
ungrabKey( result.first );
|
2009-04-22 15:29:28 +00:00
|
|
|
}
|
2023-07-20 08:02:22 +00:00
|
|
|
}
|
|
|
|
|
2009-04-22 15:29:28 +00:00
|
|
|
return result.first;
|
|
|
|
}
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2009-04-22 15:29:28 +00:00
|
|
|
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
|
2013-07-19 13:22:40 +00:00
|
|
|
X11GrabUngrabErrorHandler errorHandler;
|
2022-05-28 01:38:10 +00:00
|
|
|
XUngrabKey( displayID, i->first, i->second, XDefaultRootWindow( displayID ) );
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2009-04-22 15:29:28 +00:00
|
|
|
grabbedKeys.erase( i );
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2013-07-19 13:22:40 +00:00
|
|
|
if ( errorHandler.isError() ) {
|
2017-06-22 15:02:04 +00:00
|
|
|
gdWarning( "Cannot ungrab the hotkey" );
|
2009-04-22 15:29:28 +00:00
|
|
|
}
|
2023-07-20 08:02:22 +00:00
|
|
|
}
|
|
|
|
|
2009-04-19 21:32:18 +00:00
|
|
|
quint32 HotkeyWrapper::nativeKey( int key )
|
|
|
|
{
|
2009-04-21 18:27:26 +00:00
|
|
|
QString keySymName;
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2009-04-21 18:27:26 +00:00
|
|
|
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;
|
2009-04-21 18:27:26 +00:00
|
|
|
return XKeysymToKeycode( display, XStringToKeysym( keySymName.toLatin1().data() ) );
|
2009-04-19 21:32:18 +00:00
|
|
|
}
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2009-04-21 21:07:15 +00:00
|
|
|
void HotkeyWrapper::unregister()
|
2009-04-19 21:32:18 +00:00
|
|
|
{
|
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;
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2009-04-21 18:27:26 +00:00
|
|
|
XRecordDisableContext( display, recordContext );
|
|
|
|
XSync( display, False );
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2009-04-21 18:27:26 +00:00
|
|
|
wait();
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2009-04-21 18:27:26 +00:00
|
|
|
XRecordFreeContext( display, recordContext );
|
|
|
|
XFree( recordRange );
|
|
|
|
XCloseDisplay( dataDisplay );
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2009-04-22 15:29:28 +00:00
|
|
|
while ( grabbedKeys.size() )
|
|
|
|
ungrabKey( grabbedKeys.begin() );
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2009-04-21 18:27:26 +00:00
|
|
|
( static_cast< QHotkeyApplication * >( qApp ) )->unregisterWrapper( this );
|
2009-04-19 21:32:18 +00:00
|
|
|
}
|
2023-07-20 08:02:22 +00:00
|
|
|
|
2009-04-19 21:32:18 +00:00
|
|
|
#endif
|
2011-08-06 18:39:16 +00:00
|
|
|
#endif
|