1 /*
2     KWin - the KDE window manager
3     This file is part of the KDE project.
4 
5     SPDX-FileCopyrightText: 1999, 2000 Matthias Ettrich <ettrich@kde.org>
6     SPDX-FileCopyrightText: 2003 Lubos Lunak <l.lunak@kde.org>
7     SPDX-FileCopyrightText: 2013 Martin Gräßlin <mgraesslin@kde.org>
8 
9     SPDX-License-Identifier: GPL-2.0-or-later
10 */
11 // own
12 #include "netinfo.h"
13 // kwin
14 #include "x11client.h"
15 #include "rootinfo_filter.h"
16 #include "virtualdesktops.h"
17 #include "workspace.h"
18 // Qt
19 #include <QDebug>
20 
21 namespace KWin
22 {
23 extern int screen_number;
24 
25 RootInfo *RootInfo::s_self = nullptr;
26 
create()27 RootInfo *RootInfo::create()
28 {
29     Q_ASSERT(!s_self);
30     xcb_window_t supportWindow = xcb_generate_id(connection());
31     const uint32_t values[] = {true};
32     xcb_create_window(connection(), XCB_COPY_FROM_PARENT, supportWindow, KWin::rootWindow(),
33                       0, 0, 1, 1, 0, XCB_COPY_FROM_PARENT,
34                       XCB_COPY_FROM_PARENT, XCB_CW_OVERRIDE_REDIRECT, values);
35     const uint32_t lowerValues[] = { XCB_STACK_MODE_BELOW }; // See usage in layers.cpp
36     // we need to do the lower window with a roundtrip, otherwise NETRootInfo is not functioning
37     ScopedCPointer<xcb_generic_error_t> error(xcb_request_check(connection(),
38         xcb_configure_window_checked(connection(), supportWindow, XCB_CONFIG_WINDOW_STACK_MODE, lowerValues)));
39     if (!error.isNull()) {
40         qCDebug(KWIN_CORE) << "Error occurred while lowering support window: " << error->error_code;
41     }
42 
43     const NET::Properties properties = NET::Supported |
44         NET::SupportingWMCheck |
45         NET::ClientList |
46         NET::ClientListStacking |
47         NET::DesktopGeometry |
48         NET::NumberOfDesktops |
49         NET::CurrentDesktop |
50         NET::ActiveWindow |
51         NET::WorkArea |
52         NET::CloseWindow |
53         NET::DesktopNames |
54         NET::WMName |
55         NET::WMVisibleName |
56         NET::WMDesktop |
57         NET::WMWindowType |
58         NET::WMState |
59         NET::WMStrut |
60         NET::WMIconGeometry |
61         NET::WMIcon |
62         NET::WMPid |
63         NET::WMMoveResize |
64         NET::WMFrameExtents |
65         NET::WMPing;
66     const NET::WindowTypes types = NET::NormalMask |
67         NET::DesktopMask |
68         NET::DockMask |
69         NET::ToolbarMask |
70         NET::MenuMask |
71         NET::DialogMask |
72         NET::OverrideMask |
73         NET::UtilityMask |
74         NET::SplashMask; // No compositing window types here unless we support them also as managed window types
75     const NET::States states = NET::Modal |
76         //NET::Sticky | // Large desktops not supported (and probably never will be)
77         NET::MaxVert |
78         NET::MaxHoriz |
79         NET::Shaded |
80         NET::SkipTaskbar |
81         NET::KeepAbove |
82         //NET::StaysOnTop | // The same like KeepAbove
83         NET::SkipPager |
84         NET::Hidden |
85         NET::FullScreen |
86         NET::KeepBelow |
87         NET::DemandsAttention |
88         NET::SkipSwitcher |
89         NET::Focused;
90     NET::Properties2 properties2 = NET::WM2UserTime |
91         NET::WM2StartupId |
92         NET::WM2AllowedActions |
93         NET::WM2RestackWindow |
94         NET::WM2MoveResizeWindow |
95         NET::WM2ExtendedStrut |
96         NET::WM2KDETemporaryRules |
97         NET::WM2ShowingDesktop |
98         NET::WM2DesktopLayout |
99         NET::WM2FullPlacement |
100         NET::WM2FullscreenMonitors |
101         NET::WM2KDEShadow |
102         NET::WM2OpaqueRegion |
103         NET::WM2GTKFrameExtents;
104 #ifdef KWIN_BUILD_ACTIVITIES
105         properties2 |= NET::WM2Activities;
106 #endif
107     const NET::Actions actions = NET::ActionMove |
108         NET::ActionResize |
109         NET::ActionMinimize |
110         NET::ActionShade |
111         //NET::ActionStick | // Sticky state is not supported
112         NET::ActionMaxVert |
113         NET::ActionMaxHoriz |
114         NET::ActionFullScreen |
115         NET::ActionChangeDesktop |
116         NET::ActionClose;
117 
118     s_self = new RootInfo(supportWindow, "KWin", properties, types, states, properties2, actions, screen_number);
119     return s_self;
120 }
121 
destroy()122 void RootInfo::destroy()
123 {
124     if (!s_self) {
125         return;
126     }
127     xcb_window_t supportWindow = s_self->supportWindow();
128     delete s_self;
129     s_self = nullptr;
130     xcb_destroy_window(connection(), supportWindow);
131 }
132 
RootInfo(xcb_window_t w,const char * name,NET::Properties properties,NET::WindowTypes types,NET::States states,NET::Properties2 properties2,NET::Actions actions,int scr)133 RootInfo::RootInfo(xcb_window_t w, const char *name, NET::Properties properties, NET::WindowTypes types,
134                    NET::States states, NET::Properties2 properties2, NET::Actions actions, int scr)
135     : NETRootInfo(connection(), w, name, properties, types, states, properties2, actions, scr)
136     , m_activeWindow(activeWindow())
137     , m_eventFilter(std::make_unique<RootInfoFilter>(this))
138 {
139 }
140 
changeNumberOfDesktops(int n)141 void RootInfo::changeNumberOfDesktops(int n)
142 {
143     VirtualDesktopManager::self()->setCount(n);
144 }
145 
changeCurrentDesktop(int d)146 void RootInfo::changeCurrentDesktop(int d)
147 {
148     VirtualDesktopManager::self()->setCurrent(d);
149 }
150 
changeActiveWindow(xcb_window_t w,NET::RequestSource src,xcb_timestamp_t timestamp,xcb_window_t active_window)151 void RootInfo::changeActiveWindow(xcb_window_t w, NET::RequestSource src, xcb_timestamp_t timestamp, xcb_window_t active_window)
152 {
153     Workspace *workspace = Workspace::self();
154     if (X11Client *c = workspace->findClient(Predicate::WindowMatch, w)) {
155         if (timestamp == XCB_CURRENT_TIME)
156             timestamp = c->userTime();
157         if (src != NET::FromApplication && src != FromTool)
158             src = NET::FromTool;
159         if (src == NET::FromTool)
160             workspace->activateClient(c, true);   // force
161         else if (c == workspace->mostRecentlyActivatedClient()) {
162             return; // WORKAROUND? With > 1 plasma activities, we cause this ourselves. bug #240673
163         } else { // NET::FromApplication
164             X11Client *c2;
165             if (workspace->allowClientActivation(c, timestamp, false, true))
166                 workspace->activateClient(c);
167             // if activation of the requestor's window would be allowed, allow activation too
168             else if (active_window != XCB_WINDOW_NONE
169                     && (c2 = workspace->findClient(Predicate::WindowMatch, active_window)) != nullptr
170                     && workspace->allowClientActivation(c2,
171                             timestampCompare(timestamp, c2->userTime() > 0 ? timestamp : c2->userTime()), false, true)) {
172                 workspace->activateClient(c);
173             } else
174                 c->demandAttention();
175         }
176     }
177 }
178 
restackWindow(xcb_window_t w,RequestSource src,xcb_window_t above,int detail,xcb_timestamp_t timestamp)179 void RootInfo::restackWindow(xcb_window_t w, RequestSource src, xcb_window_t above, int detail, xcb_timestamp_t timestamp)
180 {
181     if (X11Client *c = Workspace::self()->findClient(Predicate::WindowMatch, w)) {
182         if (timestamp == XCB_CURRENT_TIME)
183             timestamp = c->userTime();
184         if (src != NET::FromApplication && src != FromTool)
185             src = NET::FromTool;
186         c->restackWindow(above, detail, src, timestamp, true);
187     }
188 }
189 
closeWindow(xcb_window_t w)190 void RootInfo::closeWindow(xcb_window_t w)
191 {
192     X11Client *c = Workspace::self()->findClient(Predicate::WindowMatch, w);
193     if (c)
194         c->closeWindow();
195 }
196 
moveResize(xcb_window_t w,int x_root,int y_root,unsigned long direction)197 void RootInfo::moveResize(xcb_window_t w, int x_root, int y_root, unsigned long direction)
198 {
199     X11Client *c = Workspace::self()->findClient(Predicate::WindowMatch, w);
200     if (c) {
201         updateXTime(); // otherwise grabbing may have old timestamp - this message should include timestamp
202         c->NETMoveResize(x_root, y_root, (Direction)direction);
203     }
204 }
205 
moveResizeWindow(xcb_window_t w,int flags,int x,int y,int width,int height)206 void RootInfo::moveResizeWindow(xcb_window_t w, int flags, int x, int y, int width, int height)
207 {
208     X11Client *c = Workspace::self()->findClient(Predicate::WindowMatch, w);
209     if (c)
210         c->NETMoveResizeWindow(flags, x, y, width, height);
211 }
212 
gotPing(xcb_window_t w,xcb_timestamp_t timestamp)213 void RootInfo::gotPing(xcb_window_t w, xcb_timestamp_t timestamp)
214 {
215     if (X11Client *c = Workspace::self()->findClient(Predicate::WindowMatch, w))
216         c->gotPing(timestamp);
217 }
218 
changeShowingDesktop(bool showing)219 void RootInfo::changeShowingDesktop(bool showing)
220 {
221     Workspace::self()->setShowingDesktop(showing);
222 }
223 
setActiveClient(AbstractClient * client)224 void RootInfo::setActiveClient(AbstractClient *client)
225 {
226     const xcb_window_t w = client ? client->window() : xcb_window_t{XCB_WINDOW_NONE};
227     if (m_activeWindow == w) {
228         return;
229     }
230     m_activeWindow = w;
231     setActiveWindow(m_activeWindow);
232 }
233 
234 // ****************************************
235 // WinInfo
236 // ****************************************
237 
WinInfo(X11Client * c,xcb_window_t window,xcb_window_t rwin,NET::Properties properties,NET::Properties2 properties2)238 WinInfo::WinInfo(X11Client *c, xcb_window_t window,
239                  xcb_window_t rwin, NET::Properties properties, NET::Properties2 properties2)
240     : NETWinInfo(connection(), window, rwin, properties, properties2, NET::WindowManager), m_client(c)
241 {
242 }
243 
changeDesktop(int desktop)244 void WinInfo::changeDesktop(int desktop)
245 {
246     Workspace::self()->sendClientToDesktop(m_client, desktop, true);
247 }
248 
changeFullscreenMonitors(NETFullscreenMonitors topology)249 void WinInfo::changeFullscreenMonitors(NETFullscreenMonitors topology)
250 {
251     m_client->updateFullscreenMonitors(topology);
252 }
253 
changeState(NET::States state,NET::States mask)254 void WinInfo::changeState(NET::States state, NET::States mask)
255 {
256     mask &= ~NET::Sticky; // KWin doesn't support large desktops, ignore
257     mask &= ~NET::Hidden; // clients are not allowed to change this directly
258     state &= mask; // for safety, clear all other bits
259 
260     if ((mask & NET::FullScreen) != 0 && (state & NET::FullScreen) == 0)
261         m_client->setFullScreen(false, false);
262     if ((mask & NET::Max) == NET::Max)
263         m_client->setMaximize(state & NET::MaxVert, state & NET::MaxHoriz);
264     else if (mask & NET::MaxVert)
265         m_client->setMaximize(state & NET::MaxVert, m_client->maximizeMode() & MaximizeHorizontal);
266     else if (mask & NET::MaxHoriz)
267         m_client->setMaximize(m_client->maximizeMode() & MaximizeVertical, state & NET::MaxHoriz);
268 
269     if (mask & NET::Shaded)
270         m_client->setShade(state & NET::Shaded ? ShadeNormal : ShadeNone);
271     if (mask & NET::KeepAbove)
272         m_client->setKeepAbove((state & NET::KeepAbove) != 0);
273     if (mask & NET::KeepBelow)
274         m_client->setKeepBelow((state & NET::KeepBelow) != 0);
275     if (mask & NET::SkipTaskbar)
276         m_client->setOriginalSkipTaskbar((state & NET::SkipTaskbar) != 0);
277     if (mask & NET::SkipPager)
278         m_client->setSkipPager((state & NET::SkipPager) != 0);
279     if (mask & NET::SkipSwitcher)
280         m_client->setSkipSwitcher((state & NET::SkipSwitcher) != 0);
281     if (mask & NET::DemandsAttention)
282         m_client->demandAttention((state & NET::DemandsAttention) != 0);
283     if (mask & NET::Modal)
284         m_client->setModal((state & NET::Modal) != 0);
285     // unsetting fullscreen first, setting it last (because e.g. maximize works only for !isFullScreen() )
286     if ((mask & NET::FullScreen) != 0 && (state & NET::FullScreen) != 0)
287         m_client->setFullScreen(true, false);
288 }
289 
disable()290 void WinInfo::disable()
291 {
292     m_client = nullptr; // only used when the object is passed to Deleted
293 }
294 
295 } // namespace
296