diff --git a/autotests/integration/CMakeLists.txt b/autotests/integration/CMakeLists.txt index 5f1d379925..60000224e0 100644 --- a/autotests/integration/CMakeLists.txt +++ b/autotests/integration/CMakeLists.txt @@ -43,6 +43,7 @@ integrationTest(NAME testGlobalShortcuts SRCS globalshortcuts_test.cpp) integrationTest(NAME testWindowSelection SRCS window_selection_test.cpp) integrationTest(NAME testPointerConstraints SRCS pointer_constraints_test.cpp) integrationTest(NAME testKeyboardLayout SRCS keyboard_layout_test.cpp) +integrationTest(NAME testKeymapCreationFailure SRCS keymap_creation_failure_test.cpp) if (XCB_ICCCM_FOUND) integrationTest(NAME testMoveResize SRCS move_resize_window_test.cpp LIBS XCB::ICCCM) diff --git a/autotests/integration/keymap_creation_failure_test.cpp b/autotests/integration/keymap_creation_failure_test.cpp new file mode 100644 index 0000000000..dd8d7b6411 --- /dev/null +++ b/autotests/integration/keymap_creation_failure_test.cpp @@ -0,0 +1,102 @@ +/******************************************************************** +KWin - the KDE window manager +This file is part of the KDE project. + +Copyright (C) 2017 Martin Flöser + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*********************************************************************/ +#include "kwin_wayland_test.h" +#include "keyboard_input.h" +#include "keyboard_layout.h" +#include "platform.h" +#include "shell_client.h" +#include "virtualdesktops.h" +#include "wayland_server.h" +#include "workspace.h" + +#include +#include + +#include + +using namespace KWin; +using namespace KWayland::Client; + +static const QString s_socketName = QStringLiteral("wayland_test_kwin_keymap_creation_failure-0"); + +class KeymapCreationFailureTest : public QObject +{ + Q_OBJECT +private Q_SLOTS: + void initTestCase(); + void init(); + void cleanup(); + + void testPointerButton(); +}; + +void KeymapCreationFailureTest::initTestCase() +{ + // situation for for BUG 381210 + // this will fail to create keymap + qputenv("XKB_DEFAULT_RULES", "no"); + qputenv("XKB_DEFAULT_MODEL", "no"); + qputenv("XKB_DEFAULT_LAYOUT", "no"); + qputenv("XKB_DEFAULT_VARIANT", "no"); + qputenv("XKB_DEFAULT_OPTIONS", "no"); + + qRegisterMetaType(); + qRegisterMetaType(); + QSignalSpy workspaceCreatedSpy(kwinApp(), &Application::workspaceCreated); + QVERIFY(workspaceCreatedSpy.isValid()); + kwinApp()->platform()->setInitialWindowSize(QSize(1280, 1024)); + QVERIFY(waylandServer()->init(s_socketName.toLocal8Bit())); + + kwinApp()->setConfig(KSharedConfig::openConfig(QString(), KConfig::SimpleConfig)); + kwinApp()->setKxkbConfig(KSharedConfig::openConfig(QString(), KConfig::SimpleConfig)); + KConfigGroup layoutGroup = kwinApp()->kxkbConfig()->group("Layout"); + layoutGroup.writeEntry("LayoutList", QStringLiteral("no")); + layoutGroup.writeEntry("Model", "no"); + layoutGroup.writeEntry("Options", "no"); + layoutGroup.sync(); + + kwinApp()->start(); + QVERIFY(workspaceCreatedSpy.wait()); + waylandServer()->initWorkspace(); +} + +void KeymapCreationFailureTest::init() +{ + QVERIFY(Test::setupWaylandConnection()); +} + +void KeymapCreationFailureTest::cleanup() +{ + Test::destroyWaylandConnection(); +} + +void KeymapCreationFailureTest::testPointerButton() +{ + // test case for BUG 381210 + // pressing a pointer button results in crash + + // now create the crashing condition + // which is sending in a pointer event + kwinApp()->platform()->pointerButtonPressed(BTN_LEFT, 0); + kwinApp()->platform()->pointerButtonReleased(BTN_LEFT, 1); +} + +WAYLANDTEST_MAIN(KeymapCreationFailureTest) +#include "keymap_creation_failure_test.moc" diff --git a/autotests/mock_abstract_client.cpp b/autotests/mock_abstract_client.cpp index 20795fa899..d270f636e1 100644 --- a/autotests/mock_abstract_client.cpp +++ b/autotests/mock_abstract_client.cpp @@ -30,6 +30,7 @@ AbstractClient::AbstractClient(QObject *parent) , m_hiddenInternal(false) , m_keepBelow(false) , m_geometry() + , m_resize(false) { } @@ -103,4 +104,14 @@ void AbstractClient::setKeepBelow(bool keepBelow) emit keepBelowChanged(); } +bool AbstractClient::isResize() const +{ + return m_resize; +} + +void AbstractClient::setResize(bool set) +{ + m_resize = set; +} + } diff --git a/autotests/mock_abstract_client.h b/autotests/mock_abstract_client.h index a88f04f06b..8d7e825d75 100644 --- a/autotests/mock_abstract_client.h +++ b/autotests/mock_abstract_client.h @@ -47,6 +47,8 @@ public: void setHiddenInternal(bool set); void setGeometry(const QRect &rect); void setKeepBelow(bool); + bool isResize() const; + void setResize(bool set); virtual void showOnScreenEdge() = 0; Q_SIGNALS: @@ -60,6 +62,7 @@ private: bool m_hiddenInternal; bool m_keepBelow; QRect m_geometry; + bool m_resize; }; } diff --git a/autotests/mock_client.cpp b/autotests/mock_client.cpp index 97d7e0154f..88b5fa6698 100644 --- a/autotests/mock_client.cpp +++ b/autotests/mock_client.cpp @@ -35,9 +35,4 @@ void Client::showOnScreenEdge() setHiddenInternal(false); } -bool Client::isResize() const -{ - return false; -} - } diff --git a/autotests/mock_client.h b/autotests/mock_client.h index df62ecd62d..17db3c8c7b 100644 --- a/autotests/mock_client.h +++ b/autotests/mock_client.h @@ -34,8 +34,6 @@ class Client : public AbstractClient public: explicit Client(QObject *parent); virtual ~Client(); - - bool isResize() const; void showOnScreenEdge() override; }; diff --git a/autotests/test_screen_edges.cpp b/autotests/test_screen_edges.cpp index e26459e5c2..8411781044 100644 --- a/autotests/test_screen_edges.cpp +++ b/autotests/test_screen_edges.cpp @@ -392,6 +392,27 @@ void TestScreenEdges::testCreatingInitialEdges() QCOMPARE(e->activatesForTouchGesture(), false); QCOMPARE(e->approachGeometry(), expectedGeometries.at(i*2+1)); } + + // let's start a move of window. + Client client(workspace()); + workspace()->setMovingClient(&client); + for (int i = 0; i < 8; ++i) { + auto e = edges.at(i); + QVERIFY(!e->isReserved()); + QCOMPARE(e->activatesForPointer(), true); + QCOMPARE(e->activatesForTouchGesture(), false); + QCOMPARE(e->approachGeometry(), expectedGeometries.at(i*2+1)); + } + // not for resize + client.setResize(true); + for (int i = 0; i < 8; ++i) { + auto e = edges.at(i); + QVERIFY(!e->isReserved()); + QCOMPARE(e->activatesForPointer(), false); + QCOMPARE(e->activatesForTouchGesture(), false); + QCOMPARE(e->approachGeometry(), expectedGeometries.at(i*2+1)); + } + workspace()->setMovingClient(nullptr); } void TestScreenEdges::testCallback() diff --git a/effects.cpp b/effects.cpp index d2c4768c04..8155de6b34 100644 --- a/effects.cpp +++ b/effects.cpp @@ -48,6 +48,7 @@ along with this program. If not, see . #include "kwinglutils.h" #include +#include #include @@ -599,6 +600,11 @@ bool EffectsHandlerImpl::grabKeyboard(Effect* effect) bool ret = grabXKeyboard(); if (!ret) return false; + // Workaround for Qt 5.9 regression introduced with 2b34aefcf02f09253473b096eb4faffd3e62b5f4 + // we no longer get any events for the root window, one needs to call winId() on the desktop window + // TODO: change effects event handling to create the appropriate QKeyEvent without relying on Qt + // as it's done already in the Wayland case. + qApp->desktop()->winId(); } keyboard_grab_effect = effect; return true; diff --git a/screenedge.cpp b/screenedge.cpp index 589c1d03f2..0b17a0e413 100644 --- a/screenedge.cpp +++ b/screenedge.cpp @@ -183,6 +183,12 @@ bool Edge::activatesForPointer() const if (m_edges->isDesktopSwitching()) { return true; } + if (m_edges->isDesktopSwitchingMovingClients()) { + auto c = Workspace::self()->getMovingClient(); + if (c && !c->isResize()) { + return true; + } + } if (!m_callBacks.isEmpty()) { return true; } @@ -293,10 +299,7 @@ bool Edge::canActivate(const QPoint &cursorPos, const QDateTime &triggerTime) void Edge::handle(const QPoint &cursorPos) { AbstractClient *movingClient = Workspace::self()->getMovingClient(); - bool isResize = false; - if (Client *movingClientClient = qobject_cast(movingClient)) - isResize = movingClientClient->isResize(); - if ((edges()->isDesktopSwitchingMovingClients() && movingClient && !isResize) || + if ((edges()->isDesktopSwitchingMovingClients() && movingClient && !movingClient->isResize()) || (edges()->isDesktopSwitching() && isScreenEdge())) { // always switch desktops in case: // moving a Client and option for switch on client move is enabled diff --git a/tabbox/tabbox.cpp b/tabbox/tabbox.cpp index d6d3c7849a..1438dd6743 100644 --- a/tabbox/tabbox.cpp +++ b/tabbox/tabbox.cpp @@ -284,7 +284,7 @@ TabBoxClientList TabBoxHandlerImpl::stackingOrder() const ToplevelList stacking = Workspace::self()->stackingOrder(); TabBoxClientList ret; foreach (Toplevel *toplevel, stacking) { - if (Client *client = qobject_cast(toplevel)) { + if (auto client = qobject_cast(toplevel)) { ret.append(client->tabBoxClient()); } } @@ -331,7 +331,7 @@ void TabBoxHandlerImpl::shadeClient(TabBoxClient *c, bool b) const QWeakPointer TabBoxHandlerImpl::desktopClient() const { foreach (Toplevel *toplevel, Workspace::self()->stackingOrder()) { - Client *client = qobject_cast(toplevel); + auto client = qobject_cast(toplevel); if (client && client->isDesktop() && client->isOnCurrentDesktop() && client->screen() == screens()->current()) { return client->tabBoxClient(); } @@ -1319,7 +1319,7 @@ void TabBox::walkThroughDesktops(bool forward) void TabBox::CDEWalkThroughWindows(bool forward) { - Client* c = nullptr; + AbstractClient* c = nullptr; // this function find the first suitable client for unreasonable focus // policies - the topmost one, with some exceptions (can't be keepabove/below, // otherwise it gets stuck on them) @@ -1327,7 +1327,7 @@ void TabBox::CDEWalkThroughWindows(bool forward) for (int i = Workspace::self()->stackingOrder().size() - 1; i >= 0 ; --i) { - Client* it = qobject_cast(Workspace::self()->stackingOrder().at(i)); + auto it = qobject_cast(Workspace::self()->stackingOrder().at(i)); if (it && it->isOnCurrentActivity() && it->isOnCurrentDesktop() && !it->isSpecialWindow() && it->isShown(false) && it->wantsTabFocus() && !it->keepAbove() && !it->keepBelow()) { @@ -1335,14 +1335,14 @@ void TabBox::CDEWalkThroughWindows(bool forward) break; } } - Client* nc = c; + AbstractClient* nc = c; bool options_traverse_all; { KConfigGroup group(kwinApp()->config(), "TabBox"); options_traverse_all = group.readEntry("TraverseAll", false); } - Client* firstClient = nullptr; + AbstractClient* firstClient = nullptr; do { nc = forward ? nextClientStatic(nc) : previousClientStatic(nc); if (!firstClient) { @@ -1641,34 +1641,36 @@ int TabBox::previousDesktopStatic(int iDesktop) const auxiliary functions to travers all clients according to the static order. Useful for the CDE-style Alt-tab feature. */ -Client* TabBox::nextClientStatic(Client* c) const +AbstractClient* TabBox::nextClientStatic(AbstractClient* c) const { - if (!c || Workspace::self()->clientList().isEmpty()) + const auto &list = Workspace::self()->allClientList(); + if (!c || list.isEmpty()) return 0; - int pos = Workspace::self()->clientList().indexOf(c); + int pos = list.indexOf(c); if (pos == -1) - return Workspace::self()->clientList().first(); + return list.first(); ++pos; - if (pos == Workspace::self()->clientList().count()) - return Workspace::self()->clientList().first(); - return Workspace::self()->clientList()[ pos ]; + if (pos == list.count()) + return list.first(); + return list.at(pos); } /*! auxiliary functions to travers all clients according to the static order. Useful for the CDE-style Alt-tab feature. */ -Client* TabBox::previousClientStatic(Client* c) const +AbstractClient* TabBox::previousClientStatic(AbstractClient* c) const { - if (!c || Workspace::self()->clientList().isEmpty()) + const auto &list = Workspace::self()->allClientList(); + if (!c || list.isEmpty()) return 0; - int pos = Workspace::self()->clientList().indexOf(c); + int pos = list.indexOf(c); if (pos == -1) - return Workspace::self()->clientList().last(); + return list.last(); if (pos == 0) - return Workspace::self()->clientList().last(); + return list.last(); --pos; - return Workspace::self()->clientList()[ pos ]; + return list.at(pos); } bool TabBox::establishTabBoxGrab() diff --git a/tabbox/tabbox.h b/tabbox/tabbox.h index 012cd815da..3cb8c7cf76 100644 --- a/tabbox/tabbox.h +++ b/tabbox/tabbox.h @@ -176,8 +176,8 @@ public: void initShortcuts(); - Client* nextClientStatic(Client*) const; - Client* previousClientStatic(Client*) const; + AbstractClient* nextClientStatic(AbstractClient*) const; + AbstractClient* previousClientStatic(AbstractClient*) const; int nextDesktopStatic(int iDesktop) const; int previousDesktopStatic(int iDesktop) const; void keyPress(int key); diff --git a/xkb.cpp b/xkb.cpp index f8dc6ed219..cdee22b68d 100644 --- a/xkb.cpp +++ b/xkb.cpp @@ -354,13 +354,16 @@ QString Xkb::layoutName() const QString Xkb::layoutName(xkb_layout_index_t layout) const { + if (!m_keymap) { + return QString{}; + } return QString::fromLocal8Bit(xkb_keymap_layout_get_name(m_keymap, layout)); } QMap Xkb::layoutNames() const { QMap layouts; - const auto size = xkb_keymap_num_layouts(m_keymap); + const auto size = m_keymap ? xkb_keymap_num_layouts(m_keymap) : 0u; for (xkb_layout_index_t i = 0; i < size; i++) { layouts.insert(i, layoutName(i)); } @@ -387,6 +390,9 @@ void Xkb::updateConsumedModifiers(uint32_t key) Qt::KeyboardModifiers Xkb::modifiersRelevantForGlobalShortcuts() const { + if (!m_state) { + return Qt::NoModifier; + } Qt::KeyboardModifiers mods = Qt::NoModifier; if (xkb_state_mod_index_is_active(m_state, m_shiftModifier, XKB_STATE_MODS_EFFECTIVE) == 1) { mods |= Qt::ShiftModifier;