File indexing completed on 2024-04-28 05:30:24

0001 /*
0002     KWin - the KDE window manager
0003     This file is part of the KDE project.
0004 
0005     SPDX-FileCopyrightText: 1999, 2000 Matthias Ettrich <ettrich@kde.org>
0006     SPDX-FileCopyrightText: 2003 Lubos Lunak <l.lunak@kde.org>
0007     SPDX-FileCopyrightText: 2013 Martin Gräßlin <mgraesslin@kde.org>
0008 
0009     SPDX-License-Identifier: GPL-2.0-or-later
0010 */
0011 // own
0012 #include "netinfo.h"
0013 // kwin
0014 #include "rootinfo_filter.h"
0015 #include "virtualdesktops.h"
0016 #include "workspace.h"
0017 #include "x11window.h"
0018 // Qt
0019 #include <QDebug>
0020 
0021 namespace KWin
0022 {
0023 
0024 std::unique_ptr<RootInfo> RootInfo::s_self;
0025 
0026 RootInfo *RootInfo::create()
0027 {
0028     Q_ASSERT(!s_self);
0029     xcb_window_t supportWindow = xcb_generate_id(kwinApp()->x11Connection());
0030     const uint32_t values[] = {true};
0031     xcb_create_window(kwinApp()->x11Connection(), XCB_COPY_FROM_PARENT, supportWindow, kwinApp()->x11RootWindow(),
0032                       0, 0, 1, 1, 0, XCB_COPY_FROM_PARENT,
0033                       XCB_COPY_FROM_PARENT, XCB_CW_OVERRIDE_REDIRECT, values);
0034     const uint32_t lowerValues[] = {XCB_STACK_MODE_BELOW}; // See usage in layers.cpp
0035     // we need to do the lower window with a roundtrip, otherwise NETRootInfo is not functioning
0036     UniqueCPtr<xcb_generic_error_t> error(xcb_request_check(kwinApp()->x11Connection(),
0037                                                             xcb_configure_window_checked(kwinApp()->x11Connection(), supportWindow, XCB_CONFIG_WINDOW_STACK_MODE, lowerValues)));
0038     if (error) {
0039         qCDebug(KWIN_CORE) << "Error occurred while lowering support window: " << error->error_code;
0040     }
0041 
0042     const NET::Properties properties = NET::Supported
0043         | NET::SupportingWMCheck
0044         | NET::ClientList
0045         | NET::ClientListStacking
0046         | NET::DesktopGeometry
0047         | NET::NumberOfDesktops
0048         | NET::CurrentDesktop
0049         | NET::ActiveWindow
0050         | NET::WorkArea
0051         | NET::CloseWindow
0052         | NET::DesktopNames
0053         | NET::WMName
0054         | NET::WMVisibleName
0055         | NET::WMDesktop
0056         | NET::WMWindowType
0057         | NET::WMState
0058         | NET::WMStrut
0059         | NET::WMIconGeometry
0060         | NET::WMIcon
0061         | NET::WMPid
0062         | NET::WMMoveResize
0063         | NET::WMFrameExtents
0064         | NET::WMPing;
0065     const NET::WindowTypes types = NET::NormalMask
0066         | NET::DesktopMask
0067         | NET::DockMask
0068         | NET::ToolbarMask
0069         | NET::MenuMask
0070         | NET::DialogMask
0071         | NET::OverrideMask
0072         | NET::UtilityMask
0073         | NET::SplashMask; // No compositing window types here unless we support them also as managed window types
0074     const NET::States states = NET::Modal
0075         // | NET::Sticky // Large desktops not supported (and probably never will be)
0076         | NET::MaxVert
0077         | NET::MaxHoriz
0078         | NET::Shaded
0079         | NET::SkipTaskbar
0080         | NET::KeepAbove
0081         // | NET::StaysOnTop // The same like KeepAbove
0082         | NET::SkipPager
0083         | NET::Hidden
0084         | NET::FullScreen
0085         | NET::KeepBelow
0086         | NET::DemandsAttention
0087         | NET::SkipSwitcher
0088         | NET::Focused;
0089     NET::Properties2 properties2 = NET::WM2UserTime
0090         | NET::WM2StartupId
0091         | NET::WM2AllowedActions
0092         | NET::WM2RestackWindow
0093         | NET::WM2MoveResizeWindow
0094         | NET::WM2ExtendedStrut
0095         | NET::WM2ShowingDesktop
0096         | NET::WM2DesktopLayout
0097         | NET::WM2FullPlacement
0098         | NET::WM2FullscreenMonitors
0099         | NET::WM2KDEShadow
0100         | NET::WM2OpaqueRegion
0101         | NET::WM2GTKFrameExtents
0102         | NET::WM2GTKShowWindowMenu
0103         | NET::WM2Opacity;
0104 #if KWIN_BUILD_ACTIVITIES
0105     properties2 |= NET::WM2Activities;
0106 #endif
0107     const NET::Actions actions = NET::ActionMove
0108         | NET::ActionResize
0109         | NET::ActionMinimize
0110         | NET::ActionShade
0111         // | NET::ActionStick // Sticky state is not supported
0112         | NET::ActionMaxVert
0113         | NET::ActionMaxHoriz
0114         | NET::ActionFullScreen
0115         | NET::ActionChangeDesktop
0116         | NET::ActionClose;
0117 
0118     s_self = std::make_unique<RootInfo>(supportWindow, "KWin", properties, types, states, properties2, actions);
0119     return s_self.get();
0120 }
0121 
0122 void RootInfo::destroy()
0123 {
0124     if (!s_self) {
0125         return;
0126     }
0127     xcb_window_t supportWindow = s_self->supportWindow();
0128     s_self.reset();
0129     xcb_destroy_window(kwinApp()->x11Connection(), supportWindow);
0130 }
0131 
0132 RootInfo::RootInfo(xcb_window_t w, const char *name, NET::Properties properties, NET::WindowTypes types,
0133                    NET::States states, NET::Properties2 properties2, NET::Actions actions, int scr)
0134     : NETRootInfo(kwinApp()->x11Connection(), w, name, properties, types, states, properties2, actions, scr)
0135     , m_activeWindow(activeWindow())
0136     , m_eventFilter(std::make_unique<RootInfoFilter>(this))
0137 {
0138 }
0139 
0140 void RootInfo::changeNumberOfDesktops(int n)
0141 {
0142     VirtualDesktopManager::self()->setCount(n);
0143 }
0144 
0145 void RootInfo::changeCurrentDesktop(int d)
0146 {
0147     VirtualDesktopManager::self()->setCurrent(d);
0148 }
0149 
0150 void RootInfo::changeActiveWindow(xcb_window_t w, NET::RequestSource src, xcb_timestamp_t timestamp, xcb_window_t active_window)
0151 {
0152     Workspace *workspace = Workspace::self();
0153     if (X11Window *c = workspace->findClient(Predicate::WindowMatch, w)) {
0154         if (timestamp == XCB_CURRENT_TIME) {
0155             timestamp = c->userTime();
0156         }
0157         if (src != NET::FromApplication && src != FromTool) {
0158             src = NET::FromTool;
0159         }
0160         if (src == NET::FromTool) {
0161             workspace->activateWindow(c, true); // force
0162         } else if (c == workspace->mostRecentlyActivatedWindow()) {
0163             return; // WORKAROUND? With > 1 plasma activities, we cause this ourselves. bug #240673
0164         } else { // NET::FromApplication
0165             X11Window *c2;
0166             if (c->allowWindowActivation(timestamp, false)) {
0167                 workspace->activateWindow(c);
0168                 // if activation of the requestor's window would be allowed, allow activation too
0169             } else if (active_window != XCB_WINDOW_NONE
0170                        && (c2 = workspace->findClient(Predicate::WindowMatch, active_window)) != nullptr
0171                        && c2->allowWindowActivation(timestampCompare(timestamp, c2->userTime() > 0 ? timestamp : c2->userTime()), false)) {
0172                 workspace->activateWindow(c);
0173             } else {
0174                 c->demandAttention();
0175             }
0176         }
0177     }
0178 }
0179 
0180 void RootInfo::restackWindow(xcb_window_t w, RequestSource src, xcb_window_t above, int detail, xcb_timestamp_t timestamp)
0181 {
0182     if (X11Window *c = Workspace::self()->findClient(Predicate::WindowMatch, w)) {
0183         if (timestamp == XCB_CURRENT_TIME) {
0184             timestamp = c->userTime();
0185         }
0186         if (src != NET::FromApplication && src != FromTool) {
0187             src = NET::FromTool;
0188         }
0189         c->restackWindow(above, detail, src, timestamp, true);
0190     }
0191 }
0192 
0193 void RootInfo::closeWindow(xcb_window_t w)
0194 {
0195     X11Window *c = Workspace::self()->findClient(Predicate::WindowMatch, w);
0196     if (c) {
0197         c->closeWindow();
0198     }
0199 }
0200 
0201 void RootInfo::moveResize(xcb_window_t w, int x_root, int y_root, unsigned long direction)
0202 {
0203     X11Window *c = Workspace::self()->findClient(Predicate::WindowMatch, w);
0204     if (c) {
0205         kwinApp()->updateXTime(); // otherwise grabbing may have old timestamp - this message should include timestamp
0206         c->NETMoveResize(Xcb::fromXNative(x_root), Xcb::fromXNative(y_root), (Direction)direction);
0207     }
0208 }
0209 
0210 void RootInfo::moveResizeWindow(xcb_window_t w, int flags, int x, int y, int width, int height)
0211 {
0212     X11Window *c = Workspace::self()->findClient(Predicate::WindowMatch, w);
0213     if (c) {
0214         c->NETMoveResizeWindow(flags, Xcb::fromXNative(x), Xcb::fromXNative(y), Xcb::fromXNative(width), Xcb::fromXNative(height));
0215     }
0216 }
0217 
0218 void RootInfo::showWindowMenu(xcb_window_t w, int device_id, int x_root, int y_root)
0219 {
0220     if (X11Window *c = Workspace::self()->findClient(Predicate::WindowMatch, w)) {
0221         c->GTKShowWindowMenu(Xcb::fromXNative(x_root), Xcb::fromXNative(y_root));
0222     }
0223 }
0224 
0225 void RootInfo::gotPing(xcb_window_t w, xcb_timestamp_t timestamp)
0226 {
0227     if (X11Window *c = Workspace::self()->findClient(Predicate::WindowMatch, w)) {
0228         c->gotPing(timestamp);
0229     }
0230 }
0231 
0232 void RootInfo::changeShowingDesktop(bool showing)
0233 {
0234     Workspace::self()->setShowingDesktop(showing);
0235 }
0236 
0237 void RootInfo::setActiveClient(Window *client)
0238 {
0239     xcb_window_t windowId = XCB_WINDOW_NONE;
0240     if (auto x11Window = qobject_cast<X11Window *>(client)) {
0241         windowId = x11Window->window();
0242     }
0243     if (m_activeWindow == windowId) {
0244         return;
0245     }
0246     m_activeWindow = windowId;
0247     setActiveWindow(m_activeWindow);
0248 }
0249 
0250 // ****************************************
0251 // WinInfo
0252 // ****************************************
0253 
0254 WinInfo::WinInfo(X11Window *c, xcb_window_t window,
0255                  xcb_window_t rwin, NET::Properties properties, NET::Properties2 properties2)
0256     : NETWinInfo(kwinApp()->x11Connection(), window, rwin, properties, properties2, NET::WindowManager)
0257     , m_client(c)
0258 {
0259 }
0260 
0261 void WinInfo::changeDesktop(int desktopId)
0262 {
0263     if (VirtualDesktop *desktop = VirtualDesktopManager::self()->desktopForX11Id(desktopId)) {
0264         Workspace::self()->sendWindowToDesktops(m_client, {desktop}, true);
0265     }
0266 }
0267 
0268 void WinInfo::changeFullscreenMonitors(NETFullscreenMonitors topology)
0269 {
0270     m_client->updateFullscreenMonitors(topology);
0271 }
0272 
0273 void WinInfo::changeState(NET::States state, NET::States mask)
0274 {
0275     mask &= ~NET::Sticky; // KWin doesn't support large desktops, ignore
0276     mask &= ~NET::Hidden; // clients are not allowed to change this directly
0277     state &= mask; // for safety, clear all other bits
0278 
0279     if ((mask & NET::FullScreen) != 0 && (state & NET::FullScreen) == 0) {
0280         m_client->setFullScreen(false);
0281     }
0282     if ((mask & NET::Max) == NET::Max) {
0283         m_client->setMaximize(state & NET::MaxVert, state & NET::MaxHoriz);
0284     } else if (mask & NET::MaxVert) {
0285         m_client->setMaximize(state & NET::MaxVert, m_client->maximizeMode() & MaximizeHorizontal);
0286     } else if (mask & NET::MaxHoriz) {
0287         m_client->setMaximize(m_client->maximizeMode() & MaximizeVertical, state & NET::MaxHoriz);
0288     }
0289 
0290     if (mask & NET::Shaded) {
0291         m_client->setShade(state & NET::Shaded ? ShadeNormal : ShadeNone);
0292     }
0293     if (mask & NET::KeepAbove) {
0294         m_client->setKeepAbove((state & NET::KeepAbove) != 0);
0295     }
0296     if (mask & NET::KeepBelow) {
0297         m_client->setKeepBelow((state & NET::KeepBelow) != 0);
0298     }
0299     if (mask & NET::SkipTaskbar) {
0300         m_client->setOriginalSkipTaskbar((state & NET::SkipTaskbar) != 0);
0301     }
0302     if (mask & NET::SkipPager) {
0303         m_client->setSkipPager((state & NET::SkipPager) != 0);
0304     }
0305     if (mask & NET::SkipSwitcher) {
0306         m_client->setSkipSwitcher((state & NET::SkipSwitcher) != 0);
0307     }
0308     if (mask & NET::DemandsAttention) {
0309         m_client->demandAttention((state & NET::DemandsAttention) != 0);
0310     }
0311     if (mask & NET::Modal) {
0312         m_client->setModal((state & NET::Modal) != 0);
0313     }
0314     // unsetting fullscreen first, setting it last (because e.g. maximize works only for !isFullScreen() )
0315     if ((mask & NET::FullScreen) != 0 && (state & NET::FullScreen) != 0) {
0316         m_client->setFullScreen(true);
0317     }
0318 }
0319 
0320 void WinInfo::disable()
0321 {
0322     m_client = nullptr; // only used when the object is passed to Deleted
0323 }
0324 
0325 } // namespace