Skip to content

Commit df684dc

Browse files
authored
Merge pull request #32 from alexandrvicente/releasedSignal
Hotkey release signal
2 parents 9583eaf + e113ace commit df684dc

File tree

6 files changed

+97
-13
lines changed

6 files changed

+97
-13
lines changed

QHotkey/qhotkey.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,13 @@ void QHotkeyPrivate::activateShortcut(QHotkey::NativeShortcut shortcut)
262262
signal.invoke(hkey, Qt::QueuedConnection);
263263
}
264264

265+
void QHotkeyPrivate::releaseShortcut(QHotkey::NativeShortcut shortcut)
266+
{
267+
QMetaMethod signal = QMetaMethod::fromSignal(&QHotkey::released);
268+
for(QHotkey *hkey : shortcuts.values(shortcut))
269+
signal.invoke(hkey, Qt::QueuedConnection);
270+
}
271+
265272
void QHotkeyPrivate::addMappingInvoked(Qt::Key keycode, Qt::KeyboardModifiers modifiers, const QHotkey::NativeShortcut &nativeShortcut)
266273
{
267274
mapping.insert({keycode, modifiers}, nativeShortcut);

QHotkey/qhotkey.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,9 @@ public slots:
100100
//! Will be emitted if the shortcut is pressed
101101
void activated(QPrivateSignal);
102102

103+
//! Will be emitted if the shortcut press is released
104+
void released(QPrivateSignal);
105+
103106
//! @notifyAcFn{QHotkey::registered}
104107
void registeredChanged(bool registered);
105108

QHotkey/qhotkey_mac.cpp

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ class QHotkeyPrivateMac : public QHotkeyPrivate
99
// QAbstractNativeEventFilter interface
1010
bool nativeEventFilter(const QByteArray &eventType, void *message, long *result) Q_DECL_OVERRIDE;
1111

12-
static OSStatus hotkeyEventHandler(EventHandlerCallRef nextHandler, EventRef event, void* data);
12+
static OSStatus hotkeyPressEventHandler(EventHandlerCallRef nextHandler, EventRef event, void* data);
13+
static OSStatus hotkeyReleaseEventHandler(EventHandlerCallRef nextHandler, EventRef event, void* data);
1314

1415
protected:
1516
// QHotkeyPrivate interface
@@ -203,10 +204,15 @@ bool QHotkeyPrivateMac::registerShortcut(QHotkey::NativeShortcut shortcut)
203204
{
204205
if (!this->isHotkeyHandlerRegistered)
205206
{
206-
EventTypeSpec eventSpec;
207-
eventSpec.eventClass = kEventClassKeyboard;
208-
eventSpec.eventKind = kEventHotKeyPressed;
209-
InstallApplicationEventHandler(&QHotkeyPrivateMac::hotkeyEventHandler, 1, &eventSpec, NULL, NULL);
207+
EventTypeSpec pressEventSpec;
208+
pressEventSpec.eventClass = kEventClassKeyboard;
209+
pressEventSpec.eventKind = kEventHotKeyPressed;
210+
InstallApplicationEventHandler(&QHotkeyPrivateMac::hotkeyPressEventHandler, 1, &pressEventSpec, NULL, NULL);
211+
212+
EventTypeSpec releaseEventSpec;
213+
releaseEventSpec.eventClass = kEventClassKeyboard;
214+
releaseEventSpec.eventKind = kEventHotKeyReleased;
215+
InstallApplicationEventHandler(&QHotkeyPrivateMac::hotkeyReleaseEventHandler, 1, &releaseEventSpec, NULL, NULL);
210216
}
211217

212218
EventHotKeyID hkeyID;
@@ -244,7 +250,7 @@ bool QHotkeyPrivateMac::unregisterShortcut(QHotkey::NativeShortcut shortcut)
244250
}
245251
}
246252

