mirror of
https://github.com/xiaoyifang/goldendict-ng.git
synced 2024-11-23 20:14:05 +00:00
Win-specific: Add global hotkeys handling via low-level keyboard hook
This commit is contained in:
parent
eb6a7c83a6
commit
68fc27b7d7
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -18,6 +18,8 @@ Makefile.Release
|
|||
|
||||
goldendict.pro.user
|
||||
goldendict.pro.user.*
|
||||
hotkeys.pro.user
|
||||
hotkeys.pro.user.*
|
||||
object_script.goldendict.Debug
|
||||
object_script.goldendict.Release
|
||||
|
||||
|
|
|
@ -511,7 +511,8 @@ win32 {
|
|||
sapi.hh \
|
||||
sphelper.hh \
|
||||
speechclient.hh \
|
||||
speechhlp.hh
|
||||
speechhlp.hh \
|
||||
hotkeys.h
|
||||
}
|
||||
|
||||
mac {
|
||||
|
|
211
hotkeys.c
Normal file
211
hotkeys.c
Normal file
|
@ -0,0 +1,211 @@
|
|||
#include <windows.h>
|
||||
#include "hotkeys.h"
|
||||
#include "stdio.h"
|
||||
|
||||
static LRESULT CALLBACK lowLevelKeyboardProc(int, WPARAM, LPARAM);
|
||||
|
||||
// Max number of hotkeys
|
||||
#define MAX_HOTKEYS 5
|
||||
|
||||
// Max time interval between first and second part of hotkey (ms)
|
||||
#define MAX_KEYS_TIME_INTERVAL 500
|
||||
|
||||
static HINSTANCE hInstance;
|
||||
static HWND hGDWindow;
|
||||
static HHOOK hKbdHook;
|
||||
|
||||
typedef struct HotkeyStruct
|
||||
{
|
||||
DWORD key1;
|
||||
DWORD key2;
|
||||
DWORD mods;
|
||||
DWORD lasttime;
|
||||
} HotkeyStruct;
|
||||
|
||||
static HotkeyStruct hotkeys[ MAX_HOTKEYS ];
|
||||
|
||||
__declspec (dllexport) void removeHook()
|
||||
{
|
||||
if( hKbdHook )
|
||||
{
|
||||
UnhookWindowsHookEx( hKbdHook );
|
||||
hKbdHook = 0;
|
||||
}
|
||||
}
|
||||
|
||||
__declspec (dllexport) BOOL setHook( HWND hwnd )
|
||||
{
|
||||
hGDWindow = hwnd;
|
||||
removeHook();
|
||||
hKbdHook = SetWindowsHookEx( WH_KEYBOARD_LL, lowLevelKeyboardProc, hInstance, 0 );
|
||||
return hKbdHook != 0;
|
||||
}
|
||||
|
||||
__declspec (dllexport) BOOL setHotkeys( DWORD key1, DWORD key2, DWORD modifiers, int nom )
|
||||
{
|
||||
if( nom < 0 || nom >= MAX_HOTKEYS )
|
||||
return FALSE;
|
||||
hotkeys[ nom ].key1 = key1;
|
||||
hotkeys[ nom ].key2 = key2;
|
||||
hotkeys[ nom ].mods = modifiers;
|
||||
hotkeys[ nom ].lasttime = 0;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
__declspec (dllexport) void clearHotkeys()
|
||||
{
|
||||
int i;
|
||||
for( i = 0; i < MAX_HOTKEYS; i++ )
|
||||
memset( hotkeys + i, 0, sizeof( HotkeyStruct ) );
|
||||
}
|
||||
|
||||
static BOOL isModifiersPressed( DWORD modifiers )
|
||||
{
|
||||
int n = GetAsyncKeyState( VK_MENU ) & 0x8000;
|
||||
if( ( ( modifiers & MOD_ALT ) && n == 0 )
|
||||
|| ( ( modifiers & MOD_ALT ) == 0 && n ) )
|
||||
return FALSE;
|
||||
|
||||
n = GetAsyncKeyState( VK_SHIFT ) & 0x8000;
|
||||
if( ( ( modifiers & MOD_SHIFT ) && n == 0 )
|
||||
|| ( ( modifiers & MOD_SHIFT ) == 0 && n ) )
|
||||
return FALSE;
|
||||
|
||||
n = GetAsyncKeyState( VK_CONTROL ) & 0x8000;
|
||||
if( ( ( modifiers & MOD_CONTROL ) && n == 0 )
|
||||
|| ( ( modifiers & MOD_CONTROL ) == 0 && n ) )
|
||||
return FALSE;
|
||||
|
||||
n = ( GetAsyncKeyState( VK_LWIN ) & 0x8000 ) | ( GetAsyncKeyState( VK_RWIN ) & 0x8000 );
|
||||
if( ( ( modifiers & MOD_WIN ) && n == 0 )
|
||||
|| ( ( modifiers & MOD_WIN ) == 0 && n ) )
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static LRESULT CALLBACK lowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
int i;
|
||||
PKBDLLHOOKSTRUCT p;
|
||||
BOOL stop = FALSE;
|
||||
|
||||
if ( nCode < 0 ) return CallNextHookEx( hKbdHook, nCode, wParam, lParam );
|
||||
|
||||
if( nCode == HC_ACTION )
|
||||
{
|
||||
p = (PKBDLLHOOKSTRUCT)lParam;
|
||||
|
||||
for( ; ; )
|
||||
{
|
||||
// Check hotkeys
|
||||
|
||||
if( wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN )
|
||||
{
|
||||
DWORD new_time = GetTickCount();
|
||||
|
||||
// Check if key is second part of hotkey
|
||||
|
||||
for( i = 0; i < MAX_HOTKEYS; i++ )
|
||||
{
|
||||
if( hotkeys[ i ].key1 == 0 )
|
||||
break;
|
||||
if( hotkeys[ i ].key2 == 0 || hotkeys[ i ].lasttime == 0 )
|
||||
continue;
|
||||
if( hotkeys[ i ].key2 == p->vkCode && isModifiersPressed( hotkeys[ i ].mods ) )
|
||||
{
|
||||
if( new_time - hotkeys[ i ].lasttime <= MAX_KEYS_TIME_INTERVAL )
|
||||
{
|
||||
// Hotkey completed
|
||||
// Clear all flags for first part
|
||||
|
||||
int j;
|
||||
for( j = 0; j < MAX_HOTKEYS; j++ )
|
||||
hotkeys[ j ].lasttime = 0;
|
||||
|
||||
PostMessage( hGDWindow, GD_HOTKEY_MESSAGE, (WPARAM)i, 0 );
|
||||
|
||||
stop = TRUE;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Interval exceeded, reset time
|
||||
hotkeys[ i ].lasttime = new_time;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Key is not second part, clear flag
|
||||
hotkeys[ i ].lasttime = 0;
|
||||
}
|
||||
}
|
||||
if( stop )
|
||||
break;
|
||||
|
||||
// Check if key is first part of hotkey
|
||||
|
||||
for( i = 0; i < MAX_HOTKEYS; i++ )
|
||||
{
|
||||
if( hotkeys[ i ].key1 == 0 )
|
||||
break;
|
||||
if( hotkeys[ i ].key1 == p->vkCode && isModifiersPressed( hotkeys[ i ].mods ) )
|
||||
{
|
||||
// Match found
|
||||
if( hotkeys[ i ].key2 == 0 )
|
||||
{
|
||||
// No second part, hotkey completed
|
||||
|
||||
// Clear all flags for first part
|
||||
int j;
|
||||
for( j = 0; j < MAX_HOTKEYS; j++ )
|
||||
hotkeys[ j ].lasttime = 0;
|
||||
|
||||
PostMessage( hGDWindow, GD_HOTKEY_MESSAGE, (WPARAM)i, 0 );
|
||||
|
||||
stop = TRUE;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// First part detected, need wait for second part
|
||||
hotkeys[ i ].lasttime = new_time;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
LRESULT result = CallNextHookEx(hKbdHook, nCode, wParam, lParam);
|
||||
return ( stop ? 1 : result );
|
||||
}
|
||||
|
||||
BOOL APIENTRY DllMain (HINSTANCE hInst /* Library instance handle. */ ,
|
||||
DWORD reason /* Reason this function is being called. */ ,
|
||||
LPVOID reserved /* Not used. */ )
|
||||
{
|
||||
(void) reserved;
|
||||
switch (reason)
|
||||
{
|
||||
case DLL_PROCESS_ATTACH:
|
||||
hInstance = hInst;
|
||||
break;
|
||||
|
||||
case DLL_PROCESS_DETACH:
|
||||
removeHook();
|
||||
break;
|
||||
|
||||
case DLL_THREAD_ATTACH:
|
||||
break;
|
||||
|
||||
case DLL_THREAD_DETACH:
|
||||
break;
|
||||
}
|
||||
|
||||
/* Returns TRUE on success, FALSE on failure */
|
||||
return TRUE;
|
||||
}
|
14
hotkeys.h
Normal file
14
hotkeys.h
Normal file
|
@ -0,0 +1,14 @@
|
|||
#ifndef __HOTKEYS_H_INCLUDED__
|
||||
#define __HOTKEYS_H_INCLUDED__
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
// Message if hotkey completed; WPARAM - hotkey number
|
||||
#define GD_HOTKEY_MESSAGE ( WM_APP + 1 )
|
||||
|
||||
typedef BOOL ( *setHookProc )( HWND hwnd );
|
||||
typedef void ( *removeHookProc )();
|
||||
typedef BOOL ( *setHotkeysProc )( DWORD, DWORD, DWORD, int );
|
||||
typedef void ( *clearHotkeysProc )();
|
||||
|
||||
#endif // __HOTKEYS_H_INCLUDED__
|
22
hotkeys.pro
Normal file
22
hotkeys.pro
Normal file
|
@ -0,0 +1,22 @@
|
|||
#-------------------------------------------------
|
||||
#
|
||||
# DLL for global hotkeys handling
|
||||
# Should be build for Windows only
|
||||
# Place GdHotkey.dll beside GoldenDict.exe
|
||||
#
|
||||
#-------------------------------------------------
|
||||
|
||||
win32 {
|
||||
QT -= core gui
|
||||
|
||||
TARGET = GdHotkeys
|
||||
TEMPLATE = lib
|
||||
|
||||
DEFINES += HOTKEYS_LIBRARY
|
||||
|
||||
SOURCES += \
|
||||
hotkeys.c
|
||||
|
||||
HEADERS += \
|
||||
hotkeys.h \
|
||||
}
|
|
@ -9,7 +9,7 @@
|
|||
#include <QWidget>
|
||||
#include <QMainWindow>
|
||||
|
||||
#ifdef Q_OS_WIN32
|
||||
#ifdef Q_OS_WIN
|
||||
#include "mainwindow.hh"
|
||||
#endif
|
||||
|
||||
|
@ -107,6 +107,27 @@ HotkeyWrapper::HotkeyWrapper(QObject *parent) : QThread( parent ),
|
|||
{
|
||||
#ifdef Q_OS_WIN
|
||||
hwnd=(HWND)((static_cast<QMainWindow*>(parent))->winId());
|
||||
|
||||
dllHandler.hDLLHandle = LoadLibraryA( "GdHotkeys.dll" );
|
||||
if( dllHandler.hDLLHandle )
|
||||
{
|
||||
dllHandler.setHook = ( setHookProc )GetProcAddress( dllHandler.hDLLHandle, "setHook" );
|
||||
dllHandler.removeHook = ( removeHookProc )GetProcAddress( dllHandler.hDLLHandle, "removeHook" );
|
||||
dllHandler.setHotkeys = ( setHotkeysProc )GetProcAddress( dllHandler.hDLLHandle, "setHotkeys" );
|
||||
dllHandler.clearHotkeys = ( clearHotkeysProc )GetProcAddress( dllHandler.hDLLHandle, "clearHotkeys" );
|
||||
|
||||
if( !dllHandler.setHook || !dllHandler.removeHook || !dllHandler.setHotkeys || !dllHandler.clearHotkeys )
|
||||
{
|
||||
FreeLibrary( dllHandler.hDLLHandle );
|
||||
dllHandler.hDLLHandle = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if( dllHandler.hDLLHandle )
|
||||
gdWarning( "Handle global hotkeys via GdHotkeys.dll" );
|
||||
else
|
||||
gdWarning( "Handle global hotkeys via RegisterHotkey()" );
|
||||
|
||||
#else
|
||||
init();
|
||||
#endif
|
||||
|
@ -116,6 +137,8 @@ HotkeyWrapper::HotkeyWrapper(QObject *parent) : QThread( parent ),
|
|||
HotkeyWrapper::~HotkeyWrapper()
|
||||
{
|
||||
unregister();
|
||||
if( dllHandler.hDLLHandle )
|
||||
FreeLibrary( dllHandler.hDLLHandle );
|
||||
}
|
||||
|
||||
void HotkeyWrapper::waitKey2()
|
||||
|
@ -305,6 +328,14 @@ bool HotkeyWrapper::setGlobalKey( int key, int key2,
|
|||
|
||||
hotkeys.append( HotkeyStruct( vk, vk2, mod, handle, id ) );
|
||||
|
||||
if( dllHandler.hDLLHandle )
|
||||
{
|
||||
dllHandler.removeHook();
|
||||
dllHandler.setHotkeys( vk, vk2, mod, hotkeys.size() - 1 );
|
||||
dllHandler.setHook( hwnd );
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!RegisterHotKey(hwnd, id++, mod, vk))
|
||||
return false;
|
||||
|
||||
|
@ -320,6 +351,14 @@ bool HotkeyWrapper::winEvent ( MSG * message, long * result )
|
|||
if (message->message == WM_HOTKEY)
|
||||
return checkState( (message->lParam >> 16), (message->lParam & 0xffff) );
|
||||
|
||||
if( message->message == GD_HOTKEY_MESSAGE )
|
||||
{
|
||||
int n = (int)message->wParam;
|
||||
if( n < hotkeys.size() && n >= 0 )
|
||||
emit hotkeyActivated( hotkeys.at( n ).handle );
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -416,14 +455,22 @@ quint32 HotkeyWrapper::nativeKey(int key)
|
|||
|
||||
void HotkeyWrapper::unregister()
|
||||
{
|
||||
for (int i = 0; i < hotkeys.count(); i++)
|
||||
if( dllHandler.hDLLHandle )
|
||||
{
|
||||
HotkeyStruct const & hk = hotkeys.at( i );
|
||||
dllHandler.removeHook();
|
||||
dllHandler.clearHotkeys();
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < hotkeys.count(); i++)
|
||||
{
|
||||
HotkeyStruct const & hk = hotkeys.at( i );
|
||||
|
||||
UnregisterHotKey( hwnd, hk.id );
|
||||
UnregisterHotKey( hwnd, hk.id );
|
||||
|
||||
if ( hk.key2 && hk.key2 != hk.key )
|
||||
UnregisterHotKey( hwnd, hk.id+1 );
|
||||
if ( hk.key2 && hk.key2 != hk.key )
|
||||
UnregisterHotKey( hwnd, hk.id+1 );
|
||||
}
|
||||
}
|
||||
|
||||
(static_cast<QHotkeyApplication*>(qApp))->unregisterWrapper(this);
|
||||
|
@ -436,7 +483,7 @@ bool QHotkeyApplication::nativeEventFilter( const QByteArray & /*eventType*/, vo
|
|||
{
|
||||
MSG * msg = reinterpret_cast< MSG * >( message );
|
||||
|
||||
if ( msg->message == WM_HOTKEY )
|
||||
if ( msg->message == WM_HOTKEY || msg->message == GD_HOTKEY_MESSAGE )
|
||||
{
|
||||
for (int i = 0; i < hotkeyWrappers.size(); i++)
|
||||
{
|
||||
|
@ -458,7 +505,7 @@ bool QHotkeyApplication::nativeEventFilter( const QByteArray & /*eventType*/, vo
|
|||
|
||||
bool QHotkeyApplication::winEventFilter ( MSG * message, long * result )
|
||||
{
|
||||
if (message->message == WM_HOTKEY)
|
||||
if (message->message == WM_HOTKEY || message->message == GD_HOTKEY_MESSAGE)
|
||||
{
|
||||
for (int i = 0; i < hotkeyWrappers.size(); i++)
|
||||
{
|
||||
|
|
|
@ -27,6 +27,10 @@
|
|||
#include "qtsingleapplication.h"
|
||||
#include "qt4x5.hh"
|
||||
|
||||
#ifdef Q_OS_WIN32
|
||||
#include "hotkeys.h"
|
||||
#endif
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct HotkeyStruct
|
||||
|
@ -67,6 +71,9 @@ public:
|
|||
/// Unregisters everything
|
||||
void unregister();
|
||||
|
||||
bool handleViaDLL()
|
||||
{ return dllHandler.hDLLHandle != 0; }
|
||||
|
||||
signals:
|
||||
|
||||
void hotkeyActivated( int );
|
||||
|
@ -95,6 +102,17 @@ private:
|
|||
virtual bool winEvent ( MSG * message, long * result );
|
||||
HWND hwnd;
|
||||
|
||||
struct DLL_HANDLER
|
||||
{
|
||||
HMODULE hDLLHandle;
|
||||
setHookProc setHook;
|
||||
removeHookProc removeHook;
|
||||
setHotkeysProc setHotkeys;
|
||||
clearHotkeysProc clearHotkeys;
|
||||
};
|
||||
|
||||
DLL_HANDLER dllHandler;
|
||||
|
||||
#elif defined(Q_OS_MAC)
|
||||
|
||||
public:
|
||||
|
|
|
@ -2827,6 +2827,24 @@ void MainWindow::toggleMainWindow( bool onlyShow )
|
|||
if ( !isVisible() )
|
||||
{
|
||||
show();
|
||||
|
||||
#ifdef Q_OS_WIN32
|
||||
if( hotkeyWrapper->handleViaDLL() )
|
||||
{
|
||||
// Some dances with tambourine
|
||||
HWND wId = (HWND) winId();
|
||||
DWORD pId = GetWindowThreadProcessId( wId, NULL );
|
||||
DWORD fpId = GetWindowThreadProcessId( GetForegroundWindow(), NULL );
|
||||
|
||||
//Attach Thread to get the Input - i am now allowed to set the Foreground window!
|
||||
AttachThreadInput( fpId, pId, true );
|
||||
SetActiveWindow( wId );
|
||||
SetForegroundWindow( wId );
|
||||
SetFocus( wId );
|
||||
AttachThreadInput( fpId, pId, false );
|
||||
}
|
||||
#endif
|
||||
|
||||
qApp->setActiveWindow( this );
|
||||
activateWindow();
|
||||
raise();
|
||||
|
@ -2846,8 +2864,25 @@ void MainWindow::toggleMainWindow( bool onlyShow )
|
|||
else
|
||||
if ( !isActiveWindow() )
|
||||
{
|
||||
activateWindow();
|
||||
qApp->setActiveWindow( this );
|
||||
#ifdef Q_OS_WIN32
|
||||
if( hotkeyWrapper->handleViaDLL() )
|
||||
{
|
||||
// Some dances with tambourine
|
||||
HWND wId = (HWND) winId();
|
||||
DWORD pId = GetWindowThreadProcessId( wId, NULL );
|
||||
DWORD fpId = GetWindowThreadProcessId( GetForegroundWindow(), NULL );
|
||||
|
||||
//Attach Thread to get the Input - i am now allowed to set the Foreground window!
|
||||
AttachThreadInput( fpId, pId, true );
|
||||
SetActiveWindow( wId );
|
||||
SetForegroundWindow( wId );
|
||||
SetFocus( wId );
|
||||
AttachThreadInput( fpId, pId, false );
|
||||
}
|
||||
#endif
|
||||
raise();
|
||||
activateWindow();
|
||||
shown = true;
|
||||
}
|
||||
else
|
||||
|
@ -2944,7 +2979,8 @@ void MainWindow::installHotKeys()
|
|||
}
|
||||
|
||||
connect( hotkeyWrapper.get(), SIGNAL( hotkeyActivated( int ) ),
|
||||
this, SLOT( hotKeyActivated( int ) ) );
|
||||
this, SLOT( hotKeyActivated( int ) ),
|
||||
hotkeyWrapper->handleViaDLL() ? Qt::QueuedConnection : Qt::AutoConnection );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue