Files
flameshot/external/QHotkey/qhotkey.cpp
2021-01-15 17:54:56 -06:00

392 lines
11 KiB
C++

#include "qhotkey.h"
#include "qhotkey_p.h"
#include <QAbstractEventDispatcher>
#include <QCoreApplication>
#include <QDebug>
#include <QMetaMethod>
#include <QThread>
Q_LOGGING_CATEGORY(logQHotkey, "QHotkey")
void QHotkey::addGlobalMapping(const QKeySequence& shortcut,
QHotkey::NativeShortcut nativeShortcut)
{
QMetaObject::invokeMethod(
QHotkeyPrivate::instance(),
"addMappingInvoked",
Qt::QueuedConnection,
Q_ARG(Qt::Key, Qt::Key(shortcut[0] & ~Qt::KeyboardModifierMask)),
Q_ARG(Qt::KeyboardModifiers,
Qt::KeyboardModifiers(shortcut[0] & Qt::KeyboardModifierMask)),
Q_ARG(QHotkey::NativeShortcut, nativeShortcut));
}
bool QHotkey::isPlatformSupported()
{
return QHotkeyPrivate::isPlatformSupported();
}
QHotkey::QHotkey(QObject* parent)
: QObject(parent)
, _keyCode(Qt::Key_unknown)
, _modifiers(Qt::NoModifier)
, _registered(false)
{}
QHotkey::QHotkey(const QKeySequence& shortcut,
bool autoRegister,
QObject* parent)
: QHotkey(parent)
{
setShortcut(shortcut, autoRegister);
}
QHotkey::QHotkey(Qt::Key keyCode,
Qt::KeyboardModifiers modifiers,
bool autoRegister,
QObject* parent)
: QHotkey(parent)
{
setShortcut(keyCode, modifiers, autoRegister);
}
QHotkey::QHotkey(QHotkey::NativeShortcut shortcut,
bool autoRegister,
QObject* parent)
: QHotkey(parent)
{
setNativeShortcut(shortcut, autoRegister);
}
QHotkey::~QHotkey()
{
if (_registered)
QHotkeyPrivate::instance()->removeShortcut(this);
}
QKeySequence QHotkey::shortcut() const
{
if (_keyCode == Qt::Key_unknown)
return QKeySequence();
return QKeySequence(static_cast<int>(_keyCode | _modifiers));
}
Qt::Key QHotkey::keyCode() const
{
return _keyCode;
}
Qt::KeyboardModifiers QHotkey::modifiers() const
{
return _modifiers;
}
QHotkey::NativeShortcut QHotkey::currentNativeShortcut() const
{
return _nativeShortcut;
}
bool QHotkey::isRegistered() const
{
return _registered;
}
bool QHotkey::setShortcut(const QKeySequence& shortcut, bool autoRegister)
{
if (shortcut.isEmpty())
return resetShortcut();
if (shortcut.count() > 1) {
qCWarning(logQHotkey,
"Keysequences with multiple shortcuts are not allowed! "
"Only the first shortcut will be used!");
}
return setShortcut(
Qt::Key(shortcut[0] & ~Qt::KeyboardModifierMask),
Qt::KeyboardModifiers(shortcut[0] & Qt::KeyboardModifierMask),
autoRegister);
}
bool QHotkey::setShortcut(Qt::Key keyCode,
Qt::KeyboardModifiers modifiers,
bool autoRegister)
{
if (_registered) {
if (autoRegister) {
if (!QHotkeyPrivate::instance()->removeShortcut(this))
return false;
} else
return false;
}
if (keyCode == Qt::Key_unknown) {
_keyCode = Qt::Key_unknown;
_modifiers = Qt::NoModifier;
_nativeShortcut = NativeShortcut();
return true;
}
_keyCode = keyCode;
_modifiers = modifiers;
_nativeShortcut =
QHotkeyPrivate::instance()->nativeShortcut(keyCode, modifiers);
if (_nativeShortcut.isValid()) {
if (autoRegister)
return QHotkeyPrivate::instance()->addShortcut(this);
return true;
}
qCWarning(logQHotkey) << "Unable to map shortcut to native keys. Key:"
<< keyCode << "Modifiers:" << modifiers;
_keyCode = Qt::Key_unknown;
_modifiers = Qt::NoModifier;
_nativeShortcut = NativeShortcut();
return false;
}
bool QHotkey::resetShortcut()
{
if (_registered && !QHotkeyPrivate::instance()->removeShortcut(this)) {
return false;
}
_keyCode = Qt::Key_unknown;
_modifiers = Qt::NoModifier;
_nativeShortcut = NativeShortcut();
return true;
}
bool QHotkey::setNativeShortcut(QHotkey::NativeShortcut nativeShortcut,
bool autoRegister)
{
if (_registered) {
if (autoRegister) {
if (!QHotkeyPrivate::instance()->removeShortcut(this))
return false;
} else
return false;
}
if (nativeShortcut.isValid()) {
_keyCode = Qt::Key_unknown;
_modifiers = Qt::NoModifier;
_nativeShortcut = nativeShortcut;
if (autoRegister)
return QHotkeyPrivate::instance()->addShortcut(this);
return true;
}
_keyCode = Qt::Key_unknown;
_modifiers = Qt::NoModifier;
_nativeShortcut = NativeShortcut();
return true;
}
bool QHotkey::setRegistered(bool registered)
{
if (_registered && !registered)
return QHotkeyPrivate::instance()->removeShortcut(this);
if (!_registered && registered) {
if (!_nativeShortcut.isValid())
return false;
return QHotkeyPrivate::instance()->addShortcut(this);
}
return true;
}
// ---------- QHotkeyPrivate implementation ----------
QHotkeyPrivate::QHotkeyPrivate()
{
Q_ASSERT_X(qApp,
Q_FUNC_INFO,
"QHotkey requires QCoreApplication to be instantiated");
qApp->eventDispatcher()->installNativeEventFilter(this);
}
QHotkeyPrivate::~QHotkeyPrivate()
{
if (!shortcuts.isEmpty())
qCWarning(logQHotkey)
<< "QHotkeyPrivate destroyed with registered shortcuts!";
if (qApp && qApp->eventDispatcher())
qApp->eventDispatcher()->removeNativeEventFilter(this);
}
QHotkey::NativeShortcut QHotkeyPrivate::nativeShortcut(
Qt::Key keycode,
Qt::KeyboardModifiers modifiers)
{
Qt::ConnectionType conType =
(QThread::currentThread() == thread() ? Qt::DirectConnection
: Qt::BlockingQueuedConnection);
QHotkey::NativeShortcut res;
if (!QMetaObject::invokeMethod(this,
"nativeShortcutInvoked",
conType,
Q_RETURN_ARG(QHotkey::NativeShortcut, res),
Q_ARG(Qt::Key, keycode),
Q_ARG(Qt::KeyboardModifiers, modifiers))) {
return QHotkey::NativeShortcut();
}
return res;
}
bool QHotkeyPrivate::addShortcut(QHotkey* hotkey)
{
if (hotkey->_registered)
return false;
Qt::ConnectionType conType =
(QThread::currentThread() == thread() ? Qt::DirectConnection
: Qt::BlockingQueuedConnection);
bool res = false;
if (!QMetaObject::invokeMethod(this,
"addShortcutInvoked",
conType,
Q_RETURN_ARG(bool, res),
Q_ARG(QHotkey*, hotkey))) {
return false;
}
if (res)
emit hotkey->registeredChanged(true);
return res;
}
bool QHotkeyPrivate::removeShortcut(QHotkey* hotkey)
{
if (!hotkey->_registered)
return false;
Qt::ConnectionType conType =
(QThread::currentThread() == thread() ? Qt::DirectConnection
: Qt::BlockingQueuedConnection);
bool res = false;
if (!QMetaObject::invokeMethod(this,
"removeShortcutInvoked",
conType,
Q_RETURN_ARG(bool, res),
Q_ARG(QHotkey*, hotkey))) {
return false;
}
if (res)
emit hotkey->registeredChanged(false);
return res;
}
void QHotkeyPrivate::activateShortcut(QHotkey::NativeShortcut shortcut)
{
QMetaMethod signal = QMetaMethod::fromSignal(&QHotkey::activated);
for (QHotkey* hkey : shortcuts.values(shortcut))
signal.invoke(hkey, Qt::QueuedConnection);
}
void QHotkeyPrivate::releaseShortcut(QHotkey::NativeShortcut shortcut)
{
QMetaMethod signal = QMetaMethod::fromSignal(&QHotkey::released);
for (QHotkey* hkey : shortcuts.values(shortcut))
signal.invoke(hkey, Qt::QueuedConnection);
}
void QHotkeyPrivate::addMappingInvoked(Qt::Key keycode,
Qt::KeyboardModifiers modifiers,
QHotkey::NativeShortcut nativeShortcut)
{
mapping.insert({ keycode, modifiers }, nativeShortcut);
}
bool QHotkeyPrivate::addShortcutInvoked(QHotkey* hotkey)
{
QHotkey::NativeShortcut shortcut = hotkey->_nativeShortcut;
if (!shortcuts.contains(shortcut)) {
if (!registerShortcut(shortcut)) {
qCWarning(logQHotkey)
<< QHotkey::tr("Failed to register %1. Error: %2")
.arg(hotkey->shortcut().toString(), error);
return false;
}
}
shortcuts.insert(shortcut, hotkey);
hotkey->_registered = true;
return true;
}
bool QHotkeyPrivate::removeShortcutInvoked(QHotkey* hotkey)
{
QHotkey::NativeShortcut shortcut = hotkey->_nativeShortcut;
if (shortcuts.remove(shortcut, hotkey) == 0)
return false;
hotkey->_registered = false;
emit hotkey->registeredChanged(true);
if (shortcuts.count(shortcut) == 0) {
if (!unregisterShortcut(shortcut)) {
qCWarning(logQHotkey)
<< QHotkey::tr("Failed to unregister %1. Error: %2")
.arg(hotkey->shortcut().toString(), error);
return false;
}
return true;
}
return true;
}
QHotkey::NativeShortcut QHotkeyPrivate::nativeShortcutInvoked(
Qt::Key keycode,
Qt::KeyboardModifiers modifiers)
{
if (mapping.contains({ keycode, modifiers }))
return mapping.value({ keycode, modifiers });
bool ok1 = false;
auto k = nativeKeycode(keycode, ok1);
bool ok2 = false;
auto m = nativeModifiers(modifiers, ok2);
if (ok1 && ok2)
return { k, m };
return {};
}
QHotkey::NativeShortcut::NativeShortcut()
: key()
, modifier()
, valid(false)
{}
QHotkey::NativeShortcut::NativeShortcut(quint32 key, quint32 modifier)
: key(key)
, modifier(modifier)
, valid(true)
{}
bool QHotkey::NativeShortcut::isValid() const
{
return valid;
}
bool QHotkey::NativeShortcut::operator==(QHotkey::NativeShortcut other) const
{
return (key == other.key) && (modifier == other.modifier) &&
valid == other.valid;
}
bool QHotkey::NativeShortcut::operator!=(QHotkey::NativeShortcut other) const
{
return (key != other.key) || (modifier != other.modifier) ||
valid != other.valid;
}
uint qHash(QHotkey::NativeShortcut key)
{
return qHash(key.key) ^ qHash(key.modifier);
}
uint qHash(QHotkey::NativeShortcut key, uint seed)
{
return qHash(key.key, seed) ^ qHash(key.modifier, seed);
}