247-
OSStatus QHotkeyPrivateMac::hotkeyEventHandler(EventHandlerCallRef nextHandler, EventRef event, void* data)
253+
OSStatus QHotkeyPrivateMac::hotkeyPressEventHandler(EventHandlerCallRef nextHandler, EventRef event, void* data)
248254
{
249255
Q_UNUSED(nextHandler);
250256
Q_UNUSED(data);
@@ -264,3 +270,24 @@ OSStatus QHotkeyPrivateMac::hotkeyEventHandler(EventHandlerCallRef nextHandler,
264270

265271
return noErr;
266272
}
273+
274+
OSStatus QHotkeyPrivateMac::hotkeyReleaseEventHandler(EventHandlerCallRef nextHandler, EventRef event, void* data)
275+
{
276+
Q_UNUSED(nextHandler);
277+
Q_UNUSED(data);
278+
279+
if (GetEventClass(event) == kEventClassKeyboard &&
280+
GetEventKind(event) == kEventHotKeyReleased) {
281+
EventHotKeyID hkeyID;
282+
GetEventParameter(event,
283+
kEventParamDirectObject,
284+
typeEventHotKeyID,
285+
NULL,
286+
sizeof(EventHotKeyID),
287+
NULL,
288+
&hkeyID);
289+
hotkeyPrivate->releaseShortcut({hkeyID.signature, hkeyID.id});
290+
}
291+
292+
return noErr;
293+
}

QHotkey/qhotkey_p.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ class QHOTKEY_SHARED_EXPORT QHotkeyPrivate : public QObject, public QAbstractNat
2525

2626
protected:
2727
void activateShortcut(QHotkey::NativeShortcut shortcut);
28+
void releaseShortcut(QHotkey::NativeShortcut shortcut);
2829

2930
virtual quint32 nativeKeycode(Qt::Key keycode, bool &ok) = 0;//platform implement
3031
virtual quint32 nativeModifiers(Qt::KeyboardModifiers modifiers, bool &ok) = 0;//platform implement

QHotkey/qhotkey_win.cpp

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,19 @@
22
#include "qhotkey_p.h"
33
#include <qt_windows.h>
44
#include <QDebug>
5+
#include <QTimer>
56

67
#define HKEY_ID(nativeShortcut) (((nativeShortcut.key ^ (nativeShortcut.modifier << 8)) & 0x0FFF) | 0x7000)
78

89
class QHotkeyPrivateWin : public QHotkeyPrivate
910
{
1011
public:
12+
QHotkeyPrivateWin();
1113
// QAbstractNativeEventFilter interface
1214
bool nativeEventFilter(const QByteArray &eventType, void *message, long *result) Q_DECL_OVERRIDE;
1315

1416
protected:
17+
void pollForHotkeyRelease();
1518
// QHotkeyPrivate interface
1619
quint32 nativeKeycode(Qt::Key keycode, bool &ok) Q_DECL_OVERRIDE;
1720
quint32 nativeModifiers(Qt::KeyboardModifiers modifiers, bool &ok) Q_DECL_OVERRIDE;
@@ -20,9 +23,16 @@ class QHotkeyPrivateWin : public QHotkeyPrivate
2023

2124
private:
2225
static QString formatWinError(DWORD winError);
26+
QTimer pollTimer;
27+
QHotkey::NativeShortcut polledShortcut;
2328
};
2429
NATIVE_INSTANCE(QHotkeyPrivateWin)
2530

31+
QHotkeyPrivateWin::QHotkeyPrivateWin(){
32+
pollTimer.setInterval(50);
33+
connect(&pollTimer, &QTimer::timeout, this, &QHotkeyPrivateWin::pollForHotkeyRelease);
34+
}
35+
2636
bool QHotkeyPrivate::isPlatformSupported()
2737
{
2838
return true;
@@ -34,12 +44,25 @@ bool QHotkeyPrivateWin::nativeEventFilter(const QByteArray &eventType, void *mes
3444
Q_UNUSED(result)
3545

3646
MSG* msg = static_cast<MSG*>(message);
37-
if(msg->message == WM_HOTKEY)
38-
this->activateShortcut({HIWORD(msg->lParam), LOWORD(msg->lParam)});
47+
if(msg->message == WM_HOTKEY) {
48+
QHotkey::NativeShortcut shortcut = {HIWORD(msg->lParam), LOWORD(msg->lParam)};
49+
this->activateShortcut(shortcut);
50+
this->polledShortcut = shortcut;
51+
this->pollTimer.start();
52+
}
3953

4054
return false;
4155
}
4256

57+
void QHotkeyPrivateWin::pollForHotkeyRelease()
58+
{
59+
bool pressed = (GetAsyncKeyState(this->polledShortcut.key) & (1 << 15)) != 0;
60+
if(!pressed) {
61+
this->pollTimer.stop();
62+
this->releaseShortcut(this->polledShortcut);
63+
}
64+
}
65+
4366
quint32 QHotkeyPrivateWin::nativeKeycode(Qt::Key keycode, bool &ok)
4467
{
4568
ok = true;
@@ -235,7 +258,7 @@ bool QHotkeyPrivateWin::registerShortcut(QHotkey::NativeShortcut shortcut)
235258
{
236259
BOOL ok = RegisterHotKey(NULL,
237260
HKEY_ID(shortcut),
238-
shortcut.modifier,
261+
shortcut.modifier + MOD_NOREPEAT,
239262
shortcut.key);
240263
if(ok)
241264
return true;

QHotkey/qhotkey_x11.cpp

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include <QDebug>
44
#include <QX11Info>
55
#include <QThreadStorage>
6+
#include <QTimer>
67
#include <X11/Xlib.h>
78
#include <xcb/xcb.h>
89

@@ -28,7 +29,10 @@ class QHotkeyPrivateX11 : public QHotkeyPrivate
2829
private:
2930
static const QVector<quint32> specialModifiers;
3031
static const quint32 validModsMask;
31-
32+
XErrorHandler prevHandler;
33+
xcb_key_press_event_t prevHandledEvent;
34+
xcb_key_press_event_t prevEvent;
35+
3236
static QString formatX11Error(Display *display, int errorCode);
3337

3438
class HotkeyErrorHandler {
@@ -40,7 +44,7 @@ class QHotkeyPrivateX11 : public QHotkeyPrivate
4044
static QString errorString;
4145

4246
private:
43-
XErrorHandler prevHandler;
47+
QTimer *releaseTimer = nullptr;
4448

4549
static int handleError(Display *display, XErrorEvent *error);
4650
};
@@ -62,8 +66,27 @@ bool QHotkeyPrivateX11::nativeEventFilter(const QByteArray &eventType, void *mes
6266

6367
xcb_generic_event_t *genericEvent = static_cast<xcb_generic_event_t *>(message);
6468
if (genericEvent->response_type == XCB_KEY_PRESS) {
65-
xcb_key_press_event_t *keyEvent = static_cast<xcb_key_press_event_t *>(message);
66-
this->activateShortcut({keyEvent->detail, keyEvent->state & QHotkeyPrivateX11::validModsMask});
69+
xcb_key_press_event_t keyEvent = *static_cast<xcb_key_press_event_t *>(message);
70+
this->prevEvent = keyEvent;
71+
if (this->prevHandledEvent.response_type == XCB_KEY_RELEASE) {
72+
if(this->prevHandledEvent.time == keyEvent.time) return false;
73+
}
74+
this->activateShortcut({keyEvent.detail, keyEvent.state & QHotkeyPrivateX11::validModsMask});
75+
} else if (genericEvent->response_type == XCB_KEY_RELEASE) {
76+
xcb_key_release_event_t keyEvent = *static_cast<xcb_key_release_event_t *>(message);
77+
this->prevEvent = keyEvent;
78+
QTimer *timer = new QTimer(this);
79+
timer->setSingleShot(true);
80+
timer->setInterval(50);
81+
connect(timer, &QTimer::timeout, this, [this, keyEvent, timer] {
82+
if(this->prevEvent.time == keyEvent.time && this->prevEvent.response_type == keyEvent.response_type && this->prevEvent.detail == keyEvent.detail){
83+
this->releaseShortcut({keyEvent.detail, keyEvent.state & QHotkeyPrivateX11::validModsMask});
84+
}
85+
delete timer;
86+
});
87+
timer->start();
88+
this->releaseTimer = timer;
89+
this->prevHandledEvent = keyEvent;
6790
}
6891

6992
return false;

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy