10#include "kkeysequencerecorder.h"
12#include "keyboardgrabber_p.h"
13#include "kguiaddons_debug.h"
14#include "shortcutinhibition_p.h"
15#include "waylandinhibition_p.h"
17#include <QGuiApplication>
27class KKeySequenceRecorderGlobal :
public QObject
31 static KKeySequenceRecorderGlobal *self()
33 static KKeySequenceRecorderGlobal s_self;
38 void sequenceRecordingStarted();
41class KKeySequenceRecorderPrivate :
public QObject
46 enum { MaxKeyCount = 4 };
50 void controlModifierlessTimeout();
54 void finishRecording();
55 void receivedRecording();
62 bool m_multiKeyShortcutsAllowed;
63 bool m_modifierlessAllowed;
64 bool m_modifierOnlyAllowed =
false;
67 QTimer m_modifierlessTimer;
68 std::unique_ptr<ShortcutInhibition> m_inhibition;
71 bool m_isReleasingModifierOnly =
false;
72 std::chrono::nanoseconds m_modifierFirstReleaseTime;
81 keyQt &=
~Qt::KeyboardModifierMask;
270static bool isOkWhenModifierless(
int key)
292 if (sequence.
count() >= KKeySequenceRecorderPrivate::MaxKeyCount) {
293 qCWarning(KGUIADDONS_LOG) <<
"Cannot append to a key to a sequence which is already of length" << sequence.
count();
297 std::array<int, KKeySequenceRecorderPrivate::MaxKeyCount> keys{sequence[0].toCombined(),
298 sequence[1].toCombined(),
299 sequence[2].toCombined(),
300 sequence[3].toCombined()};
308 keys[sequence.
count() - 1] = key;
309 return QKeySequence(keys[0], keys[1], keys[2], keys[3]);
312 keys[sequence.
count()] = key;
313 return QKeySequence(keys[0], keys[1], keys[2], keys[3]);
322void KKeySequenceRecorderPrivate::controlModifierlessTimeout()
324 if (m_currentKeySequence != 0 && !m_currentModifiers) {
326 m_modifierlessTimer.
start(600);
329 m_modifierlessTimer.
stop();
333bool KKeySequenceRecorderPrivate::eventFilter(
QObject *watched,
QEvent *event)
335 if (!m_isRecording) {
373void KKeySequenceRecorderPrivate::handleKeyPress(
QKeyEvent *event)
375 m_isReleasingModifierOnly =
false;
376 m_currentModifiers =
event->modifiers() & modifierMask;
377 int key =
event->key();
380 qCWarning(KGUIADDONS_LOG) <<
"Got unknown key";
394 m_currentModifiers |= keyToModifier(key);
395 m_lastPressedModifiers = m_currentModifiers;
396 controlModifierlessTimeout();
397 Q_EMIT q->currentKeySequenceChanged();
403 if (!(isOkWhenModifierless(key) || m_modifierlessAllowed)) {
413 key |= m_currentModifiers;
415 key |= (m_currentModifiers &
~Qt::ShiftModifier);
418 m_currentKeySequence = appendToSequence(m_currentKeySequence, key);
419 Q_EMIT q->currentKeySequenceChanged();
424 if ((!m_multiKeyShortcutsAllowed) || (m_currentKeySequence.
count() == MaxKeyCount)) {
428 controlModifierlessTimeout();
450void KKeySequenceRecorderPrivate::handleKeyRelease(
QKeyEvent *event)
454 switch (
event->key()) {
463 modifiers &= ~keyToModifier(
event->key());
465 if ((modifiers & m_currentModifiers) < m_currentModifiers) {
466 constexpr auto releaseTimeout = std::chrono::milliseconds(200);
467 const auto currentTime = std::chrono::steady_clock::now().time_since_epoch();
468 if (!m_isReleasingModifierOnly) {
469 m_isReleasingModifierOnly =
true;
470 m_modifierFirstReleaseTime = currentTime;
472 if (m_modifierOnlyAllowed && !modifiers && (currentTime - m_modifierFirstReleaseTime) < releaseTimeout) {
473 m_currentKeySequence = appendToSequence(m_currentKeySequence, prettifyModifierOnly(m_lastPressedModifiers));
476 m_currentModifiers = modifiers;
477 Q_EMIT q->currentKeySequenceChanged();
478 if (m_currentKeySequence.
count() == (m_multiKeyShortcutsAllowed ? MaxKeyCount : 1)) {
481 controlModifierlessTimeout();
485void KKeySequenceRecorderPrivate::receivedRecording()
487 m_modifierlessTimer.
stop();
488 m_isRecording =
false;
491 m_isReleasingModifierOnly =
false;
493 m_inhibition->disableInhibition();
496 Q_EMIT q->recordingChanged();
499void KKeySequenceRecorderPrivate::finishRecording()
507 , d(new KKeySequenceRecorderPrivate(this))
509 d->m_isRecording =
false;
510 d->m_modifierlessAllowed =
false;
511 d->m_multiKeyShortcutsAllowed =
true;
514 connect(&d->m_modifierlessTimer, &
QTimer::timeout, d.get(), &KKeySequenceRecorderPrivate::finishRecording);
517KKeySequenceRecorder::~KKeySequenceRecorder() noexcept
519 if (d->m_inhibition && d->m_inhibition->shortcutsAreInhibited()) {
520 d->m_inhibition->disableInhibition();
526 d->m_previousKeySequence = d->m_currentKeySequence;
528 KKeySequenceRecorderGlobal::self()->sequenceRecordingStarted();
529 connect(KKeySequenceRecorderGlobal::self(),
530 &KKeySequenceRecorderGlobal::sequenceRecordingStarted,
536 qCWarning(KGUIADDONS_LOG) <<
"Cannot record without a window";
539 d->m_isRecording =
true;
541 if (d->m_inhibition) {
542 d->m_inhibition->enableInhibition();
544 Q_EMIT recordingChanged();
545 Q_EMIT currentKeySequenceChanged();
550 setCurrentKeySequence(d->m_previousKeySequence);
551 d->receivedRecording();
557 return d->m_isRecording;
564 if (d->m_isRecording && d->m_currentKeySequence.count() < KKeySequenceRecorderPrivate::MaxKeyCount) {
565 return appendToSequence(d->m_currentKeySequence, d->m_currentModifiers);
567 return d->m_currentKeySequence;
571void KKeySequenceRecorder::setCurrentKeySequence(
const QKeySequence &sequence)
573 if (d->m_currentKeySequence == sequence) {
576 d->m_currentKeySequence = sequence;
577 Q_EMIT currentKeySequenceChanged();
585void KKeySequenceRecorder::setWindow(
QWindow *window)
587 if (
window == d->m_window) {
592 d->m_window->removeEventFilter(d.get());
597 qCDebug(KGUIADDONS_LOG) <<
"listening for events in" <<
window;
602 d->m_inhibition.reset(
new WaylandInhibition(
window));
605 d->m_inhibition.reset(
new KeyboardGrabber(
window));
615 return d->m_multiKeyShortcutsAllowed;
618void KKeySequenceRecorder::setMultiKeyShortcutsAllowed(
bool allowed)
620 if (allowed == d->m_multiKeyShortcutsAllowed) {
623 d->m_multiKeyShortcutsAllowed = allowed;
624 Q_EMIT multiKeyShortcutsAllowedChanged();
629 return d->m_modifierlessAllowed;
632void KKeySequenceRecorder::setModifierlessAllowed(
bool allowed)
634 if (allowed == d->m_modifierlessAllowed) {
637 d->m_modifierlessAllowed = allowed;
638 Q_EMIT modifierlessAllowedChanged();
643 return d->m_modifierOnlyAllowed;
646void KKeySequenceRecorder::setModifierOnlyAllowed(
bool allowed)
648 if (allowed == d->m_modifierOnlyAllowed) {
651 d->m_modifierOnlyAllowed = allowed;
652 Q_EMIT modifierOnlyAllowedChanged();
655#include "kkeysequencerecorder.moc"
656#include "moc_kkeysequencerecorder.cpp"
Record a QKeySequence by listening to key events in a window.
Q_INVOKABLE void startRecording()
Start recording.
bool multiKeyShortcutsAllowed
Controls the amount of key combinations that are captured until recording stops and gotKeySequence is...
void gotKeySequence(const QKeySequence &keySequence)
This signal is emitted when a key sequence has been recorded.
bool modifierOnlyAllowed
It makes it acceptable for the key sequence to be just a modifier (e.g.
KKeySequenceRecorder(QWindow *window, QObject *parent=nullptr)
Constructor.
QKeySequence currentKeySequence
The recorded key sequence.
bool isRecording
Whether key events are currently recorded.
QWindow * window
The window in which the key events are happening that should be recorded.
void cancelRecording()
Stops the recording session.
bool modifierlessAllowed
If key presses of "plain" keys without a modifier are considered to be a valid finished key combinati...
char * toString(const EngineQuery &query)
bool isShiftAsModifierAllowed(int keyQt)
bool isLetter() const const
int toCombined() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
bool disconnect(const QMetaObject::Connection &connection)
virtual bool event(QEvent *e)
virtual bool eventFilter(QObject *watched, QEvent *event)
void installEventFilter(QObject *filterObj)
typedef KeyboardModifiers