~gerboland/qtmir/small-hooks-change

« back to all changes in this revision

Viewing changes to src/modules/Unity/Application/mirsurface.cpp

  • Committer: Bileto Bot
  • Author(s): Daniel d'Andrada
  • Date: 2017-03-28 17:12:39 UTC
  • mfrom: (625.1.4 keyState)
  • Revision ID: ci-train-bot@canonical.com-20170328171239-nd8gj4bz4srkgof1
Ensure the window that got a key down also gets the corresponding key up

Otherwise it will be left in a inconsistent state (with a pressed key hanging around).

QQuickWindow's input dispatching doesn't guarantee that for its QQuickItem.
So we have to do it ourselves.

This can happen when qml active focus changes in response to a key press.
Eg: client creates a child window in response to a Ctrl+O. By the time the user
releases the Ctrl, active focus will already be in the child window, so the child window
will get the release event instead of the top-level one. (LP: #1671072)

Approved by: Gerry Boland, Unity8 CI Bot

Show diffs side-by-side

added added

removed removed

Lines of Context:
41
41
#include <logging.h>
42
42
 
43
43
// Qt
 
44
#include <QElapsedTimer>
44
45
#include <QQmlEngine>
45
46
#include <QScreen>
46
47
 
68
69
};
69
70
Q_DECLARE_FLAGS(DirtyStates, DirtyState)
70
71
 
 
72
qint64 msecsSinceReference()
 
73
{
 
74
    static QElapsedTimer elapsedTimer;
 
75
    elapsedTimer.start();
 
76
    return elapsedTimer.msecsSinceReference();
 
77
}
 
78
 
71
79
} // namespace {
72
80
 
 
81
 
73
82
class MirSurface::SurfaceObserverImpl : public SurfaceObserver, public mir::scene::SurfaceObserver
74
83
{
75
84
public:
409
418
 
410
419
    m_focused = value;
411
420
    Q_EMIT focusedChanged(value);
 
421
 
 
422
    if (m_focused) {
 
423
        /*
 
424
            Ensure the window that got a key down also gets the corresponding key up
 
425
 
 
426
            Otherwise it will be left in a inconsistent state (with a pressed key hanging around).
 
427
 
 
428
            QQuickWindow's input dispatching doesn't guarantee that for its QQuickItems.
 
429
            So we have to do it ourselves.
 
430
 
 
431
            This can happen when qml active focus changes in response to a key press.
 
432
            Eg: client creates a child window in response to a Ctrl+O. By the time the user
 
433
            releases the Ctrl, active focus will already be in the child window, so the child window
 
434
            will get the release event instead of the top-level one. To solve this, once the top-level
 
435
            window gets active focus again, we synthesize KeyRelease events for all keys this window
 
436
            thinks are still pressed.
 
437
        */
 
438
        releaseAllPressedKeys();
 
439
    }
412
440
}
413
441
 
414
442
void MirSurface::setViewActiveFocus(qintptr viewId, bool value)
661
689
 
662
690
void MirSurface::keyPressEvent(QKeyEvent *qtEvent)
663
691
{
 
692
    {
 
693
        if (!qtEvent->isAutoRepeat()) {
 
694
            Q_ASSERT(!isKeyPressed(qtEvent->nativeVirtualKey()));
 
695
            PressedKey pressedKey(qtEvent, msecsSinceReference());
 
696
            auto info = EventBuilder::instance()->findInfo(qtEvent->timestamp());
 
697
            if (info) {
 
698
                pressedKey.deviceId = info->deviceId;
 
699
            }
 
700
            m_pressedKeys.append(std::move(pressedKey));
 
701
        }
 
702
    }
 
703
 
664
704
    auto ev = EventBuilder::instance()->makeMirEvent(qtEvent);
665
705
    auto ev1 = reinterpret_cast<MirKeyboardEvent const*>(ev.get());
666
706
    m_controller->deliverKeyboardEvent(m_window, ev1);
669
709
 
670
710
void MirSurface::keyReleaseEvent(QKeyEvent *qtEvent)
671
711
{
672
 
    auto ev = EventBuilder::instance()->makeMirEvent(qtEvent);
673
 
    auto ev1 = reinterpret_cast<MirKeyboardEvent const*>(ev.get());
674
 
    m_controller->deliverKeyboardEvent(m_window, ev1);
675
 
    qtEvent->accept();
 
712
    if (isKeyPressed(qtEvent->nativeVirtualKey())) {
 
713
        forgetPressedKey(qtEvent->nativeVirtualKey());
 
714
        auto ev = EventBuilder::instance()->makeMirEvent(qtEvent);
 
715
        auto ev1 = reinterpret_cast<MirKeyboardEvent const*>(ev.get());
 
716
        m_controller->deliverKeyboardEvent(m_window, ev1);
 
717
    } else {
 
718
        // don't send a release event for a key for which we did not send a press in the first place
 
719
    }
676
720
}
677
721
 
678
722
void MirSurface::touchEvent(Qt::KeyboardModifiers mods,
1264
1308
    QPoint point(m_surface->top_left().x.as_int(),m_surface->top_left().y.as_int());
1265
1309
    setPosition(point);
1266
1310
}
 
1311
 
 
1312
bool MirSurface::isKeyPressed(quint32 nativeVirtualKey) const
 
1313
{
 
1314
    for (const auto &pressedKey : m_pressedKeys) {
 
1315
        if (pressedKey.nativeVirtualKey == nativeVirtualKey) {
 
1316
            return true;
 
1317
        }
 
1318
    }
 
1319
    return false;
 
1320
}
 
1321
 
 
1322
void MirSurface::forgetPressedKey(quint32 nativeVirtualKey)
 
1323
{
 
1324
    for (int i = 0; i < m_pressedKeys.count(); ++i) {
 
1325
        if (m_pressedKeys[i].nativeVirtualKey == nativeVirtualKey) {
 
1326
            m_pressedKeys.removeAt(i);
 
1327
            return;
 
1328
        }
 
1329
    }
 
1330
}
 
1331
 
 
1332
void MirSurface::releaseAllPressedKeys()
 
1333
{
 
1334
    for (auto &pressedKey : m_pressedKeys) {
 
1335
        auto deltaMs = (ulong)(msecsSinceReference() - pressedKey.msecsSinceReference);
 
1336
        ulong timestamp = pressedKey.timestamp + deltaMs;
 
1337
        std::vector<uint8_t> cookie{};
 
1338
 
 
1339
        auto ev = mir::events::make_event(pressedKey.deviceId,
 
1340
                uncompressTimestamp<qtmir::Timestamp>(qtmir::Timestamp(timestamp)),
 
1341
                cookie, mir_keyboard_action_up, pressedKey.nativeVirtualKey, pressedKey.nativeScanCode,
 
1342
                mir_input_event_modifier_none);
 
1343
 
 
1344
        auto ev1 = reinterpret_cast<MirKeyboardEvent const*>(ev.get());
 
1345
        m_controller->deliverKeyboardEvent(m_window, ev1);
 
1346
    }
 
1347
    m_pressedKeys.clear();
 
1348
}
 
1349
 
 
1350
MirSurface::PressedKey::PressedKey(QKeyEvent *qtEvent, qint64 msecsSinceReference)
 
1351
    : nativeVirtualKey(qtEvent->nativeVirtualKey())
 
1352
    , nativeScanCode(qtEvent->nativeScanCode())
 
1353
    , timestamp(qtEvent->timestamp())
 
1354
    , msecsSinceReference(msecsSinceReference)
 
1355
{
 
1356
}