diff --git a/autotests/kwindowinfox11test.cpp b/autotests/kwindowinfox11test.cpp index 018c2a6..634c650 100644 --- a/autotests/kwindowinfox11test.cpp +++ b/autotests/kwindowinfox11test.cpp @@ -1,782 +1,784 @@ /* * Copyright 2014 Martin Gräßlin * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #include "kwindowinfo.h" #include "kwindowsystem.h" #include "netwm.h" #include "nettesthelper.h" #include #include #include #include #include Q_DECLARE_METATYPE(WId) Q_DECLARE_METATYPE(NET::State) Q_DECLARE_METATYPE(NET::States) Q_DECLARE_METATYPE(NET::WindowType) Q_DECLARE_METATYPE(NET::WindowTypeMask) Q_DECLARE_METATYPE(NET::WindowTypes) Q_DECLARE_METATYPE(NET::Properties) Q_DECLARE_METATYPE(NET::Properties2) class KWindowInfoX11Test : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase(); void init(); void cleanup(); void testState_data(); void testState(); void testDemandsAttention(); void testMinimized(); void testMappingState(); void testWindowType_data(); void testWindowType(); void testDesktop(); void testActivities(); void testWindowClass(); void testWindowRole(); void testClientMachine(); void testName(); void testTransientFor(); void testGroupLeader(); void testExtendedStrut(); void testGeometry(); void testDesktopFileName(); void testPid(); // actionSupported is not tested as it's too window manager specific // we could write a test against KWin's behavior, but that would fail on // build.kde.org as we use OpenBox there. private: void showWidget(QWidget *widget); bool waitForWindow(QSignalSpy &spy, WId winId, NET::Properties property, NET::Properties2 properties2 = NET::Properties2()) const; bool verifyMinimized(WId window) const; QScopedPointer window; }; void KWindowInfoX11Test::initTestCase() { QCoreApplication::setAttribute(Qt::AA_ForceRasterWidgets); qRegisterMetaType(); qRegisterMetaType(); } bool KWindowInfoX11Test::waitForWindow(QSignalSpy& spy, WId winId, NET::Properties property, NET::Properties2 property2) const { // we need to wait, window manager has to react and update the property. bool foundOurWindow = false; for (int i = 0; i < 10; ++i) { spy.wait(50); if (spy.isEmpty()) { continue; } for (auto it = spy.constBegin(); it != spy.constEnd(); ++it) { if (it->first().value() != winId) { continue; } if (property != NET::Properties()) { if (it->at(1).value() != property) { continue; } } if (property2 != NET::Properties2()) { if (it->at(2).value() != property2) { continue; } } foundOurWindow = true; break; } if (foundOurWindow) { break; } spy.clear(); } return foundOurWindow; } bool KWindowInfoX11Test::verifyMinimized(WId window) const { KWindowInfo info(window, NET::WMState | NET::XAWMState); return info.isMinimized(); } void KWindowInfoX11Test::init() { // create the window and ensure it has been managed window.reset(new QWidget()); showWidget(window.data()); } void KWindowInfoX11Test::showWidget(QWidget *window) { qRegisterMetaType("WId"); QSignalSpy spy(KWindowSystem::self(), SIGNAL(windowAdded(WId))); window->show(); bool foundOurWindow = false; for (int i = 0; i < 50; ++i) { spy.wait(50); if (spy.isEmpty()) { continue; } for (auto it = spy.constBegin(); it != spy.constEnd(); ++it) { if (it->isEmpty()) { continue; } if (it->first().value() == window->winId()) { foundOurWindow = true; break; } } if (foundOurWindow) { break; } spy.clear(); } } void KWindowInfoX11Test::cleanup() { // we hide the window and wait till it is gone so that we have a clean state in next test if (!window.isNull() && window->isVisible()) { WId id = window->winId(); QSignalSpy spy(KWindowSystem::self(), SIGNAL(windowRemoved(WId))); window->hide(); bool foundOurWindow = false; for (int i = 0; i < 50; ++i) { spy.wait(50); if (spy.isEmpty()) { continue; } for (auto it = spy.constBegin(); it != spy.constEnd(); ++it) { if (it->first().value() == id) { foundOurWindow = true; break; } } if (foundOurWindow) { break; } spy.clear(); } } window.reset(); } void KWindowInfoX11Test::testState_data() { QTest::addColumn("state"); QTest::newRow("max") << NET::States(NET::Max); QTest::newRow("maxHoriz") << NET::States(NET::MaxHoriz); QTest::newRow("shaded") << NET::States(NET::Shaded); QTest::newRow("skipTaskbar") << NET::States(NET::SkipTaskbar); QTest::newRow("skipPager") << NET::States(NET::SkipPager); QTest::newRow("keep above") << NET::States(NET::KeepAbove); QTest::newRow("keep below") << NET::States(NET::KeepBelow); QTest::newRow("fullscreen") << NET::States(NET::FullScreen); NETRootInfo info(QX11Info::connection(), NET::Supported); if (info.isSupported(NET::SkipSwitcher)) { QTest::newRow("skipSwitcher") << NET::States(NET::SkipSwitcher); } // NOTE: modal, sticky and hidden cannot be tested with this variant // demands attention is not tested as that's already part of the first run adjustments } void KWindowInfoX11Test::testState() { QFETCH(NET::States, state); QX11Info::getTimestamp(); KWindowInfo info(window->winId(), NET::WMState); QVERIFY(info.valid()); // all states except demands attention for (int i = 0; i < 12; ++i) { QVERIFY(!info.hasState(NET::States(1 << i))); } QSignalSpy spy(KWindowSystem::self(), static_cast(&KWindowSystem::windowChanged)); QVERIFY(spy.isValid()); // now we have a clean window and can do fun stuff KWindowSystem::setState(window->winId(), state); QVERIFY(waitForWindow(spy, window->winId(), NET::WMState)); KWindowInfo info3(window->winId(), NET::WMState); QVERIFY(info3.valid()); QCOMPARE(int(info3.state()), int(state)); QVERIFY(info3.hasState(state)); } // This struct is defined here to avoid a dependency on xcb-icccm struct kde_wm_hints { uint32_t flags; uint32_t input; int32_t initial_state; xcb_pixmap_t icon_pixmap; xcb_window_t icon_window; int32_t icon_x; int32_t icon_y; xcb_pixmap_t icon_mask; xcb_window_t window_group; }; void KWindowInfoX11Test::testDemandsAttention() { QSignalSpy activeWindowSpy(KWindowSystem::self(), SIGNAL(activeWindowChanged(WId))); QVERIFY(activeWindowSpy.isValid()); if (KWindowSystem::activeWindow() != window->winId()) { // we force activate as KWin's focus stealing prevention might kick in KWindowSystem::forceActiveWindow(window->winId()); QVERIFY(activeWindowSpy.wait()); QCOMPARE(activeWindowSpy.first().first().toULongLong(), window->winId()); activeWindowSpy.clear(); } // need a second window for proper interaction QWidget win2; showWidget(&win2); KWindowSystem::forceActiveWindow(win2.winId()); if (activeWindowSpy.isEmpty()) { QVERIFY(activeWindowSpy.wait()); } KWindowInfo info(window->winId(), NET::WMState); QVERIFY(info.valid()); QVERIFY(!info.hasState(NET::DemandsAttention)); QSignalSpy spy(KWindowSystem::self(), static_cast(&KWindowSystem::windowChanged)); QVERIFY(spy.isValid()); // now we have a clean window and can do fun stuff KWindowSystem::demandAttention(window->winId()); QVERIFY(waitForWindow(spy, window->winId(), NET::WMState)); KWindowInfo info2(window->winId(), NET::WMState); QVERIFY(info2.valid()); QCOMPARE(info2.state(), NET::DemandsAttention); QVERIFY(info2.hasState(NET::DemandsAttention)); // now activate win1, that should remove demands attention spy.clear(); KWindowSystem::forceActiveWindow(window->winId()); QTest::qWait(200); QVERIFY(waitForWindow(spy, window->winId(), NET::WMState)); KWindowInfo info3(window->winId(), NET::WMState); QVERIFY(info3.valid()); QVERIFY(!info3.hasState(NET::DemandsAttention)); // we should be able to demand attention on win2 spy.clear(); KWindowSystem::demandAttention(win2.winId()); xcb_flush(QX11Info::connection()); QVERIFY(waitForWindow(spy, win2.winId(), NET::WMState)); KWindowInfo info4(win2.winId(), NET::WMState); QVERIFY(info4.valid()); QCOMPARE(info4.state(), NET::DemandsAttention); QVERIFY(info4.hasState(NET::DemandsAttention)); // and to remove demand attention on win2 spy.clear(); QTest::qWait(200); KWindowSystem::demandAttention(win2.winId(), false); xcb_flush(QX11Info::connection()); QVERIFY(waitForWindow(spy, win2.winId(), NET::WMState)); KWindowInfo info5(win2.winId(), NET::WMState); QVERIFY(info5.valid()); QVERIFY(!info5.hasState(NET::DemandsAttention)); // WM2Urgency should be mapped to state NET::DemandsAttention kde_wm_hints hints; hints.flags = (1 << 8); hints.icon_mask = XCB_PIXMAP_NONE; hints.icon_pixmap = XCB_PIXMAP_NONE; hints.icon_window = XCB_WINDOW_NONE; hints.input = 0; hints.window_group = XCB_WINDOW_NONE; hints.icon_x = 0; hints.icon_y = 0; hints.initial_state = 0; xcb_change_property(QX11Info::connection(), XCB_PROP_MODE_REPLACE, win2.winId(), XCB_ATOM_WM_HINTS, XCB_ATOM_WM_HINTS, 32, 9, &hints); xcb_flush(QX11Info::connection()); // window managers map urgency to demands attention spy.clear(); QVERIFY(waitForWindow(spy, win2.winId(), NET::WMState)); QTest::qWait(100); // a window info with NET::WM2Urgency should show demands attention KWindowInfo urgencyInfo(win2.winId(), NET::WMState); QVERIFY(urgencyInfo.valid()); QVERIFY(urgencyInfo.hasState(NET::DemandsAttention)); // remove urgency again hints.flags = 0; xcb_change_property(QX11Info::connection(), XCB_PROP_MODE_REPLACE, win2.winId(), XCB_ATOM_WM_HINTS, XCB_ATOM_WM_HINTS, 32, 9, &hints); xcb_flush(QX11Info::connection()); // TODO: test whether it gets removed again. At least KWin does not remove demands attention // prevent openbox crash (see https://bugzilla.icculus.org/show_bug.cgi?id=6315 ) QTest::qWait(600); } void KWindowInfoX11Test::testMinimized() { // should not be minimized, now QVERIFY(!verifyMinimized(window->winId())); window->showMinimized(); // TODO: improve by using signalspy? QTest::qWait(100); // should be minimized, now QVERIFY(verifyMinimized(window->winId())); // back to normal window->showNormal(); // TODO: improve by using signalspy? QTest::qWait(100); // should no longer be minimized QVERIFY(!verifyMinimized(window->winId())); } void KWindowInfoX11Test::testMappingState() { KWindowInfo info(window->winId(), NET::XAWMState); QCOMPARE(info.mappingState(), NET::Visible); window->showMinimized(); // TODO: improve by using signalspy? QTest::qWait(100); KWindowInfo info2(window->winId(), NET::XAWMState); QCOMPARE(info2.mappingState(), NET::Iconic); window->hide(); // TODO: improve by using signalspy? QTest::qWait(100); KWindowInfo info3(window->winId(), NET::XAWMState); QCOMPARE(info3.mappingState(), NET::Withdrawn); } void KWindowInfoX11Test::testWindowType_data() { QTest::addColumn("mask"); QTest::addColumn("type"); QTest::addColumn("expectedType"); QTest::newRow("desktop") << NET::DesktopMask << NET::Desktop << NET::Desktop; QTest::newRow("dock") << NET::DockMask << NET::Dock << NET::Dock; QTest::newRow("toolbar") << NET::ToolbarMask << NET::Toolbar << NET::Toolbar; QTest::newRow("menu") << NET::MenuMask << NET::Menu << NET::Menu; QTest::newRow("dialog") << NET::DialogMask << NET::Dialog << NET::Dialog; QTest::newRow("override") << NET::OverrideMask << NET::Override << NET::Override; QTest::newRow("override as normal") << NET::NormalMask << NET::Override << NET::Normal; QTest::newRow("topmenu") << NET::TopMenuMask << NET::TopMenu << NET::TopMenu; QTest::newRow("topmenu as dock") << NET::DockMask << NET::TopMenu << NET::Dock; QTest::newRow("utility") << NET::UtilityMask << NET::Utility << NET::Utility; QTest::newRow("utility as dialog") << NET::DialogMask << NET::Utility << NET::Dialog; QTest::newRow("splash") << NET::SplashMask << NET::Splash << NET::Splash; QTest::newRow("splash as dock") << NET::DockMask << NET::Splash << NET::Dock; QTest::newRow("dropdownmenu") << NET::DropdownMenuMask << NET::DropdownMenu << NET::DropdownMenu; QTest::newRow("popupmenu") << NET::PopupMenuMask << NET::PopupMenu << NET::PopupMenu; QTest::newRow("popupmenu as menu") << NET::MenuMask << NET::Menu << NET::Menu; QTest::newRow("tooltip") << NET::TooltipMask << NET::Tooltip << NET::Tooltip; QTest::newRow("notification") << NET::NotificationMask << NET::Notification << NET::Notification; QTest::newRow("ComboBox") << NET::ComboBoxMask << NET::ComboBox << NET::ComboBox; QTest::newRow("DNDIcon") << NET::DNDIconMask << NET::DNDIcon << NET::DNDIcon; QTest::newRow("OnScreenDisplay") << NET::OnScreenDisplayMask << NET::OnScreenDisplay << NET::OnScreenDisplay; + QTest::newRow("CriticalNotification") << NET::CriticalNotificationMask << NET::CriticalNotification << NET::CriticalNotification; // incorrect masks QTest::newRow("desktop-unknown") << NET::NormalMask << NET::Desktop << NET::Unknown; QTest::newRow("dock-unknown") << NET::NormalMask << NET::Dock << NET::Unknown; QTest::newRow("toolbar-unknown") << NET::NormalMask << NET::Toolbar << NET::Unknown; QTest::newRow("menu-unknown") << NET::NormalMask << NET::Menu << NET::Unknown; QTest::newRow("dialog-unknown") << NET::NormalMask << NET::Dialog << NET::Unknown; QTest::newRow("override-unknown") << NET::DialogMask << NET::Override << NET::Unknown; QTest::newRow("topmenu-unknown") << NET::NormalMask << NET::TopMenu << NET::Unknown; QTest::newRow("utility-unknown") << NET::NormalMask << NET::Utility << NET::Unknown; QTest::newRow("splash-unknown") << NET::NormalMask << NET::Splash << NET::Unknown; QTest::newRow("dropdownmenu-unknown") << NET::NormalMask << NET::DropdownMenu << NET::Unknown; QTest::newRow("popupmenu-unknown") << NET::NormalMask << NET::PopupMenu << NET::Unknown; QTest::newRow("tooltip-unknown") << NET::NormalMask << NET::Tooltip << NET::Unknown; QTest::newRow("notification-unknown") << NET::NormalMask << NET::Notification << NET::Unknown; QTest::newRow("ComboBox-unknown") << NET::NormalMask << NET::ComboBox << NET::Unknown; QTest::newRow("DNDIcon-unknown") << NET::NormalMask << NET::DNDIcon << NET::Unknown; QTest::newRow("OnScreenDisplay-unknown") << NET::NormalMask << NET::OnScreenDisplay << NET::Unknown; + QTest::newRow("CriticalNotification-unknown") << NET::NormalMask << NET::CriticalNotification << NET::Unknown; } void KWindowInfoX11Test::testWindowType() { KWindowInfo info(window->winId(), NET::WMWindowType); QCOMPARE(info.windowType(NET::NormalMask), NET::Normal); QFETCH(NET::WindowTypeMask, mask); QFETCH(NET::WindowType, type); QFETCH(NET::WindowType, expectedType); KWindowSystem::setType(window->winId(), type); // setWindowType just changes an xproperty, so a roundtrip waiting for another property ensures we are updated QX11Info::getTimestamp(); KWindowInfo info2(window->winId(), NET::WMWindowType); QCOMPARE(info2.windowType(mask), expectedType); } void KWindowInfoX11Test::testDesktop() { if (KWindowSystem::numberOfDesktops() < 2) { QSKIP("We need at least two virtual desktops to perform proper virtual desktop testing"); } KWindowInfo info(window->winId(), NET::WMDesktop); QVERIFY(info.isOnCurrentDesktop()); QVERIFY(!info.onAllDesktops()); QCOMPARE(info.desktop(), KWindowSystem::currentDesktop()); for (int i = 1; i < KWindowSystem::numberOfDesktops(); i++) { if (i == KWindowSystem::currentDesktop()) { QVERIFY(info.isOnDesktop(i)); } else { QVERIFY(!info.isOnDesktop(i)); } } // set on all desktop QSignalSpy spy(KWindowSystem::self(), static_cast(&KWindowSystem::windowChanged)); QVERIFY(spy.isValid()); KWindowSystem::setOnAllDesktops(window->winId(), true); QVERIFY(waitForWindow(spy, window->winId(), NET::WMDesktop)); KWindowInfo info2(window->winId(), NET::WMDesktop); QVERIFY(info2.isOnCurrentDesktop()); QVERIFY(info2.onAllDesktops()); QCOMPARE(info2.desktop(), int(NET::OnAllDesktops)); for (int i = 1; i < KWindowSystem::numberOfDesktops(); i++) { QVERIFY(info2.isOnDesktop(i)); } const int desktop = (KWindowSystem::currentDesktop() % KWindowSystem::numberOfDesktops()) + 1; spy.clear(); KWindowSystem::setOnDesktop(window->winId(), desktop); QX11Info::getTimestamp(); QVERIFY(waitForWindow(spy, window->winId(), NET::WMDesktop)); KWindowInfo info3(window->winId(), NET::WMDesktop); QVERIFY(!info3.isOnCurrentDesktop()); QVERIFY(!info3.onAllDesktops()); QCOMPARE(info3.desktop(), desktop); for (int i = 1; i < KWindowSystem::numberOfDesktops(); i++) { if (i == desktop) { QVERIFY(info3.isOnDesktop(i)); } else { QVERIFY(!info3.isOnDesktop(i)); } } } void KWindowInfoX11Test::testActivities() { NETRootInfo rootInfo(QX11Info::connection(), NET::Supported | NET::SupportingWMCheck); QSignalSpy spyReal(KWindowSystem::self(), static_cast(&KWindowSystem::windowChanged)); QVERIFY(spyReal.isValid()); KWindowInfo info(window->winId(), NET::Properties(), NET::WM2Activities); QVERIFY(info.valid()); QStringList startingActivities = info.activities(); // The window is either on a specific activity when created, // or on all of them (aka startingActivities is empty or contains // just one element) QVERIFY(startingActivities.size() <= 1); // Window on all activities KWindowSystem::self()->setOnActivities(window->winId(), QStringList()); QVERIFY(waitForWindow(spyReal, window->winId(), NET::Properties(), NET::WM2Activities)); KWindowInfo info2(window->winId(), NET::Properties(), NET::WM2Activities); QVERIFY(info2.activities().size() == 0); // Window on a specific activity KWindowSystem::self()->setOnActivities(window->winId(), QStringList() << "test-activity"); QVERIFY(waitForWindow(spyReal, window->winId(), NET::Properties(), NET::WM2Activities)); KWindowInfo info3(window->winId(), NET::Properties(), NET::WM2Activities); QVERIFY(info3.activities().size() == 1); QVERIFY(info3.activities()[0] == "test-activity"); // Window on a two activities KWindowSystem::self()->setOnActivities(window->winId(), QStringList() << "test-activity" << "test-activity2"); QVERIFY(waitForWindow(spyReal, window->winId(), NET::Properties(), NET::WM2Activities)); KWindowInfo info4(window->winId(), NET::Properties(), NET::WM2Activities); QVERIFY(info4.activities().size() == 2); QVERIFY(info4.activities()[0] == "test-activity"); QVERIFY(info4.activities()[1] == "test-activity2"); // Window on the starting activity KWindowSystem::self()->setOnActivities(window->winId(), startingActivities); QVERIFY(waitForWindow(spyReal, window->winId(), NET::Properties(), NET::WM2Activities)); KWindowInfo info5(window->winId(), NET::Properties(), NET::WM2Activities); QVERIFY(info5.activities() == startingActivities); } void KWindowInfoX11Test::testWindowClass() { KWindowInfo info(window->winId(), NET::Properties(), NET::WM2WindowClass); QCOMPARE(info.windowClassName(), QByteArrayLiteral("kwindowinfox11test")); QCOMPARE(info.windowClassClass(), QByteArrayLiteral("kwindowinfox11test")); // window class needs to be changed using xcb xcb_change_property(QX11Info::connection(), XCB_PROP_MODE_REPLACE, window->winId(), XCB_ATOM_WM_CLASS, XCB_ATOM_STRING, 8, 7, "foo\0bar"); xcb_flush(QX11Info::connection()); // it's just a property change so we can easily refresh QX11Info::getTimestamp(); KWindowInfo info2(window->winId(), NET::Properties(), NET::WM2WindowClass); QCOMPARE(info2.windowClassName(), QByteArrayLiteral("foo")); QCOMPARE(info2.windowClassClass(), QByteArrayLiteral("bar")); } void KWindowInfoX11Test::testWindowRole() { KWindowInfo info(window->winId(), NET::Properties(), NET::WM2WindowRole); QVERIFY(info.windowRole().isNull()); // window role needs to be changed using xcb KXUtils::Atom atom(QX11Info::connection(), QByteArrayLiteral("WM_WINDOW_ROLE")); xcb_change_property(QX11Info::connection(), XCB_PROP_MODE_REPLACE, window->winId(), atom, XCB_ATOM_STRING, 8, 3, "bar"); xcb_flush(QX11Info::connection()); // it's just a property change so we can easily refresh QX11Info::getTimestamp(); KWindowInfo info2(window->winId(), NET::Properties(), NET::WM2WindowRole); QCOMPARE(info2.windowRole(), QByteArrayLiteral("bar")); } void KWindowInfoX11Test::testClientMachine() { KWindowInfo info(window->winId(), NET::Properties(), NET::WM2ClientMachine); QVERIFY(info.clientMachine().isNull()); // client machine needs to be set through xcb xcb_change_property(QX11Info::connection(), XCB_PROP_MODE_REPLACE, window->winId(), XCB_ATOM_WM_CLIENT_MACHINE, XCB_ATOM_STRING, 8, 9, "localhost"); xcb_flush(QX11Info::connection()); // it's just a property change so we can easily refresh QX11Info::getTimestamp(); KWindowInfo info2(window->winId(), NET::Properties(), NET::WM2ClientMachine); QCOMPARE(info2.clientMachine(), QByteArrayLiteral("localhost")); } void KWindowInfoX11Test::testName() { KWindowInfo info(window->winId(), NET::WMName | NET::WMVisibleName | NET::WMIconName | NET::WMVisibleIconName | NET::WMState | NET::XAWMState); QCOMPARE(info.name(), QStringLiteral("kwindowinfox11test")); QCOMPARE(info.visibleName(), QStringLiteral("kwindowinfox11test")); QCOMPARE(info.visibleNameWithState(), QStringLiteral("kwindowinfox11test")); QCOMPARE(info.iconName(), QStringLiteral("kwindowinfox11test")); QCOMPARE(info.visibleIconName(), QStringLiteral("kwindowinfox11test")); QCOMPARE(info.visibleIconNameWithState(), QStringLiteral("kwindowinfox11test")); window->showMinimized(); // TODO: improve by using signalspy? QTest::qWait(100); // should be minimized, now QVERIFY(verifyMinimized(window->winId())); // that should have changed the visible name KWindowInfo info2(window->winId(), NET::WMName | NET::WMVisibleName | NET::WMIconName | NET::WMVisibleIconName | NET::WMState | NET::XAWMState); QCOMPARE(info2.name(), QStringLiteral("kwindowinfox11test")); QCOMPARE(info2.visibleName(), QStringLiteral("kwindowinfox11test")); QCOMPARE(info2.visibleNameWithState(), QStringLiteral("(kwindowinfox11test)")); QCOMPARE(info2.iconName(), QStringLiteral("kwindowinfox11test")); QCOMPARE(info2.visibleIconName(), QStringLiteral("kwindowinfox11test")); QCOMPARE(info2.visibleIconNameWithState(), QStringLiteral("(kwindowinfox11test)")); NETRootInfo rootInfo(QX11Info::connection(), NET::Supported | NET::SupportingWMCheck); if (qstrcmp(rootInfo.wmName(), "Openbox") == 0) { QSKIP("setting name test fails on openbox"); } // create a low level NETWinInfo to manipulate the name NETWinInfo winInfo(QX11Info::connection(), window->winId(), QX11Info::appRootWindow(), NET::WMName, NET::Properties2()); winInfo.setName("foobar"); QX11Info::getTimestamp(); KWindowInfo info3(window->winId(), NET::WMName | NET::WMVisibleName | NET::WMIconName | NET::WMVisibleIconName | NET::WMState | NET::XAWMState); QCOMPARE(info3.name(), QStringLiteral("foobar")); QCOMPARE(info3.visibleName(), QStringLiteral("foobar")); QCOMPARE(info3.visibleNameWithState(), QStringLiteral("(foobar)")); QCOMPARE(info3.iconName(), QStringLiteral("foobar")); QCOMPARE(info3.visibleIconName(), QStringLiteral("foobar")); QCOMPARE(info3.visibleIconNameWithState(), QStringLiteral("(foobar)")); } void KWindowInfoX11Test::testTransientFor() { KWindowInfo info(window->winId(), NET::Properties(), NET::WM2TransientFor); QCOMPARE(info.transientFor(), WId(0)); // let's create a second window QScopedPointer window2(new QWidget()); window2->show(); QVERIFY(QTest::qWaitForWindowExposed(window2.data())); // update the transient for of window1 to window2 const uint32_t id = window2->winId(); xcb_change_property(QX11Info::connection(), XCB_PROP_MODE_REPLACE, window->winId(), XCB_ATOM_WM_TRANSIENT_FOR, XCB_ATOM_WINDOW, 32, 1, &id); xcb_flush(QX11Info::connection()); KWindowInfo info2(window->winId(), NET::Properties(), NET::WM2TransientFor); QCOMPARE(info2.transientFor(), window2->winId()); } void KWindowInfoX11Test::testGroupLeader() { KWindowInfo info(window->winId(), NET::Properties(), NET::WM2GroupLeader); QCOMPARE(info.groupLeader(), WId(0)); // TODO: here we should try to set a group leader and re-read it // this needs setting and parsing the WMHints } void KWindowInfoX11Test::testExtendedStrut() { KWindowInfo info(window->winId(), NET::Properties(), NET::WM2ExtendedStrut); NETExtendedStrut strut = info.extendedStrut(); QCOMPARE(strut.bottom_end, 0); QCOMPARE(strut.bottom_start, 0); QCOMPARE(strut.bottom_width, 0); QCOMPARE(strut.left_end, 0); QCOMPARE(strut.left_start, 0); QCOMPARE(strut.left_width, 0); QCOMPARE(strut.right_end, 0); QCOMPARE(strut.right_start, 0); QCOMPARE(strut.right_width, 0); QCOMPARE(strut.top_end, 0); QCOMPARE(strut.top_start, 0); QCOMPARE(strut.top_width, 0); KWindowSystem::setExtendedStrut(window->winId(), 10, 20, 30, 40, 5, 15, 25, 35, 2, 12, 22, 32); // it's just an xprop, so one roundtrip is good enough QX11Info::getTimestamp(); KWindowInfo info2(window->winId(), NET::Properties(), NET::WM2ExtendedStrut); strut = info2.extendedStrut(); QCOMPARE(strut.bottom_end, 32); QCOMPARE(strut.bottom_start, 22); QCOMPARE(strut.bottom_width, 12); QCOMPARE(strut.left_end, 30); QCOMPARE(strut.left_start, 20); QCOMPARE(strut.left_width, 10); QCOMPARE(strut.right_end, 15); QCOMPARE(strut.right_start, 5); QCOMPARE(strut.right_width, 40); QCOMPARE(strut.top_end, 2); QCOMPARE(strut.top_start, 35); QCOMPARE(strut.top_width, 25); } void KWindowInfoX11Test::testGeometry() { KWindowInfo info(window->winId(), NET::WMGeometry | NET::WMFrameExtents); QCOMPARE(info.geometry().size(), window->geometry().size()); QCOMPARE(info.frameGeometry().size(), window->frameGeometry().size()); QSignalSpy spy(KWindowSystem::self(), static_cast(&KWindowSystem::windowChanged)); QVERIFY(spy.isValid()); // this is tricky, KWin is smart and doesn't allow all geometries we pass in // setting to center of screen should work, though QRect geo(window->windowHandle()->screen()->geometry().center() - QPoint(window->width()/2-5, window->height()/2-5), window->size() + QSize(10, 10)); window->setGeometry(geo); waitForWindow(spy, window->winId(), NET::WMGeometry); KWindowInfo info2(window->winId(), NET::WMGeometry | NET::WMFrameExtents); QCOMPARE(info2.geometry(), window->geometry()); QCOMPARE(info2.geometry(), geo); QCOMPARE(info2.frameGeometry(), window->frameGeometry()); } void KWindowInfoX11Test::testDesktopFileName() { KWindowInfo info(window->winId(), NET::Properties(), NET::WM2DesktopFileName); QVERIFY(info.valid()); QCOMPARE(info.desktopFileName(), QByteArray()); QSignalSpy spy(KWindowSystem::self(), static_cast(&KWindowSystem::windowChanged)); QVERIFY(spy.isValid()); // create a NETWinInfo to set the desktop file name NETWinInfo netInfo(QX11Info::connection(), window->winId(), QX11Info::appRootWindow(), NET::Properties(), NET::Properties2()); netInfo.setDesktopFileName("org.kde.foo"); xcb_flush(QX11Info::connection()); // it's just a property change so we can easily refresh QX11Info::getTimestamp(); QTRY_COMPARE(spy.count(), 1); QCOMPARE(spy.first().at(0).value(), window->winId()); QCOMPARE(spy.first().at(2).value(), NET::Properties2(NET::WM2DesktopFileName)); KWindowInfo info2(window->winId(), NET::Properties(), NET::WM2DesktopFileName); QVERIFY(info2.valid()); QCOMPARE(info2.desktopFileName(), QByteArrayLiteral("org.kde.foo")); } void KWindowInfoX11Test::testPid() { KWindowInfo info(window->winId(), NET::WMPid); QVERIFY(info.valid()); QCOMPARE(info.pid(), getpid()); } QTEST_MAIN(KWindowInfoX11Test) #include "kwindowinfox11test.moc" diff --git a/autotests/netrootinfotestwm.cpp b/autotests/netrootinfotestwm.cpp index a2871df..841fd95 100644 --- a/autotests/netrootinfotestwm.cpp +++ b/autotests/netrootinfotestwm.cpp @@ -1,875 +1,875 @@ /* * Copyright 2013 Martin Gräßlin * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #include "nettesthelper.h" #include #include #include // system #include class Property : public QScopedPointer { public: Property(xcb_get_property_reply_t *p = nullptr) : QScopedPointer(p) {} }; Q_DECLARE_METATYPE(NET::Orientation) Q_DECLARE_METATYPE(NET::DesktopLayoutCorner) static const char *s_wmName = "netrootinfotest"; class NetRootInfoTestWM : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase(); void cleanupTestCase(); void init(); void cleanup(); void testCtor(); void testSupported(); void testClientList(); void testClientListStacking(); void testNumberOfDesktops(); void testCurrentDesktop(); void testDesktopNames(); void testDesktopLayout_data(); void testDesktopLayout(); void testDesktopGeometry(); void testDesktopViewports(); void testShowingDesktop_data(); void testShowingDesktop(); void testWorkArea(); void testActiveWindow(); void testVirtualRoots(); void testDontCrashMapViewports(); private: void waitForPropertyChange(NETRootInfo *info, xcb_atom_t atom, NET::Property prop, NET::Property2 prop2 = NET::Property2(0)); xcb_connection_t *connection() { return m_connection; } xcb_connection_t *m_connection; QVector m_connections; QScopedPointer m_xvfb; xcb_window_t m_supportWindow; xcb_window_t m_rootWindow; }; void NetRootInfoTestWM::cleanupTestCase() { while (!m_connections.isEmpty()) { xcb_disconnect(m_connections.takeFirst()); } } void NetRootInfoTestWM::initTestCase() { qsrand(QDateTime::currentMSecsSinceEpoch()); } void NetRootInfoTestWM::init() { // first reset just to be sure m_connection = nullptr; m_supportWindow = XCB_WINDOW_NONE; // start Xvfb m_xvfb.reset(new QProcess); // use pipe to pass fd to Xvfb to get back the display id int pipeFds[2]; QVERIFY(pipe(pipeFds) == 0); m_xvfb->start(QStringLiteral("Xvfb"), QStringList{ QStringLiteral("-displayfd"), QString::number(pipeFds[1]) }); QVERIFY(m_xvfb->waitForStarted()); QCOMPARE(m_xvfb->state(), QProcess::Running); // reads from pipe, closes write side close(pipeFds[1]); QFile readPipe; QVERIFY(readPipe.open(pipeFds[0], QIODevice::ReadOnly, QFileDevice::AutoCloseHandle)); QByteArray displayNumber = readPipe.readLine(); readPipe.close(); displayNumber.prepend(QByteArray(":")); displayNumber.remove(displayNumber.size() -1, 1); // create X connection int screen = 0; m_connection = xcb_connect(displayNumber.constData(), &screen); QVERIFY(m_connection); QVERIFY(!xcb_connection_has_error(m_connection)); m_rootWindow = KXUtils::rootWindow(m_connection, screen); uint32_t values[] = {XCB_EVENT_MASK_PROPERTY_CHANGE}; xcb_change_window_attributes(m_connection, m_rootWindow, XCB_CW_EVENT_MASK, values); // create support window values[0] = true; m_supportWindow = xcb_generate_id(m_connection); xcb_create_window(m_connection, XCB_COPY_FROM_PARENT, m_supportWindow, m_rootWindow, 0, 0, 1, 1, 0, XCB_COPY_FROM_PARENT, XCB_COPY_FROM_PARENT, XCB_CW_OVERRIDE_REDIRECT, values); const uint32_t lowerValues[] = { XCB_STACK_MODE_BELOW }; // we need to do the lower window with a roundtrip, otherwise NETRootInfo is not functioning QScopedPointer error(xcb_request_check(m_connection, xcb_configure_window_checked(m_connection, m_supportWindow, XCB_CONFIG_WINDOW_STACK_MODE, lowerValues))); QVERIFY(error.isNull()); } void NetRootInfoTestWM::cleanup() { // destroy support window xcb_destroy_window(connection(), m_supportWindow); m_supportWindow = XCB_WINDOW_NONE; // close connection // delay till clenupTestCase as otherwise xcb reuses the same memory address m_connections << connection(); // kill Xvfb m_xvfb->terminate(); m_xvfb->waitForFinished(); m_xvfb.reset(); } void NetRootInfoTestWM::waitForPropertyChange(NETRootInfo *info, xcb_atom_t atom, NET::Property prop, NET::Property2 prop2) { while (true) { KXUtils::ScopedCPointer event(xcb_wait_for_event(connection())); if (event.isNull()) { break; } if ((event->response_type & ~0x80) != XCB_PROPERTY_NOTIFY) { continue; } xcb_property_notify_event_t *pe = reinterpret_cast(event.data()); if (pe->window != m_rootWindow) { continue; } if (pe->atom != atom) { continue; } NET::Properties dirty; NET::Properties2 dirty2; info->event(event.data(), &dirty, &dirty2); if (prop != 0) { QVERIFY(dirty & prop); } if (prop2 != 0) { QVERIFY(dirty2 & prop2); } if (!prop) { QCOMPARE(dirty, NET::Properties()); } if (!prop2) { QCOMPARE(dirty2, NET::Properties2()); } break; } } void NetRootInfoTestWM::testCtor() { QVERIFY(connection()); NETRootInfo rootInfo(connection(), m_supportWindow, s_wmName, NET::WMAllProperties, NET::AllTypesMask, NET::States(~0u), NET::WM2AllProperties, NET::Actions(~0u)); QCOMPARE(rootInfo.xcbConnection(), connection()); QCOMPARE(rootInfo.rootWindow(), m_rootWindow); QCOMPARE(rootInfo.supportWindow(), m_supportWindow); QCOMPARE(rootInfo.wmName(), s_wmName); QCOMPARE(rootInfo.supportedProperties(), NET::WMAllProperties); QCOMPARE(rootInfo.supportedProperties2(), NET::WM2AllProperties); QCOMPARE(rootInfo.supportedActions(), NET::Actions(~0u)); QCOMPARE(rootInfo.supportedStates(), NET::States(~0u)); QCOMPARE(rootInfo.supportedWindowTypes(), NET::AllTypesMask); QCOMPARE(rootInfo.passedProperties(), NET::WMAllProperties); QCOMPARE(rootInfo.passedProperties2(), NET::WM2AllProperties); QCOMPARE(rootInfo.passedActions(), NET::Actions(~0u)); QCOMPARE(rootInfo.passedStates(), NET::States(~0u)); QCOMPARE(rootInfo.passedWindowTypes(), NET::AllTypesMask); } void NetRootInfoTestWM::testSupported() { KXUtils::Atom supported(connection(), QByteArrayLiteral("_NET_SUPPORTED")); KXUtils::Atom wmCheck(connection(), QByteArrayLiteral("_NET_SUPPORTING_WM_CHECK")); KXUtils::Atom wmName(connection(), QByteArrayLiteral("_NET_WM_NAME")); KXUtils::Atom utf8String(connection(), QByteArrayLiteral("UTF8_STRING")); QVERIFY(connection()); NETRootInfo rootInfo(connection(), m_supportWindow, s_wmName, NET::WMAllProperties, NET::AllTypesMask, NET::States(~0u), NET::WM2AllProperties, NET::Actions(~0u)); int count = 0; - for (int i = 0; i < 32; ++i) { + for (int i = 0; i < 33; ++i) { if (i == 12) { continue; } QVERIFY(rootInfo.isSupported(NET::Property(1 << i))); count++; } for (int i = 0; i < 22; ++i) { QVERIFY(rootInfo.isSupported(NET::Property2(1 << i))); count++; } for (int i = 0; i < 17; ++i) { QVERIFY(rootInfo.isSupported(NET::WindowTypeMask(1 << i))); count++; } for (int i = 0; i < 13; ++i) { QVERIFY(rootInfo.isSupported(NET::State(1 << i))); count++; } for (int i = 0; i < 10; ++i) { QVERIFY(rootInfo.isSupported(NET::Action(1 << i))); count++; } // NET::WMFrameExtents has two properties count += 1; // XAWState, WMGeometry, WM2TransientFor, WM2GroupLeader, WM2WindowClass, WM2WindowRole, WM2ClientMachine count -= 7; // WM2BlockCompositing has 3 properties count += 2; QVERIFY(supported != XCB_ATOM_NONE); QVERIFY(utf8String != XCB_ATOM_NONE); QVERIFY(wmCheck != XCB_ATOM_NONE); QVERIFY(wmName != XCB_ATOM_NONE); // we should have got some events waitForPropertyChange(&rootInfo, supported, NET::Supported); waitForPropertyChange(&rootInfo, wmCheck, NET::SupportingWMCheck); // get the cookies of the things to check xcb_get_property_cookie_t supportedCookie = xcb_get_property_unchecked(connection(), false, rootInfo.rootWindow(), supported, XCB_ATOM_ATOM, 0, 101); xcb_get_property_cookie_t wmCheckRootCookie = xcb_get_property_unchecked(connection(), false, rootInfo.rootWindow(), wmCheck, XCB_ATOM_WINDOW, 0, 1); xcb_get_property_cookie_t wmCheckSupportWinCookie = xcb_get_property_unchecked(connection(), false, m_supportWindow, wmCheck, XCB_ATOM_WINDOW, 0, 1); xcb_get_property_cookie_t wmNameCookie = xcb_get_property_unchecked(connection(), false, m_supportWindow, wmName, utf8String, 0, 16); Property supportedReply(xcb_get_property_reply(connection(), supportedCookie, nullptr)); QVERIFY(!supportedReply.isNull()); QCOMPARE(supportedReply->format, uint8_t(32)); QCOMPARE(supportedReply->value_len, uint32_t(count)); // TODO: check that the correct atoms are set? Property wmCheckRootReply(xcb_get_property_reply(connection(), wmCheckRootCookie, nullptr)); QVERIFY(!wmCheckRootReply.isNull()); QCOMPARE(wmCheckRootReply->format, uint8_t(32)); QCOMPARE(wmCheckRootReply->value_len, uint32_t(1)); QCOMPARE(reinterpret_cast(xcb_get_property_value(wmCheckRootReply.data()))[0], m_supportWindow); Property wmCheckSupportReply(xcb_get_property_reply(connection(), wmCheckSupportWinCookie, nullptr)); QVERIFY(!wmCheckSupportReply.isNull()); QCOMPARE(wmCheckSupportReply->format, uint8_t(32)); QCOMPARE(wmCheckSupportReply->value_len, uint32_t(1)); QCOMPARE(reinterpret_cast(xcb_get_property_value(wmCheckSupportReply.data()))[0], m_supportWindow); Property wmNameReply(xcb_get_property_reply(connection(), wmNameCookie, nullptr)); QVERIFY(!wmNameReply.isNull()); QCOMPARE(wmNameReply->format, uint8_t(8)); QCOMPARE(wmNameReply->value_len, uint32_t(15)); QCOMPARE(reinterpret_cast(xcb_get_property_value(wmNameReply.data())), s_wmName); // disable some supported rootInfo.setSupported(NET::WMFrameExtents, false); rootInfo.setSupported(NET::WM2KDETemporaryRules, false); rootInfo.setSupported(NET::ActionChangeDesktop, false); rootInfo.setSupported(NET::FullScreen, false); QVERIFY(rootInfo.isSupported(NET::ToolbarMask)); QVERIFY(rootInfo.isSupported(NET::OnScreenDisplayMask)); QVERIFY(rootInfo.isSupported(NET::DockMask)); rootInfo.setSupported(NET::ToolbarMask, false); rootInfo.setSupported(NET::OnScreenDisplayMask, false); QVERIFY(!rootInfo.isSupported(NET::WMFrameExtents)); QVERIFY(!rootInfo.isSupported(NET::WM2KDETemporaryRules)); QVERIFY(!rootInfo.isSupported(NET::ActionChangeDesktop)); QVERIFY(!rootInfo.isSupported(NET::FullScreen)); QVERIFY(!rootInfo.isSupported(NET::ToolbarMask)); QVERIFY(!rootInfo.isSupported(NET::OnScreenDisplayMask)); QVERIFY(rootInfo.isSupported(NET::DockMask)); // lets get supported again supportedCookie = xcb_get_property_unchecked(connection(), false, rootInfo.rootWindow(), supported, XCB_ATOM_ATOM, 0, 90); supportedReply.reset(xcb_get_property_reply(connection(), supportedCookie, nullptr)); QVERIFY(!supportedReply.isNull()); QCOMPARE(supportedReply->format, uint8_t(32)); QCOMPARE(supportedReply->value_len, uint32_t(count - 7)); for (int i = 0; i < 5; ++i) { // we should have got some events waitForPropertyChange(&rootInfo, supported, NET::Supported); waitForPropertyChange(&rootInfo, wmCheck, NET::SupportingWMCheck); } // turn something off, just to get another event rootInfo.setSupported(NET::WM2BlockCompositing, false); // lets get supported again supportedCookie = xcb_get_property_unchecked(connection(), false, rootInfo.rootWindow(), supported, XCB_ATOM_ATOM, 0, 90); supportedReply.reset(xcb_get_property_reply(connection(), supportedCookie, nullptr)); QVERIFY(!supportedReply.isNull()); QCOMPARE(supportedReply->format, uint8_t(32)); QCOMPARE(supportedReply->value_len, uint32_t(count - 9)); NETRootInfo clientInfo(connection(), NET::Supported | NET::SupportingWMCheck); waitForPropertyChange(&clientInfo, supported, NET::Supported); waitForPropertyChange(&clientInfo, wmCheck, NET::SupportingWMCheck); } void NetRootInfoTestWM::testClientList() { KXUtils::Atom atom(connection(), QByteArrayLiteral("_NET_CLIENT_LIST")); QVERIFY(connection()); NETRootInfo rootInfo(connection(), m_supportWindow, s_wmName, NET::WMAllProperties, NET::AllTypesMask, NET::States(~0u), NET::WM2AllProperties, NET::Actions(~0u)); QCOMPARE(rootInfo.clientListCount(), 0); QVERIFY(!rootInfo.clientList()); xcb_window_t windows[] = {xcb_generate_id(connection()), xcb_generate_id(connection()), xcb_generate_id(connection()), xcb_generate_id(connection()), xcb_generate_id(connection()) }; rootInfo.setClientList(windows, 5); QCOMPARE(rootInfo.clientListCount(), 5); const xcb_window_t *otherWins = rootInfo.clientList(); for (int i = 0; i < 5; ++i) { QCOMPARE(otherWins[i], windows[i]); } // compare with the X property QVERIFY(atom != XCB_ATOM_NONE); xcb_get_property_cookie_t cookie = xcb_get_property_unchecked(connection(), false, rootInfo.rootWindow(), atom, XCB_ATOM_WINDOW, 0, 5); Property reply(xcb_get_property_reply(connection(), cookie, nullptr)); QVERIFY(!reply.isNull()); QCOMPARE(reply->format, uint8_t(32)); QCOMPARE(reply->value_len, uint32_t(5)); const xcb_window_t *propWins = reinterpret_cast(xcb_get_property_value(reply.data())); for (int i = 0; i < 5; ++i) { QCOMPARE(propWins[i], windows[i]); } // wait for our property NETRootInfo clientInfo(connection(), NET::Supported | NET::SupportingWMCheck | NET::ClientList); waitForPropertyChange(&clientInfo, atom, NET::ClientList); QCOMPARE(clientInfo.clientListCount(), 5); const xcb_window_t *otherWins2 = clientInfo.clientList(); for (int i = 0; i < 5; ++i) { QCOMPARE(otherWins2[i], windows[i]); } } void NetRootInfoTestWM::testClientListStacking() { KXUtils::Atom atom(connection(), QByteArrayLiteral("_NET_CLIENT_LIST_STACKING")); QVERIFY(connection()); NETRootInfo rootInfo(connection(), m_supportWindow, s_wmName, NET::WMAllProperties, NET::AllTypesMask, NET::States(~0u), NET::WM2AllProperties, NET::Actions(~0u)); QCOMPARE(rootInfo.clientListStackingCount(), 0); QVERIFY(!rootInfo.clientListStacking()); xcb_window_t windows[] = {xcb_generate_id(connection()), xcb_generate_id(connection()), xcb_generate_id(connection()), xcb_generate_id(connection()), xcb_generate_id(connection()) }; rootInfo.setClientListStacking(windows, 5); QCOMPARE(rootInfo.clientListStackingCount(), 5); const xcb_window_t *otherWins = rootInfo.clientListStacking(); for (int i = 0; i < 5; ++i) { QCOMPARE(otherWins[i], windows[i]); } // compare with the X property QVERIFY(atom != XCB_ATOM_NONE); xcb_get_property_cookie_t cookie = xcb_get_property_unchecked(connection(), false, rootInfo.rootWindow(), atom, XCB_ATOM_WINDOW, 0, 5); Property reply(xcb_get_property_reply(connection(), cookie, nullptr)); QVERIFY(!reply.isNull()); QCOMPARE(reply->format, uint8_t(32)); QCOMPARE(reply->value_len, uint32_t(5)); const xcb_window_t *propWins = reinterpret_cast(xcb_get_property_value(reply.data())); for (int i = 0; i < 5; ++i) { QCOMPARE(propWins[i], windows[i]); } // wait for our property waitForPropertyChange(&rootInfo, atom, NET::ClientListStacking); QCOMPARE(rootInfo.clientListStackingCount(), 5); const xcb_window_t *otherWins2 = rootInfo.clientListStacking(); for (int i = 0; i < 5; ++i) { QCOMPARE(otherWins2[i], windows[i]); } } void NetRootInfoTestWM::testVirtualRoots() { KXUtils::Atom atom(connection(), QByteArrayLiteral("_NET_VIRTUAL_ROOTS")); QVERIFY(connection()); NETRootInfo rootInfo(connection(), m_supportWindow, s_wmName, NET::WMAllProperties, NET::AllTypesMask, NET::States(~0u), NET::WM2AllProperties, NET::Actions(~0u)); QCOMPARE(rootInfo.virtualRootsCount(), 0); QVERIFY(!rootInfo.virtualRoots()); xcb_window_t windows[] = {xcb_generate_id(connection()), xcb_generate_id(connection()), xcb_generate_id(connection()), xcb_generate_id(connection()), xcb_generate_id(connection()) }; rootInfo.setVirtualRoots(windows, 5); QCOMPARE(rootInfo.virtualRootsCount(), 5); const xcb_window_t *otherWins = rootInfo.virtualRoots(); for (int i = 0; i < 5; ++i) { QCOMPARE(otherWins[i], windows[i]); } // compare with the X property QVERIFY(atom != XCB_ATOM_NONE); xcb_get_property_cookie_t cookie = xcb_get_property_unchecked(connection(), false, rootInfo.rootWindow(), atom, XCB_ATOM_WINDOW, 0, 5); Property reply(xcb_get_property_reply(connection(), cookie, nullptr)); QVERIFY(!reply.isNull()); QCOMPARE(reply->format, uint8_t(32)); QCOMPARE(reply->value_len, uint32_t(5)); const xcb_window_t *propWins = reinterpret_cast(xcb_get_property_value(reply.data())); for (int i = 0; i < 5; ++i) { QCOMPARE(propWins[i], windows[i]); } // wait for our property - reported to a Client NETRootInfo NETRootInfo clientInfo(connection(), NET::VirtualRoots); waitForPropertyChange(&clientInfo, atom, NET::VirtualRoots); QCOMPARE(rootInfo.virtualRootsCount(), 5); const xcb_window_t *otherWins2 = rootInfo.virtualRoots(); for (int i = 0; i < 5; ++i) { QCOMPARE(otherWins2[i], windows[i]); } } void NetRootInfoTestWM::testNumberOfDesktops() { KXUtils::Atom atom(connection(), QByteArrayLiteral("_NET_NUMBER_OF_DESKTOPS")); QVERIFY(connection()); NETRootInfo rootInfo(connection(), m_supportWindow, s_wmName, NET::WMAllProperties, NET::AllTypesMask, NET::States(~0u), NET::WM2AllProperties, NET::Actions(~0u)); QCOMPARE(rootInfo.numberOfDesktops(), 1); rootInfo.setNumberOfDesktops(4); QCOMPARE(rootInfo.numberOfDesktops(), 4); // compare with the X property QVERIFY(atom != XCB_ATOM_NONE); xcb_get_property_cookie_t cookie = xcb_get_property_unchecked(connection(), false, rootInfo.rootWindow(), atom, XCB_ATOM_CARDINAL, 0, 1); Property reply(xcb_get_property_reply(connection(), cookie, nullptr)); QVERIFY(!reply.isNull()); QCOMPARE(reply->format, uint8_t(32)); QCOMPARE(reply->value_len, uint32_t(1)); QCOMPARE(reinterpret_cast(xcb_get_property_value(reply.data()))[0], uint32_t(4)); // wait for our property waitForPropertyChange(&rootInfo, atom, NET::NumberOfDesktops); QCOMPARE(rootInfo.numberOfDesktops(), 4); } void NetRootInfoTestWM::testCurrentDesktop() { KXUtils::Atom atom(connection(), QByteArrayLiteral("_NET_CURRENT_DESKTOP")); // TODO: verify that current desktop cannot be higher than number of desktops QVERIFY(connection()); NETRootInfo rootInfo(connection(), m_supportWindow, s_wmName, NET::WMAllProperties, NET::AllTypesMask, NET::States(~0u), NET::WM2AllProperties, NET::Actions(~0u)); QCOMPARE(rootInfo.currentDesktop(), 1); rootInfo.setCurrentDesktop(5); QCOMPARE(rootInfo.currentDesktop(), 5); // compare with the X property QVERIFY(atom != XCB_ATOM_NONE); xcb_get_property_cookie_t cookie = xcb_get_property_unchecked(connection(), false, rootInfo.rootWindow(), atom, XCB_ATOM_CARDINAL, 0, 1); Property reply(xcb_get_property_reply(connection(), cookie, nullptr)); QVERIFY(!reply.isNull()); QCOMPARE(reply->format, uint8_t(32)); QCOMPARE(reply->value_len, uint32_t(1)); // note: API starts counting at 1, but property starts counting at 5, because of that subtracting one QCOMPARE(reinterpret_cast(xcb_get_property_value(reply.data()))[0], uint32_t(5 - 1)); // wait for our property waitForPropertyChange(&rootInfo, atom, NET::CurrentDesktop); QCOMPARE(rootInfo.currentDesktop(), 5); } void NetRootInfoTestWM::testDesktopNames() { KXUtils::Atom atom(connection(), QByteArrayLiteral("_NET_DESKTOP_NAMES")); KXUtils::Atom utf8String(connection(), QByteArrayLiteral("UTF8_STRING")); QVERIFY(connection()); NETRootInfo rootInfo(connection(), m_supportWindow, s_wmName, NET::WMAllProperties, NET::AllTypesMask, NET::States(~0u), NET::WM2AllProperties, NET::Actions(~0u)); QVERIFY(!rootInfo.desktopName(0)); QVERIFY(!rootInfo.desktopName(1)); QVERIFY(!rootInfo.desktopName(2)); rootInfo.setDesktopName(1, "foo"); rootInfo.setDesktopName(2, "bar"); rootInfo.setNumberOfDesktops(2); QCOMPARE(rootInfo.desktopName(1), "foo"); QCOMPARE(rootInfo.desktopName(2), "bar"); // compare with the X property QVERIFY(atom != XCB_ATOM_NONE); QVERIFY(utf8String != XCB_ATOM_NONE); xcb_get_property_cookie_t cookie = xcb_get_property_unchecked(connection(), false, rootInfo.rootWindow(), atom, utf8String, 0, 10000); Property reply(xcb_get_property_reply(connection(), cookie, nullptr)); QVERIFY(!reply.isNull()); QCOMPARE(reply->format, uint8_t(8)); QCOMPARE(reply->value_len, uint32_t(8)); QCOMPARE(reinterpret_cast(xcb_get_property_value(reply.data())), "foo\0bar"); // wait for our property waitForPropertyChange(&rootInfo, atom, NET::DesktopNames); QCOMPARE(rootInfo.desktopName(1), "foo"); QCOMPARE(rootInfo.desktopName(2), "bar"); // there should be two events waitForPropertyChange(&rootInfo, atom, NET::DesktopNames); QCOMPARE(rootInfo.desktopName(1), "foo"); QCOMPARE(rootInfo.desktopName(2), "bar"); } void NetRootInfoTestWM::testActiveWindow() { KXUtils::Atom atom(connection(), QByteArrayLiteral("_NET_ACTIVE_WINDOW")); QVERIFY(connection()); NETRootInfo rootInfo(connection(), m_supportWindow, s_wmName, NET::WMAllProperties, NET::AllTypesMask, NET::States(~0u), NET::WM2AllProperties, NET::Actions(~0u)); QVERIFY(rootInfo.activeWindow() == XCB_WINDOW_NONE); // rootinfo doesn't verify whether our window is a window, so we just generate an ID xcb_window_t activeWindow = xcb_generate_id(connection()); rootInfo.setActiveWindow(activeWindow); QCOMPARE(rootInfo.activeWindow(), activeWindow); // compare with the X property QVERIFY(atom != XCB_ATOM_NONE); xcb_get_property_cookie_t cookie = xcb_get_property_unchecked(connection(), false, rootInfo.rootWindow(), atom, XCB_ATOM_WINDOW, 0, 1); Property reply(xcb_get_property_reply(connection(), cookie, nullptr)); QVERIFY(!reply.isNull()); QCOMPARE(reply->format, uint8_t(32)); QCOMPARE(reply->value_len, uint32_t(1)); QCOMPARE(reinterpret_cast(xcb_get_property_value(reply.data()))[0], activeWindow); // wait for our property waitForPropertyChange(&rootInfo, atom, NET::ActiveWindow); QCOMPARE(rootInfo.activeWindow(), activeWindow); } void NetRootInfoTestWM::testDesktopGeometry() { KXUtils::Atom atom(connection(), QByteArrayLiteral("_NET_DESKTOP_GEOMETRY")); QVERIFY(connection()); NETRootInfo rootInfo(connection(), m_supportWindow, s_wmName, NET::WMAllProperties, NET::AllTypesMask, NET::States(~0u), NET::WM2AllProperties, NET::Actions(~0u)); QCOMPARE(rootInfo.desktopGeometry().width, 0); QCOMPARE(rootInfo.desktopGeometry().height, 0); NETSize size; size.width = 1000; size.height = 800; rootInfo.setDesktopGeometry(size); QCOMPARE(rootInfo.desktopGeometry().width, size.width); QCOMPARE(rootInfo.desktopGeometry().height, size.height); // compare with the X property QVERIFY(atom != XCB_ATOM_NONE); xcb_get_property_cookie_t cookie = xcb_get_property_unchecked(connection(), false, rootInfo.rootWindow(), atom, XCB_ATOM_CARDINAL, 0, 2); Property reply(xcb_get_property_reply(connection(), cookie, nullptr)); QVERIFY(!reply.isNull()); QCOMPARE(reply->format, uint8_t(32)); QCOMPARE(reply->value_len, uint32_t(2)); uint32_t *data = reinterpret_cast(xcb_get_property_value(reply.data())); QCOMPARE(data[0], uint32_t(size.width)); QCOMPARE(data[1], uint32_t(size.height)); // wait for our property waitForPropertyChange(&rootInfo, atom, NET::DesktopGeometry); QCOMPARE(rootInfo.desktopGeometry().width, size.width); QCOMPARE(rootInfo.desktopGeometry().height, size.height); } void NetRootInfoTestWM::testDesktopLayout_data() { QTest::addColumn("orientation"); QTest::addColumn("columnsRows"); QTest::addColumn("corner"); QTest::newRow("h/1/1/tl") << NET::OrientationHorizontal << QSize(1, 1) << NET::DesktopLayoutCornerTopLeft; QTest::newRow("h/1/0/tr") << NET::OrientationHorizontal << QSize(1, 0) << NET::DesktopLayoutCornerTopRight; QTest::newRow("h/0/1/bl") << NET::OrientationHorizontal << QSize(0, 1) << NET::DesktopLayoutCornerBottomLeft; QTest::newRow("h/1/2/br") << NET::OrientationHorizontal << QSize(1, 2) << NET::DesktopLayoutCornerBottomRight; QTest::newRow("v/3/2/tl") << NET::OrientationVertical << QSize(3, 2) << NET::DesktopLayoutCornerTopLeft; QTest::newRow("v/5/4/tr") << NET::OrientationVertical << QSize(5, 4) << NET::DesktopLayoutCornerTopRight; QTest::newRow("v/2/1/bl") << NET::OrientationVertical << QSize(2, 1) << NET::DesktopLayoutCornerBottomLeft; QTest::newRow("v/3/2/br") << NET::OrientationVertical << QSize(3, 2) << NET::DesktopLayoutCornerBottomRight; } void NetRootInfoTestWM::testDesktopLayout() { KXUtils::Atom atom(connection(), QByteArrayLiteral("_NET_DESKTOP_LAYOUT")); QVERIFY(connection()); NETRootInfo rootInfo(connection(), m_supportWindow, s_wmName, NET::WMAllProperties, NET::AllTypesMask, NET::States(~0u), NET::WM2AllProperties, NET::Actions(~0u)); QFETCH(NET::Orientation, orientation); QFETCH(QSize, columnsRows); QFETCH(NET::DesktopLayoutCorner, corner); rootInfo.setDesktopLayout(orientation, columnsRows.width(), columnsRows.height(), corner); QCOMPARE(rootInfo.desktopLayoutOrientation(), orientation); QCOMPARE(rootInfo.desktopLayoutColumnsRows(), columnsRows); QCOMPARE(rootInfo.desktopLayoutCorner(), corner); // compare with the X property QVERIFY(atom != XCB_ATOM_NONE); xcb_get_property_cookie_t cookie = xcb_get_property_unchecked(connection(), false, rootInfo.rootWindow(), atom, XCB_ATOM_CARDINAL, 0, 4); Property reply(xcb_get_property_reply(connection(), cookie, nullptr)); QVERIFY(!reply.isNull()); QCOMPARE(reply->format, uint8_t(32)); QCOMPARE(reply->value_len, uint32_t(4)); uint32_t *data = reinterpret_cast(xcb_get_property_value(reply.data())); QCOMPARE(data[0], uint32_t(orientation)); QCOMPARE(data[1], uint32_t(columnsRows.width())); QCOMPARE(data[2], uint32_t(columnsRows.height())); QCOMPARE(data[3], uint32_t(corner)); // wait for our property waitForPropertyChange(&rootInfo, atom, NET::Property(0), NET::WM2DesktopLayout); QCOMPARE(rootInfo.desktopLayoutOrientation(), orientation); QCOMPARE(rootInfo.desktopLayoutColumnsRows(), columnsRows); QCOMPARE(rootInfo.desktopLayoutCorner(), corner); NETRootInfo info2(connection(), NET::WMAllProperties, NET::WM2AllProperties); QCOMPARE(info2.desktopLayoutColumnsRows(), columnsRows); } void NetRootInfoTestWM::testDesktopViewports() { KXUtils::Atom atom(connection(), QByteArrayLiteral("_NET_DESKTOP_VIEWPORT")); QVERIFY(connection()); NETRootInfo rootInfo(connection(), m_supportWindow, s_wmName, NET::WMAllProperties, NET::AllTypesMask, NET::States(~0u), NET::WM2AllProperties, NET::Actions(~0u)); // we need to know the number of desktops, therefore setting it rootInfo.setNumberOfDesktops(4); NETPoint desktopOne; desktopOne.x = 100; desktopOne.y = 50; NETPoint desktopTwo; desktopTwo.x = 200; desktopTwo.y = 100; rootInfo.setDesktopViewport(1, desktopOne); rootInfo.setDesktopViewport(2, desktopTwo); const NETPoint compareZero = rootInfo.desktopViewport(0); QCOMPARE(compareZero.x, 0); QCOMPARE(compareZero.y, 0); const NETPoint compareOne = rootInfo.desktopViewport(1); QCOMPARE(compareOne.x, desktopOne.x); QCOMPARE(compareOne.y, desktopOne.y); const NETPoint compareTwo = rootInfo.desktopViewport(2); QCOMPARE(compareTwo.x, desktopTwo.x); QCOMPARE(compareTwo.y, desktopTwo.y); const NETPoint compareThree = rootInfo.desktopViewport(3); QCOMPARE(compareThree.x, 0); QCOMPARE(compareThree.y, 0); // compare with the X property QVERIFY(atom != XCB_ATOM_NONE); xcb_get_property_cookie_t cookie = xcb_get_property_unchecked(connection(), false, rootInfo.rootWindow(), atom, XCB_ATOM_CARDINAL, 0, 8); Property reply(xcb_get_property_reply(connection(), cookie, nullptr)); QVERIFY(!reply.isNull()); QCOMPARE(reply->format, uint8_t(32)); QCOMPARE(reply->value_len, uint32_t(8)); uint32_t *data = reinterpret_cast(xcb_get_property_value(reply.data())); QCOMPARE(data[0], uint32_t(desktopOne.x)); QCOMPARE(data[1], uint32_t(desktopOne.y)); QCOMPARE(data[2], uint32_t(desktopTwo.x)); QCOMPARE(data[3], uint32_t(desktopTwo.y)); QCOMPARE(data[4], uint32_t(0)); QCOMPARE(data[5], uint32_t(0)); QCOMPARE(data[6], uint32_t(0)); QCOMPARE(data[7], uint32_t(0)); // wait for our property - two events waitForPropertyChange(&rootInfo, atom, NET::DesktopViewport); waitForPropertyChange(&rootInfo, atom, NET::DesktopViewport); const NETPoint compareOne2 = rootInfo.desktopViewport(1); QCOMPARE(compareOne2.x, desktopOne.x); QCOMPARE(compareOne2.y, desktopOne.y); const NETPoint compareTwo2 = rootInfo.desktopViewport(2); QCOMPARE(compareTwo2.x, desktopTwo.x); QCOMPARE(compareTwo2.y, desktopTwo.y); const NETPoint compareThree2 = rootInfo.desktopViewport(3); QCOMPARE(compareThree2.x, 0); QCOMPARE(compareThree2.y, 0); } void NetRootInfoTestWM::testShowingDesktop_data() { QTest::addColumn("set"); QTest::addColumn("setValue"); QTest::newRow("true") << true << uint32_t(1); QTest::newRow("false") << false << uint32_t(0); } void NetRootInfoTestWM::testShowingDesktop() { KXUtils::Atom atom(connection(), QByteArrayLiteral("_NET_SHOWING_DESKTOP")); QVERIFY(connection()); NETRootInfo rootInfo(connection(), m_supportWindow, s_wmName, NET::WMAllProperties, NET::AllTypesMask, NET::States(~0u), NET::WM2AllProperties, NET::Actions(~0u)); QFETCH(bool, set); rootInfo.setShowingDesktop(set); QCOMPARE(rootInfo.showingDesktop(), set); // compare with the X property QVERIFY(atom != XCB_ATOM_NONE); xcb_get_property_cookie_t cookie = xcb_get_property_unchecked(connection(), false, rootInfo.rootWindow(), atom, XCB_ATOM_CARDINAL, 0, 1); Property reply(xcb_get_property_reply(connection(), cookie, nullptr)); QVERIFY(!reply.isNull()); QCOMPARE(reply->format, uint8_t(32)); QCOMPARE(reply->value_len, uint32_t(1)); QTEST(reinterpret_cast(xcb_get_property_value(reply.data()))[0], "setValue"); // wait for our property waitForPropertyChange(&rootInfo, atom, NET::Property(0), NET::WM2ShowingDesktop); QCOMPARE(rootInfo.showingDesktop(), set); } void NetRootInfoTestWM::testWorkArea() { KXUtils::Atom atom(connection(), QByteArrayLiteral("_NET_WORKAREA")); QVERIFY(connection()); NETRootInfo rootInfo(connection(), m_supportWindow, s_wmName, NET::WMAllProperties, NET::AllTypesMask, NET::States(~0u), NET::WM2AllProperties, NET::Actions(~0u)); // we need to know the number of desktops, therefore setting it rootInfo.setNumberOfDesktops(4); NETRect desktopOne; desktopOne.pos.x = 10; desktopOne.pos.y = 5; desktopOne.size.width = 1000; desktopOne.size.height = 800; NETRect desktopTwo; desktopTwo.pos.x = 20; desktopTwo.pos.y = 10; desktopTwo.size.width = 800; desktopTwo.size.height = 750; rootInfo.setWorkArea(1, desktopOne); rootInfo.setWorkArea(2, desktopTwo); const NETRect compareZero = rootInfo.workArea(0); QCOMPARE(compareZero.pos.x, 0); QCOMPARE(compareZero.pos.y, 0); QCOMPARE(compareZero.size.width, 0); QCOMPARE(compareZero.size.height, 0); const NETRect compareOne = rootInfo.workArea(1); QCOMPARE(compareOne.pos.x, desktopOne.pos.x); QCOMPARE(compareOne.pos.y, desktopOne.pos.y); QCOMPARE(compareOne.size.width, desktopOne.size.width); QCOMPARE(compareOne.size.height, desktopOne.size.height); const NETRect compareTwo = rootInfo.workArea(2); QCOMPARE(compareTwo.pos.x, desktopTwo.pos.x); QCOMPARE(compareTwo.pos.y, desktopTwo.pos.y); QCOMPARE(compareTwo.size.width, desktopTwo.size.width); QCOMPARE(compareTwo.size.height, desktopTwo.size.height); const NETRect compareThree = rootInfo.workArea(3); QCOMPARE(compareThree.pos.x, 0); QCOMPARE(compareThree.pos.y, 0); QCOMPARE(compareThree.size.width, 0); QCOMPARE(compareThree.size.height, 0); // compare with the X property QVERIFY(atom != XCB_ATOM_NONE); xcb_get_property_cookie_t cookie = xcb_get_property_unchecked(connection(), false, rootInfo.rootWindow(), atom, XCB_ATOM_CARDINAL, 0, 16); Property reply(xcb_get_property_reply(connection(), cookie, nullptr)); QVERIFY(!reply.isNull()); QCOMPARE(reply->format, uint8_t(32)); QCOMPARE(reply->value_len, uint32_t(16)); uint32_t *data = reinterpret_cast(xcb_get_property_value(reply.data())); QCOMPARE(data[ 0], uint32_t(desktopOne.pos.x)); QCOMPARE(data[ 1], uint32_t(desktopOne.pos.y)); QCOMPARE(data[ 2], uint32_t(desktopOne.size.width)); QCOMPARE(data[ 3], uint32_t(desktopOne.size.height)); QCOMPARE(data[ 4], uint32_t(desktopTwo.pos.x)); QCOMPARE(data[ 5], uint32_t(desktopTwo.pos.y)); QCOMPARE(data[ 6], uint32_t(desktopTwo.size.width)); QCOMPARE(data[ 7], uint32_t(desktopTwo.size.height)); QCOMPARE(data[ 8], uint32_t(0)); QCOMPARE(data[ 9], uint32_t(0)); QCOMPARE(data[10], uint32_t(0)); QCOMPARE(data[11], uint32_t(0)); QCOMPARE(data[12], uint32_t(0)); QCOMPARE(data[13], uint32_t(0)); QCOMPARE(data[14], uint32_t(0)); QCOMPARE(data[15], uint32_t(0)); // wait for our property - two events waitForPropertyChange(&rootInfo, atom, NET::WorkArea); waitForPropertyChange(&rootInfo, atom, NET::WorkArea); const NETRect compareOne2 = rootInfo.workArea(1); QCOMPARE(compareOne2.pos.x, desktopOne.pos.x); QCOMPARE(compareOne2.pos.y, desktopOne.pos.y); QCOMPARE(compareOne2.size.width, desktopOne.size.width); QCOMPARE(compareOne2.size.height, desktopOne.size.height); const NETRect compareTwo2 = rootInfo.workArea(2); QCOMPARE(compareTwo2.pos.x, desktopTwo.pos.x); QCOMPARE(compareTwo2.pos.y, desktopTwo.pos.y); QCOMPARE(compareTwo2.size.width, desktopTwo.size.width); QCOMPARE(compareTwo2.size.height, desktopTwo.size.height); const NETRect compareThree2 = rootInfo.workArea(3); QCOMPARE(compareThree2.pos.x, 0); QCOMPARE(compareThree2.pos.y, 0); QCOMPARE(compareThree2.size.width, 0); QCOMPARE(compareThree2.size.height, 0); } void NetRootInfoTestWM::testDontCrashMapViewports() { QProcess p; const QString processName = QFINDTESTDATA("dontcrashmapviewport"); QVERIFY(!processName.isEmpty()); p.start(processName, QStringList()); QVERIFY(p.waitForFinished()); QCOMPARE(p.exitStatus(), QProcess::NormalExit); QCOMPARE(p.exitCode(), 0); } QTEST_GUILESS_MAIN(NetRootInfoTestWM) #include "netrootinfotestwm.moc" diff --git a/autotests/netwininfotestclient.cpp b/autotests/netwininfotestclient.cpp index 654adbe..aaff71d 100644 --- a/autotests/netwininfotestclient.cpp +++ b/autotests/netwininfotestclient.cpp @@ -1,1122 +1,1123 @@ /* * Copyright 2013 Martin Gräßlin * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #include "nettesthelper.h" #include #include #include // system #include class Property : public QScopedPointer { public: Property(xcb_get_property_reply_t *p = nullptr) : QScopedPointer(p) {} }; #define INFO NETWinInfo info(m_connection, m_testWindow, m_rootWindow, NET::WMAllProperties, NET::WM2AllProperties, NET::Client); #define ATOM(name) \ KXUtils::Atom atom(connection(), QByteArrayLiteral(#name)); #define UTF8 KXUtils::Atom utf8String(connection(), QByteArrayLiteral("UTF8_STRING")); #define GETPROP(type, length, formatSize) \ xcb_get_property_cookie_t cookie = xcb_get_property_unchecked(connection(), false, m_testWindow, \ atom, type, 0, length); \ Property reply(xcb_get_property_reply(connection(), cookie, nullptr)); \ QVERIFY(!reply.isNull()); \ QCOMPARE(reply->format, uint8_t(formatSize)); \ QCOMPARE(reply->value_len, uint32_t(length)); #define VERIFYDELETED(t) \ xcb_get_property_cookie_t cookieDeleted = xcb_get_property_unchecked(connection(), false, m_testWindow, \ atom, t, 0, 1); \ Property replyDeleted(xcb_get_property_reply(connection(), cookieDeleted, nullptr)); \ QVERIFY(!replyDeleted.isNull()); \ QVERIFY(replyDeleted->type == XCB_ATOM_NONE); class NetWinInfoTestClient : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase(); void cleanupTestCase(); void init(); void cleanup(); void testBlockCompositing(); void testUserTime(); void testStartupId(); void testDesktopFileName(); void testHandledIcons_data(); void testHandledIcons(); void testPid(); void testName(); void testIconName(); void testStrut(); void testExtendedStrut(); void testIconGeometry(); void testWindowType_data(); void testWindowType(); void testActivities_data(); void testActivities(); void testWindowRole(); void testWindowClass(); void testClientMachine(); void testGroupLeader(); void testUrgency_data(); void testUrgency(); void testInput_data(); void testInput(); void testInitialMappingState_data(); void testInitialMappingState(); void testIconPixmap_data(); void testIconPixmap(); void testTransientFor(); void testProtocols_data(); void testProtocols(); void testOpaqueRegion_data(); void testOpaqueRegion(); private: void performNameTest(xcb_atom_t atom, const char *(NETWinInfo:: *getter)(void)const, void (NETWinInfo:: *setter)(const char *), NET::Property property); void waitForPropertyChange(NETWinInfo *info, xcb_atom_t atom, NET::Property prop, NET::Property2 prop2 = NET::Property2(0)); xcb_connection_t *connection() { return m_connection; } xcb_connection_t *m_connection; QVector m_connections; QScopedPointer m_xvfb; xcb_window_t m_rootWindow; xcb_window_t m_testWindow; }; void NetWinInfoTestClient::initTestCase() { qsrand(QDateTime::currentMSecsSinceEpoch()); } void NetWinInfoTestClient::cleanupTestCase() { // close connection while (!m_connections.isEmpty()) { xcb_disconnect(m_connections.takeFirst()); } } void NetWinInfoTestClient::init() { // first reset just to be sure m_connection = nullptr; m_rootWindow = XCB_WINDOW_NONE; m_testWindow = XCB_WINDOW_NONE; // start Xvfb m_xvfb.reset(new QProcess); // use pipe to pass fd to Xvfb to get back the display id int pipeFds[2]; QVERIFY(pipe(pipeFds) == 0); m_xvfb->start(QStringLiteral("Xvfb"), QStringList{ QStringLiteral("-displayfd"), QString::number(pipeFds[1]) }); QVERIFY(m_xvfb->waitForStarted()); QCOMPARE(m_xvfb->state(), QProcess::Running); // reads from pipe, closes write side close(pipeFds[1]); QFile readPipe; QVERIFY(readPipe.open(pipeFds[0], QIODevice::ReadOnly, QFileDevice::AutoCloseHandle)); QByteArray displayNumber = readPipe.readLine(); readPipe.close(); displayNumber.prepend(QByteArray(":")); displayNumber.remove(displayNumber.size() -1, 1); // create X connection int screen = 0; m_connection = xcb_connect(displayNumber.constData(), &screen); QVERIFY(m_connection); QVERIFY(!xcb_connection_has_error(m_connection)); m_rootWindow = KXUtils::rootWindow(m_connection, screen); // create test window m_testWindow = xcb_generate_id(m_connection); uint32_t values[] = {XCB_EVENT_MASK_PROPERTY_CHANGE}; xcb_create_window(m_connection, XCB_COPY_FROM_PARENT, m_testWindow, m_rootWindow, 0, 0, 100, 100, 0, XCB_COPY_FROM_PARENT, XCB_COPY_FROM_PARENT, XCB_CW_EVENT_MASK, values); // and map it xcb_map_window(m_connection, m_testWindow); } void NetWinInfoTestClient::cleanup() { // destroy test window xcb_unmap_window(m_connection, m_testWindow); xcb_destroy_window(m_connection, m_testWindow); m_testWindow = XCB_WINDOW_NONE; // delay till clenupTestCase as otherwise xcb reuses the same memory address m_connections << connection(); // kill Xvfb m_xvfb->terminate(); m_xvfb->waitForFinished(); } void NetWinInfoTestClient::waitForPropertyChange(NETWinInfo *info, xcb_atom_t atom, NET::Property prop, NET::Property2 prop2) { while (true) { KXUtils::ScopedCPointer event(xcb_wait_for_event(connection())); if (event.isNull()) { break; } if ((event->response_type & ~0x80) != XCB_PROPERTY_NOTIFY) { continue; } xcb_property_notify_event_t *pe = reinterpret_cast(event.data()); if (pe->window != m_testWindow) { continue; } if (pe->atom != atom) { continue; } NET::Properties dirty; NET::Properties2 dirty2; info->event(event.data(), &dirty, &dirty2); if (prop != 0) { QVERIFY(dirty & prop); } if (prop2 != 0) { QVERIFY(dirty2 & prop2); } if (!prop) { QCOMPARE(dirty, NET::Properties()); } if (!prop2) { QCOMPARE(dirty2, NET::Properties2()); } break; } } void NetWinInfoTestClient::testBlockCompositing() { QVERIFY(connection()); ATOM(_KDE_NET_WM_BLOCK_COMPOSITING) INFO QVERIFY(!info.isBlockingCompositing()); info.setBlockingCompositing(true); QVERIFY(info.isBlockingCompositing()); // compare with the X property QVERIFY(atom != XCB_ATOM_NONE); GETPROP(XCB_ATOM_CARDINAL, 1, 32) QCOMPARE(reinterpret_cast(xcb_get_property_value(reply.data()))[0], uint32_t(1)); // and wait for our event waitForPropertyChange(&info, atom, NET::Property(0), NET::WM2BlockCompositing); QVERIFY(info.isBlockingCompositing()); // setting false should delete the property again info.setBlockingCompositing(false); QVERIFY(!info.isBlockingCompositing()); VERIFYDELETED(XCB_ATOM_CARDINAL) // and wait for our event waitForPropertyChange(&info, atom, NET::Property(0), NET::WM2BlockCompositing); QVERIFY(!info.isBlockingCompositing()); } void NetWinInfoTestClient::testUserTime() { QVERIFY(connection()); ATOM(_NET_WM_USER_TIME) INFO QCOMPARE(info.userTime(), uint32_t(-1)); info.setUserTime(500); QCOMPARE(info.userTime(), uint32_t(500)); // compare with the X property QVERIFY(atom != XCB_ATOM_NONE); GETPROP(XCB_ATOM_CARDINAL, 1, 32) QCOMPARE(reinterpret_cast(xcb_get_property_value(reply.data()))[0], uint32_t(500)); // and wait for our event waitForPropertyChange(&info, atom, NET::Property(0), NET::WM2UserTime); QCOMPARE(info.userTime(), uint32_t(500)); } void NetWinInfoTestClient::testStartupId() { QVERIFY(connection()); ATOM(_NET_STARTUP_ID) UTF8 INFO QVERIFY(!info.startupId()); info.setStartupId("foo"); QCOMPARE(info.startupId(), "foo"); // compare with the X property QVERIFY(atom != XCB_ATOM_NONE); QVERIFY(utf8String != XCB_ATOM_NONE); GETPROP(utf8String, 3, 8) QCOMPARE(reinterpret_cast(xcb_get_property_value(reply.data())), "foo"); // and wait for our event waitForPropertyChange(&info, atom, NET::Property(0), NET::WM2StartupId); QCOMPARE(info.startupId(), "foo"); } void NetWinInfoTestClient::testDesktopFileName() { QVERIFY(connection()); ATOM(_KDE_NET_WM_DESKTOP_FILE) UTF8 INFO QVERIFY(!info.desktopFileName()); info.setDesktopFileName("foo"); QCOMPARE(info.desktopFileName(), "foo"); // compare with the X property QVERIFY(atom != XCB_ATOM_NONE); QVERIFY(utf8String != XCB_ATOM_NONE); GETPROP(utf8String, 3, 8) QCOMPARE(reinterpret_cast(xcb_get_property_value(reply.data())), "foo"); // and wait for our event waitForPropertyChange(&info, atom, NET::Property(0), NET::WM2DesktopFileName); QCOMPARE(info.desktopFileName(), "foo"); } void NetWinInfoTestClient::testHandledIcons_data() { QTest::addColumn("handled"); QTest::addColumn("value"); QTest::newRow("enabled") << true << uint32_t(1); QTest::newRow("disabled") << false << uint32_t(0); } void NetWinInfoTestClient::testHandledIcons() { QVERIFY(connection()); ATOM(_NET_WM_HANDLED_ICONS) INFO QVERIFY(!info.handledIcons()); QFETCH(bool, handled); info.setHandledIcons(handled); QCOMPARE(info.handledIcons(), handled); // compare with the X property QVERIFY(atom != XCB_ATOM_NONE); GETPROP(XCB_ATOM_CARDINAL, 1, 32) QTEST(reinterpret_cast(xcb_get_property_value(reply.data()))[0], "value"); // and wait for our event waitForPropertyChange(&info, atom, NET::WMHandledIcons); QCOMPARE(info.handledIcons(), handled); } void NetWinInfoTestClient::testPid() { QVERIFY(connection()); ATOM(_NET_WM_PID) INFO QCOMPARE(info.pid(), 0); info.setPid(m_xvfb->pid()); QCOMPARE(info.pid(), m_xvfb->pid()); // compare with the X property QVERIFY(atom != XCB_ATOM_NONE); GETPROP(XCB_ATOM_CARDINAL, 1, 32) QCOMPARE(reinterpret_cast(xcb_get_property_value(reply.data()))[0], uint32_t(m_xvfb->pid())); // and wait for our event waitForPropertyChange(&info, atom, NET::WMPid); QCOMPARE(info.pid(), m_xvfb->pid()); } void NetWinInfoTestClient::performNameTest(xcb_atom_t atom, const char *(NETWinInfo:: *getter)(void)const, void (NETWinInfo:: *setter)(const char *), NET::Property property) { UTF8 INFO QVERIFY(!(info.*getter)()); (info.*setter)("foo"); QCOMPARE((info.*getter)(), "foo"); // compare with the X property QVERIFY(atom != XCB_ATOM_NONE); QVERIFY(utf8String != XCB_ATOM_NONE); GETPROP(utf8String, 3, 8) QCOMPARE(reinterpret_cast(xcb_get_property_value(reply.data())), "foo"); // and wait for our event waitForPropertyChange(&info, atom, property); QCOMPARE((info.*getter)(), "foo"); // delete the string (info.*setter)(""); QCOMPARE((info.*getter)(), ""); VERIFYDELETED(utf8String) // and wait for our event waitForPropertyChange(&info, atom, property); QVERIFY(!(info.*getter)()); // set it again, to ensure that we don't leak on tear-down (info.*setter)("bar"); QCOMPARE((info.*getter)(), "bar"); xcb_flush(connection()); waitForPropertyChange(&info, atom, property); QCOMPARE((info.*getter)(), "bar"); } void NetWinInfoTestClient::testIconName() { QVERIFY(connection()); ATOM(_NET_WM_ICON_NAME) performNameTest(atom, &NETWinInfo::iconName, &NETWinInfo::setIconName, NET::WMIconName); } void NetWinInfoTestClient::testName() { QVERIFY(connection()); ATOM(_NET_WM_NAME) performNameTest(atom, &NETWinInfo::name, &NETWinInfo::setName, NET::WMName); } void NetWinInfoTestClient::testStrut() { QVERIFY(connection()); ATOM(_NET_WM_STRUT) INFO NETStrut extents = info.strut(); QCOMPARE(extents.bottom, 0); QCOMPARE(extents.left, 0); QCOMPARE(extents.right, 0); QCOMPARE(extents.top, 0); NETStrut newExtents; newExtents.bottom = 10; newExtents.left = 20; newExtents.right = 30; newExtents.top = 40; info.setStrut(newExtents); extents = info.strut(); QCOMPARE(extents.bottom, newExtents.bottom); QCOMPARE(extents.left, newExtents.left); QCOMPARE(extents.right, newExtents.right); QCOMPARE(extents.top, newExtents.top); // compare with the X property QVERIFY(atom != XCB_ATOM_NONE); GETPROP(XCB_ATOM_CARDINAL, 4, 32) uint32_t *data = reinterpret_cast(xcb_get_property_value(reply.data())); QCOMPARE(data[0], uint32_t(newExtents.left)); QCOMPARE(data[1], uint32_t(newExtents.right)); QCOMPARE(data[2], uint32_t(newExtents.top)); QCOMPARE(data[3], uint32_t(newExtents.bottom)); // and wait for our event waitForPropertyChange(&info, atom, NET::WMStrut); extents = info.strut(); QCOMPARE(extents.bottom, newExtents.bottom); QCOMPARE(extents.left, newExtents.left); QCOMPARE(extents.right, newExtents.right); QCOMPARE(extents.top, newExtents.top); } void NetWinInfoTestClient::testExtendedStrut() { QVERIFY(connection()); ATOM(_NET_WM_STRUT_PARTIAL) INFO NETExtendedStrut extents = info.extendedStrut(); QCOMPARE(extents.left_width, 0); QCOMPARE(extents.right_width, 0); QCOMPARE(extents.top_width, 0); QCOMPARE(extents.bottom_width, 0); QCOMPARE(extents.left_start, 0); QCOMPARE(extents.left_end, 0); QCOMPARE(extents.right_start, 0); QCOMPARE(extents.right_end, 0); QCOMPARE(extents.top_start, 0); QCOMPARE(extents.top_end, 0); QCOMPARE(extents.bottom_start, 0); QCOMPARE(extents.bottom_end, 0); NETExtendedStrut newExtents; newExtents.left_width = 10; newExtents.right_width = 20; newExtents.top_width = 30; newExtents.bottom_width = 40; newExtents.left_start = 50; newExtents.left_end = 60; newExtents.right_start = 70; newExtents.right_end = 80; newExtents.top_start = 90; newExtents.top_end = 91; newExtents.bottom_start = 92; newExtents.bottom_end = 93; info.setExtendedStrut(newExtents); extents = info.extendedStrut(); QCOMPARE(extents.left_width, newExtents.left_width); QCOMPARE(extents.right_width, newExtents.right_width); QCOMPARE(extents.top_width, newExtents.top_width); QCOMPARE(extents.bottom_width, newExtents.bottom_width); QCOMPARE(extents.left_start, newExtents.left_start); QCOMPARE(extents.left_end, newExtents.left_end); QCOMPARE(extents.right_start, newExtents.right_start); QCOMPARE(extents.right_end, newExtents.right_end); QCOMPARE(extents.top_start, newExtents.top_start); QCOMPARE(extents.top_end, newExtents.top_end); QCOMPARE(extents.bottom_start, newExtents.bottom_start); QCOMPARE(extents.bottom_end, newExtents.bottom_end); // compare with the X property QVERIFY(atom != XCB_ATOM_NONE); GETPROP(XCB_ATOM_CARDINAL, 12, 32) uint32_t *data = reinterpret_cast(xcb_get_property_value(reply.data())); QCOMPARE(data[ 0], uint32_t(newExtents.left_width)); QCOMPARE(data[ 1], uint32_t(newExtents.right_width)); QCOMPARE(data[ 2], uint32_t(newExtents.top_width)); QCOMPARE(data[ 3], uint32_t(newExtents.bottom_width)); QCOMPARE(data[ 4], uint32_t(newExtents.left_start)); QCOMPARE(data[ 5], uint32_t(newExtents.left_end)); QCOMPARE(data[ 6], uint32_t(newExtents.right_start)); QCOMPARE(data[ 7], uint32_t(newExtents.right_end)); QCOMPARE(data[ 8], uint32_t(newExtents.top_start)); QCOMPARE(data[ 9], uint32_t(newExtents.top_end)); QCOMPARE(data[10], uint32_t(newExtents.bottom_start)); QCOMPARE(data[11], uint32_t(newExtents.bottom_end)); // and wait for our event waitForPropertyChange(&info, atom, NET::Property(0), NET::WM2ExtendedStrut); extents = info.extendedStrut(); QCOMPARE(extents.left_width, newExtents.left_width); QCOMPARE(extents.right_width, newExtents.right_width); QCOMPARE(extents.top_width, newExtents.top_width); QCOMPARE(extents.bottom_width, newExtents.bottom_width); QCOMPARE(extents.left_start, newExtents.left_start); QCOMPARE(extents.left_end, newExtents.left_end); QCOMPARE(extents.right_start, newExtents.right_start); QCOMPARE(extents.right_end, newExtents.right_end); QCOMPARE(extents.top_start, newExtents.top_start); QCOMPARE(extents.top_end, newExtents.top_end); QCOMPARE(extents.bottom_start, newExtents.bottom_start); QCOMPARE(extents.bottom_end, newExtents.bottom_end); } void NetWinInfoTestClient::testIconGeometry() { QVERIFY(connection()); ATOM(_NET_WM_ICON_GEOMETRY) INFO NETRect geo = info.iconGeometry(); QCOMPARE(geo.pos.x, 0); QCOMPARE(geo.pos.y, 0); QCOMPARE(geo.size.width, 0); QCOMPARE(geo.size.height, 0); NETRect newGeo; newGeo.pos.x = 10; newGeo.pos.y = 20; newGeo.size.width = 30; newGeo.size.height = 40; info.setIconGeometry(newGeo); geo = info.iconGeometry(); QCOMPARE(geo.pos.x, newGeo.pos.x); QCOMPARE(geo.pos.y, newGeo.pos.y); QCOMPARE(geo.size.width, newGeo.size.width); QCOMPARE(geo.size.height, newGeo.size.height); // compare with the X property QVERIFY(atom != XCB_ATOM_NONE); GETPROP(XCB_ATOM_CARDINAL, 4, 32) uint32_t *data = reinterpret_cast(xcb_get_property_value(reply.data())); QCOMPARE(data[0], uint32_t(newGeo.pos.x)); QCOMPARE(data[1], uint32_t(newGeo.pos.y)); QCOMPARE(data[2], uint32_t(newGeo.size.width)); QCOMPARE(data[3], uint32_t(newGeo.size.height)); // and wait for our event waitForPropertyChange(&info, atom, NET::WMIconGeometry); geo = info.iconGeometry(); QCOMPARE(geo.pos.x, newGeo.pos.x); QCOMPARE(geo.pos.y, newGeo.pos.y); QCOMPARE(geo.size.width, newGeo.size.width); QCOMPARE(geo.size.height, newGeo.size.height); } Q_DECLARE_METATYPE(NET::WindowType) void NetWinInfoTestClient::testWindowType_data() { QTest::addColumn("type"); QTest::addColumn("length"); QTest::addColumn("typeAtom"); QTest::addColumn("secondaryTypeAtom"); QTest::newRow("override") << NET::Override << 2 << QByteArrayLiteral("_KDE_NET_WM_WINDOW_TYPE_OVERRIDE") << QByteArrayLiteral("_NET_WM_WINDOW_TYPE_NORMAL"); QTest::newRow("TopMenu") << NET::TopMenu << 2 << QByteArrayLiteral("_KDE_NET_WM_WINDOW_TYPE_TOPMENU") << QByteArrayLiteral("_NET_WM_WINDOW_TYPE_DOCK"); QTest::newRow("Utility") << NET::Utility << 2 << QByteArrayLiteral("_NET_WM_WINDOW_TYPE_UTILITY") << QByteArrayLiteral("_NET_WM_WINDOW_TYPE_DIALOG"); QTest::newRow("Splash") << NET::Splash << 2 << QByteArrayLiteral("_NET_WM_WINDOW_TYPE_SPLASH") << QByteArrayLiteral("_NET_WM_WINDOW_TYPE_DOCK"); // TODO: this should be 2 QTest::newRow("DropdownMenu") << NET::DropdownMenu << 1 << QByteArrayLiteral("_NET_WM_WINDOW_TYPE_DROPDOWN_MENU") << QByteArrayLiteral("_NET_WM_WINDOW_TYPE_MENU"); // TODO: this should be 2 QTest::newRow("PopupMenu") << NET::PopupMenu << 1 << QByteArrayLiteral("_NET_WM_WINDOW_TYPE_POPUP_MENU") << QByteArrayLiteral("_NET_WM_WINDOW_TYPE_MENU"); // TODO: this should be 2 QTest::newRow("Notification") << NET::Notification << 1 << QByteArrayLiteral("_NET_WM_WINDOW_TYPE_NOTIFICATION") << QByteArrayLiteral("_NET_WM_WINDOW_TYPE_UTILITY"); QTest::newRow("Dialog") << NET::Dialog << 1 << QByteArrayLiteral("_NET_WM_WINDOW_TYPE_DIALOG") << QByteArray(); QTest::newRow("Menu") << NET::Menu << 1 << QByteArrayLiteral("_NET_WM_WINDOW_TYPE_MENU") << QByteArray(); QTest::newRow("Toolbar") << NET::Toolbar << 1 << QByteArrayLiteral("_NET_WM_WINDOW_TYPE_TOOLBAR") << QByteArray(); QTest::newRow("Dock") << NET::Dock << 1 << QByteArrayLiteral("_NET_WM_WINDOW_TYPE_DOCK") << QByteArray(); QTest::newRow("Desktop") << NET::Desktop << 1 << QByteArrayLiteral("_NET_WM_WINDOW_TYPE_DESKTOP") << QByteArray(); QTest::newRow("Tooltip") << NET::Tooltip << 1 << QByteArrayLiteral("_NET_WM_WINDOW_TYPE_TOOLTIP") << QByteArray(); QTest::newRow("ComboBox") << NET::ComboBox << 1 << QByteArrayLiteral("_NET_WM_WINDOW_TYPE_COMBO") << QByteArray(); QTest::newRow("DNDIcon") << NET::DNDIcon << 1 << QByteArrayLiteral("_NET_WM_WINDOW_TYPE_DND") << QByteArray(); QTest::newRow("Normal") << NET::Normal << 1 << QByteArrayLiteral("_NET_WM_WINDOW_TYPE_NORMAL") << QByteArray(); QTest::newRow("OnScreenDisplay") << NET::OnScreenDisplay << 1 << QByteArrayLiteral("_KDE_NET_WM_WINDOW_TYPE_ON_SCREEN_DISPLAY") << QByteArray(); + QTest::newRow("CriticalNotification") << NET::CriticalNotification << 1 << QByteArrayLiteral("_KDE_NET_WM_WINDOW_TYPE_CRITICAL_NOTIFICATION") << QByteArray(); } void NetWinInfoTestClient::testWindowType() { QVERIFY(connection()); ATOM(_NET_WM_WINDOW_TYPE) INFO QVERIFY(info.hasWindowType()); QVERIFY(!info.hasNETSupport()); QCOMPARE(info.windowType(NET::AllTypesMask), NET::Unknown); QFETCH(NET::WindowType, type); info.setWindowType(type); // it does not update the internal type! QCOMPARE(info.windowType(NET::AllTypesMask), NET::Unknown); QFETCH(int, length); QFETCH(QByteArray, typeAtom); // compare the X property KXUtils::Atom type1(connection(), typeAtom); QVERIFY(atom != XCB_ATOM_NONE); GETPROP(XCB_ATOM_ATOM, length, 32) xcb_atom_t *atoms = reinterpret_cast(xcb_get_property_value(reply.data())); QCOMPARE(atoms[0], xcb_atom_t(type1)); if (reply->value_len > 1) { QFETCH(QByteArray, secondaryTypeAtom); KXUtils::Atom type2(connection(), secondaryTypeAtom); QVERIFY(type2 != XCB_ATOM_NONE); QCOMPARE(atoms[1], xcb_atom_t(type2)); } waitForPropertyChange(&info, atom, NET::WMWindowType); QCOMPARE(info.windowType(NET::AllTypesMask), type); QVERIFY(info.hasNETSupport()); } void NetWinInfoTestClient::testClientMachine() { QVERIFY(connection()); INFO QVERIFY(!info.clientMachine()); // client machine needs to be changed using xcb xcb_change_property(connection(), XCB_PROP_MODE_REPLACE, m_testWindow, XCB_ATOM_WM_CLIENT_MACHINE, XCB_ATOM_STRING, 8, 9, "localhost"); xcb_flush(connection()); // only updated after event waitForPropertyChange(&info, XCB_ATOM_WM_CLIENT_MACHINE, NET::Property(0), NET::WM2ClientMachine); QCOMPARE(info.clientMachine(), "localhost"); } void NetWinInfoTestClient::testGroupLeader() { QVERIFY(connection()); INFO QVERIFY(info.groupLeader() == XCB_WINDOW_NONE); // group leader needs to be changed through wm hints uint32_t values[] = { 1 << 6, /* WindowGroupHint*/ 1, /* Input */ 1, /* Normal State */ XCB_NONE, /* icon pixmap */ XCB_NONE, /* icon window */ XCB_NONE, /* icon x */ XCB_NONE, /* icon y */ XCB_NONE, /* icon mask */ m_rootWindow /* group leader */ }; xcb_change_property(connection(), XCB_PROP_MODE_REPLACE, m_testWindow, XCB_ATOM_WM_HINTS, XCB_ATOM_WM_HINTS, 32, 9, values); xcb_flush(connection()); // only updated after event waitForPropertyChange(&info, XCB_ATOM_WM_HINTS, NET::Property(0), NET::WM2GroupLeader); QCOMPARE(info.groupLeader(), m_rootWindow); } void NetWinInfoTestClient::testUrgency_data() { QTest::addColumn("flags"); QTest::addColumn("expected"); QTest::newRow("urgency") << quint32(1 << 8) << true; QTest::newRow("none") << quint32(0) << false; QTest::newRow("group_urgency") << quint32((1 << 6) | (1 << 8)) << true; QTest::newRow("input") << quint32(1) << false; } void NetWinInfoTestClient::testUrgency() { QVERIFY(connection()); INFO QVERIFY(!info.urgency()); QFETCH(quint32, flags); // group leader needs to be changed through wm hints uint32_t values[] = { flags, 1, /* Input */ 1, /* Normal State */ XCB_NONE, /* icon pixmap */ XCB_NONE, /* icon window */ XCB_NONE, /* icon x */ XCB_NONE, /* icon y */ XCB_NONE, /* icon mask */ XCB_NONE /* group leader */ }; xcb_change_property(connection(), XCB_PROP_MODE_REPLACE, m_testWindow, XCB_ATOM_WM_HINTS, XCB_ATOM_WM_HINTS, 32, 9, values); xcb_flush(connection()); // only updated after event waitForPropertyChange(&info, XCB_ATOM_WM_HINTS, NET::Property(0), NET::WM2Urgency); QTEST(info.urgency(), "expected"); } void NetWinInfoTestClient::testInput_data() { QTest::addColumn("flags"); QTest::addColumn("input"); QTest::addColumn("expected"); QTest::newRow("flag_input") << quint32(1) << quint32(1) << true; QTest::newRow("flag_noinput") << quint32(1) << quint32(0) << false; QTest::newRow("noflag_input") << quint32(0) << quint32(1) << true; QTest::newRow("noflag_noinput") << quint32(0) << quint32(0) << true; QTest::newRow("flag_with_other_input") << quint32(1 | 1 << 8) << quint32(1) << true; QTest::newRow("flag_with_other_noinput") << quint32(1 | 1 << 8) << quint32(0) << false; } void NetWinInfoTestClient::testInput() { QVERIFY(connection()); INFO QVERIFY(info.input()); QFETCH(quint32, flags); QFETCH(quint32, input); // group leader needs to be changed through wm hints uint32_t values[] = { flags, input, /* Input */ 1, /* Normal State */ XCB_NONE, /* icon pixmap */ XCB_NONE, /* icon window */ XCB_NONE, /* icon x */ XCB_NONE, /* icon y */ XCB_NONE, /* icon mask */ XCB_NONE /* group leader */ }; xcb_change_property(connection(), XCB_PROP_MODE_REPLACE, m_testWindow, XCB_ATOM_WM_HINTS, XCB_ATOM_WM_HINTS, 32, 9, values); xcb_flush(connection()); // only updated after event waitForPropertyChange(&info, XCB_ATOM_WM_HINTS, NET::Property(0), NET::WM2Urgency); QTEST(info.input(), "expected"); } Q_DECLARE_METATYPE(NET::MappingState) void NetWinInfoTestClient::testInitialMappingState_data() { QTest::addColumn("flags"); QTest::addColumn("state"); QTest::addColumn("expected"); QTest::newRow("flag-iconic") << quint32(2) << quint32(3) << NET::Iconic; QTest::newRow("flag-normal") << quint32(2) << quint32(1) << NET::Visible; QTest::newRow("flag-invalid") << quint32(2) << quint32(8) << NET::Withdrawn; QTest::newRow("noflag-iconic") << quint32(256) << quint32(3) << NET::Withdrawn; QTest::newRow("noflag-normal") << quint32(256) << quint32(1) << NET::Withdrawn; } void NetWinInfoTestClient::testInitialMappingState() { QVERIFY(connection()); INFO QCOMPARE(info.initialMappingState(), NET::Withdrawn); QFETCH(quint32, flags); QFETCH(quint32, state); // group leader needs to be changed through wm hints uint32_t values[] = { flags, 1, /* Input */ state, /* Normal State */ XCB_NONE, /* icon pixmap */ XCB_NONE, /* icon window */ XCB_NONE, /* icon x */ XCB_NONE, /* icon y */ XCB_NONE, /* icon mask */ XCB_NONE /* group leader */ }; xcb_change_property(connection(), XCB_PROP_MODE_REPLACE, m_testWindow, XCB_ATOM_WM_HINTS, XCB_ATOM_WM_HINTS, 32, 9, values); xcb_flush(connection()); // only updated after event waitForPropertyChange(&info, XCB_ATOM_WM_HINTS, NET::Property(0), NET::WM2InitialMappingState); QTEST(info.initialMappingState(), "expected"); } void NetWinInfoTestClient::testIconPixmap_data() { QTest::addColumn("flags"); QTest::addColumn("icon"); QTest::addColumn("mask"); QTest::addColumn("expectedPixmap"); QTest::addColumn("expectedMask"); QTest::newRow("invalid-flags") << 1u << 2u << 3u << 0u << 0u; QTest::newRow("pixmap-flags") << 4u << 2u << 3u << 2u << 0u; QTest::newRow("mask-flags") << 32u << 2u << 3u << 0u << 3u; QTest::newRow("pixmap-mask-flags") << 36u << 2u << 3u << 2u << 3u; } void NetWinInfoTestClient::testIconPixmap() { QVERIFY(connection()); INFO QCOMPARE(info.icccmIconPixmap(), 0u); QCOMPARE(info.icccmIconPixmapMask(), 0u); QFETCH(quint32, flags); QFETCH(quint32, icon); QFETCH(quint32, mask); // icon pixmap needs to be changed through wm hints uint32_t values[] = { flags, 1, /* Input */ XCB_NONE, /* Normal State */ icon, /* icon pixmap */ XCB_NONE, /* icon window */ XCB_NONE, /* icon x */ XCB_NONE, /* icon y */ mask, /* icon mask */ XCB_NONE /* group leader */ }; xcb_change_property(connection(), XCB_PROP_MODE_REPLACE, m_testWindow, XCB_ATOM_WM_HINTS, XCB_ATOM_WM_HINTS, 32, 9, values); xcb_flush(connection()); // only updated after event waitForPropertyChange(&info, XCB_ATOM_WM_HINTS, NET::Property(0), NET::WM2IconPixmap); QTEST(info.icccmIconPixmap(), "expectedPixmap"); QTEST(info.icccmIconPixmapMask(), "expectedMask"); } void NetWinInfoTestClient::testTransientFor() { QVERIFY(connection()); INFO QVERIFY(info.transientFor() == XCB_WINDOW_NONE); // transient for needs to be changed using xcb xcb_change_property(connection(), XCB_PROP_MODE_REPLACE, m_testWindow, XCB_ATOM_WM_TRANSIENT_FOR, XCB_ATOM_WINDOW, 32, 1, &m_rootWindow); xcb_flush(connection()); // only updated after event waitForPropertyChange(&info, XCB_ATOM_WM_TRANSIENT_FOR, NET::Property(0), NET::WM2TransientFor); QCOMPARE(info.transientFor(), m_rootWindow); } void NetWinInfoTestClient::testWindowClass() { QVERIFY(connection()); INFO QVERIFY(!info.windowClassClass()); QVERIFY(!info.windowClassName()); // window class needs to be changed using xcb xcb_change_property(connection(), XCB_PROP_MODE_REPLACE, m_testWindow, XCB_ATOM_WM_CLASS, XCB_ATOM_STRING, 8, 7, "foo\0bar"); xcb_flush(connection()); // only updated after event waitForPropertyChange(&info, XCB_ATOM_WM_CLASS, NET::Property(0), NET::WM2WindowClass); QCOMPARE(info.windowClassName(), "foo"); QCOMPARE(info.windowClassClass(), "bar"); } void NetWinInfoTestClient::testWindowRole() { QVERIFY(connection()); ATOM(WM_WINDOW_ROLE) INFO QVERIFY(!info.windowRole()); // window role needs to be changed using xcb xcb_change_property(connection(), XCB_PROP_MODE_REPLACE, m_testWindow, atom, XCB_ATOM_STRING, 8, 3, "bar"); xcb_flush(connection()); // only updated after event waitForPropertyChange(&info, atom, NET::Property(0), NET::WM2WindowRole); QCOMPARE(info.windowRole(), "bar"); } void NetWinInfoTestClient::testActivities_data() { QTest::addColumn("activities"); QTest::addColumn("expectedActivities"); const QByteArray testActivities = QByteArrayLiteral("foo,bar"); const QByteArray allActivities = QByteArrayLiteral(KDE_ALL_ACTIVITIES_UUID); QTest::newRow("activites") << testActivities << testActivities; QTest::newRow("empty") << QByteArray() << allActivities; QTest::newRow("\\0") << QByteArrayLiteral("\0") << allActivities; } void NetWinInfoTestClient::testActivities() { QVERIFY(connection()); ATOM(_KDE_NET_WM_ACTIVITIES) INFO QVERIFY(!info.activities()); QFETCH(QByteArray, activities); // activities needs to be changed using xcb info.setActivities(activities.isNull() ? nullptr : activities.constData()); xcb_flush(connection()); // only updated after event waitForPropertyChange(&info, atom, NET::Property(0), NET::WM2Activities); QTEST(QByteArray(info.activities()), "expectedActivities"); } Q_DECLARE_METATYPE(NET::Protocols) void NetWinInfoTestClient::testProtocols_data() { QTest::addColumn("protocols"); QTest::addColumn("takeFocus"); QTest::addColumn("deleteWindow"); QTest::addColumn("ping"); QTest::addColumn("sync"); QTest::addColumn("context"); const NET::Protocol t = NET::TakeFocusProtocol; const NET::Protocol d = NET::DeleteWindowProtocol; const NET::Protocol p = NET::PingProtocol; const NET::Protocol s = NET::SyncRequestProtocol; const NET::Protocol c = NET::ContextHelpProtocol; QTest::newRow("none") << NET::Protocols(NET::NoProtocol) << false << false << false << false << false; QTest::newRow("t") << NET::Protocols(t) << true << false << false << false << false; QTest::newRow("d") << NET::Protocols(d) << false << true << false << false << false; QTest::newRow("p") << NET::Protocols(p) << false << false << true << false << false; QTest::newRow("s") << NET::Protocols(s) << false << false << false << true << false; QTest::newRow("c") << NET::Protocols(c) << false << false << false << false << true; // all two combinations with t QTest::newRow("t/d") << NET::Protocols(t | d) << true << true << false << false << false; QTest::newRow("t/p") << NET::Protocols(t | p) << true << false << true << false << false; QTest::newRow("t/s") << NET::Protocols(t | s) << true << false << false << true << false; QTest::newRow("t/c") << NET::Protocols(t | c) << true << false << false << false << true; // all two combinations with d QTest::newRow("d/p") << NET::Protocols(d | p) << false << true << true << false << false; QTest::newRow("d/s") << NET::Protocols(d | s) << false << true << false << true << false; QTest::newRow("d/c") << NET::Protocols(d | c) << false << true << false << false << true; // all two combinations with p QTest::newRow("p/s") << NET::Protocols(p | s) << false << false << true << true << false; QTest::newRow("p/c") << NET::Protocols(p | c) << false << false << true << false << true; // and remaining two combination QTest::newRow("s/c") << NET::Protocols(s | c) << false << false << false << true << true; // all three combinations with t QTest::newRow("t/d/p") << NET::Protocols(t | d | p) << true << true << true << false << false; QTest::newRow("t/d/s") << NET::Protocols(t | d | s) << true << true << false << true << false; QTest::newRow("t/d/c") << NET::Protocols(t | d | c) << true << true << false << false << true; QTest::newRow("t/p/s") << NET::Protocols(t | p | s) << true << false << true << true << false; QTest::newRow("t/p/c") << NET::Protocols(t | p | c) << true << false << true << false << true; QTest::newRow("t/s/c") << NET::Protocols(t | s | c) << true << false << false << true << true; // all three combinations with d QTest::newRow("d/p/s") << NET::Protocols(d | p | s) << false << true << true << true << false; QTest::newRow("d/p/c") << NET::Protocols(d | p | c) << false << true << true << false << true; QTest::newRow("d/s/c") << NET::Protocols(d | s | c) << false << true << false << true << true; // and remaining QTest::newRow("p/s/c") << NET::Protocols(p | s | c) << false << false << true << true << true; QTest::newRow("t/d/p/s") << NET::Protocols(t | d | p | s) << true << true << true << true << false; QTest::newRow("t/d/p/c") << NET::Protocols(t | d | p | c) << true << true << true << false << true; QTest::newRow("t/d/s/c") << NET::Protocols(t | d | s | c) << true << true << false << true << true; QTest::newRow("t/p/s/c") << NET::Protocols(t | p | s | c) << true << false << true << true << true; QTest::newRow("d/p/s/c") << NET::Protocols(d | p | s | c) << false << true << true << true << true; QTest::newRow("all") << NET::Protocols(t | d | p | s | c) << true << true << true << true << true; } void NetWinInfoTestClient::testProtocols() { QVERIFY(connection()); ATOM(WM_PROTOCOLS) KXUtils::Atom takeFocus(connection(), QByteArrayLiteral("WM_TAKE_FOCUS")); KXUtils::Atom deleteWindow(connection(), QByteArrayLiteral("WM_DELETE_WINDOW")); KXUtils::Atom ping(connection(), QByteArrayLiteral("_NET_WM_PING")); KXUtils::Atom syncRequest(connection(), QByteArrayLiteral("_NET_WM_SYNC_REQUEST")); KXUtils::Atom contextHelp(connection(), QByteArrayLiteral("_NET_WM_CONTEXT_HELP")); INFO QVERIFY(!info.supportsProtocol(NET::TakeFocusProtocol)); QVERIFY(!info.supportsProtocol(NET::DeleteWindowProtocol)); QVERIFY(!info.supportsProtocol(NET::PingProtocol)); QVERIFY(!info.supportsProtocol(NET::SyncRequestProtocol)); QVERIFY(!info.supportsProtocol(NET::ContextHelpProtocol)); QCOMPARE(info.protocols(), NET::Protocols(NET::NoProtocol)); QVector props; QFETCH(NET::Protocols, protocols); if (protocols.testFlag(NET::TakeFocusProtocol)) { props << takeFocus; } if (protocols.testFlag(NET::DeleteWindowProtocol)) { props << deleteWindow; } if (protocols.testFlag(NET::PingProtocol)) { props << ping; } if (protocols.testFlag(NET::SyncRequestProtocol)) { props << syncRequest; } if (protocols.testFlag(NET::ContextHelpProtocol)) { props << contextHelp; } xcb_change_property(connection(), XCB_PROP_MODE_REPLACE, m_testWindow, atom, XCB_ATOM_ATOM, 32, props.size(), props.constData()); xcb_flush(connection()); // only updated after event waitForPropertyChange(&info, atom, NET::Property(0), NET::WM2Protocols); QCOMPARE(info.protocols(), protocols); QTEST(info.supportsProtocol(NET::TakeFocusProtocol), "takeFocus"); QTEST(info.supportsProtocol(NET::DeleteWindowProtocol), "deleteWindow"); QTEST(info.supportsProtocol(NET::PingProtocol), "ping"); QTEST(info.supportsProtocol(NET::SyncRequestProtocol), "sync"); QTEST(info.supportsProtocol(NET::ContextHelpProtocol), "context"); xcb_delete_property(connection(), m_testWindow, atom); xcb_flush(connection()); waitForPropertyChange(&info, atom, NET::Property(0), NET::WM2Protocols); QVERIFY(!info.supportsProtocol(NET::TakeFocusProtocol)); QVERIFY(!info.supportsProtocol(NET::DeleteWindowProtocol)); QVERIFY(!info.supportsProtocol(NET::PingProtocol)); QVERIFY(!info.supportsProtocol(NET::SyncRequestProtocol)); QVERIFY(!info.supportsProtocol(NET::ContextHelpProtocol)); QCOMPARE(info.protocols(), NET::Protocols(NET::NoProtocol)); } void NetWinInfoTestClient::testOpaqueRegion_data() { QTest::addColumn >("geometries"); QTest::newRow("none") << QVector(); QTest::newRow("empty") << QVector({QRect(0, 0, 0, 0)}); QTest::newRow("one rect") << QVector({QRect(10, 20, 30, 40)}); QTest::newRow("two rect") << QVector({QRect(10, 20, 30, 40), QRect(1, 2, 4, 5)}); QTest::newRow("multiple") << QVector({QRect(10, 20, 30, 40), QRect(1, 2, 4, 5), QRect(100, 0, 200, 400), QRect(1, 2, 4, 5)}); } void NetWinInfoTestClient::testOpaqueRegion() { QVERIFY(connection()); ATOM(_NET_WM_OPAQUE_REGION) INFO QCOMPARE(info.opaqueRegion().size(), std::size_t(0)); QFETCH(QVector, geometries); QVector data; for (auto it = geometries.constBegin(); it != geometries.constEnd(); ++it) { const QRect &r = *it; data << r.x(); data << r.y(); data << r.width(); data << r.height(); } xcb_change_property(connection(), XCB_PROP_MODE_REPLACE, m_testWindow, atom, XCB_ATOM_CARDINAL, 32, data.size(), data.constData()); xcb_flush(connection()); // only updated after event waitForPropertyChange(&info, atom, NET::Property(0), NET::WM2OpaqueRegion); const auto opaqueRegion = info.opaqueRegion(); QCOMPARE(opaqueRegion.size(), std::size_t(geometries.size())); for (std::size_t i = 0; i < opaqueRegion.size(); ++i) { auto r1 = opaqueRegion.at(i); auto r2 = geometries.at(i); QCOMPARE(r1.pos.x, r2.x()); QCOMPARE(r1.pos.y, r2.y()); QCOMPARE(r1.size.width, r2.width()); QCOMPARE(r1.size.height, r2.height()); } xcb_delete_property(connection(), m_testWindow, atom); xcb_flush(connection()); waitForPropertyChange(&info, atom, NET::Property(0), NET::WM2OpaqueRegion); QCOMPARE(info.opaqueRegion().size(), std::size_t(0)); } QTEST_GUILESS_MAIN(NetWinInfoTestClient) #include "netwininfotestclient.moc" diff --git a/src/netwm_def.h b/src/netwm_def.h index 864e0ce..0740b9a 100644 --- a/src/netwm_def.h +++ b/src/netwm_def.h @@ -1,816 +1,822 @@ /* Copyright (c) 2000 Troll Tech AS Copyright (c) 2003 Lubos Lunak Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef netwm_def_h #define netwm_def_h #include #include /** Simple point class for NET classes. This class is a convenience class defining a point x, y. The existence of this class is to keep the implementation from being dependent on a separate framework/library. NETPoint is only used by the NET API. Usually QPoint is the appropriate class for representing a point. @author Bradley T. Hughes **/ struct NETPoint { /** Constructor to initialize this point to 0,0. **/ NETPoint() : x(0), y(0) { } /* Public data member. **/ int x, ///< x coordinate. y; ///< y coordinate }; /** Simple size class for NET classes. This class is a convenience class defining a size width by height. The existence of this class is to keep the implementation from being dependent on a separate framework/library. NETSize is only used by the NET API. Usually QSize is the appropriate class for representing a size. @author Bradley T. Hughes **/ struct NETSize { /** Constructor to initialize this size to 0x0 **/ NETSize() : width(0), height(0) { } /* Public data member. **/ int width, ///< Width. height; ///< Height. }; /** Simple rectangle class for NET classes. This class is a convenience class defining a rectangle as a point x,y with a size width by height. The existence of this class is to keep the implementation from being dependent on a separate framework/library; NETRect is only used by the NET API. Usually QRect is the appropriate class for representing a rectangle. **/ struct NETRect { /** Position of the rectangle. @see NETPoint **/ NETPoint pos; /** Size of the rectangle. @see NETSize **/ NETSize size; }; /** Simple icon class for NET classes. This class is a convenience class defining an icon of size width by height. The existence of this class is to keep the implementation from being dependent on a separate framework/library. NETIcon is only used by the NET API. Usually QIcon is the appropriate class for representing an icon. **/ struct NETIcon { /** Constructor to initialize this icon to 0x0 with data=0 **/ NETIcon() : data(nullptr) { } /** Size of the icon. @see NETSize **/ NETSize size; /** Image data for the icon. This is an array of 32bit packed CARDINAL ARGB with high byte being A, low byte being B. First two bytes are width, height. Data is in rows, left to right and top to bottom. **/ unsigned char *data; }; /** Partial strut class for NET classes. This class is a convenience class defining a strut with left, right, top and bottom border values, and ranges for them. The existence of this class is to keep the implementation from being dependent on a separate framework/library. See the _NET_WM_STRUT_PARTIAL property in the NETWM spec. **/ struct NETExtendedStrut { /** Constructor to initialize this struct to 0,0,0,0 **/ NETExtendedStrut() : left_width(0), left_start(0), left_end(0), right_width(0), right_start(0), right_end(0), top_width(0), top_start(0), top_end(0), bottom_width(0), bottom_start(0), bottom_end(0) {} /** Left border of the strut, width and range. **/ int left_width, left_start, left_end; /** Right border of the strut, width and range. **/ int right_width, right_start, right_end; /** Top border of the strut, width and range. **/ int top_width, top_start, top_end; /** Bottom border of the strut, width and range. **/ int bottom_width, bottom_start, bottom_end; }; /** @deprecated use NETExtendedStrut Simple strut class for NET classes. This class is a convenience class defining a strut with left, right, top and bottom border values. The existence of this class is to keep the implementation from being dependent on a separate framework/library. See the _NET_WM_STRUT property in the NETWM spec. **/ struct NETStrut { /** Constructor to initialize this struct to 0,0,0,0 **/ NETStrut() : left(0), right(0), top(0), bottom(0) { } /** Left border of the strut. **/ int left; /** Right border of the strut. **/ int right; /** Top border of the strut. **/ int top; /** Bottom border of the strut. **/ int bottom; }; /** Simple multiple monitor topology class for NET classes. This class is a convenience class, defining a multiple monitor topology for fullscreen applications that wish to be present on more than one monitor/head. As per the _NET_WM_FULLSCREEN_MONITORS hint in the EWMH spec, this topology consists of 4 monitor indices such that the bounding rectangle is defined by the top edge of the top monitor, the bottom edge of the bottom monitor, the left edge of the left monitor, and the right edge of the right monitor. See the _NET_WM_FULLSCREEN_MONITORS hint in the EWMH spec. **/ struct NETFullscreenMonitors { /** Constructor to initialize this struct to -1,0,0,0 (an initialized, albeit invalid, topology). **/ NETFullscreenMonitors() : top(-1), bottom(0), left(0), right(0) { } /** Monitor index whose top border defines the top edge of the topology. **/ int top; /** Monitor index whose bottom border defines the bottom edge of the topology. **/ int bottom; /** Monitor index whose left border defines the left edge of the topology. **/ int left; /** Monitor index whose right border defines the right edge of the topology. **/ int right; /** Convenience check to make sure that we are not holding the initial (invalid) values. Note that we don't want to call this isValid() because we're not actually validating the monitor topology here, but merely that our initial values were overwritten at some point by real (non-negative) monitor indices. **/ bool isSet() const { return (top != -1); } }; /** Base namespace class. The NET API is an implementation of the NET Window Manager Specification. This class is the base class for the NETRootInfo and NETWinInfo classes, which are used to retrieve and modify the properties of windows. To keep the namespace relatively clean, all enums are defined here. @see http://www.freedesktop.org/standards/wm-spec/ **/ class KWINDOWSYSTEM_EXPORT NET { public: /** Application role. This is used internally to determine how several action should be performed (if at all). **/ enum Role { /** indicates that the application is a client application. **/ Client, /** indicates that the application is a window manager application. **/ WindowManager }; /** Window type. **/ enum WindowType { /** indicates that the window did not define a window type. **/ Unknown = -1, /** indicates that this is a normal, top-level window **/ Normal = 0, /** indicates a desktop feature. This can include a single window containing desktop icons with the same dimensions as the screen, allowing the desktop environment to have full control of the desktop, without the need for proxying root window clicks. **/ Desktop = 1, /** indicates a dock or panel feature **/ Dock = 2, /** indicates a toolbar window **/ Toolbar = 3, /** indicates a pinnable (torn-off) menu window **/ Menu = 4, /** indicates that this is a dialog window **/ Dialog = 5, /** @deprecated has unclear meaning and is KDE-only **/ Override = 6, // NON STANDARD /** indicates a toplevel menu (AKA macmenu). This is a KDE extension to the _NET_WM_WINDOW_TYPE mechanism. **/ TopMenu = 7, // NON STANDARD /** indicates a utility window **/ Utility = 8, /** indicates that this window is a splash screen window. **/ Splash = 9, /** indicates a dropdown menu (from a menubar typically) **/ DropdownMenu = 10, /** indicates a popup menu (a context menu typically) **/ PopupMenu = 11, /** indicates a tooltip window **/ Tooltip = 12, /** indicates a notification window **/ Notification = 13, /** indicates that the window is a list for a combobox **/ ComboBox = 14, /** indicates a window that represents the dragged object during DND operation **/ DNDIcon = 15, /** indicates an On Screen Display window (such as volume feedback) @since 5.6 **/ - OnScreenDisplay = 16 // NON STANDARD + OnScreenDisplay = 16, // NON STANDARD + /** + indicates a critical notification (such as battery is running out) + @since 5.58 + **/ + CriticalNotification = 17, // NON STANDARD }; /** Values for WindowType when they should be OR'ed together, e.g. for the properties argument of the NETRootInfo constructor. **/ enum WindowTypeMask { NormalMask = 1u << 0, ///< @see Normal DesktopMask = 1u << 1, ///< @see Desktop DockMask = 1u << 2, ///< @see Dock ToolbarMask = 1u << 3, ///< @see Toolbar MenuMask = 1u << 4, ///< @see Menu DialogMask = 1u << 5, ///< @see Dialog OverrideMask = 1u << 6, ///< @see Override TopMenuMask = 1u << 7, ///< @see TopMenu UtilityMask = 1u << 8, ///< @see Utility SplashMask = 1u << 9, ///< @see Splash DropdownMenuMask = 1u << 10, ///< @see DropdownMenu PopupMenuMask = 1u << 11, ///< @see PopupMenu TooltipMask = 1u << 12, ///< @see Tooltip NotificationMask = 1u << 13, ///< @see Notification ComboBoxMask = 1u << 14, ///< @see ComboBox DNDIconMask = 1u << 15, ///< @see DNDIcon OnScreenDisplayMask = 1u << 16, ///< NON STANDARD @see OnScreenDisplay @since 5.6 + CriticalNotificationMask = 1u << 17, ///< NON STANDARD @see CriticalNotification @since 5.58 AllTypesMask = 0U - 1 ///< All window types. }; Q_DECLARE_FLAGS(WindowTypes, WindowTypeMask) /** * Returns true if the given window type matches the mask given * using WindowTypeMask flags. */ static bool typeMatchesMask(WindowType type, WindowTypes mask); /** Window state. To set the state of a window, you'll typically do something like: \code KWindowSystem::setState( winId(), NET::SkipTaskbar | NET::SkipPager | NET::SkipSwitcher ); \endcode for example to not show the window on the taskbar, desktop pager, or window switcher. winId() is a function of QWidget() Note that KeepAbove (StaysOnTop) and KeepBelow are meant as user preference and applications should avoid setting these states themselves. **/ enum State { /** indicates that this is a modal dialog box. The WM_TRANSIENT_FOR hint MUST be set to indicate which window the dialog is a modal for, or set to the root window if the dialog is a modal for its window group. **/ Modal = 1u << 0, /** indicates that the Window Manager SHOULD keep the window's position fixed on the screen, even when the virtual desktop scrolls. Note that this is different from being kept on all desktops. **/ Sticky = 1u << 1, /** indicates that the window is vertically maximized. **/ MaxVert = 1u << 2, /** indicates that the window is horizontally maximized. **/ MaxHoriz = 1u << 3, /** convenience value. Equal to MaxVert | MaxHoriz. **/ Max = MaxVert | MaxHoriz, /** indicates that the window is shaded (rolled-up). **/ Shaded = 1u << 4, /** indicates that a window should not be included on a taskbar. **/ SkipTaskbar = 1u << 5, /** indicates that a window should on top of most windows (but below fullscreen windows). **/ KeepAbove = 1u << 6, /** @deprecated This is an obsolete name for KeepAbove. **/ StaysOnTop = KeepAbove, // NOT STANDARD /** indicates that a window should not be included on a pager. **/ SkipPager = 1u << 7, /** indicates that a window should not be visible on the screen (e.g. when minimised). Only the window manager is allowed to change it. **/ Hidden = 1u << 8, /** indicates that a window should fill the entire screen and have no window decorations. **/ FullScreen = 1u << 9, /** indicates that a window should be below most windows (but above any desktop windows). **/ KeepBelow = 1u << 10, /** there was an attempt to activate this window, but the window manager prevented this. E.g. taskbar should mark such window specially to bring user's attention to this window. Only the window manager is allowed to change it. **/ DemandsAttention = 1u << 11, /** indicates that a window should not be included on a switcher. @since 5.45 **/ SkipSwitcher = 1u << 12, /** indicates that a client should render as though it has focus Only the window manager is allowed to change it. @since 5.58 **/ Focused = 1u << 13, }; Q_DECLARE_FLAGS(States, State) /** Direction for WMMoveResize. When a client wants the Window Manager to start a WMMoveResize, it should specify one of: @li TopLeft @li Top @li TopRight @li Right @li BottomRight @li Bottom @li BottomLeft @li Left @li Move (for movement only) @li KeyboardSize (resizing via keyboard) @li KeyboardMove (movement via keyboard) **/ enum Direction { TopLeft = 0, Top = 1, TopRight = 2, Right = 3, BottomRight = 4, Bottom = 5, BottomLeft = 6, Left = 7, Move = 8, // movement only KeyboardSize = 9, // size via keyboard KeyboardMove = 10, // move via keyboard MoveResizeCancel = 11 // to ask the WM to stop moving a window }; /** Client window mapping state. The class automatically watches the mapping state of the client windows, and uses the mapping state to determine how to set/change different properties. Note that this is very lowlevel and you most probably don't want to use this state. **/ enum MappingState { /** indicates the client window is visible to the user. **/ Visible = 1, //NormalState, /** indicates that neither the client window nor its icon is visible. **/ Withdrawn = 0, //WithdrawnState, /** indicates that the client window is not visible, but its icon is. This can be when the window is minimized or when it's on a different virtual desktop. See also NET::Hidden. **/ Iconic = 3 // IconicState }; /** Actions that can be done with a window (_NET_WM_ALLOWED_ACTIONS). **/ enum Action { ActionMove = 1u << 0, ActionResize = 1u << 1, ActionMinimize = 1u << 2, ActionShade = 1u << 3, ActionStick = 1u << 4, ActionMaxVert = 1u << 5, ActionMaxHoriz = 1u << 6, ActionMax = ActionMaxVert | ActionMaxHoriz, ActionFullScreen = 1u << 7, ActionChangeDesktop = 1u << 8, ActionClose = 1u << 9 }; Q_DECLARE_FLAGS(Actions, Action) /** Supported properties. Clients and Window Managers must define which properties/protocols it wants to support. Root/Desktop window properties and protocols: @li Supported @li ClientList @li ClientListStacking @li NumberOfDesktops @li DesktopGeometry @li DesktopViewport @li CurrentDesktop @li DesktopNames @li ActiveWindow @li WorkArea @li SupportingWMCheck @li VirtualRoots @li CloseWindow @li WMMoveResize Client window properties and protocols: @li WMName @li WMVisibleName @li WMDesktop @li WMWindowType @li WMState @li WMStrut (obsoleted by WM2ExtendedStrut) @li WMGeometry @li WMFrameExtents @li WMIconGeometry @li WMIcon @li WMIconName @li WMVisibleIconName @li WMHandledIcons @li WMPid @li WMPing ICCCM properties (provided for convenience): @li XAWMState **/ enum Property { // root Supported = 1u << 0, ClientList = 1u << 1, ClientListStacking = 1u << 2, NumberOfDesktops = 1u << 3, DesktopGeometry = 1u << 4, DesktopViewport = 1u << 5, CurrentDesktop = 1u << 6, DesktopNames = 1u << 7, ActiveWindow = 1u << 8, WorkArea = 1u << 9, SupportingWMCheck = 1u << 10, VirtualRoots = 1u << 11, // CloseWindow = 1u << 13, WMMoveResize = 1u << 14, // window WMName = 1u << 15, WMVisibleName = 1u << 16, WMDesktop = 1u << 17, WMWindowType = 1u << 18, WMState = 1u << 19, WMStrut = 1u << 20, WMIconGeometry = 1u << 21, WMIcon = 1u << 22, WMPid = 1u << 23, WMHandledIcons = 1u << 24, WMPing = 1u << 25, XAWMState = 1u << 27, WMFrameExtents = 1u << 28, // Need to be reordered WMIconName = 1u << 29, WMVisibleIconName = 1u << 30, WMGeometry = 1u << 31, WMAllProperties = ~0u }; Q_DECLARE_FLAGS(Properties, Property) /** Supported properties. This enum is an extension to NET::Property, because them enum is limited only to 32 bits. Client window properties and protocols: @li WM2UserTime @li WM2StartupId @li WM2TransientFor mainwindow for the window (WM_TRANSIENT_FOR) @li WM2GroupLeader group leader (window_group in WM_HINTS) @li WM2AllowedActions @li WM2RestackWindow @li WM2MoveResizeWindow @li WM2ExtendedStrut @li WM2TemporaryRules internal, for kstart @li WM2WindowClass WM_CLASS @li WM2WindowRole WM_WINDOW_ROLE @li WM2ClientMachine WM_CLIENT_MACHINE @li WM2ShowingDesktop @li WM2Opacity _NET_WM_WINDOW_OPACITY @li WM2DesktopLayout _NET_DESKTOP_LAYOUT @li WM2FullPlacement _NET_WM_FULL_PLACEMENT @li WM2FullscreenMonitors _NET_WM_FULLSCREEN_MONITORS @li WM2Urgency urgency hint in WM_HINTS (see ICCCM 4.1.2.4) @li WM2Input input hint (input in WM_HINTS, see ICCCM 4.1.2.4) @li WM2Protocols see NET::Protocol @li WM2InitialMappingState initial state hint of WM_HINTS (see ICCCM 4.1.2.4) @li WM2IconPixmap icon pixmap and mask in WM_HINTS (see ICCCM 4.1.2.4) @li WM2OpaqueRegion @li WM2DesktopFileName the base name of the desktop file name or the full path to the desktop file **/ enum Property2 { WM2UserTime = 1u << 0, WM2StartupId = 1u << 1, WM2TransientFor = 1u << 2, WM2GroupLeader = 1u << 3, WM2AllowedActions = 1u << 4, WM2RestackWindow = 1u << 5, WM2MoveResizeWindow = 1u << 6, WM2ExtendedStrut = 1u << 7, WM2KDETemporaryRules = 1u << 8, // NOT STANDARD WM2WindowClass = 1u << 9, WM2WindowRole = 1u << 10, WM2ClientMachine = 1u << 11, WM2ShowingDesktop = 1u << 12, WM2Opacity = 1u << 13, WM2DesktopLayout = 1u << 14, WM2FullPlacement = 1u << 15, WM2FullscreenMonitors = 1u << 16, WM2FrameOverlap = 1u << 17, // NOT STANDARD WM2Activities = 1u << 18, // NOT STANDARD @since 4.6 WM2BlockCompositing = 1u << 19, // NOT STANDARD @since 4.7, STANDARD @since 5.17 WM2KDEShadow = 1u << 20, // NOT Standard @since 4.7 WM2Urgency = 1u << 21, // @since 5.3 WM2Input = 1u << 22, // @since 5.3 WM2Protocols = 1u << 23, // @since 5.3 WM2InitialMappingState = 1u << 24, // @since 5.5 WM2IconPixmap = 1u << 25, // @since 5.7 WM2OpaqueRegion = 1u << 25, // @since 5.7 WM2DesktopFileName = 1u << 26, // NOT STANDARD @since 5.28 WM2AllProperties = ~0u }; Q_DECLARE_FLAGS(Properties2, Property2) /** Sentinel value to indicate that the client wishes to be visible on all desktops. **/ enum { OnAllDesktops = -1 }; /** Source of the request. **/ // must match the values for data.l[0] field in _NET_ACTIVE_WINDOW message enum RequestSource { /** @internal indicates that the source of the request is unknown **/ FromUnknown = 0, // internal /** indicates that the request comes from a normal application **/ FromApplication = 1, /** indicated that the request comes from pager or similar tool **/ FromTool = 2 }; /** Orientation. **/ enum Orientation { OrientationHorizontal = 0, OrientationVertical = 1 }; /** Starting corner for desktop layout. **/ enum DesktopLayoutCorner { DesktopLayoutCornerTopLeft = 0, DesktopLayoutCornerTopRight = 1, DesktopLayoutCornerBottomLeft = 2, DesktopLayoutCornerBottomRight = 3 }; /** * Protocols supported by the client. * See ICCCM 4.1.2.7. * * @since 5.3 **/ enum Protocol { NoProtocol = 0, TakeFocusProtocol = 1 << 0, ///< WM_TAKE_FOCUS DeleteWindowProtocol = 1 << 1, ///< WM_DELETE_WINDOW PingProtocol = 1 << 2, ///< _NET_WM_PING from EWMH SyncRequestProtocol = 1 << 3, ///< _NET_WM_SYNC_REQUEST from EWMH ContextHelpProtocol = 1 << 4 ///< _NET_WM_CONTEXT_HELP, NON STANDARD! }; Q_DECLARE_FLAGS(Protocols, Protocol) /** Compares two X timestamps, taking into account wrapping and 64bit architectures. Return value is like with strcmp(), 0 for equal, -1 for time1 < time2, 1 for time1 > time2. */ static int timestampCompare(unsigned long time1, unsigned long time2); /** Returns a difference of two X timestamps, time2 - time1, where time2 must be later than time1, as returned by timestampCompare(). */ static int timestampDiff(unsigned long time1, unsigned long time2); }; Q_DECLARE_OPERATORS_FOR_FLAGS(NET::Properties) Q_DECLARE_OPERATORS_FOR_FLAGS(NET::Properties2) Q_DECLARE_OPERATORS_FOR_FLAGS(NET::WindowTypes) Q_DECLARE_OPERATORS_FOR_FLAGS(NET::States) Q_DECLARE_OPERATORS_FOR_FLAGS(NET::Actions) Q_DECLARE_OPERATORS_FOR_FLAGS(NET::Protocols) #endif // netwm_def_h diff --git a/src/platforms/xcb/atoms_p.h b/src/platforms/xcb/atoms_p.h index e1d7914..f7ef339 100644 --- a/src/platforms/xcb/atoms_p.h +++ b/src/platforms/xcb/atoms_p.h @@ -1,179 +1,180 @@ /* * Copyright 2015 Thomas Lübking * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #if (!defined ATOMS_H) || (defined ENUM_CREATE_CHAR_ARRAY) #undef ENUM_BEGIN #undef ENUM #undef ENUM_END #undef ENUM_COUNT // the following macros are set in a way so that // the code below will either construct an enum for "" // or a const *char array "Strings" containing all enum // symbols as strings, depending on whether ENUM_CREATE_CHAR_ARRAY is // defined // The enum gets one extra item "Count", describing also the // length of the array // The header is safe for re-inclusion unless you define ENUM_CREATE_CHAR_ARRAY // which is therefore undefined after usage // => You *must* "#define ENUM_CREATE_CHAR_ARRAY 1" *every* time you want to create // a string array! #ifndef ENUM_CREATE_CHAR_ARRAY #define ATOMS_H #define ENUM_BEGIN(typ) enum typ { #define ENUM(nam) nam #define ENUM_COUNT(typ) , typ##Count #else #define ENUM_BEGIN(typ) const char * typ##Strings [] = { #define ENUM(nam) #nam #define ENUM_COUNT(typ) #undef ENUM_CREATE_CHAR_ARRAY #endif #define ENUM_END(typ) }; ENUM_BEGIN(KwsAtom) ENUM(UTF8_STRING), // root window properties ENUM(_NET_SUPPORTED), ENUM(_NET_SUPPORTING_WM_CHECK), ENUM(_NET_CLIENT_LIST), ENUM(_NET_CLIENT_LIST_STACKING), ENUM(_NET_NUMBER_OF_DESKTOPS), ENUM(_NET_DESKTOP_GEOMETRY), ENUM(_NET_DESKTOP_VIEWPORT), ENUM(_NET_CURRENT_DESKTOP), ENUM(_NET_DESKTOP_NAMES), ENUM(_NET_ACTIVE_WINDOW), ENUM(_NET_WORKAREA), ENUM(_NET_VIRTUAL_ROOTS), ENUM(_NET_DESKTOP_LAYOUT), ENUM(_NET_SHOWING_DESKTOP), // root window messages ENUM(_NET_CLOSE_WINDOW), ENUM(_NET_RESTACK_WINDOW), ENUM(_NET_WM_MOVERESIZE), ENUM(_NET_MOVERESIZE_WINDOW), // application window properties ENUM(_NET_WM_NAME), ENUM(_NET_WM_VISIBLE_NAME), ENUM(_NET_WM_ICON_NAME), ENUM(_NET_WM_VISIBLE_ICON_NAME), ENUM(_NET_WM_DESKTOP), ENUM(_NET_WM_WINDOW_TYPE), ENUM(_NET_WM_STATE), ENUM(_NET_WM_STRUT), ENUM(_NET_WM_STRUT_PARTIAL), ENUM(_NET_WM_ICON_GEOMETRY), ENUM(_NET_WM_ICON), ENUM(_NET_WM_PID), ENUM(_NET_WM_USER_TIME), ENUM(_NET_WM_HANDLED_ICONS), ENUM(_NET_STARTUP_ID), ENUM(_NET_WM_ALLOWED_ACTIONS), ENUM(WM_WINDOW_ROLE), ENUM(_NET_FRAME_EXTENTS), ENUM(_NET_WM_WINDOW_OPACITY), ENUM(_NET_WM_FULLSCREEN_MONITORS), ENUM(_NET_WM_OPAQUE_REGION), ENUM(_KDE_NET_WM_DESKTOP_FILE), // used to determine whether application window is managed or not ENUM(WM_STATE), // application window types ENUM(_NET_WM_WINDOW_TYPE_NORMAL), ENUM(_NET_WM_WINDOW_TYPE_DESKTOP), ENUM(_NET_WM_WINDOW_TYPE_DOCK), ENUM(_NET_WM_WINDOW_TYPE_TOOLBAR), ENUM(_NET_WM_WINDOW_TYPE_MENU), ENUM(_NET_WM_WINDOW_TYPE_DIALOG), ENUM(_NET_WM_WINDOW_TYPE_UTILITY), ENUM(_NET_WM_WINDOW_TYPE_SPLASH), ENUM(_NET_WM_WINDOW_TYPE_DROPDOWN_MENU), ENUM(_NET_WM_WINDOW_TYPE_POPUP_MENU), ENUM(_NET_WM_WINDOW_TYPE_TOOLTIP), ENUM(_NET_WM_WINDOW_TYPE_NOTIFICATION), ENUM(_NET_WM_WINDOW_TYPE_COMBO), ENUM(_NET_WM_WINDOW_TYPE_DND), // application window state ENUM(_NET_WM_STATE_MODAL), ENUM(_NET_WM_STATE_STICKY), ENUM(_NET_WM_STATE_MAXIMIZED_VERT), ENUM(_NET_WM_STATE_MAXIMIZED_HORZ), ENUM(_NET_WM_STATE_SHADED), ENUM(_NET_WM_STATE_SKIP_TASKBAR), ENUM(_NET_WM_STATE_SKIP_PAGER), ENUM(_NET_WM_STATE_HIDDEN), ENUM(_NET_WM_STATE_FULLSCREEN), ENUM(_NET_WM_STATE_ABOVE), ENUM(_NET_WM_STATE_BELOW), ENUM(_NET_WM_STATE_DEMANDS_ATTENTION), ENUM(_NET_WM_STATE_FOCUSED), // KDE-specific atom ENUM(_KDE_NET_WM_STATE_SKIP_SWITCHER), // allowed actions ENUM(_NET_WM_ACTION_MOVE), ENUM(_NET_WM_ACTION_RESIZE), ENUM(_NET_WM_ACTION_MINIMIZE), ENUM(_NET_WM_ACTION_SHADE), ENUM(_NET_WM_ACTION_STICK), ENUM(_NET_WM_ACTION_MAXIMIZE_VERT), ENUM(_NET_WM_ACTION_MAXIMIZE_HORZ), ENUM(_NET_WM_ACTION_FULLSCREEN), ENUM(_NET_WM_ACTION_CHANGE_DESKTOP), ENUM(_NET_WM_ACTION_CLOSE), // KDE extensions ENUM(_KDE_NET_WM_FRAME_STRUT), ENUM(_KDE_NET_WM_WINDOW_TYPE_OVERRIDE), ENUM(_KDE_NET_WM_WINDOW_TYPE_TOPMENU), ENUM(_KDE_NET_WM_WINDOW_TYPE_ON_SCREEN_DISPLAY), + ENUM(_KDE_NET_WM_WINDOW_TYPE_CRITICAL_NOTIFICATION), ENUM(_KDE_NET_WM_TEMPORARY_RULES), ENUM(_NET_WM_FRAME_OVERLAP), // deprecated and naming convention violation ENUM(_NET_WM_STATE_STAYS_ON_TOP), // application protocols ENUM(WM_PROTOCOLS), ENUM(WM_TAKE_FOCUS), ENUM(WM_DELETE_WINDOW), ENUM(_NET_WM_PING), ENUM(_NET_WM_SYNC_REQUEST), ENUM(_NET_WM_CONTEXT_HELP), // ability flags ENUM(_NET_WM_FULL_PLACEMENT), ENUM(_NET_WM_BYPASS_COMPOSITOR), ENUM(_KDE_NET_WM_ACTIVITIES), ENUM(_KDE_NET_WM_BLOCK_COMPOSITING), ENUM(_KDE_NET_WM_SHADOW) ENUM_COUNT(KwsAtom) ENUM_END(KwsAtom) #endif // ATOMS_H diff --git a/src/platforms/xcb/netwm.cpp b/src/platforms/xcb/netwm.cpp index 9fc1f73..2fc6225 100644 --- a/src/platforms/xcb/netwm.cpp +++ b/src/platforms/xcb/netwm.cpp @@ -1,4902 +1,4918 @@ /* Copyright (c) 2000 Troll Tech AS Copyright (c) 2003 Lubos Lunak Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ //#define NETWMDEBUG #include "netwm.h" #include #include #include "netwm_p.h" #include "atoms_p.h" #include #if KWINDOWSYSTEM_HAVE_X11 //FIXME #include #include #include #include #include #include #include // This struct is defined here to avoid a dependency on xcb-icccm struct kde_wm_hints { uint32_t flags; uint32_t input; int32_t initial_state; xcb_pixmap_t icon_pixmap; xcb_window_t icon_window; int32_t icon_x; int32_t icon_y; xcb_pixmap_t icon_mask; xcb_window_t window_group; }; typedef QHash< xcb_connection_t*, QSharedDataPointer > AtomHash; Q_GLOBAL_STATIC(AtomHash, s_gAtomsHash) static QSharedDataPointer atomsForConnection(xcb_connection_t *c) { auto it = s_gAtomsHash->constFind(c); if (it == s_gAtomsHash->constEnd()) { QSharedDataPointer atom(new Atoms(c)); s_gAtomsHash->insert(c, atom); return atom; } return it.value(); } Atoms::Atoms(xcb_connection_t *c) : QSharedData() , m_connection(c) { for (int i = 0; i < KwsAtomCount; ++i) { m_atoms[i] = XCB_ATOM_NONE; } init(); } static const uint32_t netwm_sendevent_mask = (XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY); const long MAX_PROP_SIZE = 100000; static char *nstrdup(const char *s1) { if (! s1) { return (char *) nullptr; } int l = strlen(s1) + 1; char *s2 = new char[l]; strncpy(s2, s1, l); return s2; } static char *nstrndup(const char *s1, int l) { if (! s1 || l == 0) { return (char *) nullptr; } char *s2 = new char[l + 1]; strncpy(s2, s1, l); s2[l] = '\0'; return s2; } static xcb_window_t *nwindup(const xcb_window_t *w1, int n) { if (! w1 || n == 0) { return (xcb_window_t *) nullptr; } xcb_window_t *w2 = new xcb_window_t[n]; while (n--) { w2[n] = w1[n]; } return w2; } static void refdec_nri(NETRootInfoPrivate *p) { #ifdef NETWMDEBUG fprintf(stderr, "NET: decrementing NETRootInfoPrivate::ref (%d)\n", p->ref - 1); #endif if (! --p->ref) { #ifdef NETWMDEBUG fprintf(stderr, "NET: \tno more references, deleting\n"); #endif delete [] p->name; delete [] p->stacking; delete [] p->clients; delete [] p->virtual_roots; delete [] p->temp_buf; int i; for (i = 0; i < p->desktop_names.size(); i++) { delete [] p->desktop_names[i]; } } } static void refdec_nwi(NETWinInfoPrivate *p) { #ifdef NETWMDEBUG fprintf(stderr, "NET: decrementing NETWinInfoPrivate::ref (%d)\n", p->ref - 1); #endif if (! --p->ref) { #ifdef NETWMDEBUG fprintf(stderr, "NET: \tno more references, deleting\n"); #endif delete [] p->name; delete [] p->visible_name; delete [] p->window_role; delete [] p->icon_name; delete [] p->visible_icon_name; delete [] p->startup_id; delete [] p->class_class; delete [] p->class_name; delete [] p->activities; delete [] p->client_machine; delete [] p->desktop_file; int i; for (i = 0; i < p->icons.size(); i++) { delete [] p->icons[i].data; } delete [] p->icon_sizes; } } template T get_value_reply(xcb_connection_t *c, const xcb_get_property_cookie_t cookie, xcb_atom_t type, T def, bool *success = nullptr) { T value = def; xcb_get_property_reply_t *reply = xcb_get_property_reply(c, cookie, nullptr); if (success) { *success = false; } if (reply) { if (reply->type == type && reply->value_len == 1 && reply->format == sizeof(T) * 8) { value = *reinterpret_cast(xcb_get_property_value(reply)); if (success) { *success = true; } } free(reply); } return value; } template QVector get_array_reply(xcb_connection_t *c, const xcb_get_property_cookie_t cookie, xcb_atom_t type) { xcb_get_property_reply_t *reply = xcb_get_property_reply(c, cookie, nullptr); if (!reply) { return QVector(); } QVector vector; if (reply->type == type && reply->value_len > 0 && reply->format == sizeof(T) * 8) { T *data = reinterpret_cast(xcb_get_property_value(reply)); vector.resize(reply->value_len); memcpy((void *)&vector.first(), (void *)data, reply->value_len * sizeof(T)); } free(reply); return vector; } static QByteArray get_string_reply(xcb_connection_t *c, const xcb_get_property_cookie_t cookie, xcb_atom_t type) { xcb_get_property_reply_t *reply = xcb_get_property_reply(c, cookie, nullptr); if (!reply) { return QByteArray(); } QByteArray value; if (reply->type == type && reply->format == 8 && reply->value_len > 0) { const char *data = (const char *) xcb_get_property_value(reply); int len = reply->value_len; if (data) { value = QByteArray(data, data[len - 1] ? len : len - 1); } } free(reply); return value; } static QList get_stringlist_reply(xcb_connection_t *c, const xcb_get_property_cookie_t cookie, xcb_atom_t type) { xcb_get_property_reply_t *reply = xcb_get_property_reply(c, cookie, nullptr); if (!reply) { return QList(); } QList list; if (reply->type == type && reply->format == 8 && reply->value_len > 0) { const char *data = (const char *) xcb_get_property_value(reply); int len = reply->value_len; if (data) { const QByteArray ba = QByteArray(data, data[len - 1] ? len : len - 1); list = ba.split('\0'); } } free(reply); return list; } #ifdef NETWMDEBUG static QByteArray get_atom_name(xcb_connection_t *c, xcb_atom_t atom) { const xcb_get_atom_name_cookie_t cookie = xcb_get_atom_name(c, atom); xcb_get_atom_name_reply_t *reply = xcb_get_atom_name_reply(c, cookie, 0); if (!reply) { return QByteArray(); } QByteArray ba(xcb_get_atom_name_name(reply)); free(reply); return ba; } #endif void Atoms::init() { #define ENUM_CREATE_CHAR_ARRAY 1 #include "atoms_p.h" // creates const char* array "KwsAtomStrings" // Send the intern atom requests xcb_intern_atom_cookie_t cookies[KwsAtomCount]; for (int i = 0; i < KwsAtomCount; ++i) { cookies[i] = xcb_intern_atom(m_connection, false, strlen(KwsAtomStrings[i]), KwsAtomStrings[i]); } // Get the replies for (int i = 0; i < KwsAtomCount; ++i) { xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(m_connection, cookies[i], nullptr); if (!reply) { continue; } m_atoms[i] = reply->atom; free(reply); } } static void readIcon(xcb_connection_t *c, const xcb_get_property_cookie_t cookie, NETRArray &icons, int &icon_count) { #ifdef NETWMDEBUG fprintf(stderr, "NET: readIcon\n"); #endif // reset for (int i = 0; i < icons.size(); i++) { delete [] icons[i].data; } icons.reset(); icon_count = 0; xcb_get_property_reply_t *reply = xcb_get_property_reply(c, cookie, nullptr); if (!reply || reply->value_len < 3 || reply->format != 32 || reply->type != XCB_ATOM_CARDINAL) { if (reply) { free(reply); } return; } uint32_t *data = (uint32_t *) xcb_get_property_value(reply); for (unsigned int i = 0, j = 0; j < reply->value_len - 2; i++) { uint32_t width = data[j++]; uint32_t height = data[j++]; uint32_t size = width * height * sizeof(uint32_t); if (j + width * height > reply->value_len) { fprintf(stderr, "Ill-encoded icon data; proposed size leads to out of bounds access. Skipping. (%d x %d)\n", width, height); break; } if (width > 1024 || height > 1024) { fprintf(stderr, "Warning: found huge icon. The icon data may be ill-encoded. (%d x %d)\n", width, height); // do not break nor continue - the data may likely be junk, but causes no harm (yet) and might actually be just a huge icon, eg. when the icon system is abused to transfer wallpapers or such. } icons[i].size.width = width; icons[i].size.height = height; icons[i].data = new unsigned char[size]; memcpy((void *)icons[i].data, (const void *)&data[j], size); j += width * height; icon_count++; } free(reply); #ifdef NETWMDEBUG fprintf(stderr, "NET: readIcon got %d icons\n", icon_count); #endif } static void send_client_message(xcb_connection_t *c, uint32_t mask, xcb_window_t destination, xcb_window_t window, xcb_atom_t message, const uint32_t data[]) { xcb_client_message_event_t event; event.response_type = XCB_CLIENT_MESSAGE; event.format = 32; event.sequence = 0; event.window = window; event.type = message; for (int i = 0; i < 5; i++) { event.data.data32[i] = data[i]; } xcb_send_event(c, false, destination, mask, (const char *) &event); } template NETRArray::NETRArray() : sz(0), capacity(2) { d = (Z *) calloc(capacity, sizeof(Z)); // allocate 2 elts and set to zero } template NETRArray::~NETRArray() { free(d); } template void NETRArray::reset() { sz = 0; capacity = 2; d = (Z *) realloc(d, sizeof(Z) * capacity); memset((void *) d, 0, sizeof(Z)*capacity); } template Z &NETRArray::operator[](int index) { if (index >= capacity) { // allocate space for the new data // open table has amortized O(1) access time // when N elements appended consecutively -- exa int newcapacity = 2 * capacity > index + 1 ? 2 * capacity : index + 1; // max // copy into new larger memory block using realloc d = (Z *) realloc(d, sizeof(Z) * newcapacity); memset((void *) &d[capacity], 0, sizeof(Z) * (newcapacity - capacity)); capacity = newcapacity; } if (index >= sz) { // at this point capacity>index sz = index + 1; } return d[index]; } /* The viewport<->desktop matching is a bit backwards, since NET* classes are the base (and were originally even created with the intention of being the reference WM spec implementation) and KWindowSystem builds on top of it. However it's simpler to add watching whether the WM uses viewport is simpler to KWindowSystem and not having this mapping in NET* classes could result in some code using it directly and not supporting viewport. So NET* classes check if mapping is needed and if yes they forward to KWindowSystem, which will forward again back to NET* classes, but to viewport calls instead of desktop calls. */ // Construct a new NETRootInfo object. NETRootInfo::NETRootInfo(xcb_connection_t *connection, xcb_window_t supportWindow, const char *wmName, NET::Properties properties, NET::WindowTypes windowTypes, NET::States states, NET::Properties2 properties2, NET::Actions actions, int screen, bool doActivate) { #ifdef NETWMDEBUG fprintf(stderr, "NETRootInfo::NETRootInfo: using window manager constructor\n"); #endif p = new NETRootInfoPrivate; p->ref = 1; p->atoms = atomsForConnection(connection); p->name = nstrdup(wmName); p->conn = connection; p->temp_buf = nullptr; p->temp_buf_size = 0; const xcb_setup_t *setup = xcb_get_setup(p->conn); xcb_screen_iterator_t it = xcb_setup_roots_iterator(setup); if (screen != -1 && screen < setup->roots_len) { for (int i = 0; i < screen; i++) { xcb_screen_next(&it); } } p->root = it.data->root; p->supportwindow = supportWindow; p->number_of_desktops = p->current_desktop = 0; p->active = XCB_WINDOW_NONE; p->clients = p->stacking = p->virtual_roots = (xcb_window_t *) nullptr; p->clients_count = p->stacking_count = p->virtual_roots_count = 0; p->showing_desktop = false; p->desktop_layout_orientation = OrientationHorizontal; p->desktop_layout_corner = DesktopLayoutCornerTopLeft; p->desktop_layout_columns = p->desktop_layout_rows = 0; setDefaultProperties(); p->properties = properties; p->properties2 = properties2; p->windowTypes = windowTypes; p->states = states; p->actions = actions; // force support for Supported and SupportingWMCheck for window managers p->properties |= (Supported | SupportingWMCheck); p->clientProperties = DesktopNames // the only thing that can be changed by clients | WMPing; // or they can reply to this p->clientProperties2 = WM2DesktopLayout; p->role = WindowManager; if (doActivate) { activate(); } } NETRootInfo::NETRootInfo(xcb_connection_t *connection, NET::Properties properties, NET::Properties2 properties2, int screen, bool doActivate) { #ifdef NETWMDEBUG fprintf(stderr, "NETRootInfo::NETRootInfo: using Client constructor\n"); #endif p = new NETRootInfoPrivate; p->ref = 1; p->atoms = atomsForConnection(connection); p->name = nullptr; p->conn = connection; p->temp_buf = nullptr; p->temp_buf_size = 0; const xcb_setup_t *setup = xcb_get_setup(p->conn); xcb_screen_iterator_t it = xcb_setup_roots_iterator(setup); if (screen != -1 && screen < setup->roots_len) { for (int i = 0; i < screen; i++) { xcb_screen_next(&it); } } p->root = it.data->root; p->rootSize.width = it.data->width_in_pixels; p->rootSize.height = it.data->height_in_pixels; p->supportwindow = XCB_WINDOW_NONE; p->number_of_desktops = p->current_desktop = 0; p->active = XCB_WINDOW_NONE; p->clients = p->stacking = p->virtual_roots = (xcb_window_t *) nullptr; p->clients_count = p->stacking_count = p->virtual_roots_count = 0; p->showing_desktop = false; p->desktop_layout_orientation = OrientationHorizontal; p->desktop_layout_corner = DesktopLayoutCornerTopLeft; p->desktop_layout_columns = p->desktop_layout_rows = 0; setDefaultProperties(); p->clientProperties = properties; p->clientProperties2 = properties2; p->properties = NET::Properties(); p->properties2 = NET::Properties2(); p->windowTypes = NET::WindowTypes(); p->states = NET::States(); p->actions = NET::Actions(); p->role = Client; if (doActivate) { activate(); } } // Copy an existing NETRootInfo object. NETRootInfo::NETRootInfo(const NETRootInfo &rootinfo) { #ifdef NETWMDEBUG fprintf(stderr, "NETRootInfo::NETRootInfo: using copy constructor\n"); #endif p = rootinfo.p; p->ref++; } // Be gone with our NETRootInfo. NETRootInfo::~NETRootInfo() { refdec_nri(p); if (! p->ref) { delete p; } } void NETRootInfo::setDefaultProperties() { p->properties = Supported | SupportingWMCheck; p->windowTypes = NormalMask | DesktopMask | DockMask | ToolbarMask | MenuMask | DialogMask; p->states = Modal | Sticky | MaxVert | MaxHoriz | Shaded | SkipTaskbar | StaysOnTop; p->properties2 = NET::Properties2(); p->actions = NET::Actions(); p->clientProperties = NET::Properties(); p->clientProperties2 = NET::Properties2(); } void NETRootInfo::activate() { if (p->role == WindowManager) { #ifdef NETWMDEBUG fprintf(stderr, "NETRootInfo::activate: setting supported properties on root\n"); #endif setSupported(); update(p->clientProperties, p->clientProperties2); } else { #ifdef NETWMDEBUG fprintf(stderr, "NETRootInfo::activate: updating client information\n"); #endif update(p->clientProperties, p->clientProperties2); } } void NETRootInfo::setClientList(const xcb_window_t *windows, unsigned int count) { if (p->role != WindowManager) { return; } p->clients_count = count; delete [] p->clients; p->clients = nwindup(windows, count); #ifdef NETWMDEBUG fprintf(stderr, "NETRootInfo::setClientList: setting list with %ld windows\n", p->clients_count); #endif xcb_change_property(p->conn, XCB_PROP_MODE_REPLACE, p->root, p->atom(_NET_CLIENT_LIST), XCB_ATOM_WINDOW, 32, p->clients_count, (const void *) windows); } void NETRootInfo::setClientListStacking(const xcb_window_t *windows, unsigned int count) { if (p->role != WindowManager) { return; } p->stacking_count = count; delete [] p->stacking; p->stacking = nwindup(windows, count); #ifdef NETWMDEBUG fprintf(stderr, "NETRootInfo::setClientListStacking: setting list with %ld windows\n", p->clients_count); #endif xcb_change_property(p->conn, XCB_PROP_MODE_REPLACE, p->root, p->atom(_NET_CLIENT_LIST_STACKING), XCB_ATOM_WINDOW, 32, p->stacking_count, (const void *) windows); } void NETRootInfo::setNumberOfDesktops(int numberOfDesktops) { #ifdef NETWMDEBUG fprintf(stderr, "NETRootInfo::setNumberOfDesktops: setting desktop count to %d (%s)\n", numberOfDesktops, (p->role == WindowManager) ? "WM" : "Client"); #endif if (p->role == WindowManager) { p->number_of_desktops = numberOfDesktops; const uint32_t d = numberOfDesktops; xcb_change_property(p->conn, XCB_PROP_MODE_REPLACE, p->root, p->atom(_NET_NUMBER_OF_DESKTOPS), XCB_ATOM_CARDINAL, 32, 1, (const void *) &d); } else { const uint32_t data[5] = { uint32_t(numberOfDesktops), 0, 0, 0, 0 }; send_client_message(p->conn, netwm_sendevent_mask, p->root, p->root, p->atom(_NET_NUMBER_OF_DESKTOPS), data); } } void NETRootInfo::setCurrentDesktop(int desktop, bool ignore_viewport) { #ifdef NETWMDEBUG fprintf(stderr, "NETRootInfo::setCurrentDesktop: setting current desktop = %d (%s)\n", desktop, (p->role == WindowManager) ? "WM" : "Client"); #endif if (p->role == WindowManager) { p->current_desktop = desktop; uint32_t d = p->current_desktop - 1; xcb_change_property(p->conn, XCB_PROP_MODE_REPLACE, p->root, p->atom(_NET_CURRENT_DESKTOP), XCB_ATOM_CARDINAL, 32, 1, (const void *) &d); } else { if (!ignore_viewport && KWindowSystem::mapViewport()) { KWindowSystem::setCurrentDesktop(desktop); return; } const uint32_t data[5] = { uint32_t(desktop - 1), 0, 0, 0, 0 }; send_client_message(p->conn, netwm_sendevent_mask, p->root, p->root, p->atom(_NET_CURRENT_DESKTOP), data); } } void NETRootInfo::setDesktopName(int desktop, const char *desktopName) { // Allow setting desktop names even for non-existent desktops, see the spec, sect.3.7. if (desktop < 1) { return; } delete [] p->desktop_names[desktop - 1]; p->desktop_names[desktop - 1] = nstrdup(desktopName); unsigned int i, proplen, num = ((p->number_of_desktops > p->desktop_names.size()) ? p->number_of_desktops : p->desktop_names.size()); for (i = 0, proplen = 0; i < num; i++) { proplen += (p->desktop_names[i] != nullptr ? strlen(p->desktop_names[i]) + 1 : 1); } char *prop = new char[proplen], *propp = prop; for (i = 0; i < num; i++) if (p->desktop_names[i]) { strcpy(propp, p->desktop_names[i]); propp += strlen(p->desktop_names[i]) + 1; } else { *propp++ = '\0'; } #ifdef NETWMDEBUG fprintf(stderr, "NETRootInfo::setDesktopName(%d, '%s')\n" "NETRootInfo::setDesktopName: total property length = %d", desktop, desktopName, proplen); #endif xcb_change_property(p->conn, XCB_PROP_MODE_REPLACE, p->root, p->atom(_NET_DESKTOP_NAMES), p->atom(UTF8_STRING), 8, proplen, (const void *) prop); delete [] prop; } void NETRootInfo::setDesktopGeometry(const NETSize &geometry) { #ifdef NETWMDEBUG fprintf(stderr, "NETRootInfo::setDesktopGeometry( -- , { %d, %d }) (%s)\n", geometry.width, geometry.height, (p->role == WindowManager) ? "WM" : "Client"); #endif if (p->role == WindowManager) { p->geometry = geometry; uint32_t data[2]; data[0] = p->geometry.width; data[1] = p->geometry.height; xcb_change_property(p->conn, XCB_PROP_MODE_REPLACE, p->root, p->atom(_NET_DESKTOP_GEOMETRY), XCB_ATOM_CARDINAL, 32, 2, (const void *) data); } else { uint32_t data[5] = { uint32_t(geometry.width), uint32_t(geometry.height), 0, 0, 0 }; send_client_message(p->conn, netwm_sendevent_mask, p->root, p->root, p->atom(_NET_DESKTOP_GEOMETRY), data); } } void NETRootInfo::setDesktopViewport(int desktop, const NETPoint &viewport) { #ifdef NETWMDEBUG fprintf(stderr, "NETRootInfo::setDesktopViewport(%d, { %d, %d }) (%s)\n", desktop, viewport.x, viewport.y, (p->role == WindowManager) ? "WM" : "Client"); #endif if (desktop < 1) { return; } if (p->role == WindowManager) { p->viewport[desktop - 1] = viewport; int d, i, l; l = p->number_of_desktops * 2; uint32_t *data = new uint32_t[l]; for (d = 0, i = 0; d < p->number_of_desktops; d++) { data[i++] = p->viewport[d].x; data[i++] = p->viewport[d].y; } xcb_change_property(p->conn, XCB_PROP_MODE_REPLACE, p->root, p->atom(_NET_DESKTOP_VIEWPORT), XCB_ATOM_CARDINAL, 32, l, (const void *) data); delete [] data; } else { const uint32_t data[5] = { uint32_t(viewport.x), uint32_t(viewport.y), 0, 0, 0 }; send_client_message(p->conn, netwm_sendevent_mask, p->root, p->root, p->atom(_NET_DESKTOP_VIEWPORT), data); } } void NETRootInfo::setSupported() { if (p->role != WindowManager) { #ifdef NETWMDEBUG fprintf(stderr, "NETRootInfo::setSupported - role != WindowManager\n"); #endif return; } xcb_atom_t atoms[KwsAtomCount]; int pnum = 2; // Root window properties/messages atoms[0] = p->atom(_NET_SUPPORTED); atoms[1] = p->atom(_NET_SUPPORTING_WM_CHECK); if (p->properties & ClientList) { atoms[pnum++] = p->atom(_NET_CLIENT_LIST); } if (p->properties & ClientListStacking) { atoms[pnum++] = p->atom(_NET_CLIENT_LIST_STACKING); } if (p->properties & NumberOfDesktops) { atoms[pnum++] = p->atom(_NET_NUMBER_OF_DESKTOPS); } if (p->properties & DesktopGeometry) { atoms[pnum++] = p->atom(_NET_DESKTOP_GEOMETRY); } if (p->properties & DesktopViewport) { atoms[pnum++] = p->atom(_NET_DESKTOP_VIEWPORT); } if (p->properties & CurrentDesktop) { atoms[pnum++] = p->atom(_NET_CURRENT_DESKTOP); } if (p->properties & DesktopNames) { atoms[pnum++] = p->atom(_NET_DESKTOP_NAMES); } if (p->properties & ActiveWindow) { atoms[pnum++] = p->atom(_NET_ACTIVE_WINDOW); } if (p->properties & WorkArea) { atoms[pnum++] = p->atom(_NET_WORKAREA); } if (p->properties & VirtualRoots) { atoms[pnum++] = p->atom(_NET_VIRTUAL_ROOTS); } if (p->properties2 & WM2DesktopLayout) { atoms[pnum++] = p->atom(_NET_DESKTOP_LAYOUT); } if (p->properties & CloseWindow) { atoms[pnum++] = p->atom(_NET_CLOSE_WINDOW); } if (p->properties2 & WM2RestackWindow) { atoms[pnum++] = p->atom(_NET_RESTACK_WINDOW); } if (p->properties2 & WM2ShowingDesktop) { atoms[pnum++] = p->atom(_NET_SHOWING_DESKTOP); } // Application window properties/messages if (p->properties & WMMoveResize) { atoms[pnum++] = p->atom(_NET_WM_MOVERESIZE); } if (p->properties2 & WM2MoveResizeWindow) { atoms[pnum++] = p->atom(_NET_MOVERESIZE_WINDOW); } if (p->properties & WMName) { atoms[pnum++] = p->atom(_NET_WM_NAME); } if (p->properties & WMVisibleName) { atoms[pnum++] = p->atom(_NET_WM_VISIBLE_NAME); } if (p->properties & WMIconName) { atoms[pnum++] = p->atom(_NET_WM_ICON_NAME); } if (p->properties & WMVisibleIconName) { atoms[pnum++] = p->atom(_NET_WM_VISIBLE_ICON_NAME); } if (p->properties & WMDesktop) { atoms[pnum++] = p->atom(_NET_WM_DESKTOP); } if (p->properties & WMWindowType) { atoms[pnum++] = p->atom(_NET_WM_WINDOW_TYPE); // Application window types if (p->windowTypes & NormalMask) { atoms[pnum++] = p->atom(_NET_WM_WINDOW_TYPE_NORMAL); } if (p->windowTypes & DesktopMask) { atoms[pnum++] = p->atom(_NET_WM_WINDOW_TYPE_DESKTOP); } if (p->windowTypes & DockMask) { atoms[pnum++] = p->atom(_NET_WM_WINDOW_TYPE_DOCK); } if (p->windowTypes & ToolbarMask) { atoms[pnum++] = p->atom(_NET_WM_WINDOW_TYPE_TOOLBAR); } if (p->windowTypes & MenuMask) { atoms[pnum++] = p->atom(_NET_WM_WINDOW_TYPE_MENU); } if (p->windowTypes & DialogMask) { atoms[pnum++] = p->atom(_NET_WM_WINDOW_TYPE_DIALOG); } if (p->windowTypes & UtilityMask) { atoms[pnum++] = p->atom(_NET_WM_WINDOW_TYPE_UTILITY); } if (p->windowTypes & SplashMask) { atoms[pnum++] = p->atom(_NET_WM_WINDOW_TYPE_SPLASH); } if (p->windowTypes & DropdownMenuMask) { atoms[pnum++] = p->atom(_NET_WM_WINDOW_TYPE_DROPDOWN_MENU); } if (p->windowTypes & PopupMenuMask) { atoms[pnum++] = p->atom(_NET_WM_WINDOW_TYPE_POPUP_MENU); } if (p->windowTypes & TooltipMask) { atoms[pnum++] = p->atom(_NET_WM_WINDOW_TYPE_TOOLTIP); } if (p->windowTypes & NotificationMask) { atoms[pnum++] = p->atom(_NET_WM_WINDOW_TYPE_NOTIFICATION); } if (p->windowTypes & ComboBoxMask) { atoms[pnum++] = p->atom(_NET_WM_WINDOW_TYPE_COMBO); } if (p->windowTypes & DNDIconMask) { atoms[pnum++] = p->atom(_NET_WM_WINDOW_TYPE_DND); } // KDE extensions if (p->windowTypes & OverrideMask) { atoms[pnum++] = p->atom(_KDE_NET_WM_WINDOW_TYPE_OVERRIDE); } if (p->windowTypes & TopMenuMask) { atoms[pnum++] = p->atom(_KDE_NET_WM_WINDOW_TYPE_TOPMENU); } if (p->windowTypes & OnScreenDisplayMask) { atoms[pnum++] = p->atom(_KDE_NET_WM_WINDOW_TYPE_ON_SCREEN_DISPLAY); } + if (p->windowTypes & CriticalNotificationMask) { + atoms[pnum++] = p->atom(_KDE_NET_WM_WINDOW_TYPE_CRITICAL_NOTIFICATION); + } } if (p->properties & WMState) { atoms[pnum++] = p->atom(_NET_WM_STATE); // Application window states if (p->states & Modal) { atoms[pnum++] = p->atom(_NET_WM_STATE_MODAL); } if (p->states & Sticky) { atoms[pnum++] = p->atom(_NET_WM_STATE_STICKY); } if (p->states & MaxVert) { atoms[pnum++] = p->atom(_NET_WM_STATE_MAXIMIZED_VERT); } if (p->states & MaxHoriz) { atoms[pnum++] = p->atom(_NET_WM_STATE_MAXIMIZED_HORZ); } if (p->states & Shaded) { atoms[pnum++] = p->atom(_NET_WM_STATE_SHADED); } if (p->states & SkipTaskbar) { atoms[pnum++] = p->atom(_NET_WM_STATE_SKIP_TASKBAR); } if (p->states & SkipPager) { atoms[pnum++] = p->atom(_NET_WM_STATE_SKIP_PAGER); } if (p->states & SkipSwitcher) { atoms[pnum++] = p->atom(_KDE_NET_WM_STATE_SKIP_SWITCHER); } if (p->states & Hidden) { atoms[pnum++] = p->atom(_NET_WM_STATE_HIDDEN); } if (p->states & FullScreen) { atoms[pnum++] = p->atom(_NET_WM_STATE_FULLSCREEN); } if (p->states & KeepAbove) { atoms[pnum++] = p->atom(_NET_WM_STATE_ABOVE); } if (p->states & KeepBelow) { atoms[pnum++] = p->atom(_NET_WM_STATE_BELOW); } if (p->states & DemandsAttention) { atoms[pnum++] = p->atom(_NET_WM_STATE_DEMANDS_ATTENTION); } if (p->states & StaysOnTop) { atoms[pnum++] = p->atom(_NET_WM_STATE_STAYS_ON_TOP); } if (p->states & Focused) { atoms[pnum++] = p->atom(_NET_WM_STATE_FOCUSED); } } if (p->properties & WMStrut) { atoms[pnum++] = p->atom(_NET_WM_STRUT); } if (p->properties2 & WM2ExtendedStrut) { atoms[pnum++] = p->atom(_NET_WM_STRUT_PARTIAL); } if (p->properties & WMIconGeometry) { atoms[pnum++] = p->atom(_NET_WM_ICON_GEOMETRY); } if (p->properties & WMIcon) { atoms[pnum++] = p->atom(_NET_WM_ICON); } if (p->properties & WMPid) { atoms[pnum++] = p->atom(_NET_WM_PID); } if (p->properties & WMHandledIcons) { atoms[pnum++] = p->atom(_NET_WM_HANDLED_ICONS); } if (p->properties & WMPing) { atoms[pnum++] = p->atom(_NET_WM_PING); } if (p->properties2 & WM2UserTime) { atoms[pnum++] = p->atom(_NET_WM_USER_TIME); } if (p->properties2 & WM2StartupId) { atoms[pnum++] = p->atom(_NET_STARTUP_ID); } if (p->properties2 & WM2Opacity) { atoms[pnum++] = p->atom(_NET_WM_WINDOW_OPACITY); } if (p->properties2 & WM2FullscreenMonitors) { atoms[pnum++] = p->atom(_NET_WM_FULLSCREEN_MONITORS); } if (p->properties2 & WM2AllowedActions) { atoms[pnum++] = p->atom(_NET_WM_ALLOWED_ACTIONS); // Actions if (p->actions & ActionMove) { atoms[pnum++] = p->atom(_NET_WM_ACTION_MOVE); } if (p->actions & ActionResize) { atoms[pnum++] = p->atom(_NET_WM_ACTION_RESIZE); } if (p->actions & ActionMinimize) { atoms[pnum++] = p->atom(_NET_WM_ACTION_MINIMIZE); } if (p->actions & ActionShade) { atoms[pnum++] = p->atom(_NET_WM_ACTION_SHADE); } if (p->actions & ActionStick) { atoms[pnum++] = p->atom(_NET_WM_ACTION_STICK); } if (p->actions & ActionMaxVert) { atoms[pnum++] = p->atom(_NET_WM_ACTION_MAXIMIZE_VERT); } if (p->actions & ActionMaxHoriz) { atoms[pnum++] = p->atom(_NET_WM_ACTION_MAXIMIZE_HORZ); } if (p->actions & ActionFullScreen) { atoms[pnum++] = p->atom(_NET_WM_ACTION_FULLSCREEN); } if (p->actions & ActionChangeDesktop) { atoms[pnum++] = p->atom(_NET_WM_ACTION_CHANGE_DESKTOP); } if (p->actions & ActionClose) { atoms[pnum++] = p->atom(_NET_WM_ACTION_CLOSE); } } if (p->properties & WMFrameExtents) { atoms[pnum++] = p->atom(_NET_FRAME_EXTENTS); atoms[pnum++] = p->atom(_KDE_NET_WM_FRAME_STRUT); } if (p->properties2 & WM2FrameOverlap) { atoms[pnum++] = p->atom(_NET_WM_FRAME_OVERLAP); } if (p->properties2 & WM2KDETemporaryRules) { atoms[pnum++] = p->atom(_KDE_NET_WM_TEMPORARY_RULES); } if (p->properties2 & WM2FullPlacement) { atoms[pnum++] = p->atom(_NET_WM_FULL_PLACEMENT); } if (p->properties2 & WM2Activities) { atoms[pnum++] = p->atom(_KDE_NET_WM_ACTIVITIES); } if (p->properties2 & WM2BlockCompositing) { atoms[pnum++] = p->atom(_KDE_NET_WM_BLOCK_COMPOSITING); atoms[pnum++] = p->atom(_NET_WM_BYPASS_COMPOSITOR); } if (p->properties2 & WM2KDEShadow) { atoms[pnum++] = p->atom(_KDE_NET_WM_SHADOW); } if (p->properties2 & WM2OpaqueRegion) { atoms[pnum++] = p->atom(_NET_WM_OPAQUE_REGION); } xcb_change_property(p->conn, XCB_PROP_MODE_REPLACE, p->root, p->atom(_NET_SUPPORTED), XCB_ATOM_ATOM, 32, pnum, (const void *) atoms); xcb_change_property(p->conn, XCB_PROP_MODE_REPLACE, p->root, p->atom(_NET_SUPPORTING_WM_CHECK), XCB_ATOM_WINDOW, 32, 1, (const void *) & (p->supportwindow)); #ifdef NETWMDEBUG fprintf(stderr, "NETRootInfo::setSupported: _NET_SUPPORTING_WM_CHECK = 0x%lx on 0x%lx\n" " : _NET_WM_NAME = '%s' on 0x%lx\n", p->supportwindow, p->supportwindow, p->name, p->supportwindow); #endif xcb_change_property(p->conn, XCB_PROP_MODE_REPLACE, p->supportwindow, p->atom(_NET_SUPPORTING_WM_CHECK), XCB_ATOM_WINDOW, 32, 1, (const void *) & (p->supportwindow)); xcb_change_property(p->conn, XCB_PROP_MODE_REPLACE, p->supportwindow, p->atom(_NET_WM_NAME), p->atom(UTF8_STRING), 8, strlen(p->name), (const void *) p->name); } void NETRootInfo::updateSupportedProperties(xcb_atom_t atom) { if (atom == p->atom(_NET_SUPPORTED)) { p->properties |= Supported; } else if (atom == p->atom(_NET_SUPPORTING_WM_CHECK)) { p->properties |= SupportingWMCheck; } else if (atom == p->atom(_NET_CLIENT_LIST)) { p->properties |= ClientList; } else if (atom == p->atom(_NET_CLIENT_LIST_STACKING)) { p->properties |= ClientListStacking; } else if (atom == p->atom(_NET_NUMBER_OF_DESKTOPS)) { p->properties |= NumberOfDesktops; } else if (atom == p->atom(_NET_DESKTOP_GEOMETRY)) { p->properties |= DesktopGeometry; } else if (atom == p->atom(_NET_DESKTOP_VIEWPORT)) { p->properties |= DesktopViewport; } else if (atom == p->atom(_NET_CURRENT_DESKTOP)) { p->properties |= CurrentDesktop; } else if (atom == p->atom(_NET_DESKTOP_NAMES)) { p->properties |= DesktopNames; } else if (atom == p->atom(_NET_ACTIVE_WINDOW)) { p->properties |= ActiveWindow; } else if (atom == p->atom(_NET_WORKAREA)) { p->properties |= WorkArea; } else if (atom == p->atom(_NET_VIRTUAL_ROOTS)) { p->properties |= VirtualRoots; } else if (atom == p->atom(_NET_DESKTOP_LAYOUT)) { p->properties2 |= WM2DesktopLayout; } else if (atom == p->atom(_NET_CLOSE_WINDOW)) { p->properties |= CloseWindow; } else if (atom == p->atom(_NET_RESTACK_WINDOW)) { p->properties2 |= WM2RestackWindow; } else if (atom == p->atom(_NET_SHOWING_DESKTOP)) { p->properties2 |= WM2ShowingDesktop; } // Application window properties/messages else if (atom == p->atom(_NET_WM_MOVERESIZE)) { p->properties |= WMMoveResize; } else if (atom == p->atom(_NET_MOVERESIZE_WINDOW)) { p->properties2 |= WM2MoveResizeWindow; } else if (atom == p->atom(_NET_WM_NAME)) { p->properties |= WMName; } else if (atom == p->atom(_NET_WM_VISIBLE_NAME)) { p->properties |= WMVisibleName; } else if (atom == p->atom(_NET_WM_ICON_NAME)) { p->properties |= WMIconName; } else if (atom == p->atom(_NET_WM_VISIBLE_ICON_NAME)) { p->properties |= WMVisibleIconName; } else if (atom == p->atom(_NET_WM_DESKTOP)) { p->properties |= WMDesktop; } else if (atom == p->atom(_NET_WM_WINDOW_TYPE)) { p->properties |= WMWindowType; } // Application window types else if (atom == p->atom(_NET_WM_WINDOW_TYPE_NORMAL)) { p->windowTypes |= NormalMask; } else if (atom == p->atom(_NET_WM_WINDOW_TYPE_DESKTOP)) { p->windowTypes |= DesktopMask; } else if (atom == p->atom(_NET_WM_WINDOW_TYPE_DOCK)) { p->windowTypes |= DockMask; } else if (atom == p->atom(_NET_WM_WINDOW_TYPE_TOOLBAR)) { p->windowTypes |= ToolbarMask; } else if (atom == p->atom(_NET_WM_WINDOW_TYPE_MENU)) { p->windowTypes |= MenuMask; } else if (atom == p->atom(_NET_WM_WINDOW_TYPE_DIALOG)) { p->windowTypes |= DialogMask; } else if (atom == p->atom(_NET_WM_WINDOW_TYPE_UTILITY)) { p->windowTypes |= UtilityMask; } else if (atom == p->atom(_NET_WM_WINDOW_TYPE_SPLASH)) { p->windowTypes |= SplashMask; } else if (atom == p->atom(_NET_WM_WINDOW_TYPE_DROPDOWN_MENU)) { p->windowTypes |= DropdownMenuMask; } else if (atom == p->atom(_NET_WM_WINDOW_TYPE_POPUP_MENU)) { p->windowTypes |= PopupMenuMask; } else if (atom == p->atom(_NET_WM_WINDOW_TYPE_TOOLTIP)) { p->windowTypes |= TooltipMask; } else if (atom == p->atom(_NET_WM_WINDOW_TYPE_NOTIFICATION)) { p->windowTypes |= NotificationMask; } else if (atom == p->atom(_NET_WM_WINDOW_TYPE_COMBO)) { p->windowTypes |= ComboBoxMask; } else if (atom == p->atom(_NET_WM_WINDOW_TYPE_DND)) { p->windowTypes |= DNDIconMask; } // KDE extensions else if (atom == p->atom(_KDE_NET_WM_WINDOW_TYPE_OVERRIDE)) { p->windowTypes |= OverrideMask; } else if (atom == p->atom(_KDE_NET_WM_WINDOW_TYPE_TOPMENU)) { p->windowTypes |= TopMenuMask; } else if (atom == p->atom(_KDE_NET_WM_WINDOW_TYPE_ON_SCREEN_DISPLAY)) { p->windowTypes |= OnScreenDisplayMask; + } else if (atom == p->atom(_KDE_NET_WM_WINDOW_TYPE_CRITICAL_NOTIFICATION)) { + p->windowTypes |= CriticalNotificationMask; } else if (atom == p->atom(_NET_WM_STATE)) { p->properties |= WMState; } // Application window states else if (atom == p->atom(_NET_WM_STATE_MODAL)) { p->states |= Modal; } else if (atom == p->atom(_NET_WM_STATE_STICKY)) { p->states |= Sticky; } else if (atom == p->atom(_NET_WM_STATE_MAXIMIZED_VERT)) { p->states |= MaxVert; } else if (atom == p->atom(_NET_WM_STATE_MAXIMIZED_HORZ)) { p->states |= MaxHoriz; } else if (atom == p->atom(_NET_WM_STATE_SHADED)) { p->states |= Shaded; } else if (atom == p->atom(_NET_WM_STATE_SKIP_TASKBAR)) { p->states |= SkipTaskbar; } else if (atom == p->atom(_NET_WM_STATE_SKIP_PAGER)) { p->states |= SkipPager; } else if (atom == p->atom(_KDE_NET_WM_STATE_SKIP_SWITCHER)) { p->states |= SkipSwitcher; } else if (atom == p->atom(_NET_WM_STATE_HIDDEN)) { p->states |= Hidden; } else if (atom == p->atom(_NET_WM_STATE_FULLSCREEN)) { p->states |= FullScreen; } else if (atom == p->atom(_NET_WM_STATE_ABOVE)) { p->states |= KeepAbove; } else if (atom == p->atom(_NET_WM_STATE_BELOW)) { p->states |= KeepBelow; } else if (atom == p->atom(_NET_WM_STATE_DEMANDS_ATTENTION)) { p->states |= DemandsAttention; } else if (atom == p->atom(_NET_WM_STATE_STAYS_ON_TOP)) { p->states |= StaysOnTop; } else if (atom == p->atom(_NET_WM_STATE_FOCUSED)) { p->states |= Focused; } else if (atom == p->atom(_NET_WM_STRUT)) { p->properties |= WMStrut; } else if (atom == p->atom(_NET_WM_STRUT_PARTIAL)) { p->properties2 |= WM2ExtendedStrut; } else if (atom == p->atom(_NET_WM_ICON_GEOMETRY)) { p->properties |= WMIconGeometry; } else if (atom == p->atom(_NET_WM_ICON)) { p->properties |= WMIcon; } else if (atom == p->atom(_NET_WM_PID)) { p->properties |= WMPid; } else if (atom == p->atom(_NET_WM_HANDLED_ICONS)) { p->properties |= WMHandledIcons; } else if (atom == p->atom(_NET_WM_PING)) { p->properties |= WMPing; } else if (atom == p->atom(_NET_WM_USER_TIME)) { p->properties2 |= WM2UserTime; } else if (atom == p->atom(_NET_STARTUP_ID)) { p->properties2 |= WM2StartupId; } else if (atom == p->atom(_NET_WM_WINDOW_OPACITY)) { p->properties2 |= WM2Opacity; } else if (atom == p->atom(_NET_WM_FULLSCREEN_MONITORS)) { p->properties2 |= WM2FullscreenMonitors; } else if (atom == p->atom(_NET_WM_ALLOWED_ACTIONS)) { p->properties2 |= WM2AllowedActions; } // Actions else if (atom == p->atom(_NET_WM_ACTION_MOVE)) { p->actions |= ActionMove; } else if (atom == p->atom(_NET_WM_ACTION_RESIZE)) { p->actions |= ActionResize; } else if (atom == p->atom(_NET_WM_ACTION_MINIMIZE)) { p->actions |= ActionMinimize; } else if (atom == p->atom(_NET_WM_ACTION_SHADE)) { p->actions |= ActionShade; } else if (atom == p->atom(_NET_WM_ACTION_STICK)) { p->actions |= ActionStick; } else if (atom == p->atom(_NET_WM_ACTION_MAXIMIZE_VERT)) { p->actions |= ActionMaxVert; } else if (atom == p->atom(_NET_WM_ACTION_MAXIMIZE_HORZ)) { p->actions |= ActionMaxHoriz; } else if (atom == p->atom(_NET_WM_ACTION_FULLSCREEN)) { p->actions |= ActionFullScreen; } else if (atom == p->atom(_NET_WM_ACTION_CHANGE_DESKTOP)) { p->actions |= ActionChangeDesktop; } else if (atom == p->atom(_NET_WM_ACTION_CLOSE)) { p->actions |= ActionClose; } else if (atom == p->atom(_NET_FRAME_EXTENTS)) { p->properties |= WMFrameExtents; } else if (atom == p->atom(_KDE_NET_WM_FRAME_STRUT)) { p->properties |= WMFrameExtents; } else if (atom == p->atom(_NET_WM_FRAME_OVERLAP)) { p->properties2 |= WM2FrameOverlap; } else if (atom == p->atom(_KDE_NET_WM_TEMPORARY_RULES)) { p->properties2 |= WM2KDETemporaryRules; } else if (atom == p->atom(_NET_WM_FULL_PLACEMENT)) { p->properties2 |= WM2FullPlacement; } else if (atom == p->atom(_KDE_NET_WM_ACTIVITIES)) { p->properties2 |= WM2Activities; } else if (atom == p->atom(_KDE_NET_WM_BLOCK_COMPOSITING) || atom == p->atom(_NET_WM_BYPASS_COMPOSITOR)) { p->properties2 |= WM2BlockCompositing; } else if (atom == p->atom(_KDE_NET_WM_SHADOW)) { p->properties2 |= WM2KDEShadow; } else if (atom == p->atom(_NET_WM_OPAQUE_REGION)) { p->properties2 |= WM2OpaqueRegion; } } void NETRootInfo::setActiveWindow(xcb_window_t window) { setActiveWindow(window, FromUnknown, QX11Info::appUserTime(), XCB_WINDOW_NONE); } void NETRootInfo::setActiveWindow(xcb_window_t window, NET::RequestSource src, xcb_timestamp_t timestamp, xcb_window_t active_window) { #ifdef NETWMDEBUG fprintf(stderr, "NETRootInfo::setActiveWindow(0x%lx) (%s)\n", window, (p->role == WindowManager) ? "WM" : "Client"); #endif if (p->role == WindowManager) { p->active = window; xcb_change_property(p->conn, XCB_PROP_MODE_REPLACE, p->root, p->atom(_NET_ACTIVE_WINDOW), XCB_ATOM_WINDOW, 32, 1, (const void *) & (p->active)); } else { const uint32_t data[5] = { src, timestamp, active_window, 0, 0 }; send_client_message(p->conn, netwm_sendevent_mask, p->root, window, p->atom(_NET_ACTIVE_WINDOW), data); } } void NETRootInfo::setWorkArea(int desktop, const NETRect &workarea) { #ifdef NETWMDEBUG fprintf(stderr, "NETRootInfo::setWorkArea(%d, { %d, %d, %d, %d }) (%s)\n", desktop, workarea.pos.x, workarea.pos.y, workarea.size.width, workarea.size.height, (p->role == WindowManager) ? "WM" : "Client"); #endif if (p->role != WindowManager || desktop < 1) { return; } p->workarea[desktop - 1] = workarea; uint32_t *wa = new uint32_t[p->number_of_desktops * 4]; int i, o; for (i = 0, o = 0; i < p->number_of_desktops; i++) { wa[o++] = p->workarea[i].pos.x; wa[o++] = p->workarea[i].pos.y; wa[o++] = p->workarea[i].size.width; wa[o++] = p->workarea[i].size.height; } xcb_change_property(p->conn, XCB_PROP_MODE_REPLACE, p->root, p->atom(_NET_WORKAREA), XCB_ATOM_CARDINAL, 32, p->number_of_desktops * 4, (const void *) wa); delete [] wa; } void NETRootInfo::setVirtualRoots(const xcb_window_t *windows, unsigned int count) { if (p->role != WindowManager) { return; } p->virtual_roots_count = count; delete[] p->virtual_roots; p->virtual_roots = nwindup(windows, count); #ifdef NETWMDEBUG fprintf(stderr, "NETRootInfo::setVirtualRoots: setting list with %ld windows\n", p->virtual_roots_count); #endif xcb_change_property(p->conn, XCB_PROP_MODE_REPLACE, p->root, p->atom(_NET_VIRTUAL_ROOTS), XCB_ATOM_WINDOW, 32, p->virtual_roots_count, (const void *) windows); } void NETRootInfo::setDesktopLayout(NET::Orientation orientation, int columns, int rows, NET::DesktopLayoutCorner corner) { p->desktop_layout_orientation = orientation; p->desktop_layout_columns = columns; p->desktop_layout_rows = rows; p->desktop_layout_corner = corner; #ifdef NETWMDEBUG fprintf(stderr, "NETRootInfo::setDesktopLayout: %d %d %d %d\n", orientation, columns, rows, corner); #endif uint32_t data[4]; data[0] = orientation; data[1] = columns; data[2] = rows; data[3] = corner; xcb_change_property(p->conn, XCB_PROP_MODE_REPLACE, p->root, p->atom(_NET_DESKTOP_LAYOUT), XCB_ATOM_CARDINAL, 32, 4, (const void *) data); } void NETRootInfo::setShowingDesktop(bool showing) { if (p->role == WindowManager) { uint32_t d = p->showing_desktop = showing; xcb_change_property(p->conn, XCB_PROP_MODE_REPLACE, p->root, p->atom(_NET_SHOWING_DESKTOP), XCB_ATOM_CARDINAL, 32, 1, (const void *) &d); } else { uint32_t data[5] = { uint32_t(showing ? 1 : 0), 0, 0, 0, 0 }; send_client_message(p->conn, netwm_sendevent_mask, p->root, p->root, p->atom(_NET_SHOWING_DESKTOP), data); } } bool NETRootInfo::showingDesktop() const { return p->showing_desktop; } void NETRootInfo::closeWindowRequest(xcb_window_t window) { #ifdef NETWMDEBUG fprintf(stderr, "NETRootInfo::closeWindowRequest: requesting close for 0x%lx\n", window); #endif const uint32_t data[5] = { 0, 0, 0, 0, 0 }; send_client_message(p->conn, netwm_sendevent_mask, p->root, window, p->atom(_NET_CLOSE_WINDOW), data); } void NETRootInfo::moveResizeRequest(xcb_window_t window, int x_root, int y_root, Direction direction) { #ifdef NETWMDEBUG fprintf(stderr, "NETRootInfo::moveResizeRequest: requesting resize/move for 0x%lx (%d, %d, %d)\n", window, x_root, y_root, direction); #endif const uint32_t data[5] = { uint32_t(x_root), uint32_t(y_root), uint32_t(direction), 0, 0 }; send_client_message(p->conn, netwm_sendevent_mask, p->root, window, p->atom(_NET_WM_MOVERESIZE), data); } void NETRootInfo::moveResizeWindowRequest(xcb_window_t window, int flags, int x, int y, int width, int height) { #ifdef NETWMDEBUG fprintf(stderr, "NETRootInfo::moveResizeWindowRequest: resizing/moving 0x%lx (%d, %d, %d, %d, %d)\n", window, flags, x, y, width, height); #endif const uint32_t data[5] = { uint32_t(flags), uint32_t(x), uint32_t(y), uint32_t(width), uint32_t(height) }; send_client_message(p->conn, netwm_sendevent_mask, p->root, window, p->atom(_NET_MOVERESIZE_WINDOW), data); } void NETRootInfo::restackRequest(xcb_window_t window, RequestSource src, xcb_window_t above, int detail, xcb_timestamp_t timestamp) { #ifdef NETWMDEBUG fprintf(stderr, "NETRootInfo::restackRequest: requesting restack for 0x%lx (%lx, %d)\n", window, above, detail); #endif const uint32_t data[5] = { uint32_t(src), uint32_t(above), uint32_t(detail), uint32_t(timestamp), 0 }; send_client_message(p->conn, netwm_sendevent_mask, p->root, window, p->atom(_NET_RESTACK_WINDOW), data); } void NETRootInfo::sendPing(xcb_window_t window, xcb_timestamp_t timestamp) { if (p->role != WindowManager) { return; } #ifdef NETWMDEBUG fprintf(stderr, "NETRootInfo::setPing: window 0x%lx, timestamp %lu\n", window, timestamp); #endif const uint32_t data[5] = { p->atom(_NET_WM_PING), timestamp, window, 0, 0 }; send_client_message(p->conn, 0, window, window, p->atom(WM_PROTOCOLS), data); } // assignment operator const NETRootInfo &NETRootInfo::operator=(const NETRootInfo &rootinfo) { #ifdef NETWMDEBUG fprintf(stderr, "NETRootInfo::operator=()\n"); #endif if (p != rootinfo.p) { refdec_nri(p); if (! p->ref) { delete p; } } p = rootinfo.p; p->ref++; return *this; } NET::Properties NETRootInfo::event(xcb_generic_event_t *ev) { NET::Properties props; event(ev, &props); return props; } #ifndef KWINDOWSYSTEM_NO_DEPRECATED void NETRootInfo::event(xcb_generic_event_t *ev, unsigned long *properties, int properties_size) { unsigned long props[ PROPERTIES_SIZE ] = { 0, 0, 0, 0, 0 }; assert(PROPERTIES_SIZE == 5); // add elements above NET::Properties p; NET::Properties2 p2; event(ev, &p, &p2); props[ PROTOCOLS ] = p; props[ PROTOCOLS2 ] = p2; if (properties_size > PROPERTIES_SIZE) { properties_size = PROPERTIES_SIZE; } for (int i = 0; i < properties_size; ++i) { properties[ i ] = props[ i ]; } } #endif void NETRootInfo::event(xcb_generic_event_t *event, NET::Properties *properties, NET::Properties2 *properties2) { NET::Properties dirty; NET::Properties2 dirty2; bool do_update = false; const uint8_t eventType = event->response_type & ~0x80; // the window manager will be interested in client messages... no other // client should get these messages if (p->role == WindowManager && eventType == XCB_CLIENT_MESSAGE && reinterpret_cast(event)->format == 32) { xcb_client_message_event_t *message = reinterpret_cast(event); #ifdef NETWMDEBUG fprintf(stderr, "NETRootInfo::event: handling ClientMessage event\n"); #endif if (message->type == p->atom(_NET_NUMBER_OF_DESKTOPS)) { dirty = NumberOfDesktops; #ifdef NETWMDEBUG fprintf(stderr, "NETRootInfo::event: changeNumberOfDesktops(%ld)\n", message->data.data32[0]); #endif changeNumberOfDesktops(message->data.data32[0]); } else if (message->type == p->atom(_NET_DESKTOP_GEOMETRY)) { dirty = DesktopGeometry; NETSize sz; sz.width = message->data.data32[0]; sz.height = message->data.data32[1]; #ifdef NETWMDEBUG fprintf(stderr, "NETRootInfo::event: changeDesktopGeometry( -- , { %d, %d })\n", sz.width, sz.height); #endif changeDesktopGeometry(~0, sz); } else if (message->type == p->atom(_NET_DESKTOP_VIEWPORT)) { dirty = DesktopViewport; NETPoint pt; pt.x = message->data.data32[0]; pt.y = message->data.data32[1]; #ifdef NETWMDEBUG fprintf(stderr, "NETRootInfo::event: changeDesktopViewport(%d, { %d, %d })\n", p->current_desktop, pt.x, pt.y); #endif changeDesktopViewport(p->current_desktop, pt); } else if (message->type == p->atom(_NET_CURRENT_DESKTOP)) { dirty = CurrentDesktop; #ifdef NETWMDEBUG fprintf(stderr, "NETRootInfo::event: changeCurrentDesktop(%ld)\n", message->data.data32[0] + 1); #endif changeCurrentDesktop(message->data.data32[0] + 1); } else if (message->type == p->atom(_NET_ACTIVE_WINDOW)) { dirty = ActiveWindow; #ifdef NETWMDEBUG fprintf(stderr, "NETRootInfo::event: changeActiveWindow(0x%lx)\n", message->window); #endif RequestSource src = FromUnknown; xcb_timestamp_t timestamp = XCB_TIME_CURRENT_TIME; xcb_window_t active_window = XCB_WINDOW_NONE; // make sure there aren't unknown values if (message->data.data32[0] >= FromUnknown && message->data.data32[0] <= FromTool) { src = static_cast< RequestSource >(message->data.data32[0]); timestamp = message->data.data32[1]; active_window = message->data.data32[2]; } changeActiveWindow(message->window, src, timestamp, active_window); } else if (message->type == p->atom(_NET_WM_MOVERESIZE)) { #ifdef NETWMDEBUG fprintf(stderr, "NETRootInfo::event: moveResize(%ld, %ld, %ld, %ld)\n", message->window, message->data.data32[0], message->data.data32[1], message->data.data32[2] ); #endif moveResize(message->window, message->data.data32[0], message->data.data32[1], message->data.data32[2]); } else if (message->type == p->atom(_NET_MOVERESIZE_WINDOW)) { #ifdef NETWMDEBUG fprintf(stderr, "NETRootInfo::event: moveResizeWindow(%ld, %ld, %ld, %ld, %ld, %ld)\n", message->window, message->data.data32[0], message->data.data32[1], message->data.data32[2], message->data.data32[3], message->data.data32[4] ); #endif moveResizeWindow(message->window, message->data.data32[0], message->data.data32[1], message->data.data32[2], message->data.data32[3], message->data.data32[4]); } else if (message->type == p->atom(_NET_CLOSE_WINDOW)) { #ifdef NETWMDEBUG fprintf(stderr, "NETRootInfo::event: closeWindow(0x%lx)\n", message->window); #endif closeWindow(message->window); } else if (message->type == p->atom(_NET_RESTACK_WINDOW)) { #ifdef NETWMDEBUG fprintf(stderr, "NETRootInfo::event: restackWindow(0x%lx)\n", message->window); #endif RequestSource src = FromUnknown; xcb_timestamp_t timestamp = XCB_TIME_CURRENT_TIME; // make sure there aren't unknown values if (message->data.data32[0] >= FromUnknown && message->data.data32[0] <= FromTool) { src = static_cast< RequestSource >(message->data.data32[0]); timestamp = message->data.data32[3]; } restackWindow(message->window, src, message->data.data32[1], message->data.data32[2], timestamp); } else if (message->type == p->atom(WM_PROTOCOLS) && (xcb_atom_t)message->data.data32[ 0 ] == p->atom(_NET_WM_PING)) { dirty = WMPing; #ifdef NETWMDEBUG fprintf(stderr, "NETRootInfo::event: gotPing(0x%lx,%lu)\n", message->window, message->data.data32[1]); #endif gotPing(message->data.data32[2], message->data.data32[1]); } else if (message->type == p->atom(_NET_SHOWING_DESKTOP)) { dirty2 = WM2ShowingDesktop; #ifdef NETWMDEBUG fprintf(stderr, "NETRootInfo::event: changeShowingDesktop(%ld)\n", message->data.data32[0]); #endif changeShowingDesktop(message->data.data32[0]); } } if (eventType == XCB_PROPERTY_NOTIFY) { #ifdef NETWMDEBUG fprintf(stderr, "NETRootInfo::event: handling PropertyNotify event\n"); #endif xcb_property_notify_event_t *pe = reinterpret_cast(event); if (pe->atom == p->atom(_NET_CLIENT_LIST)) { dirty |= ClientList; } else if (pe->atom == p->atom(_NET_CLIENT_LIST_STACKING)) { dirty |= ClientListStacking; } else if (pe->atom == p->atom(_NET_DESKTOP_NAMES)) { dirty |= DesktopNames; } else if (pe->atom == p->atom(_NET_WORKAREA)) { dirty |= WorkArea; } else if (pe->atom == p->atom(_NET_NUMBER_OF_DESKTOPS)) { dirty |= NumberOfDesktops; } else if (pe->atom == p->atom(_NET_DESKTOP_GEOMETRY)) { dirty |= DesktopGeometry; } else if (pe->atom == p->atom(_NET_DESKTOP_VIEWPORT)) { dirty |= DesktopViewport; } else if (pe->atom == p->atom(_NET_CURRENT_DESKTOP)) { dirty |= CurrentDesktop; } else if (pe->atom == p->atom(_NET_ACTIVE_WINDOW)) { dirty |= ActiveWindow; } else if (pe->atom == p->atom(_NET_SHOWING_DESKTOP)) { dirty2 |= WM2ShowingDesktop; } else if (pe->atom == p->atom(_NET_SUPPORTED)) { dirty |= Supported; // update here? } else if (pe->atom == p->atom(_NET_SUPPORTING_WM_CHECK)) { dirty |= SupportingWMCheck; } else if (pe->atom == p->atom(_NET_VIRTUAL_ROOTS)) { dirty |= VirtualRoots; } else if (pe->atom == p->atom(_NET_DESKTOP_LAYOUT)) { dirty2 |= WM2DesktopLayout; } do_update = true; } if (do_update) { update(dirty, dirty2); } #ifdef NETWMDEBUG fprintf(stderr, "NETRootInfo::event: handled events, returning dirty = 0x%lx, 0x%lx\n", dirty, dirty2); #endif if (properties) { *properties = dirty; } if (properties2) { *properties2 = dirty2; } } // private functions to update the data we keep void NETRootInfo::update(NET::Properties properties, NET::Properties2 properties2) { NET::Properties dirty = properties & p->clientProperties; NET::Properties2 dirty2 = properties2 & p->clientProperties2; xcb_get_property_cookie_t cookies[255]; xcb_get_property_cookie_t wm_name_cookie; int c = 0; // Send the property requests if (dirty & Supported) { cookies[c++] = xcb_get_property(p->conn, false, p->root, p->atom(_NET_SUPPORTED), XCB_ATOM_ATOM, 0, MAX_PROP_SIZE); } if (dirty & ClientList) { cookies[c++] = xcb_get_property(p->conn, false, p->root, p->atom(_NET_CLIENT_LIST), XCB_ATOM_WINDOW, 0, MAX_PROP_SIZE); } if (dirty & ClientListStacking) { cookies[c++] = xcb_get_property(p->conn, false, p->root, p->atom(_NET_CLIENT_LIST_STACKING), XCB_ATOM_WINDOW, 0, MAX_PROP_SIZE); } if (dirty & NumberOfDesktops) { cookies[c++] = xcb_get_property(p->conn, false, p->root, p->atom(_NET_NUMBER_OF_DESKTOPS), XCB_ATOM_CARDINAL, 0, 1); } if (dirty & DesktopGeometry) { cookies[c++] = xcb_get_property(p->conn, false, p->root, p->atom(_NET_DESKTOP_GEOMETRY), XCB_ATOM_CARDINAL, 0, 2); } if (dirty & DesktopViewport) { cookies[c++] = xcb_get_property(p->conn, false, p->root, p->atom(_NET_DESKTOP_VIEWPORT), XCB_ATOM_CARDINAL, 0, MAX_PROP_SIZE); } if (dirty & CurrentDesktop) { cookies[c++] = xcb_get_property(p->conn, false, p->root, p->atom(_NET_CURRENT_DESKTOP), XCB_ATOM_CARDINAL, 0, 1); } if (dirty & DesktopNames) { cookies[c++] = xcb_get_property(p->conn, false, p->root, p->atom(_NET_DESKTOP_NAMES), p->atom(UTF8_STRING), 0, MAX_PROP_SIZE); } if (dirty & ActiveWindow) { cookies[c++] = xcb_get_property(p->conn, false, p->root, p->atom(_NET_ACTIVE_WINDOW), XCB_ATOM_WINDOW, 0, 1); } if (dirty & WorkArea) { cookies[c++] = xcb_get_property(p->conn, false, p->root, p->atom(_NET_WORKAREA), XCB_ATOM_CARDINAL, 0, MAX_PROP_SIZE); } if (dirty & SupportingWMCheck) { cookies[c++] = xcb_get_property(p->conn, false, p->root, p->atom(_NET_SUPPORTING_WM_CHECK), XCB_ATOM_WINDOW, 0, 1); } if (dirty & VirtualRoots) { cookies[c++] = xcb_get_property(p->conn, false, p->root, p->atom(_NET_VIRTUAL_ROOTS), XCB_ATOM_WINDOW, 0, 1); } if (dirty2 & WM2DesktopLayout) { cookies[c++] = xcb_get_property(p->conn, false, p->root, p->atom(_NET_DESKTOP_LAYOUT), XCB_ATOM_CARDINAL, 0, MAX_PROP_SIZE); } if (dirty2 & WM2ShowingDesktop) { cookies[c++] = xcb_get_property(p->conn, false, p->root, p->atom(_NET_SHOWING_DESKTOP), XCB_ATOM_CARDINAL, 0, 1); } // Get the replies c = 0; if (dirty & Supported) { // Only in Client mode p->properties = NET::Properties(); p->properties2 = NET::Properties2(); p->windowTypes = NET::WindowTypes(); p->states = NET::States(); p->actions = NET::Actions(); const QVector atoms = get_array_reply(p->conn, cookies[c++], XCB_ATOM_ATOM); Q_FOREACH (const xcb_atom_t atom, atoms) { updateSupportedProperties(atom); } } if (dirty & ClientList) { QList clientsToRemove; QList clientsToAdd; QVector clients = get_array_reply(p->conn, cookies[c++], XCB_ATOM_WINDOW); std::sort(clients.begin(), clients.end()); if (p->clients) { if (p->role == Client) { int new_index = 0, old_index = 0; int old_count = p->clients_count; int new_count = clients.count(); while (old_index < old_count || new_index < new_count) { if (old_index == old_count) { clientsToAdd.append(clients[new_index++]); } else if (new_index == new_count) { clientsToRemove.append(p->clients[old_index++]); } else { if (p->clients[old_index] < clients[new_index]) { clientsToRemove.append(p->clients[old_index++]); } else if (clients[new_index] < p->clients[old_index]) { clientsToAdd.append(clients[new_index++]); } else { new_index++; old_index++; } } } } delete [] p->clients; p->clients = nullptr; } else { #ifdef NETWMDEBUG fprintf(stderr, "NETRootInfo::update: client list null, creating\n"); #endif clientsToAdd.reserve(clients.count()); for (int i = 0; i < clients.count(); i++) { clientsToAdd.append(clients[i]); } } if (!clients.isEmpty()) { p->clients_count = clients.count(); p->clients = new xcb_window_t[clients.count()]; for (int i = 0; i < clients.count(); i++) { p->clients[i] = clients.at(i); } } #ifdef NETWMDEBUG fprintf(stderr, "NETRootInfo::update: client list updated (%ld clients)\n", p->clients_count); #endif for (int i = 0; i < clientsToRemove.size(); ++i) { removeClient(clientsToRemove.at(i)); } for (int i = 0; i < clientsToAdd.size(); ++i) { addClient(clientsToAdd.at(i)); } } if (dirty & ClientListStacking) { p->stacking_count = 0; delete[] p->stacking; p->stacking = nullptr; const QVector wins = get_array_reply(p->conn, cookies[c++], XCB_ATOM_WINDOW); if (!wins.isEmpty()) { p->stacking_count = wins.count(); p->stacking = new xcb_window_t[wins.count()]; for (int i = 0; i < wins.count(); i++) { p->stacking[i] = wins.at(i); } } #ifdef NETWMDEBUG fprintf(stderr, "NETRootInfo::update: client stacking updated (%ld clients)\n", p->stacking_count); #endif } if (dirty & NumberOfDesktops) { p->number_of_desktops = get_value_reply(p->conn, cookies[c++], XCB_ATOM_CARDINAL, 0); #ifdef NETWMDEBUG fprintf(stderr, "NETRootInfo::update: number of desktops = %d\n", p->number_of_desktops); #endif } if (dirty & DesktopGeometry) { p->geometry = p->rootSize; const QVector data = get_array_reply(p->conn, cookies[c++], XCB_ATOM_CARDINAL); if (data.count() == 2) { p->geometry.width = data.at(0); p->geometry.height = data.at(1); } #ifdef NETWMDEBUG fprintf(stderr, "NETRootInfo::update: desktop geometry updated\n"); #endif } if (dirty & DesktopViewport) { for (int i = 0; i < p->viewport.size(); i++) { p->viewport[i].x = p->viewport[i].y = 0; } const QVector data = get_array_reply(p->conn, cookies[c++], XCB_ATOM_CARDINAL); if (data.count() >= 2) { int n = data.count() / 2; for (int d = 0, i = 0; d < n; d++) { p->viewport[d].x = data[i++]; p->viewport[d].y = data[i++]; } #ifdef NETWMDEBUG fprintf(stderr, "NETRootInfo::update: desktop viewport array updated (%d entries)\n", p->viewport.size()); if (data.count() % 2 != 0) { fprintf(stderr, "NETRootInfo::update(): desktop viewport array " "size not a multiple of 2\n"); } #endif } } if (dirty & CurrentDesktop) { p->current_desktop = get_value_reply(p->conn, cookies[c++], XCB_ATOM_CARDINAL, 0) + 1; #ifdef NETWMDEBUG fprintf(stderr, "NETRootInfo::update: current desktop = %d\n", p->current_desktop); #endif } if (dirty & DesktopNames) { for (int i = 0; i < p->desktop_names.size(); ++i) { delete[] p->desktop_names[i]; } p->desktop_names.reset(); const QList names = get_stringlist_reply(p->conn, cookies[c++], p->atom(UTF8_STRING)); for (int i = 0; i < names.count(); i++) { p->desktop_names[i] = nstrndup(names[i].constData(), names[i].length()); } #ifdef NETWMDEBUG fprintf(stderr, "NETRootInfo::update: desktop names array updated (%d entries)\n", p->desktop_names.size()); #endif } if (dirty & ActiveWindow) { p->active = get_value_reply(p->conn, cookies[c++], XCB_ATOM_WINDOW, 0); #ifdef NETWMDEBUG fprintf(stderr, "NETRootInfo::update: active window = 0x%lx\n", p->active); #endif } if (dirty & WorkArea) { p->workarea.reset(); const QVector data = get_array_reply(p->conn, cookies[c++], XCB_ATOM_CARDINAL); if (data.count() == p->number_of_desktops * 4) { for (int i = 0, j = 0; i < p->number_of_desktops; i++) { p->workarea[i].pos.x = data[j++]; p->workarea[i].pos.y = data[j++]; p->workarea[i].size.width = data[j++]; p->workarea[i].size.height = data[j++]; } } #ifdef NETWMDEBUG fprintf(stderr, "NETRootInfo::update: work area array updated (%d entries)\n", p->workarea.size()); #endif } if (dirty & SupportingWMCheck) { delete[] p->name; p->name = nullptr; p->supportwindow = get_value_reply(p->conn, cookies[c++], XCB_ATOM_WINDOW, 0); // We'll get the reply for this request at the bottom of this function, // after we've processing the other pending replies if (p->supportwindow) wm_name_cookie = xcb_get_property(p->conn, false, p->supportwindow, p->atom(_NET_WM_NAME), p->atom(UTF8_STRING), 0, MAX_PROP_SIZE); } if (dirty & VirtualRoots) { p->virtual_roots_count = 0; delete[] p->virtual_roots; p->virtual_roots = nullptr; const QVector wins = get_array_reply(p->conn, cookies[c++], XCB_ATOM_CARDINAL); if (!wins.isEmpty()) { p->virtual_roots_count = wins.count(); p->virtual_roots = new xcb_window_t[wins.count()]; for (int i = 0; i < wins.count(); i++) { p->virtual_roots[i] = wins.at(i); } } #ifdef NETWMDEBUG fprintf(stderr, "NETRootInfo::updated: virtual roots updated (%ld windows)\n", p->virtual_roots_count); #endif } if (dirty2 & WM2DesktopLayout) { p->desktop_layout_orientation = OrientationHorizontal; p->desktop_layout_corner = DesktopLayoutCornerTopLeft; p->desktop_layout_columns = p->desktop_layout_rows = 0; const QVector data = get_array_reply(p->conn, cookies[c++], XCB_ATOM_CARDINAL); if (data.count() >= 4 && data[3] <= 3) { p->desktop_layout_corner = (NET::DesktopLayoutCorner)data[3]; } if (data.count() >= 3) { if (data[0] <= 1) { p->desktop_layout_orientation = (NET::Orientation)data[0]; } p->desktop_layout_columns = data[1]; p->desktop_layout_rows = data[2]; } #ifdef NETWMDEBUG fprintf(stderr, "NETRootInfo::updated: desktop layout updated (%d %d %d %d)\n", p->desktop_layout_orientation, p->desktop_layout_columns, p->desktop_layout_rows, p->desktop_layout_corner); #endif } if (dirty2 & WM2ShowingDesktop) { const uint32_t val = get_value_reply(p->conn, cookies[c++], XCB_ATOM_CARDINAL, 0); p->showing_desktop = bool(val); #ifdef NETWMDEBUG fprintf(stderr, "NETRootInfo::update: showing desktop = %d\n", p->showing_desktop); #endif } if ((dirty & SupportingWMCheck) && p->supportwindow) { const QByteArray ba = get_string_reply(p->conn, wm_name_cookie, p->atom(UTF8_STRING)); if (ba.length() > 0) { p->name = nstrndup((const char *) ba.constData(), ba.length()); } #ifdef NETWMDEBUG fprintf(stderr, "NETRootInfo::update: supporting window manager = '%s'\n", p->name); #endif } } xcb_connection_t *NETRootInfo::xcbConnection() const { return p->conn; } xcb_window_t NETRootInfo::rootWindow() const { return p->root; } xcb_window_t NETRootInfo::supportWindow() const { return p->supportwindow; } const char *NETRootInfo::wmName() const { return p->name; } NET::Properties NETRootInfo::supportedProperties() const { return p->properties; } NET::Properties2 NETRootInfo::supportedProperties2() const { return p->properties2; } NET::States NETRootInfo::supportedStates() const { return p->states; } NET::WindowTypes NETRootInfo::supportedWindowTypes() const { return p->windowTypes; } NET::Actions NETRootInfo::supportedActions() const { return p->actions; } NET::Properties NETRootInfo::passedProperties() const { return p->role == WindowManager ? p->properties : p->clientProperties; } NET::Properties2 NETRootInfo::passedProperties2() const { return p->role == WindowManager ? p->properties2 : p->clientProperties2; } NET::States NETRootInfo::passedStates() const { return p->role == WindowManager ? p->states : NET::States(); } NET::WindowTypes NETRootInfo::passedWindowTypes() const { return p->role == WindowManager ? p->windowTypes : NET::WindowTypes(); } NET::Actions NETRootInfo::passedActions() const { return p->role == WindowManager ? p->actions : NET::Actions(); } void NETRootInfo::setSupported(NET::Property property, bool on) { if (p->role != WindowManager) { return; } if (on && !isSupported(property)) { p->properties |= property; setSupported(); } else if (!on && isSupported(property)) { p->properties &= ~property; setSupported(); } } void NETRootInfo::setSupported(NET::Property2 property, bool on) { if (p->role != WindowManager) { return; } if (on && !isSupported(property)) { p->properties2 |= property; setSupported(); } else if (!on && isSupported(property)) { p->properties2 &= ~property; setSupported(); } } void NETRootInfo::setSupported(NET::WindowTypeMask property, bool on) { if (p->role != WindowManager) { return; } if (on && !isSupported(property)) { p->windowTypes |= property; setSupported(); } else if (!on && isSupported(property)) { p->windowTypes &= ~property; setSupported(); } } void NETRootInfo::setSupported(NET::State property, bool on) { if (p->role != WindowManager) { return; } if (on && !isSupported(property)) { p->states |= property; setSupported(); } else if (!on && isSupported(property)) { p->states &= ~property; setSupported(); } } void NETRootInfo::setSupported(NET::Action property, bool on) { if (p->role != WindowManager) { return; } if (on && !isSupported(property)) { p->actions |= property; setSupported(); } else if (!on && isSupported(property)) { p->actions &= ~property; setSupported(); } } bool NETRootInfo::isSupported(NET::Property property) const { return p->properties & property; } bool NETRootInfo::isSupported(NET::Property2 property) const { return p->properties2 & property; } bool NETRootInfo::isSupported(NET::WindowTypeMask type) const { return p->windowTypes & type; } bool NETRootInfo::isSupported(NET::State state) const { return p->states & state; } bool NETRootInfo::isSupported(NET::Action action) const { return p->actions & action; } const xcb_window_t *NETRootInfo::clientList() const { return p->clients; } int NETRootInfo::clientListCount() const { return p->clients_count; } const xcb_window_t *NETRootInfo::clientListStacking() const { return p->stacking; } int NETRootInfo::clientListStackingCount() const { return p->stacking_count; } NETSize NETRootInfo::desktopGeometry() const { return p->geometry.width != 0 ? p->geometry : p->rootSize; } NETPoint NETRootInfo::desktopViewport(int desktop) const { if (desktop < 1) { NETPoint pt; // set to (0,0) return pt; } return p->viewport[desktop - 1]; } NETRect NETRootInfo::workArea(int desktop) const { if (desktop < 1) { NETRect rt; return rt; } return p->workarea[desktop - 1]; } const char *NETRootInfo::desktopName(int desktop) const { if (desktop < 1) { return nullptr; } return p->desktop_names[desktop - 1]; } const xcb_window_t *NETRootInfo::virtualRoots() const { return p->virtual_roots; } int NETRootInfo::virtualRootsCount() const { return p->virtual_roots_count; } NET::Orientation NETRootInfo::desktopLayoutOrientation() const { return p->desktop_layout_orientation; } QSize NETRootInfo::desktopLayoutColumnsRows() const { return QSize(p->desktop_layout_columns, p->desktop_layout_rows); } NET::DesktopLayoutCorner NETRootInfo::desktopLayoutCorner() const { return p->desktop_layout_corner; } int NETRootInfo::numberOfDesktops(bool ignore_viewport) const { if (!ignore_viewport && KWindowSystem::mapViewport()) { return KWindowSystem::numberOfDesktops(); } return p->number_of_desktops == 0 ? 1 : p->number_of_desktops; } int NETRootInfo::currentDesktop(bool ignore_viewport) const { if (!ignore_viewport && KWindowSystem::mapViewport()) { return KWindowSystem::currentDesktop(); } return p->current_desktop == 0 ? 1 : p->current_desktop; } xcb_window_t NETRootInfo::activeWindow() const { return p->active; } // NETWinInfo stuffs const int NETWinInfo::OnAllDesktops = NET::OnAllDesktops; NETWinInfo::NETWinInfo(xcb_connection_t *connection, xcb_window_t window, xcb_window_t rootWindow, NET::Properties properties, NET::Properties2 properties2, Role role) { #ifdef NETWMDEBUG fprintf(stderr, "NETWinInfo::NETWinInfo: constructing object with role '%s'\n", (role == WindowManager) ? "WindowManager" : "Client"); #endif p = new NETWinInfoPrivate; p->ref = 1; p->atoms = atomsForConnection(connection); p->conn = connection; p->window = window; p->root = rootWindow; p->mapping_state = Withdrawn; p->mapping_state_dirty = true; p->state = NET::States(); p->types[ 0 ] = Unknown; p->name = (char *) nullptr; p->visible_name = (char *) nullptr; p->icon_name = (char *) nullptr; p->visible_icon_name = (char *) nullptr; p->desktop = p->pid = 0; p->handled_icons = false; p->user_time = -1U; p->startup_id = nullptr; p->transient_for = XCB_NONE; p->opacity = 0xffffffffU; p->window_group = XCB_NONE; p->icon_pixmap = XCB_PIXMAP_NONE; p->icon_mask = XCB_PIXMAP_NONE; p->allowed_actions = NET::Actions(); p->has_net_support = false; p->class_class = (char *) nullptr; p->class_name = (char *) nullptr; p->window_role = (char *) nullptr; p->client_machine = (char *) nullptr; p->icon_sizes = nullptr; p->activities = (char *) nullptr; p->desktop_file = nullptr; p->blockCompositing = false; p->urgency = false; p->input = true; p->initialMappingState = NET::Withdrawn; p->protocols = NET::NoProtocol; // p->strut.left = p->strut.right = p->strut.top = p->strut.bottom = 0; // p->frame_strut.left = p->frame_strut.right = p->frame_strut.top = // p->frame_strut.bottom = 0; p->properties = properties; p->properties2 = properties2; p->icon_count = 0; p->role = role; update(p->properties, p->properties2); } #ifndef KWINDOWSYSTEM_NO_DEPRECATED NETWinInfo::NETWinInfo(xcb_connection_t *connection, xcb_window_t window, xcb_window_t rootWindow, NET::Properties properties, Role role) { #ifdef NETWMDEBUG fprintf(stderr, "NETWinInfo::NETWinInfo: constructing object with role '%s'\n", (role == WindowManager) ? "WindowManager" : "Client"); #endif p = new NETWinInfoPrivate; p->ref = 1; p->atoms = atomsForConnection(connection); p->conn = connection; p->window = window; p->root = rootWindow; p->mapping_state = Withdrawn; p->mapping_state_dirty = true; p->state = NET::States(); p->types[ 0 ] = Unknown; p->name = (char *) nullptr; p->visible_name = (char *) nullptr; p->icon_name = (char *) nullptr; p->visible_icon_name = (char *) nullptr; p->desktop = p->pid = 0; p->handled_icons = false; p->user_time = -1U; p->startup_id = nullptr; p->transient_for = XCB_NONE; p->opacity = 0xffffffffU; p->window_group = XCB_NONE; p->icon_pixmap = XCB_PIXMAP_NONE; p->icon_mask = XCB_PIXMAP_NONE; p->allowed_actions = NET::Actions(); p->has_net_support = false; p->class_class = (char *) nullptr; p->class_name = (char *) nullptr; p->window_role = (char *) nullptr; p->client_machine = (char *) nullptr; p->icon_sizes = nullptr; p->activities = (char *) nullptr; p->desktop_file = nullptr; p->blockCompositing = false; p->urgency = false; p->input = true; p->initialMappingState = NET::Withdrawn; p->protocols = NET::NoProtocol; // p->strut.left = p->strut.right = p->strut.top = p->strut.bottom = 0; // p->frame_strut.left = p->frame_strut.right = p->frame_strut.top = // p->frame_strut.bottom = 0; p->properties = properties; p->properties2 = NET::Properties2(); p->icon_count = 0; p->role = role; update(p->properties); } #endif NETWinInfo::NETWinInfo(const NETWinInfo &wininfo) { p = wininfo.p; p->ref++; } NETWinInfo::~NETWinInfo() { refdec_nwi(p); if (! p->ref) { delete p; } } // assignment operator const NETWinInfo &NETWinInfo::operator=(const NETWinInfo &wininfo) { #ifdef NETWMDEBUG fprintf(stderr, "NETWinInfo::operator=()\n"); #endif if (p != wininfo.p) { refdec_nwi(p); if (! p->ref) { delete p; } } p = wininfo.p; p->ref++; return *this; } void NETWinInfo::setIcon(NETIcon icon, bool replace) { setIconInternal(p->icons, p->icon_count, p->atom(_NET_WM_ICON), icon, replace); } void NETWinInfo::setIconInternal(NETRArray &icons, int &icon_count, xcb_atom_t property, NETIcon icon, bool replace) { if (p->role != Client) { return; } if (replace) { for (int i = 0; i < icons.size(); i++) { delete [] icons[i].data; icons[i].data = nullptr; icons[i].size.width = 0; icons[i].size.height = 0; } icon_count = 0; } // assign icon icons[icon_count] = icon; icon_count++; // do a deep copy, we want to own the data NETIcon &ni = icons[icon_count - 1]; int sz = ni.size.width * ni.size.height; uint32_t *d = new uint32_t[sz]; ni.data = (unsigned char *) d; memcpy(d, icon.data, sz * sizeof(uint32_t)); // compute property length int proplen = 0; for (int i = 0; i < icon_count; i++) { proplen += 2 + (icons[i].size.width * icons[i].size.height); } uint32_t *prop = new uint32_t[proplen], *pprop = prop; for (int i = 0; i < icon_count; i++) { // copy size into property *pprop++ = icons[i].size.width; *pprop++ = icons[i].size.height; // copy data into property sz = (icons[i].size.width * icons[i].size.height); uint32_t *d32 = (uint32_t *) icons[i].data; for (int j = 0; j < sz; j++) { *pprop++ = *d32++; } } xcb_change_property(p->conn, XCB_PROP_MODE_REPLACE, p->window, property, XCB_ATOM_CARDINAL, 32, proplen, (const void *) prop); delete [] prop; delete [] p->icon_sizes; p->icon_sizes = nullptr; } void NETWinInfo::setIconGeometry(NETRect geometry) { if (p->role != Client) { return; } p->icon_geom = geometry; if (geometry.size.width == 0) { // Empty xcb_delete_property(p->conn, p->window, p->atom(_NET_WM_ICON_GEOMETRY)); } else { uint32_t data[4]; data[0] = geometry.pos.x; data[1] = geometry.pos.y; data[2] = geometry.size.width; data[3] = geometry.size.height; xcb_change_property(p->conn, XCB_PROP_MODE_REPLACE, p->window, p->atom(_NET_WM_ICON_GEOMETRY), XCB_ATOM_CARDINAL, 32, 4, (const void *) data); } } void NETWinInfo::setExtendedStrut(const NETExtendedStrut &extended_strut) { if (p->role != Client) { return; } p->extended_strut = extended_strut; uint32_t data[12]; data[0] = extended_strut.left_width; data[1] = extended_strut.right_width; data[2] = extended_strut.top_width; data[3] = extended_strut.bottom_width; data[4] = extended_strut.left_start; data[5] = extended_strut.left_end; data[6] = extended_strut.right_start; data[7] = extended_strut.right_end; data[8] = extended_strut.top_start; data[9] = extended_strut.top_end; data[10] = extended_strut.bottom_start; data[11] = extended_strut.bottom_end; xcb_change_property(p->conn, XCB_PROP_MODE_REPLACE, p->window, p->atom(_NET_WM_STRUT_PARTIAL), XCB_ATOM_CARDINAL, 32, 12, (const void *) data); } void NETWinInfo::setStrut(NETStrut strut) { if (p->role != Client) { return; } p->strut = strut; uint32_t data[4]; data[0] = strut.left; data[1] = strut.right; data[2] = strut.top; data[3] = strut.bottom; xcb_change_property(p->conn, XCB_PROP_MODE_REPLACE, p->window, p->atom(_NET_WM_STRUT), XCB_ATOM_CARDINAL, 32, 4, (const void *) data); } void NETWinInfo::setFullscreenMonitors(NETFullscreenMonitors topology) { if (p->role == Client) { const uint32_t data[5] = { uint32_t(topology.top), uint32_t(topology.bottom), uint32_t(topology.left), uint32_t(topology.right), 1 }; send_client_message(p->conn, netwm_sendevent_mask, p->root, p->window, p->atom(_NET_WM_FULLSCREEN_MONITORS), data); } else { p->fullscreen_monitors = topology; uint32_t data[4]; data[0] = topology.top; data[1] = topology.bottom; data[2] = topology.left; data[3] = topology.right; xcb_change_property(p->conn, XCB_PROP_MODE_REPLACE, p->window, p->atom(_NET_WM_FULLSCREEN_MONITORS), XCB_ATOM_CARDINAL, 32, 4, (const void *) data); } } void NETWinInfo::setState(NET::States state, NET::States mask) { if (p->mapping_state_dirty) { updateWMState(); } // setState() needs to know the current state, so read it even if not requested if ((p->properties & WMState) == 0) { p->properties |= WMState; update(WMState); p->properties &= ~WMState; } if (p->role == Client && p->mapping_state != Withdrawn) { #ifdef NETWMDEBUG fprintf(stderr, "NETWinInfo::setState (0x%lx, 0x%lx) (Client)\n", state, mask); #endif // NETWMDEBUG xcb_client_message_event_t event; event.response_type = XCB_CLIENT_MESSAGE; event.format = 32; event.sequence = 0; event.window = p->window; event.type = p->atom(_NET_WM_STATE); event.data.data32[3] = 0; event.data.data32[4] = 0; if ((mask & Modal) && ((p->state & Modal) != (state & Modal))) { event.data.data32[0] = (state & Modal) ? 1 : 0; event.data.data32[1] = p->atom(_NET_WM_STATE_MODAL); event.data.data32[2] = 0l; xcb_send_event(p->conn, false, p->root, netwm_sendevent_mask, (const char *) &event); } if ((mask & Sticky) && ((p->state & Sticky) != (state & Sticky))) { event.data.data32[0] = (state & Sticky) ? 1 : 0; event.data.data32[1] = p->atom(_NET_WM_STATE_STICKY); event.data.data32[2] = 0l; xcb_send_event(p->conn, false, p->root, netwm_sendevent_mask, (const char *) &event); } if ((mask & Max) && (((p->state & mask) & Max) != (state & Max))) { NET::States wishstate = (p->state & ~mask) | (state & mask); if (((wishstate & MaxHoriz) != (p->state & MaxHoriz)) && ((wishstate & MaxVert) != (p->state & MaxVert))) { if ((wishstate & Max) == Max) { event.data.data32[0] = 1; event.data.data32[1] = p->atom(_NET_WM_STATE_MAXIMIZED_HORZ); event.data.data32[2] = p->atom(_NET_WM_STATE_MAXIMIZED_VERT); xcb_send_event(p->conn, false, p->root, netwm_sendevent_mask, (const char *) &event); } else if ((wishstate & Max) == 0) { event.data.data32[0] = 0; event.data.data32[1] = p->atom(_NET_WM_STATE_MAXIMIZED_HORZ); event.data.data32[2] = p->atom(_NET_WM_STATE_MAXIMIZED_VERT); xcb_send_event(p->conn, false, p->root, netwm_sendevent_mask, (const char *) &event); } else { event.data.data32[0] = (wishstate & MaxHoriz) ? 1 : 0; event.data.data32[1] = p->atom(_NET_WM_STATE_MAXIMIZED_HORZ); event.data.data32[2] = 0; xcb_send_event(p->conn, false, p->root, netwm_sendevent_mask, (const char *) &event); event.data.data32[0] = (wishstate & MaxVert) ? 1 : 0; event.data.data32[1] = p->atom(_NET_WM_STATE_MAXIMIZED_VERT); event.data.data32[2] = 0; xcb_send_event(p->conn, false, p->root, netwm_sendevent_mask, (const char *) &event); } } else if ((wishstate & MaxVert) != (p->state & MaxVert)) { event.data.data32[0] = (wishstate & MaxVert) ? 1 : 0; event.data.data32[1] = p->atom(_NET_WM_STATE_MAXIMIZED_VERT); event.data.data32[2] = 0; xcb_send_event(p->conn, false, p->root, netwm_sendevent_mask, (const char *) &event); } else if ((wishstate & MaxHoriz) != (p->state & MaxHoriz)) { event.data.data32[0] = (wishstate & MaxHoriz) ? 1 : 0; event.data.data32[1] = p->atom(_NET_WM_STATE_MAXIMIZED_HORZ); event.data.data32[2] = 0; xcb_send_event(p->conn, false, p->root, netwm_sendevent_mask, (const char *) &event); } } if ((mask & Shaded) && ((p->state & Shaded) != (state & Shaded))) { event.data.data32[0] = (state & Shaded) ? 1 : 0; event.data.data32[1] = p->atom(_NET_WM_STATE_SHADED); event.data.data32[2] = 0l; xcb_send_event(p->conn, false, p->root, netwm_sendevent_mask, (const char *) &event); } if ((mask & SkipTaskbar) && ((p->state & SkipTaskbar) != (state & SkipTaskbar))) { event.data.data32[0] = (state & SkipTaskbar) ? 1 : 0; event.data.data32[1] = p->atom(_NET_WM_STATE_SKIP_TASKBAR); event.data.data32[2] = 0l; xcb_send_event(p->conn, false, p->root, netwm_sendevent_mask, (const char *) &event); } if ((mask & SkipPager) && ((p->state & SkipPager) != (state & SkipPager))) { event.data.data32[0] = (state & SkipPager) ? 1 : 0; event.data.data32[1] = p->atom(_NET_WM_STATE_SKIP_PAGER); event.data.data32[2] = 0l; xcb_send_event(p->conn, false, p->root, netwm_sendevent_mask, (const char *) &event); } if ((mask & SkipSwitcher) && ((p->state & SkipSwitcher) != (state & SkipSwitcher))) { event.data.data32[0] = (state & SkipSwitcher) ? 1 : 0; event.data.data32[1] = p->atom(_KDE_NET_WM_STATE_SKIP_SWITCHER); event.data.data32[2] = 0l; xcb_send_event(p->conn, false, p->root, netwm_sendevent_mask, (const char *) &event); } if ((mask & Hidden) && ((p->state & Hidden) != (state & Hidden))) { event.data.data32[0] = (state & Hidden) ? 1 : 0; event.data.data32[1] = p->atom(_NET_WM_STATE_HIDDEN); event.data.data32[2] = 0l; xcb_send_event(p->conn, false, p->root, netwm_sendevent_mask, (const char *) &event); } if ((mask & FullScreen) && ((p->state & FullScreen) != (state & FullScreen))) { event.data.data32[0] = (state & FullScreen) ? 1 : 0; event.data.data32[1] = p->atom(_NET_WM_STATE_FULLSCREEN); event.data.data32[2] = 0l; xcb_send_event(p->conn, false, p->root, netwm_sendevent_mask, (const char *) &event); } if ((mask & KeepAbove) && ((p->state & KeepAbove) != (state & KeepAbove))) { event.data.data32[0] = (state & KeepAbove) ? 1 : 0; event.data.data32[1] = p->atom(_NET_WM_STATE_ABOVE); event.data.data32[2] = 0l; xcb_send_event(p->conn, false, p->root, netwm_sendevent_mask, (const char *) &event); } if ((mask & KeepBelow) && ((p->state & KeepBelow) != (state & KeepBelow))) { event.data.data32[0] = (state & KeepBelow) ? 1 : 0; event.data.data32[1] = p->atom(_NET_WM_STATE_BELOW); event.data.data32[2] = 0l; xcb_send_event(p->conn, false, p->root, netwm_sendevent_mask, (const char *) &event); } if ((mask & StaysOnTop) && ((p->state & StaysOnTop) != (state & StaysOnTop))) { event.data.data32[0] = (state & StaysOnTop) ? 1 : 0; event.data.data32[1] = p->atom(_NET_WM_STATE_STAYS_ON_TOP); event.data.data32[2] = 0l; xcb_send_event(p->conn, false, p->root, netwm_sendevent_mask, (const char *) &event); } if ((mask & DemandsAttention) && ((p->state & DemandsAttention) != (state & DemandsAttention))) { event.data.data32[0] = (state & DemandsAttention) ? 1 : 0; event.data.data32[1] = p->atom(_NET_WM_STATE_DEMANDS_ATTENTION); event.data.data32[2] = 0l; xcb_send_event(p->conn, false, p->root, netwm_sendevent_mask, (const char *) &event); } //Focused is not added here as it is effectively "read only" set by the WM, a client setting it would be silly } else { p->state &= ~mask; p->state |= state; uint32_t data[50]; int count = 0; // Hints if (p->state & Modal) { data[count++] = p->atom(_NET_WM_STATE_MODAL); } if (p->state & MaxVert) { data[count++] = p->atom(_NET_WM_STATE_MAXIMIZED_VERT); } if (p->state & MaxHoriz) { data[count++] = p->atom(_NET_WM_STATE_MAXIMIZED_HORZ); } if (p->state & Shaded) { data[count++] = p->atom(_NET_WM_STATE_SHADED); } if (p->state & Hidden) { data[count++] = p->atom(_NET_WM_STATE_HIDDEN); } if (p->state & FullScreen) { data[count++] = p->atom(_NET_WM_STATE_FULLSCREEN); } if (p->state & DemandsAttention) { data[count++] = p->atom(_NET_WM_STATE_DEMANDS_ATTENTION); } if (p->state & Focused) { data[count++] = p->atom(_NET_WM_STATE_FOCUSED); } // Policy if (p->state & KeepAbove) { data[count++] = p->atom(_NET_WM_STATE_ABOVE); } if (p->state & KeepBelow) { data[count++] = p->atom(_NET_WM_STATE_BELOW); } if (p->state & StaysOnTop) { data[count++] = p->atom(_NET_WM_STATE_STAYS_ON_TOP); } if (p->state & Sticky) { data[count++] = p->atom(_NET_WM_STATE_STICKY); } if (p->state & SkipTaskbar) { data[count++] = p->atom(_NET_WM_STATE_SKIP_TASKBAR); } if (p->state & SkipPager) { data[count++] = p->atom(_NET_WM_STATE_SKIP_PAGER); } if (p->state & SkipSwitcher) { data[count++] = p->atom(_KDE_NET_WM_STATE_SKIP_SWITCHER); } #ifdef NETWMDEBUG fprintf(stderr, "NETWinInfo::setState: setting state property (%d)\n", count); for (int i = 0; i < count; i++) { const QByteArray ba = get_atom_name(p->conn, data[i]); fprintf(stderr, "NETWinInfo::setState: state %ld '%s'\n", data[i], ba.constData()); } #endif xcb_change_property(p->conn, XCB_PROP_MODE_REPLACE, p->window, p->atom(_NET_WM_STATE), XCB_ATOM_ATOM, 32, count, (const void *) data); } } void NETWinInfo::setWindowType(WindowType type) { if (p->role != Client) { return; } int len; uint32_t data[2]; switch (type) { case Override: // spec extension: override window type. we must comply with the spec // and provide a fall back (normal seems best) data[0] = p->atom(_KDE_NET_WM_WINDOW_TYPE_OVERRIDE); data[1] = p->atom(_NET_WM_WINDOW_TYPE_NORMAL); len = 2; break; case Dialog: data[0] = p->atom(_NET_WM_WINDOW_TYPE_DIALOG); data[1] = XCB_NONE; len = 1; break; case Menu: data[0] = p->atom(_NET_WM_WINDOW_TYPE_MENU); data[1] = XCB_NONE; len = 1; break; case TopMenu: // spec extension: override window type. we must comply with the spec // and provide a fall back (dock seems best) data[0] = p->atom(_KDE_NET_WM_WINDOW_TYPE_TOPMENU); data[1] = p->atom(_NET_WM_WINDOW_TYPE_DOCK); len = 2; break; case Toolbar: data[0] = p->atom(_NET_WM_WINDOW_TYPE_TOOLBAR); data[1] = XCB_NONE; len = 1; break; case Dock: data[0] = p->atom(_NET_WM_WINDOW_TYPE_DOCK); data[1] = XCB_NONE; len = 1; break; case Desktop: data[0] = p->atom(_NET_WM_WINDOW_TYPE_DESKTOP); data[1] = XCB_NONE; len = 1; break; case Utility: data[0] = p->atom(_NET_WM_WINDOW_TYPE_UTILITY); data[1] = p->atom(_NET_WM_WINDOW_TYPE_DIALOG); // fallback for old netwm version len = 2; break; case Splash: data[0] = p->atom(_NET_WM_WINDOW_TYPE_SPLASH); data[1] = p->atom(_NET_WM_WINDOW_TYPE_DOCK); // fallback (dock seems best) len = 2; break; case DropdownMenu: data[0] = p->atom(_NET_WM_WINDOW_TYPE_DROPDOWN_MENU); data[1] = p->atom(_NET_WM_WINDOW_TYPE_MENU); // fallback (tearoff seems to be the best) len = 1; break; case PopupMenu: data[0] = p->atom(_NET_WM_WINDOW_TYPE_POPUP_MENU); data[1] = p->atom(_NET_WM_WINDOW_TYPE_MENU); // fallback (tearoff seems to be the best) len = 1; break; case Tooltip: data[0] = p->atom(_NET_WM_WINDOW_TYPE_TOOLTIP); data[1] = XCB_NONE; len = 1; break; case Notification: data[0] = p->atom(_NET_WM_WINDOW_TYPE_NOTIFICATION); data[1] = p->atom(_NET_WM_WINDOW_TYPE_UTILITY); // fallback (utility seems to be the best) len = 1; break; case ComboBox: data[0] = p->atom(_NET_WM_WINDOW_TYPE_COMBO); data[1] = XCB_NONE; len = 1; break; case DNDIcon: data[0] = p->atom(_NET_WM_WINDOW_TYPE_DND); data[1] = XCB_NONE; len = 1; break; case OnScreenDisplay: data[0] = p->atom(_KDE_NET_WM_WINDOW_TYPE_ON_SCREEN_DISPLAY); data[1] = p->atom(_NET_WM_WINDOW_TYPE_NOTIFICATION); len = 1; break; + case CriticalNotification: + data[0] = p->atom(_KDE_NET_WM_WINDOW_TYPE_CRITICAL_NOTIFICATION); + data[1] = p->atom(_NET_WM_WINDOW_TYPE_NOTIFICATION); + len = 1; + break; + default: case Normal: data[0] = p->atom(_NET_WM_WINDOW_TYPE_NORMAL); data[1] = XCB_NONE; len = 1; break; } xcb_change_property(p->conn, XCB_PROP_MODE_REPLACE, p->window, p->atom(_NET_WM_WINDOW_TYPE), XCB_ATOM_ATOM, 32, len, (const void *) &data); } void NETWinInfo::setName(const char *name) { if (p->role != Client) { return; } delete [] p->name; p->name = nstrdup(name); if (p->name[0] != '\0') xcb_change_property(p->conn, XCB_PROP_MODE_REPLACE, p->window, p->atom(_NET_WM_NAME), p->atom(UTF8_STRING), 8, strlen(p->name), (const void *) p->name); else { xcb_delete_property(p->conn, p->window, p->atom(_NET_WM_NAME)); } } void NETWinInfo::setVisibleName(const char *visibleName) { if (p->role != WindowManager) { return; } delete [] p->visible_name; p->visible_name = nstrdup(visibleName); if (p->visible_name[0] != '\0') xcb_change_property(p->conn, XCB_PROP_MODE_REPLACE, p->window, p->atom(_NET_WM_VISIBLE_NAME), p->atom(UTF8_STRING), 8, strlen(p->visible_name), (const void *) p->visible_name); else { xcb_delete_property(p->conn, p->window, p->atom(_NET_WM_VISIBLE_NAME)); } } void NETWinInfo::setIconName(const char *iconName) { if (p->role != Client) { return; } delete [] p->icon_name; p->icon_name = nstrdup(iconName); if (p->icon_name[0] != '\0') xcb_change_property(p->conn, XCB_PROP_MODE_REPLACE, p->window, p->atom(_NET_WM_ICON_NAME), p->atom(UTF8_STRING), 8, strlen(p->icon_name), (const void *) p->icon_name); else { xcb_delete_property(p->conn, p->window, p->atom(_NET_WM_ICON_NAME)); } } void NETWinInfo::setVisibleIconName(const char *visibleIconName) { if (p->role != WindowManager) { return; } delete [] p->visible_icon_name; p->visible_icon_name = nstrdup(visibleIconName); if (p->visible_icon_name[0] != '\0') xcb_change_property(p->conn, XCB_PROP_MODE_REPLACE, p->window, p->atom(_NET_WM_VISIBLE_ICON_NAME), p->atom(UTF8_STRING), 8, strlen(p->visible_icon_name), (const void *) p->visible_icon_name); else { xcb_delete_property(p->conn, p->window, p->atom(_NET_WM_VISIBLE_ICON_NAME)); } } void NETWinInfo::setDesktop(int desktop, bool ignore_viewport) { if (p->mapping_state_dirty) { updateWMState(); } if (p->role == Client && p->mapping_state != Withdrawn) { // We only send a ClientMessage if we are 1) a client and 2) managed if (desktop == 0) { return; // We can't do that while being managed } if (!ignore_viewport && KWindowSystem::mapViewport()) { KWindowSystem::setOnDesktop(p->window, desktop); return; } const uint32_t data[5] = { desktop == OnAllDesktops ? 0xffffffff : desktop - 1, 0, 0, 0, 0 }; send_client_message(p->conn, netwm_sendevent_mask, p->root, p->window, p->atom(_NET_WM_DESKTOP), data); } else { // Otherwise we just set or remove the property directly p->desktop = desktop; if (desktop == 0) { xcb_delete_property(p->conn, p->window, p->atom(_NET_WM_DESKTOP)); } else { uint32_t d = (desktop == OnAllDesktops ? 0xffffffff : desktop - 1); xcb_change_property(p->conn, XCB_PROP_MODE_REPLACE, p->window, p->atom(_NET_WM_DESKTOP), XCB_ATOM_CARDINAL, 32, 1, (const void *) &d); } } } void NETWinInfo::setPid(int pid) { if (p->role != Client) { return; } p->pid = pid; uint32_t d = pid; xcb_change_property(p->conn, XCB_PROP_MODE_REPLACE, p->window, p->atom(_NET_WM_PID), XCB_ATOM_CARDINAL, 32, 1, (const void *) &d); } void NETWinInfo::setHandledIcons(bool handled) { if (p->role != Client) { return; } p->handled_icons = handled; uint32_t d = handled; xcb_change_property(p->conn, XCB_PROP_MODE_REPLACE, p->window, p->atom(_NET_WM_HANDLED_ICONS), XCB_ATOM_CARDINAL, 32, 1, (const void *) &d); } void NETWinInfo::setStartupId(const char *id) { if (p->role != Client) { return; } delete[] p->startup_id; p->startup_id = nstrdup(id); xcb_change_property(p->conn, XCB_PROP_MODE_REPLACE, p->window, p->atom(_NET_STARTUP_ID), p->atom(UTF8_STRING), 8, strlen(p->startup_id), (const void *) p->startup_id); } void NETWinInfo::setOpacity(unsigned long opacity) { // if (p->role != Client) return; p->opacity = opacity; xcb_change_property(p->conn, XCB_PROP_MODE_REPLACE, p->window, p->atom(_NET_WM_WINDOW_OPACITY), XCB_ATOM_CARDINAL, 32, 1, (const void *) &p->opacity); } void NETWinInfo::setAllowedActions(NET::Actions actions) { if (p->role != WindowManager) { return; } uint32_t data[50]; int count = 0; p->allowed_actions = actions; if (p->allowed_actions & ActionMove) { data[count++] = p->atom(_NET_WM_ACTION_MOVE); } if (p->allowed_actions & ActionResize) { data[count++] = p->atom(_NET_WM_ACTION_RESIZE); } if (p->allowed_actions & ActionMinimize) { data[count++] = p->atom(_NET_WM_ACTION_MINIMIZE); } if (p->allowed_actions & ActionShade) { data[count++] = p->atom(_NET_WM_ACTION_SHADE); } if (p->allowed_actions & ActionStick) { data[count++] = p->atom(_NET_WM_ACTION_STICK); } if (p->allowed_actions & ActionMaxVert) { data[count++] = p->atom(_NET_WM_ACTION_MAXIMIZE_VERT); } if (p->allowed_actions & ActionMaxHoriz) { data[count++] = p->atom(_NET_WM_ACTION_MAXIMIZE_HORZ); } if (p->allowed_actions & ActionFullScreen) { data[count++] = p->atom(_NET_WM_ACTION_FULLSCREEN); } if (p->allowed_actions & ActionChangeDesktop) { data[count++] = p->atom(_NET_WM_ACTION_CHANGE_DESKTOP); } if (p->allowed_actions & ActionClose) { data[count++] = p->atom(_NET_WM_ACTION_CLOSE); } #ifdef NETWMDEBUG fprintf(stderr, "NETWinInfo::setAllowedActions: setting property (%d)\n", count); for (int i = 0; i < count; i++) { const QByteArray ba = get_atom_name(p->conn, data[i]); fprintf(stderr, "NETWinInfo::setAllowedActions: action %ld '%s'\n", data[i], ba.constData()); } #endif xcb_change_property(p->conn, XCB_PROP_MODE_REPLACE, p->window, p->atom(_NET_WM_ALLOWED_ACTIONS), XCB_ATOM_ATOM, 32, count, (const void *) data); } void NETWinInfo::setFrameExtents(NETStrut strut) { if (p->role != WindowManager) { return; } p->frame_strut = strut; uint32_t d[4]; d[0] = strut.left; d[1] = strut.right; d[2] = strut.top; d[3] = strut.bottom; xcb_change_property(p->conn, XCB_PROP_MODE_REPLACE, p->window, p->atom(_NET_FRAME_EXTENTS), XCB_ATOM_CARDINAL, 32, 4, (const void *) d); xcb_change_property(p->conn, XCB_PROP_MODE_REPLACE, p->window, p->atom(_KDE_NET_WM_FRAME_STRUT), XCB_ATOM_CARDINAL, 32, 4, (const void *) d); } NETStrut NETWinInfo::frameExtents() const { return p->frame_strut; } void NETWinInfo::setFrameOverlap(NETStrut strut) { if (strut.left != -1 || strut.top != -1 || strut.right != -1 || strut.bottom != -1) { strut.left = qMax(0, strut.left); strut.top = qMax(0, strut.top); strut.right = qMax(0, strut.right); strut.bottom = qMax(0, strut.bottom); } p->frame_overlap = strut; uint32_t d[4]; d[0] = strut.left; d[1] = strut.right; d[2] = strut.top; d[3] = strut.bottom; xcb_change_property(p->conn, XCB_PROP_MODE_REPLACE, p->window, p->atom(_NET_WM_FRAME_OVERLAP), XCB_ATOM_CARDINAL, 32, 4, (const void *) d); } NETStrut NETWinInfo::frameOverlap() const { return p->frame_overlap; } void NETWinInfo::kdeGeometry(NETRect &frame, NETRect &window) { if (p->win_geom.size.width == 0 || p->win_geom.size.height == 0) { const xcb_get_geometry_cookie_t geometry_cookie = xcb_get_geometry(p->conn, p->window); const xcb_translate_coordinates_cookie_t translate_cookie = xcb_translate_coordinates(p->conn, p->window, p->root, 0, 0); xcb_get_geometry_reply_t *geometry = xcb_get_geometry_reply(p->conn, geometry_cookie, nullptr); xcb_translate_coordinates_reply_t *translated = xcb_translate_coordinates_reply(p->conn, translate_cookie, nullptr); if (geometry && translated) { p->win_geom.pos.x = translated->dst_x; p->win_geom.pos.y = translated->dst_y; p->win_geom.size.width = geometry->width; p->win_geom.size.height = geometry->height; } if (geometry) { free(geometry); } if (translated) { free(translated); } } // TODO try to work also without _NET_WM_FRAME_EXTENTS window = p->win_geom; frame.pos.x = window.pos.x - p->frame_strut.left; frame.pos.y = window.pos.y - p->frame_strut.top; frame.size.width = window.size.width + p->frame_strut.left + p->frame_strut.right; frame.size.height = window.size.height + p->frame_strut.top + p->frame_strut.bottom; } NETIcon NETWinInfo::icon(int width, int height) const { return iconInternal(p->icons, p->icon_count, width, height); } const int *NETWinInfo::iconSizes() const { if (p->icon_sizes == nullptr) { p->icon_sizes = new int[ p->icon_count * 2 + 2 ]; for (int i = 0; i < p->icon_count; ++i) { p->icon_sizes[ i * 2 ] = p->icons[ i ].size.width; p->icon_sizes[ i * 2 + 1 ] = p->icons[ i ].size.height; } p->icon_sizes[ p->icon_count * 2 ] = 0; // terminator p->icon_sizes[ p->icon_count * 2 + 1 ] = 0; } return p->icon_sizes; } NETIcon NETWinInfo::iconInternal(NETRArray &icons, int icon_count, int width, int height) const { NETIcon result; if (!icon_count) { result.size.width = 0; result.size.height = 0; result.data = nullptr; return result; } // find the largest icon result = icons[0]; for (int i = 1; i < icons.size(); i++) { if (icons[i].size.width >= result.size.width && icons[i].size.height >= result.size.height) { result = icons[i]; } } // return the largest icon if w and h are -1 if (width == -1 && height == -1) { return result; } // find the icon that's closest in size to w x h... for (int i = 0; i < icons.size(); i++) { if ((icons[i].size.width >= width && icons[i].size.width < result.size.width) && (icons[i].size.height >= height && icons[i].size.height < result.size.height)) { result = icons[i]; } } return result; } void NETWinInfo::setUserTime(xcb_timestamp_t time) { if (p->role != Client) { return; } p->user_time = time; uint32_t d = time; xcb_change_property(p->conn, XCB_PROP_MODE_REPLACE, p->window, p->atom(_NET_WM_USER_TIME), XCB_ATOM_CARDINAL, 32, 1, (const void *) &d); } NET::Properties NETWinInfo::event(xcb_generic_event_t *ev) { NET::Properties properties; event(ev, &properties); return properties; } void NETWinInfo::event(xcb_generic_event_t *event, NET::Properties *properties, NET::Properties2 *properties2) { NET::Properties dirty; NET::Properties2 dirty2; bool do_update = false; const uint8_t eventType = event->response_type & ~0x80; if (p->role == WindowManager && eventType == XCB_CLIENT_MESSAGE && reinterpret_cast(event)->format == 32) { xcb_client_message_event_t *message = reinterpret_cast(event); #ifdef NETWMDEBUG fprintf(stderr, "NETWinInfo::event: handling ClientMessage event\n"); #endif // NETWMDEBUG if (message->type == p->atom(_NET_WM_STATE)) { dirty = WMState; // we need to generate a change mask #ifdef NETWMDEBUG fprintf(stderr, "NETWinInfo::event: state client message, getting new state/mask\n"); #endif int i; NET::States state = NET::States(), mask = NET::States(); for (i = 1; i < 3; i++) { #ifdef NETWMDEBUG const QByteArray ba = get_atom_name(p->conn, (xcb_atom_t) message->data.data32[i]); fprintf(stderr, "NETWinInfo::event: message %ld '%s'\n", message->data.data32[i], ba.constData()); #endif if ((xcb_atom_t) message->data.data32[i] == p->atom(_NET_WM_STATE_MODAL)) { mask |= Modal; } else if ((xcb_atom_t) message->data.data32[i] == p->atom(_NET_WM_STATE_STICKY)) { mask |= Sticky; } else if ((xcb_atom_t) message->data.data32[i] == p->atom(_NET_WM_STATE_MAXIMIZED_VERT)) { mask |= MaxVert; } else if ((xcb_atom_t) message->data.data32[i] == p->atom(_NET_WM_STATE_MAXIMIZED_HORZ)) { mask |= MaxHoriz; } else if ((xcb_atom_t) message->data.data32[i] == p->atom(_NET_WM_STATE_SHADED)) { mask |= Shaded; } else if ((xcb_atom_t) message->data.data32[i] == p->atom(_NET_WM_STATE_SKIP_TASKBAR)) { mask |= SkipTaskbar; } else if ((xcb_atom_t) message->data.data32[i] == p->atom(_NET_WM_STATE_SKIP_PAGER)) { mask |= SkipPager; } else if ((xcb_atom_t) message->data.data32[i] == p->atom(_KDE_NET_WM_STATE_SKIP_SWITCHER)) { mask |= SkipSwitcher; } else if ((xcb_atom_t) message->data.data32[i] == p->atom(_NET_WM_STATE_HIDDEN)) { mask |= Hidden; } else if ((xcb_atom_t) message->data.data32[i] == p->atom(_NET_WM_STATE_FULLSCREEN)) { mask |= FullScreen; } else if ((xcb_atom_t) message->data.data32[i] == p->atom(_NET_WM_STATE_ABOVE)) { mask |= KeepAbove; } else if ((xcb_atom_t) message->data.data32[i] == p->atom(_NET_WM_STATE_BELOW)) { mask |= KeepBelow; } else if ((xcb_atom_t) message->data.data32[i] == p->atom(_NET_WM_STATE_DEMANDS_ATTENTION)) { mask |= DemandsAttention; } else if ((xcb_atom_t) message->data.data32[i] == p->atom(_NET_WM_STATE_STAYS_ON_TOP)) { mask |= StaysOnTop; } else if ((xcb_atom_t) message->data.data32[i] == p->atom(_NET_WM_STATE_FOCUSED)) { mask |= Focused; } } // when removing, we just leave newstate == 0 switch (message->data.data32[0]) { case 1: // set // to set... the change state should be the same as the mask state = mask; break; case 2: // toggle // to toggle, we need to xor the current state with the new state state = (p->state & mask) ^ mask; break; default: // to clear state, the new state should stay zero ; } #ifdef NETWMDEBUG fprintf(stderr, "NETWinInfo::event: calling changeState(%lx, %lx)\n", state, mask); #endif changeState(state, mask); } else if (message->type == p->atom(_NET_WM_DESKTOP)) { dirty = WMDesktop; if (message->data.data32[0] == OnAllDesktops) { changeDesktop(OnAllDesktops); } else { changeDesktop(message->data.data32[0] + 1); } } else if (message->type == p->atom(_NET_WM_FULLSCREEN_MONITORS)) { dirty2 = WM2FullscreenMonitors; NETFullscreenMonitors topology; topology.top = message->data.data32[0]; topology.bottom = message->data.data32[1]; topology.left = message->data.data32[2]; topology.right = message->data.data32[3]; #ifdef NETWMDEBUG fprintf(stderr, "NETWinInfo2::event: calling changeFullscreenMonitors" "(%ld, %ld, %ld, %ld, %ld)\n", message->window, message->data.data32[0], message->data.data32[1], message->data.data32[2], message->data.data32[3] ); #endif changeFullscreenMonitors(topology); } } if (eventType == XCB_PROPERTY_NOTIFY) { #ifdef NETWMDEBUG fprintf(stderr, "NETWinInfo::event: handling PropertyNotify event\n"); #endif xcb_property_notify_event_t *pe = reinterpret_cast(event); if (pe->atom == p->atom(_NET_WM_NAME)) { dirty |= WMName; } else if (pe->atom == p->atom(_NET_WM_VISIBLE_NAME)) { dirty |= WMVisibleName; } else if (pe->atom == p->atom(_NET_WM_DESKTOP)) { dirty |= WMDesktop; } else if (pe->atom == p->atom(_NET_WM_WINDOW_TYPE)) { dirty |= WMWindowType; } else if (pe->atom == p->atom(_NET_WM_STATE)) { dirty |= WMState; } else if (pe->atom == p->atom(_NET_WM_STRUT)) { dirty |= WMStrut; } else if (pe->atom == p->atom(_NET_WM_STRUT_PARTIAL)) { dirty2 |= WM2ExtendedStrut; } else if (pe->atom == p->atom(_NET_WM_ICON_GEOMETRY)) { dirty |= WMIconGeometry; } else if (pe->atom == p->atom(_NET_WM_ICON)) { dirty |= WMIcon; } else if (pe->atom == p->atom(_NET_WM_PID)) { dirty |= WMPid; } else if (pe->atom == p->atom(_NET_WM_HANDLED_ICONS)) { dirty |= WMHandledIcons; } else if (pe->atom == p->atom(_NET_STARTUP_ID)) { dirty2 |= WM2StartupId; } else if (pe->atom == p->atom(_NET_WM_WINDOW_OPACITY)) { dirty2 |= WM2Opacity; } else if (pe->atom == p->atom(_NET_WM_ALLOWED_ACTIONS)) { dirty2 |= WM2AllowedActions; } else if (pe->atom == p->atom(WM_STATE)) { dirty |= XAWMState; } else if (pe->atom == p->atom(_NET_FRAME_EXTENTS)) { dirty |= WMFrameExtents; } else if (pe->atom == p->atom(_KDE_NET_WM_FRAME_STRUT)) { dirty |= WMFrameExtents; } else if (pe->atom == p->atom(_NET_WM_FRAME_OVERLAP)) { dirty2 |= WM2FrameOverlap; } else if (pe->atom == p->atom(_NET_WM_ICON_NAME)) { dirty |= WMIconName; } else if (pe->atom == p->atom(_NET_WM_VISIBLE_ICON_NAME)) { dirty |= WMVisibleIconName; } else if (pe->atom == p->atom(_NET_WM_USER_TIME)) { dirty2 |= WM2UserTime; } else if (pe->atom == XCB_ATOM_WM_HINTS) { dirty2 |= WM2GroupLeader; dirty2 |= WM2Urgency; dirty2 |= WM2Input; dirty2 |= WM2InitialMappingState; dirty2 |= WM2IconPixmap; } else if (pe->atom == XCB_ATOM_WM_TRANSIENT_FOR) { dirty2 |= WM2TransientFor; } else if (pe->atom == XCB_ATOM_WM_CLASS) { dirty2 |= WM2WindowClass; } else if (pe->atom == p->atom(WM_WINDOW_ROLE)) { dirty2 |= WM2WindowRole; } else if (pe->atom == XCB_ATOM_WM_CLIENT_MACHINE) { dirty2 |= WM2ClientMachine; } else if (pe->atom == p->atom(_KDE_NET_WM_ACTIVITIES)) { dirty2 |= WM2Activities; } else if (pe->atom == p->atom(_KDE_NET_WM_BLOCK_COMPOSITING) || pe->atom == p->atom(_NET_WM_BYPASS_COMPOSITOR)) { dirty2 |= WM2BlockCompositing; } else if (pe->atom == p->atom(_KDE_NET_WM_SHADOW)) { dirty2 |= WM2KDEShadow; } else if (pe->atom == p->atom(WM_PROTOCOLS)) { dirty2 |= WM2Protocols; } else if (pe->atom == p->atom(_NET_WM_OPAQUE_REGION)) { dirty2 |= WM2OpaqueRegion; } else if (pe->atom == p->atom(_KDE_NET_WM_DESKTOP_FILE)) { dirty2 = WM2DesktopFileName; } else if (pe->atom == p->atom(_NET_WM_FULLSCREEN_MONITORS)) { dirty2 = WM2FullscreenMonitors; } do_update = true; } else if (eventType == XCB_CONFIGURE_NOTIFY) { #ifdef NETWMDEBUG fprintf(stderr, "NETWinInfo::event: handling ConfigureNotify event\n"); #endif dirty |= WMGeometry; // update window geometry xcb_configure_notify_event_t *configure = reinterpret_cast(event); p->win_geom.pos.x = configure->x; p->win_geom.pos.y = configure->y; p->win_geom.size.width = configure->width; p->win_geom.size.height = configure->height; } if (do_update) { update(dirty, dirty2); } if (properties) { *properties = dirty; } if (properties2) { *properties2 = dirty2; } } #ifndef KWINDOWSYSTEM_NO_DEPRECATED void NETWinInfo::event(xcb_generic_event_t *ev, unsigned long *properties, int properties_size) { NET::Properties p; NET::Properties2 p2; event(ev, &p, &p2); unsigned long props[ PROPERTIES_SIZE ] = { p, p2 }; assert(PROPERTIES_SIZE == 2); // add elements above if (properties_size > PROPERTIES_SIZE) { properties_size = PROPERTIES_SIZE; } for (int i = 0; i < properties_size; ++i) { properties[ i ] = props[ i ]; } } #endif void NETWinInfo::updateWMState() { update(XAWMState); } void NETWinInfo::update(NET::Properties dirtyProperties, NET::Properties2 dirtyProperties2) { Properties dirty = dirtyProperties & p->properties; Properties2 dirty2 = dirtyProperties2 & p->properties2; // We *always* want to update WM_STATE if set in dirty_props if (dirtyProperties & XAWMState) { dirty |= XAWMState; } xcb_get_property_cookie_t cookies[255]; int c = 0; if (dirty & XAWMState) { cookies[c++] = xcb_get_property(p->conn, false, p->window, p->atom(WM_STATE), p->atom(WM_STATE), 0, 1); } if (dirty & WMState) { cookies[c++] = xcb_get_property(p->conn, false, p->window, p->atom(_NET_WM_STATE), XCB_ATOM_ATOM, 0, 2048); } if (dirty & WMDesktop) { cookies[c++] = xcb_get_property(p->conn, false, p->window, p->atom(_NET_WM_DESKTOP), XCB_ATOM_CARDINAL, 0, 1); } if (dirty & WMName) { cookies[c++] = xcb_get_property(p->conn, false, p->window, p->atom(_NET_WM_NAME), p->atom(UTF8_STRING), 0, MAX_PROP_SIZE); } if (dirty & WMVisibleName) { cookies[c++] = xcb_get_property(p->conn, false, p->window, p->atom(_NET_WM_VISIBLE_NAME), p->atom(UTF8_STRING), 0, MAX_PROP_SIZE); } if (dirty & WMIconName) { cookies[c++] = xcb_get_property(p->conn, false, p->window, p->atom(_NET_WM_ICON_NAME), p->atom(UTF8_STRING), 0, MAX_PROP_SIZE); } if (dirty & WMVisibleIconName) { cookies[c++] = xcb_get_property(p->conn, false, p->window, p->atom(_NET_WM_VISIBLE_ICON_NAME), p->atom(UTF8_STRING), 0, MAX_PROP_SIZE); } if (dirty & WMWindowType) { cookies[c++] = xcb_get_property(p->conn, false, p->window, p->atom(_NET_WM_WINDOW_TYPE), XCB_ATOM_ATOM, 0, 2048); } if (dirty & WMStrut) { cookies[c++] = xcb_get_property(p->conn, false, p->window, p->atom(_NET_WM_STRUT), XCB_ATOM_CARDINAL, 0, 4); } if (dirty2 & WM2ExtendedStrut) { cookies[c++] = xcb_get_property(p->conn, false, p->window, p->atom(_NET_WM_STRUT_PARTIAL), XCB_ATOM_CARDINAL, 0, 12); } if (dirty2 & WM2FullscreenMonitors) { cookies[c++] = xcb_get_property(p->conn, false, p->window, p->atom(_NET_WM_FULLSCREEN_MONITORS), XCB_ATOM_CARDINAL, 0, 4); } if (dirty & WMIconGeometry) { cookies[c++] = xcb_get_property(p->conn, false, p->window, p->atom(_NET_WM_ICON_GEOMETRY), XCB_ATOM_CARDINAL, 0, 4); } if (dirty & WMIcon) { cookies[c++] = xcb_get_property(p->conn, false, p->window, p->atom(_NET_WM_ICON), XCB_ATOM_CARDINAL, 0, 0xffffffff); } if (dirty & WMFrameExtents) { cookies[c++] = xcb_get_property(p->conn, false, p->window, p->atom(_NET_FRAME_EXTENTS), XCB_ATOM_CARDINAL, 0, 4); cookies[c++] = xcb_get_property(p->conn, false, p->window, p->atom(_KDE_NET_WM_FRAME_STRUT), XCB_ATOM_CARDINAL, 0, 4); } if (dirty2 & WM2FrameOverlap) { cookies[c++] = xcb_get_property(p->conn, false, p->window, p->atom(_NET_WM_FRAME_OVERLAP), XCB_ATOM_CARDINAL, 0, 4); } if (dirty2 & WM2Activities) { cookies[c++] = xcb_get_property(p->conn, false, p->window, p->atom(_KDE_NET_WM_ACTIVITIES), XCB_ATOM_STRING, 0, MAX_PROP_SIZE); } if (dirty2 & WM2BlockCompositing) { cookies[c++] = xcb_get_property(p->conn, false, p->window, p->atom(_KDE_NET_WM_BLOCK_COMPOSITING), XCB_ATOM_CARDINAL, 0, 1); cookies[c++] = xcb_get_property(p->conn, false, p->window, p->atom(_NET_WM_BYPASS_COMPOSITOR), XCB_ATOM_CARDINAL, 0, 1); } if (dirty & WMPid) { cookies[c++] = xcb_get_property(p->conn, false, p->window, p->atom(_NET_WM_PID), XCB_ATOM_CARDINAL, 0, 1); } if (dirty2 & WM2StartupId) { cookies[c++] = xcb_get_property(p->conn, false, p->window, p->atom(_NET_STARTUP_ID), p->atom(UTF8_STRING), 0, MAX_PROP_SIZE); } if (dirty2 & WM2Opacity) { cookies[c++] = xcb_get_property(p->conn, false, p->window, p->atom(_NET_WM_WINDOW_OPACITY), XCB_ATOM_CARDINAL, 0, 1); } if (dirty2 & WM2AllowedActions) { cookies[c++] = xcb_get_property(p->conn, false, p->window, p->atom(_NET_WM_ALLOWED_ACTIONS), XCB_ATOM_ATOM, 0, 2048); } if (dirty2 & WM2UserTime) { cookies[c++] = xcb_get_property(p->conn, false, p->window, p->atom(_NET_WM_USER_TIME), XCB_ATOM_CARDINAL, 0, 1); } if (dirty2 & WM2TransientFor) { cookies[c++] = xcb_get_property(p->conn, false, p->window, XCB_ATOM_WM_TRANSIENT_FOR, XCB_ATOM_WINDOW, 0, 1); } if (dirty2 & (WM2GroupLeader | WM2Urgency | WM2Input | WM2InitialMappingState | WM2IconPixmap)) { cookies[c++] = xcb_get_property(p->conn, false, p->window, XCB_ATOM_WM_HINTS, XCB_ATOM_WM_HINTS, 0, 9); } if (dirty2 & WM2WindowClass) { cookies[c++] = xcb_get_property(p->conn, false, p->window, XCB_ATOM_WM_CLASS, XCB_ATOM_STRING, 0, MAX_PROP_SIZE); } if (dirty2 & WM2WindowRole) { cookies[c++] = xcb_get_property(p->conn, false, p->window, p->atom(WM_WINDOW_ROLE), XCB_ATOM_STRING, 0, MAX_PROP_SIZE); } if (dirty2 & WM2ClientMachine) { cookies[c++] = xcb_get_property(p->conn, false, p->window, XCB_ATOM_WM_CLIENT_MACHINE, XCB_ATOM_STRING, 0, MAX_PROP_SIZE); } if (dirty2 & WM2Protocols) { cookies[c++] = xcb_get_property(p->conn, false, p->window, p->atom(WM_PROTOCOLS), XCB_ATOM_ATOM, 0, 2048); } if (dirty2 & WM2OpaqueRegion) { cookies[c++] = xcb_get_property(p->conn, false, p->window, p->atom(_NET_WM_OPAQUE_REGION), XCB_ATOM_CARDINAL, 0, MAX_PROP_SIZE); } if (dirty2 & WM2DesktopFileName) { cookies[c++] = xcb_get_property(p->conn, false, p->window, p->atom(_KDE_NET_WM_DESKTOP_FILE), p->atom(UTF8_STRING), 0, MAX_PROP_SIZE); } c = 0; if (dirty & XAWMState) { p->mapping_state = Withdrawn; bool success; uint32_t state = get_value_reply(p->conn, cookies[c++], p->atom(WM_STATE), 0, &success); if (success) { switch (state) { case 3: // IconicState p->mapping_state = Iconic; break; case 1: // NormalState p->mapping_state = Visible; break; case 0: // WithdrawnState default: p->mapping_state = Withdrawn; break; } p->mapping_state_dirty = false; } } if (dirty & WMState) { p->state = NET::States(); const QVector states = get_array_reply(p->conn, cookies[c++], XCB_ATOM_ATOM); #ifdef NETWMDEBUG fprintf(stderr, "NETWinInfo::update: updating window state (%ld)\n", states.count()); #endif Q_FOREACH (const xcb_atom_t state, states) { #ifdef NETWMDEBUG const QByteArray ba = get_atom_name(p->conn, state); fprintf(stderr, "NETWinInfo::update: adding window state %ld '%s'\n", state, ba.constData()); #endif if (state == p->atom(_NET_WM_STATE_MODAL)) { p->state |= Modal; } else if (state == p->atom(_NET_WM_STATE_STICKY)) { p->state |= Sticky; } else if (state == p->atom(_NET_WM_STATE_MAXIMIZED_VERT)) { p->state |= MaxVert; } else if (state == p->atom(_NET_WM_STATE_MAXIMIZED_HORZ)) { p->state |= MaxHoriz; } else if (state == p->atom(_NET_WM_STATE_SHADED)) { p->state |= Shaded; } else if (state == p->atom(_NET_WM_STATE_SKIP_TASKBAR)) { p->state |= SkipTaskbar; } else if (state == p->atom(_NET_WM_STATE_SKIP_PAGER)) { p->state |= SkipPager; } else if (state == p->atom(_KDE_NET_WM_STATE_SKIP_SWITCHER)) { p->state |= SkipSwitcher; } else if (state == p->atom(_NET_WM_STATE_HIDDEN)) { p->state |= Hidden; } else if (state == p->atom(_NET_WM_STATE_FULLSCREEN)) { p->state |= FullScreen; } else if (state == p->atom(_NET_WM_STATE_ABOVE)) { p->state |= KeepAbove; } else if (state == p->atom(_NET_WM_STATE_BELOW)) { p->state |= KeepBelow; } else if (state == p->atom(_NET_WM_STATE_DEMANDS_ATTENTION)) { p->state |= DemandsAttention; } else if (state == p->atom(_NET_WM_STATE_STAYS_ON_TOP)) { p->state |= StaysOnTop; } else if (state == p->atom(_NET_WM_STATE_FOCUSED)) { p->state |= Focused; } } } if (dirty & WMDesktop) { p->desktop = 0; bool success; uint32_t desktop = get_value_reply(p->conn, cookies[c++], XCB_ATOM_CARDINAL, 0, &success); if (success) { if (desktop != 0xffffffff) { p->desktop = desktop + 1; } else { p->desktop = OnAllDesktops; } } } if (dirty & WMName) { delete[] p->name; p->name = nullptr; const QByteArray str = get_string_reply(p->conn, cookies[c++], p->atom(UTF8_STRING)); if (str.length() > 0) { p->name = nstrndup(str.constData(), str.length()); } } if (dirty & WMVisibleName) { delete[] p->visible_name; p->visible_name = nullptr; const QByteArray str = get_string_reply(p->conn, cookies[c++], p->atom(UTF8_STRING)); if (str.length() > 0) { p->visible_name = nstrndup(str.constData(), str.length()); } } if (dirty & WMIconName) { delete[] p->icon_name; p->icon_name = nullptr; const QByteArray str = get_string_reply(p->conn, cookies[c++], p->atom(UTF8_STRING)); if (str.length() > 0) { p->icon_name = nstrndup(str.constData(), str.length()); } } if (dirty & WMVisibleIconName) { delete[] p->visible_icon_name; p->visible_icon_name = nullptr; const QByteArray str = get_string_reply(p->conn, cookies[c++], p->atom(UTF8_STRING)); if (str.length() > 0) { p->visible_icon_name = nstrndup(str.constData(), str.length()); } } if (dirty & WMWindowType) { p->types.reset(); p->types[0] = Unknown; p->has_net_support = false; const QVector types = get_array_reply(p->conn, cookies[c++], XCB_ATOM_ATOM); if (!types.isEmpty()) { #ifdef NETWMDEBUG fprintf(stderr, "NETWinInfo::update: getting window type (%ld)\n", types.count()); #endif p->has_net_support = true; int pos = 0; Q_FOREACH (const xcb_atom_t type, types) { #ifdef NETWMDEBUG const QByteArray name = get_atom_name(p->conn, type); fprintf(stderr, "NETWinInfo::update: examining window type %ld %s\n", type, name.constData()); #endif if (type == p->atom(_NET_WM_WINDOW_TYPE_NORMAL)) { p->types[pos++] = Normal; } else if (type == p->atom(_NET_WM_WINDOW_TYPE_DESKTOP)) { p->types[pos++] = Desktop; } else if (type == p->atom(_NET_WM_WINDOW_TYPE_DOCK)) { p->types[pos++] = Dock; } else if (type == p->atom(_NET_WM_WINDOW_TYPE_TOOLBAR)) { p->types[pos++] = Toolbar; } else if (type == p->atom(_NET_WM_WINDOW_TYPE_MENU)) { p->types[pos++] = Menu; } else if (type == p->atom(_NET_WM_WINDOW_TYPE_DIALOG)) { p->types[pos++] = Dialog; } else if (type == p->atom(_NET_WM_WINDOW_TYPE_UTILITY)) { p->types[ pos++ ] = Utility; } else if (type == p->atom(_NET_WM_WINDOW_TYPE_SPLASH)) { p->types[pos++] = Splash; } else if (type == p->atom(_NET_WM_WINDOW_TYPE_DROPDOWN_MENU)) { p->types[pos++] = DropdownMenu; } else if (type == p->atom(_NET_WM_WINDOW_TYPE_POPUP_MENU)) { p->types[pos++] = PopupMenu; } else if (type == p->atom(_NET_WM_WINDOW_TYPE_TOOLTIP)) { p->types[pos++] = Tooltip; } else if (type == p->atom(_NET_WM_WINDOW_TYPE_NOTIFICATION)) { p->types[pos++] = Notification; } else if (type == p->atom(_NET_WM_WINDOW_TYPE_COMBO)) { p->types[pos++] = ComboBox; } else if (type == p->atom(_NET_WM_WINDOW_TYPE_DND)) { p->types[pos++] = DNDIcon; } else if (type == p->atom(_KDE_NET_WM_WINDOW_TYPE_OVERRIDE)) { p->types[pos++] = Override; } else if (type == p->atom(_KDE_NET_WM_WINDOW_TYPE_TOPMENU)) { p->types[pos++] = TopMenu; } else if (type == p->atom(_KDE_NET_WM_WINDOW_TYPE_ON_SCREEN_DISPLAY)) { p->types[pos++] = OnScreenDisplay; } + + else if (type == p->atom(_KDE_NET_WM_WINDOW_TYPE_CRITICAL_NOTIFICATION)) { + p->types[pos++] = CriticalNotification; + } } } } if (dirty & WMStrut) { p->strut = NETStrut(); QVector data = get_array_reply(p->conn, cookies[c++], XCB_ATOM_CARDINAL); if (data.count() == 4) { p->strut.left = data[0]; p->strut.right = data[1]; p->strut.top = data[2]; p->strut.bottom = data[3]; } } if (dirty2 & WM2ExtendedStrut) { p->extended_strut = NETExtendedStrut(); QVector data = get_array_reply(p->conn, cookies[c++], XCB_ATOM_CARDINAL); if (data.count() == 12) { p->extended_strut.left_width = data[0]; p->extended_strut.right_width = data[1]; p->extended_strut.top_width = data[2]; p->extended_strut.bottom_width = data[3]; p->extended_strut.left_start = data[4]; p->extended_strut.left_end = data[5]; p->extended_strut.right_start = data[6]; p->extended_strut.right_end = data[7]; p->extended_strut.top_start = data[8]; p->extended_strut.top_end = data[9]; p->extended_strut.bottom_start = data[10]; p->extended_strut.bottom_end = data[11]; } } if (dirty2 & WM2FullscreenMonitors) { p->fullscreen_monitors = NETFullscreenMonitors(); QVector data = get_array_reply(p->conn, cookies[c++], XCB_ATOM_CARDINAL); if (data.count() == 4) { p->fullscreen_monitors.top = data[0]; p->fullscreen_monitors.bottom = data[1]; p->fullscreen_monitors.left = data[2]; p->fullscreen_monitors.right = data[3]; } } if (dirty & WMIconGeometry) { p->icon_geom = NETRect(); QVector data = get_array_reply(p->conn, cookies[c++], XCB_ATOM_CARDINAL); if (data.count() == 4) { p->icon_geom.pos.x = data[0]; p->icon_geom.pos.y = data[1]; p->icon_geom.size.width = data[2]; p->icon_geom.size.height = data[3]; } } if (dirty & WMIcon) { readIcon(p->conn, cookies[c++], p->icons, p->icon_count); delete[] p->icon_sizes; p->icon_sizes = nullptr; } if (dirty & WMFrameExtents) { p->frame_strut = NETStrut(); QVector data = get_array_reply(p->conn, cookies[c++], XCB_ATOM_CARDINAL); if (data.isEmpty()) { data = get_array_reply(p->conn, cookies[c++], XCB_ATOM_CARDINAL); } else { xcb_discard_reply(p->conn, cookies[c++].sequence); } if (data.count() == 4) { p->frame_strut.left = data[0]; p->frame_strut.right = data[1]; p->frame_strut.top = data[2]; p->frame_strut.bottom = data[3]; } } if (dirty2 & WM2FrameOverlap) { p->frame_overlap = NETStrut(); QVector data = get_array_reply(p->conn, cookies[c++], XCB_ATOM_CARDINAL); if (data.count() == 4) { p->frame_overlap.left = data[0]; p->frame_overlap.right = data[1]; p->frame_overlap.top = data[2]; p->frame_overlap.bottom = data[3]; } } if (dirty2 & WM2Activities) { delete[] p->activities; p->activities = nullptr; const QByteArray activities = get_string_reply(p->conn, cookies[c++], XCB_ATOM_STRING); if (activities.length() > 0) { p->activities = nstrndup(activities.constData(), activities.length()); } } if (dirty2 & WM2BlockCompositing) { bool success; p->blockCompositing = false; // _KDE_NET_WM_BLOCK_COMPOSITING uint32_t data = get_value_reply(p->conn, cookies[c++], XCB_ATOM_CARDINAL, 0, &success); if (success) p->blockCompositing = bool(data); // _NET_WM_BYPASS_COMPOSITOR data = get_value_reply(p->conn, cookies[c++], XCB_ATOM_CARDINAL, 0, &success); if (success) { switch (data) { case 1: p->blockCompositing = true; break; case 2: p->blockCompositing = false; break; default: break; // yes, the standard /is/ that stupid. } } } if (dirty & WMPid) { p->pid = get_value_reply(p->conn, cookies[c++], XCB_ATOM_CARDINAL, 0); } if (dirty2 & WM2StartupId) { delete[] p->startup_id; p->startup_id = nullptr; const QByteArray id = get_string_reply(p->conn, cookies[c++], p->atom(UTF8_STRING)); if (id.length() > 0) { p->startup_id = nstrndup(id.constData(), id.length()); } } if (dirty2 & WM2Opacity) { p->opacity = get_value_reply(p->conn, cookies[c++], XCB_ATOM_CARDINAL, 0xffffffff); } if (dirty2 & WM2AllowedActions) { p->allowed_actions = NET::Actions(); const QVector actions = get_array_reply(p->conn, cookies[c++], XCB_ATOM_ATOM); if (!actions.isEmpty()) { #ifdef NETWMDEBUG fprintf(stderr, "NETWinInfo::update: updating allowed actions (%ld)\n", actions.count()); #endif Q_FOREACH (const xcb_atom_t action, actions) { #ifdef NETWMDEBUG const QByteArray name = get_atom_name(p->conn, action); fprintf(stderr, "NETWinInfo::update: adding allowed action %ld '%s'\n", action, name.constData()); #endif if (action == p->atom(_NET_WM_ACTION_MOVE)) { p->allowed_actions |= ActionMove; } else if (action == p->atom(_NET_WM_ACTION_RESIZE)) { p->allowed_actions |= ActionResize; } else if (action == p->atom(_NET_WM_ACTION_MINIMIZE)) { p->allowed_actions |= ActionMinimize; } else if (action == p->atom(_NET_WM_ACTION_SHADE)) { p->allowed_actions |= ActionShade; } else if (action == p->atom(_NET_WM_ACTION_STICK)) { p->allowed_actions |= ActionStick; } else if (action == p->atom(_NET_WM_ACTION_MAXIMIZE_VERT)) { p->allowed_actions |= ActionMaxVert; } else if (action == p->atom(_NET_WM_ACTION_MAXIMIZE_HORZ)) { p->allowed_actions |= ActionMaxHoriz; } else if (action == p->atom(_NET_WM_ACTION_FULLSCREEN)) { p->allowed_actions |= ActionFullScreen; } else if (action == p->atom(_NET_WM_ACTION_CHANGE_DESKTOP)) { p->allowed_actions |= ActionChangeDesktop; } else if (action == p->atom(_NET_WM_ACTION_CLOSE)) { p->allowed_actions |= ActionClose; } } } } if (dirty2 & WM2UserTime) { p->user_time = -1U; bool success; uint32_t value = get_value_reply(p->conn, cookies[c++], XCB_ATOM_CARDINAL, 0, &success); if (success) { p->user_time = value; } } if (dirty2 & WM2TransientFor) { p->transient_for = get_value_reply(p->conn, cookies[c++], XCB_ATOM_WINDOW, 0); } if (dirty2 & (WM2GroupLeader | WM2Urgency | WM2Input | WM2InitialMappingState | WM2IconPixmap)) { xcb_get_property_reply_t *reply = xcb_get_property_reply(p->conn, cookies[c++], nullptr); if (reply && reply->format == 32 && reply->value_len == 9 && reply->type == XCB_ATOM_WM_HINTS) { kde_wm_hints *hints = reinterpret_cast(xcb_get_property_value(reply)); if (hints->flags & (1 << 0)/*Input*/) { p->input = hints->input; } if (hints->flags & (1 << 1)/*StateHint*/) { switch (hints->initial_state) { case 3: // IconicState p->initialMappingState = Iconic; break; case 1: // NormalState p->initialMappingState = Visible; break; case 0: // WithdrawnState default: p->initialMappingState = Withdrawn; break; } } if (hints->flags & (1 << 2)/*IconPixmapHint*/) { p->icon_pixmap = hints->icon_pixmap; } if (hints->flags & (1 << 5)/*IconMaskHint*/) { p->icon_mask = hints->icon_mask; } if (hints->flags & (1 << 6)/*WindowGroupHint*/) { p->window_group = hints->window_group; } p->urgency = (hints->flags & (1 << 8)/*UrgencyHint*/); } if (reply) { free(reply); } } if (dirty2 & WM2WindowClass) { delete[] p->class_name; delete[] p->class_class; p->class_name = nullptr; p->class_class = nullptr; const QList list = get_stringlist_reply(p->conn, cookies[c++], XCB_ATOM_STRING); if (list.count() == 2) { p->class_name = nstrdup(list.at(0).constData()); p->class_class = nstrdup(list.at(1).constData()); } } if (dirty2 & WM2WindowRole) { delete[] p->window_role; p->window_role = nullptr; const QByteArray role = get_string_reply(p->conn, cookies[c++], XCB_ATOM_STRING); if (role.length() > 0) { p->window_role = nstrndup(role.constData(), role.length()); } } if (dirty2 & WM2ClientMachine) { delete[] p->client_machine; p->client_machine = nullptr; const QByteArray value = get_string_reply(p->conn, cookies[c++], XCB_ATOM_STRING); if (value.length() > 0) { p->client_machine = nstrndup(value.constData(), value.length()); } } if (dirty2 & WM2Protocols) { const QVector protocols = get_array_reply(p->conn, cookies[c++], XCB_ATOM_ATOM); p->protocols = NET::NoProtocol; for (auto it = protocols.begin(); it != protocols.end(); ++it) { if ((*it) == p->atom(WM_TAKE_FOCUS)) { p->protocols |= TakeFocusProtocol; } else if ((*it) == p->atom(WM_DELETE_WINDOW)) { p->protocols |= DeleteWindowProtocol; } else if ((*it) == p->atom(_NET_WM_PING)) { p->protocols |= PingProtocol; } else if ((*it) == p->atom(_NET_WM_SYNC_REQUEST)) { p->protocols |= SyncRequestProtocol; } else if ((*it) == p->atom(_NET_WM_CONTEXT_HELP)) { p->protocols |= ContextHelpProtocol; } } } if (dirty2 & WM2OpaqueRegion) { const QVector values = get_array_reply(p->conn, cookies[c++], XCB_ATOM_CARDINAL); p->opaqueRegion.clear(); p->opaqueRegion.reserve(values.count() / 4); for (int i = 0; i < values.count() - 3; i += 4) { NETRect rect; rect.pos.x = values.at(i); rect.pos.y = values.at(i + 1); rect.size.width = values.at(i + 2); rect.size.height = values.at(i + 3); p->opaqueRegion.push_back(rect); } } if (dirty2 & WM2DesktopFileName) { delete[] p->desktop_file; p->desktop_file = nullptr; const QByteArray id = get_string_reply(p->conn, cookies[c++], p->atom(UTF8_STRING)); if (id.length() > 0) { p->desktop_file = nstrndup(id.constData(), id.length()); } } } NETRect NETWinInfo::iconGeometry() const { return p->icon_geom; } NET::States NETWinInfo::state() const { return p->state; } NETStrut NETWinInfo::strut() const { return p->strut; } NETExtendedStrut NETWinInfo::extendedStrut() const { return p->extended_strut; } NETFullscreenMonitors NETWinInfo::fullscreenMonitors() const { return p->fullscreen_monitors; } bool NET::typeMatchesMask(WindowType type, WindowTypes mask) { switch (type) { #define CHECK_TYPE_MASK( type ) \ case type: \ if( mask & type##Mask ) \ return true; \ break; CHECK_TYPE_MASK(Normal) CHECK_TYPE_MASK(Desktop) CHECK_TYPE_MASK(Dock) CHECK_TYPE_MASK(Toolbar) CHECK_TYPE_MASK(Menu) CHECK_TYPE_MASK(Dialog) CHECK_TYPE_MASK(Override) CHECK_TYPE_MASK(TopMenu) CHECK_TYPE_MASK(Utility) CHECK_TYPE_MASK(Splash) CHECK_TYPE_MASK(DropdownMenu) CHECK_TYPE_MASK(PopupMenu) CHECK_TYPE_MASK(Tooltip) CHECK_TYPE_MASK(Notification) CHECK_TYPE_MASK(ComboBox) CHECK_TYPE_MASK(DNDIcon) CHECK_TYPE_MASK(OnScreenDisplay) + CHECK_TYPE_MASK(CriticalNotification) #undef CHECK_TYPE_MASK default: break; } return false; } NET::WindowType NETWinInfo::windowType(WindowTypes supported_types) const { for (int i = 0; i < p->types.size(); ++i) { // return the type only if the application supports it if (typeMatchesMask(p->types[ i ], supported_types)) { return p->types[ i ]; } } return Unknown; } bool NETWinInfo::hasWindowType() const { return p->types.size() > 0; } const char *NETWinInfo::name() const { return p->name; } const char *NETWinInfo::visibleName() const { return p->visible_name; } const char *NETWinInfo::iconName() const { return p->icon_name; } const char *NETWinInfo::visibleIconName() const { return p->visible_icon_name; } int NETWinInfo::desktop(bool ignore_viewport) const { if (!ignore_viewport && KWindowSystem::mapViewport()) { const KWindowInfo info(p->window, NET::WMDesktop); return info.desktop(); } return p->desktop; } int NETWinInfo::pid() const { return p->pid; } xcb_timestamp_t NETWinInfo::userTime() const { return p->user_time; } const char *NETWinInfo::startupId() const { return p->startup_id; } unsigned long NETWinInfo::opacity() const { return p->opacity; } NET::Actions NETWinInfo::allowedActions() const { return p->allowed_actions; } bool NETWinInfo::hasNETSupport() const { return p->has_net_support; } xcb_window_t NETWinInfo::transientFor() const { return p->transient_for; } xcb_window_t NETWinInfo::groupLeader() const { return p->window_group; } bool NETWinInfo::urgency() const { return p->urgency; } bool NETWinInfo::input() const { return p->input; } NET::MappingState NETWinInfo::initialMappingState() const { return p->initialMappingState; } xcb_pixmap_t NETWinInfo::icccmIconPixmap() const { return p->icon_pixmap; } xcb_pixmap_t NETWinInfo::icccmIconPixmapMask() const { return p->icon_mask; } const char *NETWinInfo::windowClassClass() const { return p->class_class; } const char *NETWinInfo::windowClassName() const { return p->class_name; } const char *NETWinInfo::windowRole() const { return p->window_role; } const char *NETWinInfo::clientMachine() const { return p->client_machine; } const char *NETWinInfo::activities() const { return p->activities; } void NETWinInfo::setActivities(const char *activities) { delete [] p->activities; if (activities == (char *) nullptr || activities[0] == '\0') { // on all activities static const char nulluuid[] = KDE_ALL_ACTIVITIES_UUID; p->activities = nstrdup(nulluuid); } else { p->activities = nstrdup(activities); } xcb_change_property(p->conn, XCB_PROP_MODE_REPLACE, p->window, p->atom(_KDE_NET_WM_ACTIVITIES), XCB_ATOM_STRING, 8, strlen(p->activities), p->activities); } void NETWinInfo::setBlockingCompositing(bool active) { if (p->role != Client) { return; } p->blockCompositing = active; if (active) { uint32_t d = 1; xcb_change_property(p->conn, XCB_PROP_MODE_REPLACE, p->window, p->atom(_KDE_NET_WM_BLOCK_COMPOSITING), XCB_ATOM_CARDINAL, 32, 1, (const void *) &d); xcb_change_property(p->conn, XCB_PROP_MODE_REPLACE, p->window, p->atom(_NET_WM_BYPASS_COMPOSITOR), XCB_ATOM_CARDINAL, 32, 1, (const void *) &d); } else { xcb_delete_property(p->conn, p->window, p->atom(_KDE_NET_WM_BLOCK_COMPOSITING)); xcb_delete_property(p->conn, p->window, p->atom(_NET_WM_BYPASS_COMPOSITOR)); } } bool NETWinInfo::isBlockingCompositing() const { return p->blockCompositing; } bool NETWinInfo::handledIcons() const { return p->handled_icons; } NET::Properties NETWinInfo::passedProperties() const { return p->properties; } NET::Properties2 NETWinInfo::passedProperties2() const { return p->properties2; } NET::MappingState NETWinInfo::mappingState() const { return p->mapping_state; } NET::Protocols NETWinInfo::protocols() const { return p->protocols; } bool NETWinInfo::supportsProtocol(NET::Protocol protocol) const { return p->protocols.testFlag(protocol); } std::vector< NETRect > NETWinInfo::opaqueRegion() const { return p->opaqueRegion; } xcb_connection_t *NETWinInfo::xcbConnection() const { return p->conn; } void NETWinInfo::setDesktopFileName(const char *name) { if (p->role != Client) { return; } delete[] p->desktop_file; p->desktop_file = nstrdup(name); xcb_change_property(p->conn, XCB_PROP_MODE_REPLACE, p->window, p->atom(_KDE_NET_WM_DESKTOP_FILE), p->atom(UTF8_STRING), 8, strlen(p->desktop_file), (const void *) p->desktop_file); } const char *NETWinInfo::desktopFileName() const { return p->desktop_file; } void NETRootInfo::virtual_hook(int, void *) { /*BASE::virtual_hook( id, data );*/ } void NETWinInfo::virtual_hook(int, void *) { /*BASE::virtual_hook( id, data );*/ } int NET::timestampCompare(unsigned long time1, unsigned long time2) { return KXUtils::timestampCompare(time1, time2); } int NET::timestampDiff(unsigned long time1, unsigned long time2) { return KXUtils::timestampDiff(time1, time2); } #endif