diff --git a/abstract_client.h b/abstract_client.h --- a/abstract_client.h +++ b/abstract_client.h @@ -384,8 +384,10 @@ */ bool isSpecialWindow() const; void sendToScreen(int screen); - virtual const QKeySequence &shortcut() const = 0; - virtual void setShortcut(const QString &cut) = 0; + const QKeySequence &shortcut() const { + return _shortcut; + } + void setShortcut(const QString &cut); virtual bool performMouseCommand(Options::MouseCommand, const QPoint &globalPos); void setOnAllDesktops(bool set); void setDesktop(int); @@ -991,6 +993,8 @@ void setUnresponsive(bool unresponsive); + virtual void setShortcutInternal(); + private: void handlePaletteChange(); QSharedPointer m_tabBoxClient; @@ -1065,6 +1069,8 @@ bool m_unresponsive = false; + QKeySequence _shortcut; + static bool s_haveResizeEffect; }; diff --git a/autotests/integration/globalshortcuts_test.cpp b/autotests/integration/globalshortcuts_test.cpp --- a/autotests/integration/globalshortcuts_test.cpp +++ b/autotests/integration/globalshortcuts_test.cpp @@ -56,6 +56,7 @@ void testUserActionsMenu(); void testMetaShiftW(); void testX11ClientShortcut(); + void testWaylandClientShortcut(); }; void GlobalShortcutsTest::initTestCase() @@ -277,5 +278,43 @@ QVERIFY(windowClosedSpy.wait()); } +void GlobalShortcutsTest::testWaylandClientShortcut() +{ + QScopedPointer surface(Test::createSurface()); + QScopedPointer shellSurface(Test::createShellSurface(surface.data())); + auto client = Test::renderAndWaitForShown(surface.data(), QSize(100, 50), Qt::blue); + + QCOMPARE(workspace()->activeClient(), client); + QVERIFY(client->isActive()); + QCOMPARE(client->shortcut(), QKeySequence()); + const QKeySequence seq(Qt::META + Qt::SHIFT + Qt::Key_Y); + QVERIFY(workspace()->shortcutAvailable(seq)); + client->setShortcut(seq.toString()); + QCOMPARE(client->shortcut(), seq); + QVERIFY(!workspace()->shortcutAvailable(seq)); + QEXPECT_FAIL("", "Caption adjustment not yet implemented", Continue); + QCOMPARE(client->caption(), QStringLiteral(" {Meta+Shift+Y}")); + + workspace()->activateClient(nullptr); + QVERIFY(!workspace()->activeClient()); + QVERIFY(!client->isActive()); + + // now let's trigger the shortcut + quint32 timestamp = 0; + kwinApp()->platform()->keyboardKeyPressed(KEY_LEFTMETA, timestamp++); + kwinApp()->platform()->keyboardKeyPressed(KEY_LEFTSHIFT, timestamp++); + kwinApp()->platform()->keyboardKeyPressed(KEY_Y, timestamp++); + QTRY_COMPARE(workspace()->activeClient(), client); + kwinApp()->platform()->keyboardKeyReleased(KEY_Y, timestamp++); + kwinApp()->platform()->keyboardKeyReleased(KEY_LEFTSHIFT, timestamp++); + kwinApp()->platform()->keyboardKeyReleased(KEY_LEFTMETA, timestamp++); + + shellSurface.reset(); + surface.reset(); + QVERIFY(Test::waitForWindowDestroyed(client)); + QVERIFY(workspace()->shortcutAvailable(seq)); +} + + WAYLANDTEST_MAIN(GlobalShortcutsTest) #include "globalshortcuts_test.moc" diff --git a/client.h b/client.h --- a/client.h +++ b/client.h @@ -200,8 +200,6 @@ QSize sizeForClientSize(const QSize&, Sizemode mode = SizemodeAny, bool noframe = false) const override; bool providesContextHelp() const override; - const QKeySequence &shortcut() const override; - void setShortcut(const QString& cut) override; Options::WindowOperation mouseButtonToWindowOperation(Qt::MouseButtons button); bool performMouseCommand(Options::MouseCommand, const QPoint& globalPos) override; @@ -451,7 +449,7 @@ void setCaption(const QString& s, bool force = false); bool hasTransientInternal(const Client* c, bool indirect, ConstClientList& set) const; void finishWindowRules(); - void setShortcutInternal(const QKeySequence &cut = QKeySequence()); + void setShortcutInternal() override; void configureRequest(int value_mask, int rx, int ry, int rw, int rh, int gravity, bool from_tool); NETExtendedStrut strut() const; @@ -579,7 +577,6 @@ bool isPending; } syncRequest; static bool check_active_modal; ///< \see Client::checkActiveModal() - QKeySequence _shortcut; int sm_stacking_order; friend struct ResetupRulesProcedure; @@ -738,11 +735,6 @@ return m_moveResizeGrabWindow; } -inline const QKeySequence &Client::shortcut() const -{ - return _shortcut; -} - inline void Client::removeRule(Rules* rule) { client_rules.remove(rule); diff --git a/shell_client.h b/shell_client.h --- a/shell_client.h +++ b/shell_client.h @@ -89,8 +89,6 @@ void setNoBorder(bool set) override; void updateDecoration(bool check_workspace_pos, bool force = false) override; void setOnAllActivities(bool set) override; - void setShortcut(const QString &cut) override; - const QKeySequence &shortcut() const override; void takeFocus() override; void updateWindowRules(Rules::Types selection) override; bool userCanSetFullScreen() const override; diff --git a/shell_client.cpp b/shell_client.cpp --- a/shell_client.cpp +++ b/shell_client.cpp @@ -820,17 +820,6 @@ Q_UNUSED(set) } -void ShellClient::setShortcut(const QString &cut) -{ - Q_UNUSED(cut) -} - -const QKeySequence &ShellClient::shortcut() const -{ - static QKeySequence seq; - return seq; -} - void ShellClient::takeFocus() { if (rules()->checkAcceptFocus(wantsInput())) { diff --git a/useractions.cpp b/useractions.cpp --- a/useractions.cpp +++ b/useractions.cpp @@ -1044,7 +1044,7 @@ active_client->takeFocus(); } -void Workspace::clientShortcutUpdated(Client* c) +void Workspace::clientShortcutUpdated(AbstractClient* c) { QString key = QStringLiteral("_k_session:%1").arg(c->window()); QAction* action = findChild(key); @@ -1794,22 +1794,30 @@ #undef USABLE_ACTIVE_CLIENT -void Client::setShortcut(const QString& _cut) +void AbstractClient::setShortcut(const QString& _cut) { QString cut = rules()->checkShortcut(_cut); - if (cut.isEmpty()) - return setShortcutInternal(); + auto updateShortcut = [this](const QKeySequence &cut = QKeySequence()) { + if (_shortcut == cut) + return; + _shortcut = cut; + setShortcutInternal(); + }; + if (cut.isEmpty()) { + updateShortcut(); + return; + } if (cut == shortcut().toString()) { return; // no change } // Format: // base+(abcdef)base+(abcdef) // E.g. Alt+Ctrl+(ABCDEF);Meta+X,Meta+(ABCDEF) if (!cut.contains(QLatin1Char('(')) && !cut.contains(QLatin1Char(')')) && !cut.contains(QLatin1String(" - "))) { if (workspace()->shortcutAvailable(cut, this)) - setShortcutInternal(QKeySequence(cut)); + updateShortcut(QKeySequence(cut)); else - setShortcutInternal(); + updateShortcut(); return; } QList< QKeySequence > keys; @@ -1846,18 +1854,20 @@ it != keys.constEnd(); ++it) { if (workspace()->shortcutAvailable(*it, this)) { - setShortcutInternal(*it); + updateShortcut(*it); return; } } - setShortcutInternal(); + updateShortcut(); } -void Client::setShortcutInternal(const QKeySequence &cut) +void AbstractClient::setShortcutInternal() +{ + workspace()->clientShortcutUpdated(this); +} + +void Client::setShortcutInternal() { - if (_shortcut == cut) - return; - _shortcut = cut; updateCaption(); #if 0 workspace()->clientShortcutUpdated(this); @@ -1874,16 +1884,16 @@ workspace()->clientShortcutUpdated(this); } -bool Workspace::shortcutAvailable(const QKeySequence &cut, Client* ignore) const +bool Workspace::shortcutAvailable(const QKeySequence &cut, AbstractClient* ignore) const { if (ignore && cut == ignore->shortcut()) return true; if (!KGlobalAccel::getGlobalShortcutsByKey(cut).isEmpty()) { return false; } - for (ClientList::ConstIterator it = clients.constBegin(); - it != clients.constEnd(); + for (auto it = m_allClients.constBegin(); + it != m_allClients.constEnd(); ++it) { if ((*it) != ignore && (*it)->shortcut() == cut) return false; diff --git a/workspace.h b/workspace.h --- a/workspace.h +++ b/workspace.h @@ -326,8 +326,8 @@ void focusToNull(); // SELI TODO: Public? - void clientShortcutUpdated(Client* c); - bool shortcutAvailable(const QKeySequence &cut, Client* ignore = NULL) const; + void clientShortcutUpdated(AbstractClient* c); + bool shortcutAvailable(const QKeySequence &cut, AbstractClient* ignore = NULL) const; bool globalShortcutsDisabled() const; void disableGlobalShortcutsForClient(bool disable); diff --git a/workspace.cpp b/workspace.cpp --- a/workspace.cpp +++ b/workspace.cpp @@ -434,6 +434,12 @@ if (c == last_active_client) { last_active_client = nullptr; } + if (client_keys_client == c) { + setupWindowShortcutDone(false); + } + if (!c->shortcut().isEmpty()) { + c->setShortcut(QString()); // Remove from client_keys + } clientHidden(c); emit clientRemoved(c); markXStackingOrderAsDirty();