1 /*
2     SPDX-FileCopyrightText: 2000 Troll Tech AS
3     SPDX-FileCopyrightText: 2003 Lubos Lunak <l.lunak@kde.org>
4 
5     SPDX-License-Identifier: MIT
6 */
7 
8 //#define NETWMDEBUG
9 #include "netwm.h"
10 
11 #include <xcb/xproto.h>
12 
13 #include "atoms_p.h"
14 #include "netwm_p.h"
15 
16 #if KWINDOWSYSTEM_HAVE_X11 // FIXME
17 
18 #include <QGuiApplication>
19 #include <QHash>
20 #include <qx11info_x11.h>
21 
22 #include <kwindowsystem.h>
23 #include <kxutils_p.h>
24 
25 #include <assert.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 
30 // This struct is defined here to avoid a dependency on xcb-icccm
31 struct kde_wm_hints {
32     uint32_t flags;
33     uint32_t input;
34     int32_t initial_state;
35     xcb_pixmap_t icon_pixmap;
36     xcb_window_t icon_window;
37     int32_t icon_x;
38     int32_t icon_y;
39     xcb_pixmap_t icon_mask;
40     xcb_window_t window_group;
41 };
42 
43 typedef QHash<xcb_connection_t *, QSharedDataPointer<Atoms>> AtomHash;
Q_GLOBAL_STATIC(AtomHash,s_gAtomsHash)44 Q_GLOBAL_STATIC(AtomHash, s_gAtomsHash)
45 
46 static QSharedDataPointer<Atoms> atomsForConnection(xcb_connection_t *c)
47 {
48     auto it = s_gAtomsHash->constFind(c);
49     if (it == s_gAtomsHash->constEnd()) {
50         QSharedDataPointer<Atoms> atom(new Atoms(c));
51         s_gAtomsHash->insert(c, atom);
52         return atom;
53     }
54     return it.value();
55 }
56 
Atoms(xcb_connection_t * c)57 Atoms::Atoms(xcb_connection_t *c)
58     : QSharedData()
59     , m_connection(c)
60 {
61     for (int i = 0; i < KwsAtomCount; ++i) {
62         m_atoms[i] = XCB_ATOM_NONE;
63     }
64     init();
65 }
66 
67 static const uint32_t netwm_sendevent_mask = (XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY);
68 
69 const long MAX_PROP_SIZE = 100000;
70 
nstrdup(const char * s1)71 static char *nstrdup(const char *s1)
72 {
73     if (!s1) {
74         return (char *)nullptr;
75     }
76 
77     int l = strlen(s1) + 1;
78     char *s2 = new char[l];
79     strncpy(s2, s1, l);
80     return s2;
81 }
82 
nstrndup(const char * s1,int l)83 static char *nstrndup(const char *s1, int l)
84 {
85     if (!s1 || l == 0) {
86         return (char *)nullptr;
87     }
88 
89     char *s2 = new char[l + 1];
90     strncpy(s2, s1, l);
91     s2[l] = '\0';
92     return s2;
93 }
94 
nwindup(const xcb_window_t * w1,int n)95 static xcb_window_t *nwindup(const xcb_window_t *w1, int n)
96 {
97     if (!w1 || n == 0) {
98         return (xcb_window_t *)nullptr;
99     }
100 
101     xcb_window_t *w2 = new xcb_window_t[n];
102     while (n--) {
103         w2[n] = w1[n];
104     }
105     return w2;
106 }
107 
refdec_nri(NETRootInfoPrivate * p)108 static void refdec_nri(NETRootInfoPrivate *p)
109 {
110 #ifdef NETWMDEBUG
111     fprintf(stderr, "NET: decrementing NETRootInfoPrivate::ref (%d)\n", p->ref - 1);
112 #endif
113 
114     if (!--p->ref) {
115 #ifdef NETWMDEBUG
116         fprintf(stderr, "NET: \tno more references, deleting\n");
117 #endif
118 
119         delete[] p->name;
120         delete[] p->stacking;
121         delete[] p->clients;
122         delete[] p->virtual_roots;
123         delete[] p->temp_buf;
124 
125         int i;
126         for (i = 0; i < p->desktop_names.size(); i++) {
127             delete[] p->desktop_names[i];
128         }
129     }
130 }
131 
refdec_nwi(NETWinInfoPrivate * p)132 static void refdec_nwi(NETWinInfoPrivate *p)
133 {
134 #ifdef NETWMDEBUG
135     fprintf(stderr, "NET: decrementing NETWinInfoPrivate::ref (%d)\n", p->ref - 1);
136 #endif
137 
138     if (!--p->ref) {
139 #ifdef NETWMDEBUG
140         fprintf(stderr, "NET: \tno more references, deleting\n");
141 #endif
142 
143         delete[] p->name;
144         delete[] p->visible_name;
145         delete[] p->window_role;
146         delete[] p->icon_name;
147         delete[] p->visible_icon_name;
148         delete[] p->startup_id;
149         delete[] p->class_class;
150         delete[] p->class_name;
151         delete[] p->activities;
152         delete[] p->client_machine;
153         delete[] p->desktop_file;
154         delete[] p->appmenu_object_path;
155         delete[] p->appmenu_service_name;
156 
157         int i;
158         for (i = 0; i < p->icons.size(); i++) {
159             delete[] p->icons[i].data;
160         }
161         delete[] p->icon_sizes;
162     }
163 }
164 
165 template<typename T>
get_value_reply(xcb_connection_t * c,const xcb_get_property_cookie_t cookie,xcb_atom_t type,T def,bool * success=nullptr)166 T get_value_reply(xcb_connection_t *c, const xcb_get_property_cookie_t cookie, xcb_atom_t type, T def, bool *success = nullptr)
167 {
168     T value = def;
169 
170     xcb_get_property_reply_t *reply = xcb_get_property_reply(c, cookie, nullptr);
171 
172     if (success) {
173         *success = false;
174     }
175 
176     if (reply) {
177         if (reply->type == type && reply->value_len == 1 && reply->format == sizeof(T) * 8) {
178             value = *reinterpret_cast<T *>(xcb_get_property_value(reply));
179 
180             if (success) {
181                 *success = true;
182             }
183         }
184 
185         free(reply);
186     }
187 
188     return value;
189 }
190 
191 template<typename T>
get_array_reply(xcb_connection_t * c,const xcb_get_property_cookie_t cookie,xcb_atom_t type)192 QVector<T> get_array_reply(xcb_connection_t *c, const xcb_get_property_cookie_t cookie, xcb_atom_t type)
193 {
194     xcb_get_property_reply_t *reply = xcb_get_property_reply(c, cookie, nullptr);
195     if (!reply) {
196         return QVector<T>();
197     }
198 
199     QVector<T> vector;
200 
201     if (reply->type == type && reply->value_len > 0 && reply->format == sizeof(T) * 8) {
202         T *data = reinterpret_cast<T *>(xcb_get_property_value(reply));
203 
204         vector.resize(reply->value_len);
205         memcpy((void *)&vector.first(), (void *)data, reply->value_len * sizeof(T));
206     }
207 
208     free(reply);
209     return vector;
210 }
211 
get_string_reply(xcb_connection_t * c,const xcb_get_property_cookie_t cookie,xcb_atom_t type)212 static QByteArray get_string_reply(xcb_connection_t *c, const xcb_get_property_cookie_t cookie, xcb_atom_t type)
213 {
214     xcb_get_property_reply_t *reply = xcb_get_property_reply(c, cookie, nullptr);
215     if (!reply) {
216         return QByteArray();
217     }
218 
219     QByteArray value;
220 
221     if (reply->type == type && reply->format == 8 && reply->value_len > 0) {
222         const char *data = (const char *)xcb_get_property_value(reply);
223         int len = reply->value_len;
224 
225         if (data) {
226             value = QByteArray(data, data[len - 1] ? len : len - 1);
227         }
228     }
229 
230     free(reply);
231     return value;
232 }
233 
get_stringlist_reply(xcb_connection_t * c,const xcb_get_property_cookie_t cookie,xcb_atom_t type)234 static QList<QByteArray> get_stringlist_reply(xcb_connection_t *c, const xcb_get_property_cookie_t cookie, xcb_atom_t type)
235 {
236     xcb_get_property_reply_t *reply = xcb_get_property_reply(c, cookie, nullptr);
237     if (!reply) {
238         return QList<QByteArray>();
239     }
240 
241     QList<QByteArray> list;
242 
243     if (reply->type == type && reply->format == 8 && reply->value_len > 0) {
244         const char *data = (const char *)xcb_get_property_value(reply);
245         int len = reply->value_len;
246 
247         if (data) {
248             const QByteArray ba = QByteArray(data, data[len - 1] ? len : len - 1);
249             list = ba.split('\0');
250         }
251     }
252 
253     free(reply);
254     return list;
255 }
256 
257 #ifdef NETWMDEBUG
get_atom_name(xcb_connection_t * c,xcb_atom_t atom)258 static QByteArray get_atom_name(xcb_connection_t *c, xcb_atom_t atom)
259 {
260     const xcb_get_atom_name_cookie_t cookie = xcb_get_atom_name(c, atom);
261 
262     xcb_get_atom_name_reply_t *reply = xcb_get_atom_name_reply(c, cookie, 0);
263     if (!reply) {
264         return QByteArray();
265     }
266 
267     QByteArray ba(xcb_get_atom_name_name(reply));
268     free(reply);
269 
270     return ba;
271 }
272 #endif
273 
init()274 void Atoms::init()
275 {
276 #define ENUM_CREATE_CHAR_ARRAY 1
277 #include "atoms_p.h" // creates const char* array "KwsAtomStrings"
278     // Send the intern atom requests
279     xcb_intern_atom_cookie_t cookies[KwsAtomCount];
280     for (int i = 0; i < KwsAtomCount; ++i) {
281         cookies[i] = xcb_intern_atom(m_connection, false, strlen(KwsAtomStrings[i]), KwsAtomStrings[i]);
282     }
283 
284     // Get the replies
285     for (int i = 0; i < KwsAtomCount; ++i) {
286         xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(m_connection, cookies[i], nullptr);
287         if (!reply) {
288             continue;
289         }
290 
291         m_atoms[i] = reply->atom;
292         free(reply);
293     }
294 }
295 
readIcon(xcb_connection_t * c,const xcb_get_property_cookie_t cookie,NETRArray<NETIcon> & icons,int & icon_count)296 static void readIcon(xcb_connection_t *c, const xcb_get_property_cookie_t cookie, NETRArray<NETIcon> &icons, int &icon_count)
297 {
298 #ifdef NETWMDEBUG
299     fprintf(stderr, "NET: readIcon\n");
300 #endif
301 
302     // reset
303     for (int i = 0; i < icons.size(); i++) {
304         delete[] icons[i].data;
305     }
306 
307     icons.reset();
308     icon_count = 0;
309 
310     xcb_get_property_reply_t *reply = xcb_get_property_reply(c, cookie, nullptr);
311 
312     if (!reply || reply->value_len < 3 || reply->format != 32 || reply->type != XCB_ATOM_CARDINAL) {
313         if (reply) {
314             free(reply);
315         }
316 
317         return;
318     }
319 
320     uint32_t *data = (uint32_t *)xcb_get_property_value(reply);
321 
322     for (unsigned int i = 0, j = 0; j < reply->value_len - 2; i++) {
323         uint32_t width = data[j++];
324         uint32_t height = data[j++];
325         uint32_t size = width * height * sizeof(uint32_t);
326         if (j + width * height > reply->value_len) {
327             fprintf(stderr, "Ill-encoded icon data; proposed size leads to out of bounds access. Skipping. (%d x %d)\n", width, height);
328             break;
329         }
330         if (width > 1024 || height > 1024) {
331             fprintf(stderr, "Warning: found huge icon. The icon data may be ill-encoded. (%d x %d)\n", width, height);
332             // 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
333             // system is abused to transfer wallpapers or such.
334         }
335 
336         icons[i].size.width = width;
337         icons[i].size.height = height;
338         icons[i].data = new unsigned char[size];
339 
340         memcpy((void *)icons[i].data, (const void *)&data[j], size);
341 
342         j += width * height;
343         icon_count++;
344     }
345 
346     free(reply);
347 
348 #ifdef NETWMDEBUG
349     fprintf(stderr, "NET: readIcon got %d icons\n", icon_count);
350 #endif
351 }
352 
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[])353 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[])
354 {
355     xcb_client_message_event_t event;
356     event.response_type = XCB_CLIENT_MESSAGE;
357     event.format = 32;
358     event.sequence = 0;
359     event.window = window;
360     event.type = message;
361 
362     for (int i = 0; i < 5; i++) {
363         event.data.data32[i] = data[i];
364     }
365 
366     xcb_send_event(c, false, destination, mask, (const char *)&event);
367 }
368 
369 template<class Z>
NETRArray()370 NETRArray<Z>::NETRArray()
371     : sz(0)
372     , capacity(2)
373 {
374     d = (Z *)calloc(capacity, sizeof(Z)); // allocate 2 elts and set to zero
375 }
376 
377 template<class Z>
~NETRArray()378 NETRArray<Z>::~NETRArray()
379 {
380     free(d);
381 }
382 
383 template<class Z>
reset()384 void NETRArray<Z>::reset()
385 {
386     sz = 0;
387     capacity = 2;
388     d = (Z *)realloc(d, sizeof(Z) * capacity);
389     memset((void *)d, 0, sizeof(Z) * capacity);
390 }
391 
392 template<class Z>
operator [](int index)393 Z &NETRArray<Z>::operator[](int index)
394 {
395     if (index >= capacity) {
396         // allocate space for the new data
397         // open table has amortized O(1) access time
398         // when N elements appended consecutively -- exa
399         int newcapacity = 2 * capacity > index + 1 ? 2 * capacity : index + 1; // max
400         // copy into new larger memory block using realloc
401         d = (Z *)realloc(d, sizeof(Z) * newcapacity);
402         memset((void *)&d[capacity], 0, sizeof(Z) * (newcapacity - capacity));
403         capacity = newcapacity;
404     }
405     if (index >= sz) { // at this point capacity>index
406         sz = index + 1;
407     }
408 
409     return d[index];
410 }
411 
412 /*
413  The viewport<->desktop matching is a bit backwards, since NET* classes are the base
414  (and were originally even created with the intention of being the reference WM spec
415  implementation) and KWindowSystem builds on top of it. However it's simpler to add watching
416  whether the WM uses viewport is simpler to KWindowSystem and not having this mapping
417  in NET* classes could result in some code using it directly and not supporting viewport.
418  So NET* classes check if mapping is needed and if yes they forward to KWindowSystem,
419  which will forward again back to NET* classes, but to viewport calls instead of desktop calls.
420 */
421 
422 // Construct a new NETRootInfo object.
423 
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)424 NETRootInfo::NETRootInfo(xcb_connection_t *connection,
425                          xcb_window_t supportWindow,
426                          const char *wmName,
427                          NET::Properties properties,
428                          NET::WindowTypes windowTypes,
429                          NET::States states,
430                          NET::Properties2 properties2,
431                          NET::Actions actions,
432                          int screen,
433                          bool doActivate)
434 {
435 #ifdef NETWMDEBUG
436     fprintf(stderr, "NETRootInfo::NETRootInfo: using window manager constructor\n");
437 #endif
438 
439     p = new NETRootInfoPrivate;
440     p->ref = 1;
441     p->atoms = atomsForConnection(connection);
442 
443     p->name = nstrdup(wmName);
444 
445     p->conn = connection;
446 
447     p->temp_buf = nullptr;
448     p->temp_buf_size = 0;
449 
450     const xcb_setup_t *setup = xcb_get_setup(p->conn);
451     xcb_screen_iterator_t it = xcb_setup_roots_iterator(setup);
452 
453     if (screen != -1 && screen < setup->roots_len) {
454         for (int i = 0; i < screen; i++) {
455             xcb_screen_next(&it);
456         }
457     }
458 
459     p->root = it.data->root;
460     p->supportwindow = supportWindow;
461     p->number_of_desktops = p->current_desktop = 0;
462     p->active = XCB_WINDOW_NONE;
463     p->clients = p->stacking = p->virtual_roots = (xcb_window_t *)nullptr;
464     p->clients_count = p->stacking_count = p->virtual_roots_count = 0;
465     p->showing_desktop = false;
466     p->desktop_layout_orientation = OrientationHorizontal;
467     p->desktop_layout_corner = DesktopLayoutCornerTopLeft;
468     p->desktop_layout_columns = p->desktop_layout_rows = 0;
469     setDefaultProperties();
470     p->properties = properties;
471     p->properties2 = properties2;
472     p->windowTypes = windowTypes;
473     p->states = states;
474     p->actions = actions;
475     // force support for Supported and SupportingWMCheck for window managers
476     p->properties |= (Supported | SupportingWMCheck);
477     p->clientProperties = DesktopNames // the only thing that can be changed by clients
478         | WMPing; // or they can reply to this
479     p->clientProperties2 = WM2DesktopLayout;
480 
481     p->role = WindowManager;
482 
483     if (doActivate) {
484         activate();
485     }
486 }
487 
NETRootInfo(xcb_connection_t * connection,NET::Properties properties,NET::Properties2 properties2,int screen,bool doActivate)488 NETRootInfo::NETRootInfo(xcb_connection_t *connection, NET::Properties properties, NET::Properties2 properties2, int screen, bool doActivate)
489 {
490 #ifdef NETWMDEBUG
491     fprintf(stderr, "NETRootInfo::NETRootInfo: using Client constructor\n");
492 #endif
493 
494     p = new NETRootInfoPrivate;
495     p->ref = 1;
496     p->atoms = atomsForConnection(connection);
497 
498     p->name = nullptr;
499 
500     p->conn = connection;
501 
502     p->temp_buf = nullptr;
503     p->temp_buf_size = 0;
504 
505     const xcb_setup_t *setup = xcb_get_setup(p->conn);
506     xcb_screen_iterator_t it = xcb_setup_roots_iterator(setup);
507 
508     if (screen != -1 && screen < setup->roots_len) {
509         for (int i = 0; i < screen; i++) {
510             xcb_screen_next(&it);
511         }
512     }
513 
514     p->root = it.data->root;
515     p->rootSize.width = it.data->width_in_pixels;
516     p->rootSize.height = it.data->height_in_pixels;
517 
518     p->supportwindow = XCB_WINDOW_NONE;
519     p->number_of_desktops = p->current_desktop = 0;
520     p->active = XCB_WINDOW_NONE;
521     p->clients = p->stacking = p->virtual_roots = (xcb_window_t *)nullptr;
522     p->clients_count = p->stacking_count = p->virtual_roots_count = 0;
523     p->showing_desktop = false;
524     p->desktop_layout_orientation = OrientationHorizontal;
525     p->desktop_layout_corner = DesktopLayoutCornerTopLeft;
526     p->desktop_layout_columns = p->desktop_layout_rows = 0;
527     setDefaultProperties();
528     p->clientProperties = properties;
529     p->clientProperties2 = properties2;
530     p->properties = NET::Properties();
531     p->properties2 = NET::Properties2();
532     p->windowTypes = NET::WindowTypes();
533     p->states = NET::States();
534     p->actions = NET::Actions();
535 
536     p->role = Client;
537 
538     if (doActivate) {
539         activate();
540     }
541 }
542 
543 // Copy an existing NETRootInfo object.
544 
NETRootInfo(const NETRootInfo & rootinfo)545 NETRootInfo::NETRootInfo(const NETRootInfo &rootinfo)
546 {
547 #ifdef NETWMDEBUG
548     fprintf(stderr, "NETRootInfo::NETRootInfo: using copy constructor\n");
549 #endif
550 
551     p = rootinfo.p;
552 
553     p->ref++;
554 }
555 
556 // Be gone with our NETRootInfo.
557 
~NETRootInfo()558 NETRootInfo::~NETRootInfo()
559 {
560     refdec_nri(p);
561 
562     if (!p->ref) {
563         delete p;
564     }
565 }
566 
setDefaultProperties()567 void NETRootInfo::setDefaultProperties()
568 {
569     p->properties = Supported | SupportingWMCheck;
570     p->windowTypes = NormalMask | DesktopMask | DockMask | ToolbarMask | MenuMask | DialogMask;
571     p->states = Modal | Sticky | MaxVert | MaxHoriz | Shaded | SkipTaskbar | KeepAbove;
572     p->properties2 = NET::Properties2();
573     p->actions = NET::Actions();
574     p->clientProperties = NET::Properties();
575     p->clientProperties2 = NET::Properties2();
576 }
577 
activate()578 void NETRootInfo::activate()
579 {
580     if (p->role == WindowManager) {
581 #ifdef NETWMDEBUG
582         fprintf(stderr, "NETRootInfo::activate: setting supported properties on root\n");
583 #endif
584 
585         setSupported();
586         update(p->clientProperties, p->clientProperties2);
587     } else {
588 #ifdef NETWMDEBUG
589         fprintf(stderr, "NETRootInfo::activate: updating client information\n");
590 #endif
591 
592         update(p->clientProperties, p->clientProperties2);
593     }
594 }
595 
setClientList(const xcb_window_t * windows,unsigned int count)596 void NETRootInfo::setClientList(const xcb_window_t *windows, unsigned int count)
597 {
598     if (p->role != WindowManager) {
599         return;
600     }
601 
602     p->clients_count = count;
603 
604     delete[] p->clients;
605     p->clients = nwindup(windows, count);
606 
607 #ifdef NETWMDEBUG
608     fprintf(stderr, "NETRootInfo::setClientList: setting list with %ld windows\n", p->clients_count);
609 #endif
610 
611     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);
612 }
613 
setClientListStacking(const xcb_window_t * windows,unsigned int count)614 void NETRootInfo::setClientListStacking(const xcb_window_t *windows, unsigned int count)
615 {
616     if (p->role != WindowManager) {
617         return;
618     }
619 
620     p->stacking_count = count;
621     delete[] p->stacking;
622     p->stacking = nwindup(windows, count);
623 
624 #ifdef NETWMDEBUG
625     fprintf(stderr, "NETRootInfo::setClientListStacking: setting list with %ld windows\n", p->clients_count);
626 #endif
627 
628     xcb_change_property(p->conn,
629                         XCB_PROP_MODE_REPLACE,
630                         p->root,
631                         p->atom(_NET_CLIENT_LIST_STACKING),
632                         XCB_ATOM_WINDOW,
633                         32,
634                         p->stacking_count,
635                         (const void *)windows);
636 }
637 
setNumberOfDesktops(int numberOfDesktops)638 void NETRootInfo::setNumberOfDesktops(int numberOfDesktops)
639 {
640 #ifdef NETWMDEBUG
641     fprintf(stderr, "NETRootInfo::setNumberOfDesktops: setting desktop count to %d (%s)\n", numberOfDesktops, (p->role == WindowManager) ? "WM" : "Client");
642 #endif
643 
644     if (p->role == WindowManager) {
645         p->number_of_desktops = numberOfDesktops;
646         const uint32_t d = numberOfDesktops;
647         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);
648     } else {
649         const uint32_t data[5] = {uint32_t(numberOfDesktops), 0, 0, 0, 0};
650 
651         send_client_message(p->conn, netwm_sendevent_mask, p->root, p->root, p->atom(_NET_NUMBER_OF_DESKTOPS), data);
652     }
653 }
654 
setCurrentDesktop(int desktop,bool ignore_viewport)655 void NETRootInfo::setCurrentDesktop(int desktop, bool ignore_viewport)
656 {
657 #ifdef NETWMDEBUG
658     fprintf(stderr, "NETRootInfo::setCurrentDesktop: setting current desktop = %d (%s)\n", desktop, (p->role == WindowManager) ? "WM" : "Client");
659 #endif
660 
661     if (p->role == WindowManager) {
662         p->current_desktop = desktop;
663         uint32_t d = p->current_desktop - 1;
664         xcb_change_property(p->conn, XCB_PROP_MODE_REPLACE, p->root, p->atom(_NET_CURRENT_DESKTOP), XCB_ATOM_CARDINAL, 32, 1, (const void *)&d);
665     } else {
666         if (!ignore_viewport && KWindowSystem::mapViewport()) {
667             KWindowSystem::setCurrentDesktop(desktop);
668             return;
669         }
670 
671         const uint32_t data[5] = {uint32_t(desktop - 1), 0, 0, 0, 0};
672 
673         send_client_message(p->conn, netwm_sendevent_mask, p->root, p->root, p->atom(_NET_CURRENT_DESKTOP), data);
674     }
675 }
676 
setDesktopName(int desktop,const char * desktopName)677 void NETRootInfo::setDesktopName(int desktop, const char *desktopName)
678 {
679     // Allow setting desktop names even for non-existent desktops, see the spec, sect.3.7.
680     if (desktop < 1) {
681         return;
682     }
683 
684     delete[] p->desktop_names[desktop - 1];
685     p->desktop_names[desktop - 1] = nstrdup(desktopName);
686 
687     unsigned int i;
688     unsigned int proplen;
689     unsigned int num = ((p->number_of_desktops > p->desktop_names.size()) ? p->number_of_desktops : p->desktop_names.size());
690     for (i = 0, proplen = 0; i < num; i++) {
691         proplen += (p->desktop_names[i] != nullptr ? strlen(p->desktop_names[i]) + 1 : 1);
692     }
693 
694     char *prop = new char[proplen];
695     char *propp = prop;
696 
697     for (i = 0; i < num; i++) {
698         if (p->desktop_names[i]) {
699             strcpy(propp, p->desktop_names[i]);
700             propp += strlen(p->desktop_names[i]) + 1;
701         } else {
702             *propp++ = '\0';
703         }
704     }
705 
706 #ifdef NETWMDEBUG
707     fprintf(stderr,
708             "NETRootInfo::setDesktopName(%d, '%s')\n"
709             "NETRootInfo::setDesktopName: total property length = %d",
710             desktop,
711             desktopName,
712             proplen);
713 #endif
714 
715     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);
716 
717     delete[] prop;
718 }
719 
setDesktopGeometry(const NETSize & geometry)720 void NETRootInfo::setDesktopGeometry(const NETSize &geometry)
721 {
722 #ifdef NETWMDEBUG
723     fprintf(stderr, "NETRootInfo::setDesktopGeometry( -- , { %d, %d }) (%s)\n", geometry.width, geometry.height, (p->role == WindowManager) ? "WM" : "Client");
724 #endif
725 
726     if (p->role == WindowManager) {
727         p->geometry = geometry;
728 
729         uint32_t data[2];
730         data[0] = p->geometry.width;
731         data[1] = p->geometry.height;
732 
733         xcb_change_property(p->conn, XCB_PROP_MODE_REPLACE, p->root, p->atom(_NET_DESKTOP_GEOMETRY), XCB_ATOM_CARDINAL, 32, 2, (const void *)data);
734     } else {
735         uint32_t data[5] = {uint32_t(geometry.width), uint32_t(geometry.height), 0, 0, 0};
736 
737         send_client_message(p->conn, netwm_sendevent_mask, p->root, p->root, p->atom(_NET_DESKTOP_GEOMETRY), data);
738     }
739 }
740 
setDesktopViewport(int desktop,const NETPoint & viewport)741 void NETRootInfo::setDesktopViewport(int desktop, const NETPoint &viewport)
742 {
743 #ifdef NETWMDEBUG
744     fprintf(stderr, "NETRootInfo::setDesktopViewport(%d, { %d, %d }) (%s)\n", desktop, viewport.x, viewport.y, (p->role == WindowManager) ? "WM" : "Client");
745 #endif
746 
747     if (desktop < 1) {
748         return;
749     }
750 
751     if (p->role == WindowManager) {
752         p->viewport[desktop - 1] = viewport;
753 
754         int d;
755         int i;
756         int l;
757         l = p->number_of_desktops * 2;
758         uint32_t *data = new uint32_t[l];
759         for (d = 0, i = 0; d < p->number_of_desktops; d++) {
760             data[i++] = p->viewport[d].x;
761             data[i++] = p->viewport[d].y;
762         }
763 
764         xcb_change_property(p->conn, XCB_PROP_MODE_REPLACE, p->root, p->atom(_NET_DESKTOP_VIEWPORT), XCB_ATOM_CARDINAL, 32, l, (const void *)data);
765 
766         delete[] data;
767     } else {
768         const uint32_t data[5] = {uint32_t(viewport.x), uint32_t(viewport.y), 0, 0, 0};
769 
770         send_client_message(p->conn, netwm_sendevent_mask, p->root, p->root, p->atom(_NET_DESKTOP_VIEWPORT), data);
771     }
772 }
773 
setSupported()774 void NETRootInfo::setSupported()
775 {
776     if (p->role != WindowManager) {
777 #ifdef NETWMDEBUG
778         fprintf(stderr, "NETRootInfo::setSupported - role != WindowManager\n");
779 #endif
780 
781         return;
782     }
783 
784     xcb_atom_t atoms[KwsAtomCount];
785     int pnum = 2;
786 
787     // Root window properties/messages
788     atoms[0] = p->atom(_NET_SUPPORTED);
789     atoms[1] = p->atom(_NET_SUPPORTING_WM_CHECK);
790 
791     if (p->properties & ClientList) {
792         atoms[pnum++] = p->atom(_NET_CLIENT_LIST);
793     }
794 
795     if (p->properties & ClientListStacking) {
796         atoms[pnum++] = p->atom(_NET_CLIENT_LIST_STACKING);
797     }
798 
799     if (p->properties & NumberOfDesktops) {
800         atoms[pnum++] = p->atom(_NET_NUMBER_OF_DESKTOPS);
801     }
802 
803     if (p->properties & DesktopGeometry) {
804         atoms[pnum++] = p->atom(_NET_DESKTOP_GEOMETRY);
805     }
806 
807     if (p->properties & DesktopViewport) {
808         atoms[pnum++] = p->atom(_NET_DESKTOP_VIEWPORT);
809     }
810 
811     if (p->properties & CurrentDesktop) {
812         atoms[pnum++] = p->atom(_NET_CURRENT_DESKTOP);
813     }
814 
815     if (p->properties & DesktopNames) {
816         atoms[pnum++] = p->atom(_NET_DESKTOP_NAMES);
817     }
818 
819     if (p->properties & ActiveWindow) {
820         atoms[pnum++] = p->atom(_NET_ACTIVE_WINDOW);
821     }
822 
823     if (p->properties & WorkArea) {
824         atoms[pnum++] = p->atom(_NET_WORKAREA);
825     }
826 
827     if (p->properties & VirtualRoots) {
828         atoms[pnum++] = p->atom(_NET_VIRTUAL_ROOTS);
829     }
830 
831     if (p->properties2 & WM2DesktopLayout) {
832         atoms[pnum++] = p->atom(_NET_DESKTOP_LAYOUT);
833     }
834 
835     if (p->properties & CloseWindow) {
836         atoms[pnum++] = p->atom(_NET_CLOSE_WINDOW);
837     }
838 
839     if (p->properties2 & WM2RestackWindow) {
840         atoms[pnum++] = p->atom(_NET_RESTACK_WINDOW);
841     }
842 
843     if (p->properties2 & WM2ShowingDesktop) {
844         atoms[pnum++] = p->atom(_NET_SHOWING_DESKTOP);
845     }
846 
847     // Application window properties/messages
848     if (p->properties & WMMoveResize) {
849         atoms[pnum++] = p->atom(_NET_WM_MOVERESIZE);
850     }
851 
852     if (p->properties2 & WM2MoveResizeWindow) {
853         atoms[pnum++] = p->atom(_NET_MOVERESIZE_WINDOW);
854     }
855 
856     if (p->properties & WMName) {
857         atoms[pnum++] = p->atom(_NET_WM_NAME);
858     }
859 
860     if (p->properties & WMVisibleName) {
861         atoms[pnum++] = p->atom(_NET_WM_VISIBLE_NAME);
862     }
863 
864     if (p->properties & WMIconName) {
865         atoms[pnum++] = p->atom(_NET_WM_ICON_NAME);
866     }
867 
868     if (p->properties & WMVisibleIconName) {
869         atoms[pnum++] = p->atom(_NET_WM_VISIBLE_ICON_NAME);
870     }
871 
872     if (p->properties & WMDesktop) {
873         atoms[pnum++] = p->atom(_NET_WM_DESKTOP);
874     }
875 
876     if (p->properties & WMWindowType) {
877         atoms[pnum++] = p->atom(_NET_WM_WINDOW_TYPE);
878 
879         // Application window types
880         if (p->windowTypes & NormalMask) {
881             atoms[pnum++] = p->atom(_NET_WM_WINDOW_TYPE_NORMAL);
882         }
883         if (p->windowTypes & DesktopMask) {
884             atoms[pnum++] = p->atom(_NET_WM_WINDOW_TYPE_DESKTOP);
885         }
886         if (p->windowTypes & DockMask) {
887             atoms[pnum++] = p->atom(_NET_WM_WINDOW_TYPE_DOCK);
888         }
889         if (p->windowTypes & ToolbarMask) {
890             atoms[pnum++] = p->atom(_NET_WM_WINDOW_TYPE_TOOLBAR);
891         }
892         if (p->windowTypes & MenuMask) {
893             atoms[pnum++] = p->atom(_NET_WM_WINDOW_TYPE_MENU);
894         }
895         if (p->windowTypes & DialogMask) {
896             atoms[pnum++] = p->atom(_NET_WM_WINDOW_TYPE_DIALOG);
897         }
898         if (p->windowTypes & UtilityMask) {
899             atoms[pnum++] = p->atom(_NET_WM_WINDOW_TYPE_UTILITY);
900         }
901         if (p->windowTypes & SplashMask) {
902             atoms[pnum++] = p->atom(_NET_WM_WINDOW_TYPE_SPLASH);
903         }
904         if (p->windowTypes & DropdownMenuMask) {
905             atoms[pnum++] = p->atom(_NET_WM_WINDOW_TYPE_DROPDOWN_MENU);
906         }
907         if (p->windowTypes & PopupMenuMask) {
908             atoms[pnum++] = p->atom(_NET_WM_WINDOW_TYPE_POPUP_MENU);
909         }
910         if (p->windowTypes & TooltipMask) {
911             atoms[pnum++] = p->atom(_NET_WM_WINDOW_TYPE_TOOLTIP);
912         }
913         if (p->windowTypes & NotificationMask) {
914             atoms[pnum++] = p->atom(_NET_WM_WINDOW_TYPE_NOTIFICATION);
915         }
916         if (p->windowTypes & ComboBoxMask) {
917             atoms[pnum++] = p->atom(_NET_WM_WINDOW_TYPE_COMBO);
918         }
919         if (p->windowTypes & DNDIconMask) {
920             atoms[pnum++] = p->atom(_NET_WM_WINDOW_TYPE_DND);
921         }
922         // KDE extensions
923         if (p->windowTypes & OverrideMask) {
924             atoms[pnum++] = p->atom(_KDE_NET_WM_WINDOW_TYPE_OVERRIDE);
925         }
926         if (p->windowTypes & TopMenuMask) {
927             atoms[pnum++] = p->atom(_KDE_NET_WM_WINDOW_TYPE_TOPMENU);
928         }
929         if (p->windowTypes & OnScreenDisplayMask) {
930             atoms[pnum++] = p->atom(_KDE_NET_WM_WINDOW_TYPE_ON_SCREEN_DISPLAY);
931         }
932         if (p->windowTypes & CriticalNotificationMask) {
933             atoms[pnum++] = p->atom(_KDE_NET_WM_WINDOW_TYPE_CRITICAL_NOTIFICATION);
934         }
935     }
936 
937     if (p->properties & WMState) {
938         atoms[pnum++] = p->atom(_NET_WM_STATE);
939 
940         // Application window states
941         if (p->states & Modal) {
942             atoms[pnum++] = p->atom(_NET_WM_STATE_MODAL);
943         }
944         if (p->states & Sticky) {
945             atoms[pnum++] = p->atom(_NET_WM_STATE_STICKY);
946         }
947         if (p->states & MaxVert) {
948             atoms[pnum++] = p->atom(_NET_WM_STATE_MAXIMIZED_VERT);
949         }
950         if (p->states & MaxHoriz) {
951             atoms[pnum++] = p->atom(_NET_WM_STATE_MAXIMIZED_HORZ);
952         }
953         if (p->states & Shaded) {
954             atoms[pnum++] = p->atom(_NET_WM_STATE_SHADED);
955         }
956         if (p->states & SkipTaskbar) {
957             atoms[pnum++] = p->atom(_NET_WM_STATE_SKIP_TASKBAR);
958         }
959         if (p->states & SkipPager) {
960             atoms[pnum++] = p->atom(_NET_WM_STATE_SKIP_PAGER);
961         }
962         if (p->states & SkipSwitcher) {
963             atoms[pnum++] = p->atom(_KDE_NET_WM_STATE_SKIP_SWITCHER);
964         }
965         if (p->states & Hidden) {
966             atoms[pnum++] = p->atom(_NET_WM_STATE_HIDDEN);
967         }
968         if (p->states & FullScreen) {
969             atoms[pnum++] = p->atom(_NET_WM_STATE_FULLSCREEN);
970         }
971         if (p->states & KeepAbove) {
972             atoms[pnum++] = p->atom(_NET_WM_STATE_ABOVE);
973             // deprecated variant
974             atoms[pnum++] = p->atom(_NET_WM_STATE_STAYS_ON_TOP);
975         }
976         if (p->states & KeepBelow) {
977             atoms[pnum++] = p->atom(_NET_WM_STATE_BELOW);
978         }
979         if (p->states & DemandsAttention) {
980             atoms[pnum++] = p->atom(_NET_WM_STATE_DEMANDS_ATTENTION);
981         }
982 
983         if (p->states & Focused) {
984             atoms[pnum++] = p->atom(_NET_WM_STATE_FOCUSED);
985         }
986     }
987 
988     if (p->properties & WMStrut) {
989         atoms[pnum++] = p->atom(_NET_WM_STRUT);
990     }
991 
992     if (p->properties2 & WM2ExtendedStrut) {
993         atoms[pnum++] = p->atom(_NET_WM_STRUT_PARTIAL);
994     }
995 
996     if (p->properties & WMIconGeometry) {
997         atoms[pnum++] = p->atom(_NET_WM_ICON_GEOMETRY);
998     }
999 
1000     if (p->properties & WMIcon) {
1001         atoms[pnum++] = p->atom(_NET_WM_ICON);
1002     }
1003 
1004     if (p->properties & WMPid) {
1005         atoms[pnum++] = p->atom(_NET_WM_PID);
1006     }
1007 
1008     if (p->properties & WMHandledIcons) {
1009         atoms[pnum++] = p->atom(_NET_WM_HANDLED_ICONS);
1010     }
1011 
1012     if (p->properties & WMPing) {
1013         atoms[pnum++] = p->atom(_NET_WM_PING);
1014     }
1015 
1016     if (p->properties2 & WM2UserTime) {
1017         atoms[pnum++] = p->atom(_NET_WM_USER_TIME);
1018     }
1019 
1020     if (p->properties2 & WM2StartupId) {
1021         atoms[pnum++] = p->atom(_NET_STARTUP_ID);
1022     }
1023 
1024     if (p->properties2 & WM2Opacity) {
1025         atoms[pnum++] = p->atom(_NET_WM_WINDOW_OPACITY);
1026     }
1027 
1028     if (p->properties2 & WM2FullscreenMonitors) {
1029         atoms[pnum++] = p->atom(_NET_WM_FULLSCREEN_MONITORS);
1030     }
1031 
1032     if (p->properties2 & WM2AllowedActions) {
1033         atoms[pnum++] = p->atom(_NET_WM_ALLOWED_ACTIONS);
1034 
1035         // Actions
1036         if (p->actions & ActionMove) {
1037             atoms[pnum++] = p->atom(_NET_WM_ACTION_MOVE);
1038         }
1039         if (p->actions & ActionResize) {
1040             atoms[pnum++] = p->atom(_NET_WM_ACTION_RESIZE);
1041         }
1042         if (p->actions & ActionMinimize) {
1043             atoms[pnum++] = p->atom(_NET_WM_ACTION_MINIMIZE);
1044         }
1045         if (p->actions & ActionShade) {
1046             atoms[pnum++] = p->atom(_NET_WM_ACTION_SHADE);
1047         }
1048         if (p->actions & ActionStick) {
1049             atoms[pnum++] = p->atom(_NET_WM_ACTION_STICK);
1050         }
1051         if (p->actions & ActionMaxVert) {
1052             atoms[pnum++] = p->atom(_NET_WM_ACTION_MAXIMIZE_VERT);
1053         }
1054         if (p->actions & ActionMaxHoriz) {
1055             atoms[pnum++] = p->atom(_NET_WM_ACTION_MAXIMIZE_HORZ);
1056         }
1057         if (p->actions & ActionFullScreen) {
1058             atoms[pnum++] = p->atom(_NET_WM_ACTION_FULLSCREEN);
1059         }
1060         if (p->actions & ActionChangeDesktop) {
1061             atoms[pnum++] = p->atom(_NET_WM_ACTION_CHANGE_DESKTOP);
1062         }
1063         if (p->actions & ActionClose) {
1064             atoms[pnum++] = p->atom(_NET_WM_ACTION_CLOSE);
1065         }
1066     }
1067 
1068     if (p->properties & WMFrameExtents) {
1069         atoms[pnum++] = p->atom(_NET_FRAME_EXTENTS);
1070         atoms[pnum++] = p->atom(_KDE_NET_WM_FRAME_STRUT);
1071     }
1072 
1073     if (p->properties2 & WM2FrameOverlap) {
1074         atoms[pnum++] = p->atom(_NET_WM_FRAME_OVERLAP);
1075     }
1076 
1077     if (p->properties2 & WM2KDETemporaryRules) {
1078         atoms[pnum++] = p->atom(_KDE_NET_WM_TEMPORARY_RULES);
1079     }
1080     if (p->properties2 & WM2FullPlacement) {
1081         atoms[pnum++] = p->atom(_NET_WM_FULL_PLACEMENT);
1082     }
1083 
1084     if (p->properties2 & WM2Activities) {
1085         atoms[pnum++] = p->atom(_KDE_NET_WM_ACTIVITIES);
1086     }
1087 
1088     if (p->properties2 & WM2BlockCompositing) {
1089         atoms[pnum++] = p->atom(_KDE_NET_WM_BLOCK_COMPOSITING);
1090         atoms[pnum++] = p->atom(_NET_WM_BYPASS_COMPOSITOR);
1091     }
1092 
1093     if (p->properties2 & WM2KDEShadow) {
1094         atoms[pnum++] = p->atom(_KDE_NET_WM_SHADOW);
1095     }
1096 
1097     if (p->properties2 & WM2OpaqueRegion) {
1098         atoms[pnum++] = p->atom(_NET_WM_OPAQUE_REGION);
1099     }
1100 
1101     if (p->properties2 & WM2GTKFrameExtents) {
1102         atoms[pnum++] = p->atom(_GTK_FRAME_EXTENTS);
1103     }
1104 
1105     xcb_change_property(p->conn, XCB_PROP_MODE_REPLACE, p->root, p->atom(_NET_SUPPORTED), XCB_ATOM_ATOM, 32, pnum, (const void *)atoms);
1106 
1107     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));
1108 
1109 #ifdef NETWMDEBUG
1110     fprintf(stderr,
1111             "NETRootInfo::setSupported: _NET_SUPPORTING_WM_CHECK = 0x%lx on 0x%lx\n"
1112             "                         : _NET_WM_NAME = '%s' on 0x%lx\n",
1113             p->supportwindow,
1114             p->supportwindow,
1115             p->name,
1116             p->supportwindow);
1117 #endif
1118 
1119     xcb_change_property(p->conn,
1120                         XCB_PROP_MODE_REPLACE,
1121                         p->supportwindow,
1122                         p->atom(_NET_SUPPORTING_WM_CHECK),
1123                         XCB_ATOM_WINDOW,
1124                         32,
1125                         1,
1126                         (const void *)&(p->supportwindow));
1127 
1128     xcb_change_property(p->conn,
1129                         XCB_PROP_MODE_REPLACE,
1130                         p->supportwindow,
1131                         p->atom(_NET_WM_NAME),
1132                         p->atom(UTF8_STRING),
1133                         8,
1134                         strlen(p->name),
1135                         (const void *)p->name);
1136 }
1137 
updateSupportedProperties(xcb_atom_t atom)1138 void NETRootInfo::updateSupportedProperties(xcb_atom_t atom)
1139 {
1140     if (atom == p->atom(_NET_SUPPORTED)) {
1141         p->properties |= Supported;
1142     }
1143 
1144     else if (atom == p->atom(_NET_SUPPORTING_WM_CHECK)) {
1145         p->properties |= SupportingWMCheck;
1146     }
1147 
1148     else if (atom == p->atom(_NET_CLIENT_LIST)) {
1149         p->properties |= ClientList;
1150     }
1151 
1152     else if (atom == p->atom(_NET_CLIENT_LIST_STACKING)) {
1153         p->properties |= ClientListStacking;
1154     }
1155 
1156     else if (atom == p->atom(_NET_NUMBER_OF_DESKTOPS)) {
1157         p->properties |= NumberOfDesktops;
1158     }
1159 
1160     else if (atom == p->atom(_NET_DESKTOP_GEOMETRY)) {
1161         p->properties |= DesktopGeometry;
1162     }
1163 
1164     else if (atom == p->atom(_NET_DESKTOP_VIEWPORT)) {
1165         p->properties |= DesktopViewport;
1166     }
1167 
1168     else if (atom == p->atom(_NET_CURRENT_DESKTOP)) {
1169         p->properties |= CurrentDesktop;
1170     }
1171 
1172     else if (atom == p->atom(_NET_DESKTOP_NAMES)) {
1173         p->properties |= DesktopNames;
1174     }
1175 
1176     else if (atom == p->atom(_NET_ACTIVE_WINDOW)) {
1177         p->properties |= ActiveWindow;
1178     }
1179 
1180     else if (atom == p->atom(_NET_WORKAREA)) {
1181         p->properties |= WorkArea;
1182     }
1183 
1184     else if (atom == p->atom(_NET_VIRTUAL_ROOTS)) {
1185         p->properties |= VirtualRoots;
1186     }
1187 
1188     else if (atom == p->atom(_NET_DESKTOP_LAYOUT)) {
1189         p->properties2 |= WM2DesktopLayout;
1190     }
1191 
1192     else if (atom == p->atom(_NET_CLOSE_WINDOW)) {
1193         p->properties |= CloseWindow;
1194     }
1195 
1196     else if (atom == p->atom(_NET_RESTACK_WINDOW)) {
1197         p->properties2 |= WM2RestackWindow;
1198     }
1199 
1200     else if (atom == p->atom(_NET_SHOWING_DESKTOP)) {
1201         p->properties2 |= WM2ShowingDesktop;
1202     }
1203 
1204     // Application window properties/messages
1205     else if (atom == p->atom(_NET_WM_MOVERESIZE)) {
1206         p->properties |= WMMoveResize;
1207     }
1208 
1209     else if (atom == p->atom(_NET_MOVERESIZE_WINDOW)) {
1210         p->properties2 |= WM2MoveResizeWindow;
1211     }
1212 
1213     else if (atom == p->atom(_NET_WM_NAME)) {
1214         p->properties |= WMName;
1215     }
1216 
1217     else if (atom == p->atom(_NET_WM_VISIBLE_NAME)) {
1218         p->properties |= WMVisibleName;
1219     }
1220 
1221     else if (atom == p->atom(_NET_WM_ICON_NAME)) {
1222         p->properties |= WMIconName;
1223     }
1224 
1225     else if (atom == p->atom(_NET_WM_VISIBLE_ICON_NAME)) {
1226         p->properties |= WMVisibleIconName;
1227     }
1228 
1229     else if (atom == p->atom(_NET_WM_DESKTOP)) {
1230         p->properties |= WMDesktop;
1231     }
1232 
1233     else if (atom == p->atom(_NET_WM_WINDOW_TYPE)) {
1234         p->properties |= WMWindowType;
1235     }
1236 
1237     // Application window types
1238     else if (atom == p->atom(_NET_WM_WINDOW_TYPE_NORMAL)) {
1239         p->windowTypes |= NormalMask;
1240     } else if (atom == p->atom(_NET_WM_WINDOW_TYPE_DESKTOP)) {
1241         p->windowTypes |= DesktopMask;
1242     } else if (atom == p->atom(_NET_WM_WINDOW_TYPE_DOCK)) {
1243         p->windowTypes |= DockMask;
1244     } else if (atom == p->atom(_NET_WM_WINDOW_TYPE_TOOLBAR)) {
1245         p->windowTypes |= ToolbarMask;
1246     } else if (atom == p->atom(_NET_WM_WINDOW_TYPE_MENU)) {
1247         p->windowTypes |= MenuMask;
1248     } else if (atom == p->atom(_NET_WM_WINDOW_TYPE_DIALOG)) {
1249         p->windowTypes |= DialogMask;
1250     } else if (atom == p->atom(_NET_WM_WINDOW_TYPE_UTILITY)) {
1251         p->windowTypes |= UtilityMask;
1252     } else if (atom == p->atom(_NET_WM_WINDOW_TYPE_SPLASH)) {
1253         p->windowTypes |= SplashMask;
1254     } else if (atom == p->atom(_NET_WM_WINDOW_TYPE_DROPDOWN_MENU)) {
1255         p->windowTypes |= DropdownMenuMask;
1256     } else if (atom == p->atom(_NET_WM_WINDOW_TYPE_POPUP_MENU)) {
1257         p->windowTypes |= PopupMenuMask;
1258     } else if (atom == p->atom(_NET_WM_WINDOW_TYPE_TOOLTIP)) {
1259         p->windowTypes |= TooltipMask;
1260     } else if (atom == p->atom(_NET_WM_WINDOW_TYPE_NOTIFICATION)) {
1261         p->windowTypes |= NotificationMask;
1262     } else if (atom == p->atom(_NET_WM_WINDOW_TYPE_COMBO)) {
1263         p->windowTypes |= ComboBoxMask;
1264     } else if (atom == p->atom(_NET_WM_WINDOW_TYPE_DND)) {
1265         p->windowTypes |= DNDIconMask;
1266     }
1267     // KDE extensions
1268     else if (atom == p->atom(_KDE_NET_WM_WINDOW_TYPE_OVERRIDE)) {
1269         p->windowTypes |= OverrideMask;
1270     } else if (atom == p->atom(_KDE_NET_WM_WINDOW_TYPE_TOPMENU)) {
1271         p->windowTypes |= TopMenuMask;
1272     } else if (atom == p->atom(_KDE_NET_WM_WINDOW_TYPE_ON_SCREEN_DISPLAY)) {
1273         p->windowTypes |= OnScreenDisplayMask;
1274     } else if (atom == p->atom(_KDE_NET_WM_WINDOW_TYPE_CRITICAL_NOTIFICATION)) {
1275         p->windowTypes |= CriticalNotificationMask;
1276     }
1277 
1278     else if (atom == p->atom(_NET_WM_STATE)) {
1279         p->properties |= WMState;
1280     }
1281 
1282     // Application window states
1283     else if (atom == p->atom(_NET_WM_STATE_MODAL)) {
1284         p->states |= Modal;
1285     } else if (atom == p->atom(_NET_WM_STATE_STICKY)) {
1286         p->states |= Sticky;
1287     } else if (atom == p->atom(_NET_WM_STATE_MAXIMIZED_VERT)) {
1288         p->states |= MaxVert;
1289     } else if (atom == p->atom(_NET_WM_STATE_MAXIMIZED_HORZ)) {
1290         p->states |= MaxHoriz;
1291     } else if (atom == p->atom(_NET_WM_STATE_SHADED)) {
1292         p->states |= Shaded;
1293     } else if (atom == p->atom(_NET_WM_STATE_SKIP_TASKBAR)) {
1294         p->states |= SkipTaskbar;
1295     } else if (atom == p->atom(_NET_WM_STATE_SKIP_PAGER)) {
1296         p->states |= SkipPager;
1297     } else if (atom == p->atom(_KDE_NET_WM_STATE_SKIP_SWITCHER)) {
1298         p->states |= SkipSwitcher;
1299     } else if (atom == p->atom(_NET_WM_STATE_HIDDEN)) {
1300         p->states |= Hidden;
1301     } else if (atom == p->atom(_NET_WM_STATE_FULLSCREEN)) {
1302         p->states |= FullScreen;
1303     } else if (atom == p->atom(_NET_WM_STATE_ABOVE)) {
1304         p->states |= KeepAbove;
1305     } else if (atom == p->atom(_NET_WM_STATE_BELOW)) {
1306         p->states |= KeepBelow;
1307     } else if (atom == p->atom(_NET_WM_STATE_DEMANDS_ATTENTION)) {
1308         p->states |= DemandsAttention;
1309     } else if (atom == p->atom(_NET_WM_STATE_STAYS_ON_TOP)) {
1310         p->states |= KeepAbove;
1311     } else if (atom == p->atom(_NET_WM_STATE_FOCUSED)) {
1312         p->states |= Focused;
1313     }
1314 
1315     else if (atom == p->atom(_NET_WM_STRUT)) {
1316         p->properties |= WMStrut;
1317     }
1318 
1319     else if (atom == p->atom(_NET_WM_STRUT_PARTIAL)) {
1320         p->properties2 |= WM2ExtendedStrut;
1321     }
1322 
1323     else if (atom == p->atom(_NET_WM_ICON_GEOMETRY)) {
1324         p->properties |= WMIconGeometry;
1325     }
1326 
1327     else if (atom == p->atom(_NET_WM_ICON)) {
1328         p->properties |= WMIcon;
1329     }
1330 
1331     else if (atom == p->atom(_NET_WM_PID)) {
1332         p->properties |= WMPid;
1333     }
1334 
1335     else if (atom == p->atom(_NET_WM_HANDLED_ICONS)) {
1336         p->properties |= WMHandledIcons;
1337     }
1338 
1339     else if (atom == p->atom(_NET_WM_PING)) {
1340         p->properties |= WMPing;
1341     }
1342 
1343     else if (atom == p->atom(_NET_WM_USER_TIME)) {
1344         p->properties2 |= WM2UserTime;
1345     }
1346 
1347     else if (atom == p->atom(_NET_STARTUP_ID)) {
1348         p->properties2 |= WM2StartupId;
1349     }
1350 
1351     else if (atom == p->atom(_NET_WM_WINDOW_OPACITY)) {
1352         p->properties2 |= WM2Opacity;
1353     }
1354 
1355     else if (atom == p->atom(_NET_WM_FULLSCREEN_MONITORS)) {
1356         p->properties2 |= WM2FullscreenMonitors;
1357     }
1358 
1359     else if (atom == p->atom(_NET_WM_ALLOWED_ACTIONS)) {
1360         p->properties2 |= WM2AllowedActions;
1361     }
1362 
1363     // Actions
1364     else if (atom == p->atom(_NET_WM_ACTION_MOVE)) {
1365         p->actions |= ActionMove;
1366     } else if (atom == p->atom(_NET_WM_ACTION_RESIZE)) {
1367         p->actions |= ActionResize;
1368     } else if (atom == p->atom(_NET_WM_ACTION_MINIMIZE)) {
1369         p->actions |= ActionMinimize;
1370     } else if (atom == p->atom(_NET_WM_ACTION_SHADE)) {
1371         p->actions |= ActionShade;
1372     } else if (atom == p->atom(_NET_WM_ACTION_STICK)) {
1373         p->actions |= ActionStick;
1374     } else if (atom == p->atom(_NET_WM_ACTION_MAXIMIZE_VERT)) {
1375         p->actions |= ActionMaxVert;
1376     } else if (atom == p->atom(_NET_WM_ACTION_MAXIMIZE_HORZ)) {
1377         p->actions |= ActionMaxHoriz;
1378     } else if (atom == p->atom(_NET_WM_ACTION_FULLSCREEN)) {
1379         p->actions |= ActionFullScreen;
1380     } else if (atom == p->atom(_NET_WM_ACTION_CHANGE_DESKTOP)) {
1381         p->actions |= ActionChangeDesktop;
1382     } else if (atom == p->atom(_NET_WM_ACTION_CLOSE)) {
1383         p->actions |= ActionClose;
1384     }
1385 
1386     else if (atom == p->atom(_NET_FRAME_EXTENTS)) {
1387         p->properties |= WMFrameExtents;
1388     } else if (atom == p->atom(_KDE_NET_WM_FRAME_STRUT)) {
1389         p->properties |= WMFrameExtents;
1390     } else if (atom == p->atom(_NET_WM_FRAME_OVERLAP)) {
1391         p->properties2 |= WM2FrameOverlap;
1392     }
1393 
1394     else if (atom == p->atom(_KDE_NET_WM_TEMPORARY_RULES)) {
1395         p->properties2 |= WM2KDETemporaryRules;
1396     } else if (atom == p->atom(_NET_WM_FULL_PLACEMENT)) {
1397         p->properties2 |= WM2FullPlacement;
1398     }
1399 
1400     else if (atom == p->atom(_KDE_NET_WM_ACTIVITIES)) {
1401         p->properties2 |= WM2Activities;
1402     }
1403 
1404     else if (atom == p->atom(_KDE_NET_WM_BLOCK_COMPOSITING) || atom == p->atom(_NET_WM_BYPASS_COMPOSITOR)) {
1405         p->properties2 |= WM2BlockCompositing;
1406     }
1407 
1408     else if (atom == p->atom(_KDE_NET_WM_SHADOW)) {
1409         p->properties2 |= WM2KDEShadow;
1410     }
1411 
1412     else if (atom == p->atom(_NET_WM_OPAQUE_REGION)) {
1413         p->properties2 |= WM2OpaqueRegion;
1414     }
1415 
1416     else if (atom == p->atom(_GTK_FRAME_EXTENTS)) {
1417         p->properties2 |= WM2GTKFrameExtents;
1418     }
1419 
1420     else if (atom == p->atom(_KDE_NET_WM_APPMENU_OBJECT_PATH)) {
1421         p->properties2 |= WM2AppMenuObjectPath;
1422     }
1423 
1424     else if (atom == p->atom(_KDE_NET_WM_APPMENU_SERVICE_NAME)) {
1425         p->properties2 |= WM2AppMenuServiceName;
1426     }
1427 }
1428 
setActiveWindow(xcb_window_t window)1429 void NETRootInfo::setActiveWindow(xcb_window_t window)
1430 {
1431     setActiveWindow(window, FromUnknown, QX11Info::appUserTime(), XCB_WINDOW_NONE);
1432 }
1433 
setActiveWindow(xcb_window_t window,NET::RequestSource src,xcb_timestamp_t timestamp,xcb_window_t active_window)1434 void NETRootInfo::setActiveWindow(xcb_window_t window, NET::RequestSource src, xcb_timestamp_t timestamp, xcb_window_t active_window)
1435 {
1436 #ifdef NETWMDEBUG
1437     fprintf(stderr, "NETRootInfo::setActiveWindow(0x%lx) (%s)\n", window, (p->role == WindowManager) ? "WM" : "Client");
1438 #endif
1439 
1440     if (p->role == WindowManager) {
1441         p->active = window;
1442 
1443         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));
1444     } else {
1445         const uint32_t data[5] = {src, timestamp, active_window, 0, 0};
1446 
1447         send_client_message(p->conn, netwm_sendevent_mask, p->root, window, p->atom(_NET_ACTIVE_WINDOW), data);
1448     }
1449 }
1450 
setWorkArea(int desktop,const NETRect & workarea)1451 void NETRootInfo::setWorkArea(int desktop, const NETRect &workarea)
1452 {
1453 #ifdef NETWMDEBUG
1454     fprintf(stderr,
1455             "NETRootInfo::setWorkArea(%d, { %d, %d, %d, %d }) (%s)\n",
1456             desktop,
1457             workarea.pos.x,
1458             workarea.pos.y,
1459             workarea.size.width,
1460             workarea.size.height,
1461             (p->role == WindowManager) ? "WM" : "Client");
1462 #endif
1463 
1464     if (p->role != WindowManager || desktop < 1) {
1465         return;
1466     }
1467 
1468     p->workarea[desktop - 1] = workarea;
1469 
1470     uint32_t *wa = new uint32_t[p->number_of_desktops * 4];
1471     int i;
1472     int o;
1473     for (i = 0, o = 0; i < p->number_of_desktops; i++) {
1474         wa[o++] = p->workarea[i].pos.x;
1475         wa[o++] = p->workarea[i].pos.y;
1476         wa[o++] = p->workarea[i].size.width;
1477         wa[o++] = p->workarea[i].size.height;
1478     }
1479 
1480     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);
1481 
1482     delete[] wa;
1483 }
1484 
setVirtualRoots(const xcb_window_t * windows,unsigned int count)1485 void NETRootInfo::setVirtualRoots(const xcb_window_t *windows, unsigned int count)
1486 {
1487     if (p->role != WindowManager) {
1488         return;
1489     }
1490 
1491     p->virtual_roots_count = count;
1492     delete[] p->virtual_roots;
1493     p->virtual_roots = nwindup(windows, count);
1494 
1495 #ifdef NETWMDEBUG
1496     fprintf(stderr, "NETRootInfo::setVirtualRoots: setting list with %ld windows\n", p->virtual_roots_count);
1497 #endif
1498 
1499     xcb_change_property(p->conn,
1500                         XCB_PROP_MODE_REPLACE,
1501                         p->root,
1502                         p->atom(_NET_VIRTUAL_ROOTS),
1503                         XCB_ATOM_WINDOW,
1504                         32,
1505                         p->virtual_roots_count,
1506                         (const void *)windows);
1507 }
1508 
setDesktopLayout(NET::Orientation orientation,int columns,int rows,NET::DesktopLayoutCorner corner)1509 void NETRootInfo::setDesktopLayout(NET::Orientation orientation, int columns, int rows, NET::DesktopLayoutCorner corner)
1510 {
1511     p->desktop_layout_orientation = orientation;
1512     p->desktop_layout_columns = columns;
1513     p->desktop_layout_rows = rows;
1514     p->desktop_layout_corner = corner;
1515 
1516 #ifdef NETWMDEBUG
1517     fprintf(stderr, "NETRootInfo::setDesktopLayout: %d %d %d %d\n", orientation, columns, rows, corner);
1518 #endif
1519 
1520     uint32_t data[4];
1521     data[0] = orientation;
1522     data[1] = columns;
1523     data[2] = rows;
1524     data[3] = corner;
1525 
1526     xcb_change_property(p->conn, XCB_PROP_MODE_REPLACE, p->root, p->atom(_NET_DESKTOP_LAYOUT), XCB_ATOM_CARDINAL, 32, 4, (const void *)data);
1527 }
1528 
setShowingDesktop(bool showing)1529 void NETRootInfo::setShowingDesktop(bool showing)
1530 {
1531     if (p->role == WindowManager) {
1532         uint32_t d = p->showing_desktop = showing;
1533         xcb_change_property(p->conn, XCB_PROP_MODE_REPLACE, p->root, p->atom(_NET_SHOWING_DESKTOP), XCB_ATOM_CARDINAL, 32, 1, (const void *)&d);
1534     } else {
1535         uint32_t data[5] = {uint32_t(showing ? 1 : 0), 0, 0, 0, 0};
1536         send_client_message(p->conn, netwm_sendevent_mask, p->root, p->root, p->atom(_NET_SHOWING_DESKTOP), data);
1537     }
1538 }
1539 
showingDesktop() const1540 bool NETRootInfo::showingDesktop() const
1541 {
1542     return p->showing_desktop;
1543 }
1544 
closeWindowRequest(xcb_window_t window)1545 void NETRootInfo::closeWindowRequest(xcb_window_t window)
1546 {
1547 #ifdef NETWMDEBUG
1548     fprintf(stderr, "NETRootInfo::closeWindowRequest: requesting close for 0x%lx\n", window);
1549 #endif
1550 
1551     const uint32_t data[5] = {0, 0, 0, 0, 0};
1552     send_client_message(p->conn, netwm_sendevent_mask, p->root, window, p->atom(_NET_CLOSE_WINDOW), data);
1553 }
1554 
moveResizeRequest(xcb_window_t window,int x_root,int y_root,Direction direction)1555 void NETRootInfo::moveResizeRequest(xcb_window_t window, int x_root, int y_root, Direction direction)
1556 {
1557 #ifdef NETWMDEBUG
1558     fprintf(stderr, "NETRootInfo::moveResizeRequest: requesting resize/move for 0x%lx (%d, %d, %d)\n", window, x_root, y_root, direction);
1559 #endif
1560 
1561     const uint32_t data[5] = {uint32_t(x_root), uint32_t(y_root), uint32_t(direction), 0, 0};
1562 
1563     send_client_message(p->conn, netwm_sendevent_mask, p->root, window, p->atom(_NET_WM_MOVERESIZE), data);
1564 }
1565 
moveResizeWindowRequest(xcb_window_t window,int flags,int x,int y,int width,int height)1566 void NETRootInfo::moveResizeWindowRequest(xcb_window_t window, int flags, int x, int y, int width, int height)
1567 {
1568 #ifdef NETWMDEBUG
1569     fprintf(stderr, "NETRootInfo::moveResizeWindowRequest: resizing/moving 0x%lx (%d, %d, %d, %d, %d)\n", window, flags, x, y, width, height);
1570 #endif
1571 
1572     const uint32_t data[5] = {uint32_t(flags), uint32_t(x), uint32_t(y), uint32_t(width), uint32_t(height)};
1573 
1574     send_client_message(p->conn, netwm_sendevent_mask, p->root, window, p->atom(_NET_MOVERESIZE_WINDOW), data);
1575 }
1576 
restackRequest(xcb_window_t window,RequestSource src,xcb_window_t above,int detail,xcb_timestamp_t timestamp)1577 void NETRootInfo::restackRequest(xcb_window_t window, RequestSource src, xcb_window_t above, int detail, xcb_timestamp_t timestamp)
1578 {
1579 #ifdef NETWMDEBUG
1580     fprintf(stderr, "NETRootInfo::restackRequest: requesting restack for 0x%lx (%lx, %d)\n", window, above, detail);
1581 #endif
1582 
1583     const uint32_t data[5] = {uint32_t(src), uint32_t(above), uint32_t(detail), uint32_t(timestamp), 0};
1584 
1585     send_client_message(p->conn, netwm_sendevent_mask, p->root, window, p->atom(_NET_RESTACK_WINDOW), data);
1586 }
1587 
sendPing(xcb_window_t window,xcb_timestamp_t timestamp)1588 void NETRootInfo::sendPing(xcb_window_t window, xcb_timestamp_t timestamp)
1589 {
1590     if (p->role != WindowManager) {
1591         return;
1592     }
1593 
1594 #ifdef NETWMDEBUG
1595     fprintf(stderr, "NETRootInfo::setPing: window 0x%lx, timestamp %lu\n", window, timestamp);
1596 #endif
1597 
1598     const uint32_t data[5] = {p->atom(_NET_WM_PING), timestamp, window, 0, 0};
1599 
1600     send_client_message(p->conn, 0, window, window, p->atom(WM_PROTOCOLS), data);
1601 }
1602 
1603 // assignment operator
1604 
operator =(const NETRootInfo & rootinfo)1605 const NETRootInfo &NETRootInfo::operator=(const NETRootInfo &rootinfo)
1606 {
1607 #ifdef NETWMDEBUG
1608     fprintf(stderr, "NETRootInfo::operator=()\n");
1609 #endif
1610 
1611     if (p != rootinfo.p) {
1612         refdec_nri(p);
1613 
1614         if (!p->ref) {
1615             delete p;
1616         }
1617     }
1618 
1619     p = rootinfo.p;
1620     p->ref++;
1621 
1622     return *this;
1623 }
1624 
event(xcb_generic_event_t * ev)1625 NET::Properties NETRootInfo::event(xcb_generic_event_t *ev)
1626 {
1627     NET::Properties props;
1628     event(ev, &props);
1629     return props;
1630 }
1631 
1632 #if KWINDOWSYSTEM_BUILD_DEPRECATED_SINCE(5, 0)
event(xcb_generic_event_t * ev,unsigned long * properties,int properties_size)1633 void NETRootInfo::event(xcb_generic_event_t *ev, unsigned long *properties, int properties_size)
1634 {
1635     unsigned long props[PROPERTIES_SIZE] = {0, 0, 0, 0, 0};
1636     assert(PROPERTIES_SIZE == 5); // add elements above
1637     NET::Properties p;
1638     NET::Properties2 p2;
1639     event(ev, &p, &p2);
1640     props[PROTOCOLS] = p;
1641     props[PROTOCOLS2] = p2;
1642 
1643     if (properties_size > PROPERTIES_SIZE) {
1644         properties_size = PROPERTIES_SIZE;
1645     }
1646     for (int i = 0; i < properties_size; ++i) {
1647         properties[i] = props[i];
1648     }
1649 }
1650 #endif
1651 
event(xcb_generic_event_t * event,NET::Properties * properties,NET::Properties2 * properties2)1652 void NETRootInfo::event(xcb_generic_event_t *event, NET::Properties *properties, NET::Properties2 *properties2)
1653 {
1654     NET::Properties dirty;
1655     NET::Properties2 dirty2;
1656     bool do_update = false;
1657     const uint8_t eventType = event->response_type & ~0x80;
1658 
1659     // the window manager will be interested in client messages... no other
1660     // client should get these messages
1661     if (p->role == WindowManager && eventType == XCB_CLIENT_MESSAGE && reinterpret_cast<xcb_client_message_event_t *>(event)->format == 32) {
1662         xcb_client_message_event_t *message = reinterpret_cast<xcb_client_message_event_t *>(event);
1663 #ifdef NETWMDEBUG
1664         fprintf(stderr, "NETRootInfo::event: handling ClientMessage event\n");
1665 #endif
1666 
1667         if (message->type == p->atom(_NET_NUMBER_OF_DESKTOPS)) {
1668             dirty = NumberOfDesktops;
1669 
1670 #ifdef NETWMDEBUG
1671             fprintf(stderr, "NETRootInfo::event: changeNumberOfDesktops(%ld)\n", message->data.data32[0]);
1672 #endif
1673 
1674             changeNumberOfDesktops(message->data.data32[0]);
1675         } else if (message->type == p->atom(_NET_DESKTOP_GEOMETRY)) {
1676             dirty = DesktopGeometry;
1677 
1678             NETSize sz;
1679             sz.width = message->data.data32[0];
1680             sz.height = message->data.data32[1];
1681 
1682 #ifdef NETWMDEBUG
1683             fprintf(stderr, "NETRootInfo::event: changeDesktopGeometry( -- , { %d, %d })\n", sz.width, sz.height);
1684 #endif
1685 
1686             changeDesktopGeometry(~0, sz);
1687         } else if (message->type == p->atom(_NET_DESKTOP_VIEWPORT)) {
1688             dirty = DesktopViewport;
1689 
1690             NETPoint pt;
1691             pt.x = message->data.data32[0];
1692             pt.y = message->data.data32[1];
1693 
1694 #ifdef NETWMDEBUG
1695             fprintf(stderr, "NETRootInfo::event: changeDesktopViewport(%d, { %d, %d })\n", p->current_desktop, pt.x, pt.y);
1696 #endif
1697 
1698             changeDesktopViewport(p->current_desktop, pt);
1699         } else if (message->type == p->atom(_NET_CURRENT_DESKTOP)) {
1700             dirty = CurrentDesktop;
1701 
1702 #ifdef NETWMDEBUG
1703             fprintf(stderr, "NETRootInfo::event: changeCurrentDesktop(%ld)\n", message->data.data32[0] + 1);
1704 #endif
1705 
1706             changeCurrentDesktop(message->data.data32[0] + 1);
1707         } else if (message->type == p->atom(_NET_ACTIVE_WINDOW)) {
1708             dirty = ActiveWindow;
1709 
1710 #ifdef NETWMDEBUG
1711             fprintf(stderr, "NETRootInfo::event: changeActiveWindow(0x%lx)\n", message->window);
1712 #endif
1713 
1714             RequestSource src = FromUnknown;
1715             xcb_timestamp_t timestamp = XCB_TIME_CURRENT_TIME;
1716             xcb_window_t active_window = XCB_WINDOW_NONE;
1717             // make sure there aren't unknown values
1718             if (message->data.data32[0] >= FromUnknown && message->data.data32[0] <= FromTool) {
1719                 src = static_cast<RequestSource>(message->data.data32[0]);
1720                 timestamp = message->data.data32[1];
1721                 active_window = message->data.data32[2];
1722             }
1723             changeActiveWindow(message->window, src, timestamp, active_window);
1724         } else if (message->type == p->atom(_NET_WM_MOVERESIZE)) {
1725 #ifdef NETWMDEBUG
1726             fprintf(stderr,
1727                     "NETRootInfo::event: moveResize(%ld, %ld, %ld, %ld)\n",
1728                     message->window,
1729                     message->data.data32[0],
1730                     message->data.data32[1],
1731                     message->data.data32[2]);
1732 #endif
1733 
1734             moveResize(message->window, message->data.data32[0], message->data.data32[1], message->data.data32[2]);
1735         } else if (message->type == p->atom(_NET_MOVERESIZE_WINDOW)) {
1736 #ifdef NETWMDEBUG
1737             fprintf(stderr,
1738                     "NETRootInfo::event: moveResizeWindow(%ld, %ld, %ld, %ld, %ld, %ld)\n",
1739                     message->window,
1740                     message->data.data32[0],
1741                     message->data.data32[1],
1742                     message->data.data32[2],
1743                     message->data.data32[3],
1744                     message->data.data32[4]);
1745 #endif
1746 
1747             moveResizeWindow(message->window,
1748                              message->data.data32[0],
1749                              message->data.data32[1],
1750                              message->data.data32[2],
1751                              message->data.data32[3],
1752                              message->data.data32[4]);
1753         } else if (message->type == p->atom(_NET_CLOSE_WINDOW)) {
1754 #ifdef NETWMDEBUG
1755             fprintf(stderr, "NETRootInfo::event: closeWindow(0x%lx)\n", message->window);
1756 #endif
1757 
1758             closeWindow(message->window);
1759         } else if (message->type == p->atom(_NET_RESTACK_WINDOW)) {
1760 #ifdef NETWMDEBUG
1761             fprintf(stderr, "NETRootInfo::event: restackWindow(0x%lx)\n", message->window);
1762 #endif
1763 
1764             RequestSource src = FromUnknown;
1765             xcb_timestamp_t timestamp = XCB_TIME_CURRENT_TIME;
1766             // make sure there aren't unknown values
1767             if (message->data.data32[0] >= FromUnknown && message->data.data32[0] <= FromTool) {
1768                 src = static_cast<RequestSource>(message->data.data32[0]);
1769                 timestamp = message->data.data32[3];
1770             }
1771             restackWindow(message->window, src, message->data.data32[1], message->data.data32[2], timestamp);
1772         } else if (message->type == p->atom(WM_PROTOCOLS) && (xcb_atom_t)message->data.data32[0] == p->atom(_NET_WM_PING)) {
1773             dirty = WMPing;
1774 
1775 #ifdef NETWMDEBUG
1776             fprintf(stderr, "NETRootInfo::event: gotPing(0x%lx,%lu)\n", message->window, message->data.data32[1]);
1777 #endif
1778             gotPing(message->data.data32[2], message->data.data32[1]);
1779         } else if (message->type == p->atom(_NET_SHOWING_DESKTOP)) {
1780             dirty2 = WM2ShowingDesktop;
1781 
1782 #ifdef NETWMDEBUG
1783             fprintf(stderr, "NETRootInfo::event: changeShowingDesktop(%ld)\n", message->data.data32[0]);
1784 #endif
1785 
1786             changeShowingDesktop(message->data.data32[0]);
1787         }
1788     }
1789 
1790     if (eventType == XCB_PROPERTY_NOTIFY) {
1791 #ifdef NETWMDEBUG
1792         fprintf(stderr, "NETRootInfo::event: handling PropertyNotify event\n");
1793 #endif
1794 
1795         xcb_property_notify_event_t *pe = reinterpret_cast<xcb_property_notify_event_t *>(event);
1796         if (pe->atom == p->atom(_NET_CLIENT_LIST)) {
1797             dirty |= ClientList;
1798         } else if (pe->atom == p->atom(_NET_CLIENT_LIST_STACKING)) {
1799             dirty |= ClientListStacking;
1800         } else if (pe->atom == p->atom(_NET_DESKTOP_NAMES)) {
1801             dirty |= DesktopNames;
1802         } else if (pe->atom == p->atom(_NET_WORKAREA)) {
1803             dirty |= WorkArea;
1804         } else if (pe->atom == p->atom(_NET_NUMBER_OF_DESKTOPS)) {
1805             dirty |= NumberOfDesktops;
1806         } else if (pe->atom == p->atom(_NET_DESKTOP_GEOMETRY)) {
1807             dirty |= DesktopGeometry;
1808         } else if (pe->atom == p->atom(_NET_DESKTOP_VIEWPORT)) {
1809             dirty |= DesktopViewport;
1810         } else if (pe->atom == p->atom(_NET_CURRENT_DESKTOP)) {
1811             dirty |= CurrentDesktop;
1812         } else if (pe->atom == p->atom(_NET_ACTIVE_WINDOW)) {
1813             dirty |= ActiveWindow;
1814         } else if (pe->atom == p->atom(_NET_SHOWING_DESKTOP)) {
1815             dirty2 |= WM2ShowingDesktop;
1816         } else if (pe->atom == p->atom(_NET_SUPPORTED)) {
1817             dirty |= Supported; // update here?
1818         } else if (pe->atom == p->atom(_NET_SUPPORTING_WM_CHECK)) {
1819             dirty |= SupportingWMCheck;
1820         } else if (pe->atom == p->atom(_NET_VIRTUAL_ROOTS)) {
1821             dirty |= VirtualRoots;
1822         } else if (pe->atom == p->atom(_NET_DESKTOP_LAYOUT)) {
1823             dirty2 |= WM2DesktopLayout;
1824         }
1825 
1826         do_update = true;
1827     }
1828 
1829     if (do_update) {
1830         update(dirty, dirty2);
1831     }
1832 
1833 #ifdef NETWMDEBUG
1834     fprintf(stderr, "NETRootInfo::event: handled events, returning dirty = 0x%lx, 0x%lx\n", dirty, dirty2);
1835 #endif
1836 
1837     if (properties) {
1838         *properties = dirty;
1839     }
1840     if (properties2) {
1841         *properties2 = dirty2;
1842     }
1843 }
1844 
1845 // private functions to update the data we keep
1846 
update(NET::Properties properties,NET::Properties2 properties2)1847 void NETRootInfo::update(NET::Properties properties, NET::Properties2 properties2)
1848 {
1849     NET::Properties dirty = properties & p->clientProperties;
1850     NET::Properties2 dirty2 = properties2 & p->clientProperties2;
1851 
1852     xcb_get_property_cookie_t cookies[255];
1853     xcb_get_property_cookie_t wm_name_cookie;
1854     int c = 0;
1855 
1856     // Send the property requests
1857     if (dirty & Supported) {
1858         cookies[c++] = xcb_get_property(p->conn, false, p->root, p->atom(_NET_SUPPORTED), XCB_ATOM_ATOM, 0, MAX_PROP_SIZE);
1859     }
1860 
1861     if (dirty & ClientList) {
1862         cookies[c++] = xcb_get_property(p->conn, false, p->root, p->atom(_NET_CLIENT_LIST), XCB_ATOM_WINDOW, 0, MAX_PROP_SIZE);
1863     }
1864 
1865     if (dirty & ClientListStacking) {
1866         cookies[c++] = xcb_get_property(p->conn, false, p->root, p->atom(_NET_CLIENT_LIST_STACKING), XCB_ATOM_WINDOW, 0, MAX_PROP_SIZE);
1867     }
1868 
1869     if (dirty & NumberOfDesktops) {
1870         cookies[c++] = xcb_get_property(p->conn, false, p->root, p->atom(_NET_NUMBER_OF_DESKTOPS), XCB_ATOM_CARDINAL, 0, 1);
1871     }
1872 
1873     if (dirty & DesktopGeometry) {
1874         cookies[c++] = xcb_get_property(p->conn, false, p->root, p->atom(_NET_DESKTOP_GEOMETRY), XCB_ATOM_CARDINAL, 0, 2);
1875     }
1876 
1877     if (dirty & DesktopViewport) {
1878         cookies[c++] = xcb_get_property(p->conn, false, p->root, p->atom(_NET_DESKTOP_VIEWPORT), XCB_ATOM_CARDINAL, 0, MAX_PROP_SIZE);
1879     }
1880 
1881     if (dirty & CurrentDesktop) {
1882         cookies[c++] = xcb_get_property(p->conn, false, p->root, p->atom(_NET_CURRENT_DESKTOP), XCB_ATOM_CARDINAL, 0, 1);
1883     }
1884 
1885     if (dirty & DesktopNames) {
1886         cookies[c++] = xcb_get_property(p->conn, false, p->root, p->atom(_NET_DESKTOP_NAMES), p->atom(UTF8_STRING), 0, MAX_PROP_SIZE);
1887     }
1888 
1889     if (dirty & ActiveWindow) {
1890         cookies[c++] = xcb_get_property(p->conn, false, p->root, p->atom(_NET_ACTIVE_WINDOW), XCB_ATOM_WINDOW, 0, 1);
1891     }
1892 
1893     if (dirty & WorkArea) {
1894         cookies[c++] = xcb_get_property(p->conn, false, p->root, p->atom(_NET_WORKAREA), XCB_ATOM_CARDINAL, 0, MAX_PROP_SIZE);
1895     }
1896 
1897     if (dirty & SupportingWMCheck) {
1898         cookies[c++] = xcb_get_property(p->conn, false, p->root, p->atom(_NET_SUPPORTING_WM_CHECK), XCB_ATOM_WINDOW, 0, 1);
1899     }
1900 
1901     if (dirty & VirtualRoots) {
1902         cookies[c++] = xcb_get_property(p->conn, false, p->root, p->atom(_NET_VIRTUAL_ROOTS), XCB_ATOM_WINDOW, 0, 1);
1903     }
1904 
1905     if (dirty2 & WM2DesktopLayout) {
1906         cookies[c++] = xcb_get_property(p->conn, false, p->root, p->atom(_NET_DESKTOP_LAYOUT), XCB_ATOM_CARDINAL, 0, MAX_PROP_SIZE);
1907     }
1908 
1909     if (dirty2 & WM2ShowingDesktop) {
1910         cookies[c++] = xcb_get_property(p->conn, false, p->root, p->atom(_NET_SHOWING_DESKTOP), XCB_ATOM_CARDINAL, 0, 1);
1911     }
1912 
1913     // Get the replies
1914     c = 0;
1915 
1916     if (dirty & Supported) {
1917         // Only in Client mode
1918         p->properties = NET::Properties();
1919         p->properties2 = NET::Properties2();
1920         p->windowTypes = NET::WindowTypes();
1921         p->states = NET::States();
1922         p->actions = NET::Actions();
1923 
1924         const QVector<xcb_atom_t> atoms = get_array_reply<xcb_atom_t>(p->conn, cookies[c++], XCB_ATOM_ATOM);
1925         for (const xcb_atom_t atom : atoms) {
1926             updateSupportedProperties(atom);
1927         }
1928     }
1929 
1930     if (dirty & ClientList) {
1931         QList<xcb_window_t> clientsToRemove;
1932         QList<xcb_window_t> clientsToAdd;
1933 
1934         QVector<xcb_window_t> clients = get_array_reply<xcb_window_t>(p->conn, cookies[c++], XCB_ATOM_WINDOW);
1935         std::sort(clients.begin(), clients.end());
1936 
1937         if (p->clients) {
1938             if (p->role == Client) {
1939                 int new_index = 0;
1940                 int old_index = 0;
1941                 int old_count = p->clients_count;
1942                 int new_count = clients.count();
1943 
1944                 while (old_index < old_count || new_index < new_count) {
1945                     if (old_index == old_count) {
1946                         clientsToAdd.append(clients[new_index++]);
1947                     } else if (new_index == new_count) {
1948                         clientsToRemove.append(p->clients[old_index++]);
1949                     } else {
1950                         if (p->clients[old_index] < clients[new_index]) {
1951                             clientsToRemove.append(p->clients[old_index++]);
1952                         } else if (clients[new_index] < p->clients[old_index]) {
1953                             clientsToAdd.append(clients[new_index++]);
1954                         } else {
1955                             new_index++;
1956                             old_index++;
1957                         }
1958                     }
1959                 }
1960             }
1961 
1962             delete[] p->clients;
1963             p->clients = nullptr;
1964         } else {
1965 #ifdef NETWMDEBUG
1966             fprintf(stderr, "NETRootInfo::update: client list null, creating\n");
1967 #endif
1968 
1969             clientsToAdd.reserve(clients.count());
1970             for (int i = 0; i < clients.count(); i++) {
1971                 clientsToAdd.append(clients[i]);
1972             }
1973         }
1974 
1975         if (!clients.isEmpty()) {
1976             p->clients_count = clients.count();
1977             p->clients = new xcb_window_t[clients.count()];
1978             for (int i = 0; i < clients.count(); i++) {
1979                 p->clients[i] = clients.at(i);
1980             }
1981         }
1982 
1983 #ifdef NETWMDEBUG
1984         fprintf(stderr, "NETRootInfo::update: client list updated (%ld clients)\n", p->clients_count);
1985 #endif
1986 
1987         for (int i = 0; i < clientsToRemove.size(); ++i) {
1988             removeClient(clientsToRemove.at(i));
1989         }
1990 
1991         for (int i = 0; i < clientsToAdd.size(); ++i) {
1992             addClient(clientsToAdd.at(i));
1993         }
1994     }
1995 
1996     if (dirty & ClientListStacking) {
1997         p->stacking_count = 0;
1998 
1999         delete[] p->stacking;
2000         p->stacking = nullptr;
2001 
2002         const QVector<xcb_window_t> wins = get_array_reply<xcb_window_t>(p->conn, cookies[c++], XCB_ATOM_WINDOW);
2003 
2004         if (!wins.isEmpty()) {
2005             p->stacking_count = wins.count();
2006             p->stacking = new xcb_window_t[wins.count()];
2007             for (int i = 0; i < wins.count(); i++) {
2008                 p->stacking[i] = wins.at(i);
2009             }
2010         }
2011 
2012 #ifdef NETWMDEBUG
2013         fprintf(stderr, "NETRootInfo::update: client stacking updated (%ld clients)\n", p->stacking_count);
2014 #endif
2015     }
2016 
2017     if (dirty & NumberOfDesktops) {
2018         p->number_of_desktops = get_value_reply<uint32_t>(p->conn, cookies[c++], XCB_ATOM_CARDINAL, 0);
2019 
2020 #ifdef NETWMDEBUG
2021         fprintf(stderr, "NETRootInfo::update: number of desktops = %d\n", p->number_of_desktops);
2022 #endif
2023     }
2024 
2025     if (dirty & DesktopGeometry) {
2026         p->geometry = p->rootSize;
2027 
2028         const QVector<uint32_t> data = get_array_reply<uint32_t>(p->conn, cookies[c++], XCB_ATOM_CARDINAL);
2029         if (data.count() == 2) {
2030             p->geometry.width = data.at(0);
2031             p->geometry.height = data.at(1);
2032         }
2033 
2034 #ifdef NETWMDEBUG
2035         fprintf(stderr, "NETRootInfo::update: desktop geometry updated\n");
2036 #endif
2037     }
2038 
2039     if (dirty & DesktopViewport) {
2040         for (int i = 0; i < p->viewport.size(); i++) {
2041             p->viewport[i].x = p->viewport[i].y = 0;
2042         }
2043 
2044         const QVector<uint32_t> data = get_array_reply<uint32_t>(p->conn, cookies[c++], XCB_ATOM_CARDINAL);
2045 
2046         if (data.count() >= 2) {
2047             int n = data.count() / 2;
2048             for (int d = 0, i = 0; d < n; d++) {
2049                 p->viewport[d].x = data[i++];
2050                 p->viewport[d].y = data[i++];
2051             }
2052 
2053 #ifdef NETWMDEBUG
2054             fprintf(stderr, "NETRootInfo::update: desktop viewport array updated (%d entries)\n", p->viewport.size());
2055 
2056             if (data.count() % 2 != 0) {
2057                 fprintf(stderr,
2058                         "NETRootInfo::update(): desktop viewport array "
2059                         "size not a multiple of 2\n");
2060             }
2061 #endif
2062         }
2063     }
2064 
2065     if (dirty & CurrentDesktop) {
2066         p->current_desktop = get_value_reply<uint32_t>(p->conn, cookies[c++], XCB_ATOM_CARDINAL, 0) + 1;
2067 
2068 #ifdef NETWMDEBUG
2069         fprintf(stderr, "NETRootInfo::update: current desktop = %d\n", p->current_desktop);
2070 #endif
2071     }
2072 
2073     if (dirty & DesktopNames) {
2074         for (int i = 0; i < p->desktop_names.size(); ++i) {
2075             delete[] p->desktop_names[i];
2076         }
2077 
2078         p->desktop_names.reset();
2079 
2080         const QList<QByteArray> names = get_stringlist_reply(p->conn, cookies[c++], p->atom(UTF8_STRING));
2081         for (int i = 0; i < names.count(); i++) {
2082             p->desktop_names[i] = nstrndup(names[i].constData(), names[i].length());
2083         }
2084 
2085 #ifdef NETWMDEBUG
2086         fprintf(stderr, "NETRootInfo::update: desktop names array updated (%d entries)\n", p->desktop_names.size());
2087 #endif
2088     }
2089 
2090     if (dirty & ActiveWindow) {
2091         p->active = get_value_reply<xcb_window_t>(p->conn, cookies[c++], XCB_ATOM_WINDOW, 0);
2092 
2093 #ifdef NETWMDEBUG
2094         fprintf(stderr, "NETRootInfo::update: active window = 0x%lx\n", p->active);
2095 #endif
2096     }
2097 
2098     if (dirty & WorkArea) {
2099         p->workarea.reset();
2100 
2101         const QVector<uint32_t> data = get_array_reply<uint32_t>(p->conn, cookies[c++], XCB_ATOM_CARDINAL);
2102         if (data.count() == p->number_of_desktops * 4) {
2103             for (int i = 0, j = 0; i < p->number_of_desktops; i++) {
2104                 p->workarea[i].pos.x = data[j++];
2105                 p->workarea[i].pos.y = data[j++];
2106                 p->workarea[i].size.width = data[j++];
2107                 p->workarea[i].size.height = data[j++];
2108             }
2109         }
2110 
2111 #ifdef NETWMDEBUG
2112         fprintf(stderr, "NETRootInfo::update: work area array updated (%d entries)\n", p->workarea.size());
2113 #endif
2114     }
2115 
2116     if (dirty & SupportingWMCheck) {
2117         delete[] p->name;
2118         p->name = nullptr;
2119 
2120         p->supportwindow = get_value_reply<xcb_window_t>(p->conn, cookies[c++], XCB_ATOM_WINDOW, 0);
2121 
2122         // We'll get the reply for this request at the bottom of this function,
2123         // after we've processing the other pending replies
2124         if (p->supportwindow) {
2125             wm_name_cookie = xcb_get_property(p->conn, false, p->supportwindow, p->atom(_NET_WM_NAME), p->atom(UTF8_STRING), 0, MAX_PROP_SIZE);
2126         }
2127     }
2128 
2129     if (dirty & VirtualRoots) {
2130         p->virtual_roots_count = 0;
2131 
2132         delete[] p->virtual_roots;
2133         p->virtual_roots = nullptr;
2134 
2135         const QVector<xcb_window_t> wins = get_array_reply<xcb_window_t>(p->conn, cookies[c++], XCB_ATOM_CARDINAL);
2136 
2137         if (!wins.isEmpty()) {
2138             p->virtual_roots_count = wins.count();
2139             p->virtual_roots = new xcb_window_t[wins.count()];
2140             for (int i = 0; i < wins.count(); i++) {
2141                 p->virtual_roots[i] = wins.at(i);
2142             }
2143         }
2144 
2145 #ifdef NETWMDEBUG
2146         fprintf(stderr, "NETRootInfo::updated: virtual roots updated (%ld windows)\n", p->virtual_roots_count);
2147 #endif
2148     }
2149 
2150     if (dirty2 & WM2DesktopLayout) {
2151         p->desktop_layout_orientation = OrientationHorizontal;
2152         p->desktop_layout_corner = DesktopLayoutCornerTopLeft;
2153         p->desktop_layout_columns = p->desktop_layout_rows = 0;
2154 
2155         const QVector<uint32_t> data = get_array_reply<uint32_t>(p->conn, cookies[c++], XCB_ATOM_CARDINAL);
2156 
2157         if (data.count() >= 4 && data[3] <= 3) {
2158             p->desktop_layout_corner = (NET::DesktopLayoutCorner)data[3];
2159         }
2160 
2161         if (data.count() >= 3) {
2162             if (data[0] <= 1) {
2163                 p->desktop_layout_orientation = (NET::Orientation)data[0];
2164             }
2165 
2166             p->desktop_layout_columns = data[1];
2167             p->desktop_layout_rows = data[2];
2168         }
2169 
2170 #ifdef NETWMDEBUG
2171         fprintf(stderr,
2172                 "NETRootInfo::updated: desktop layout updated (%d %d %d %d)\n",
2173                 p->desktop_layout_orientation,
2174                 p->desktop_layout_columns,
2175                 p->desktop_layout_rows,
2176                 p->desktop_layout_corner);
2177 #endif
2178     }
2179 
2180     if (dirty2 & WM2ShowingDesktop) {
2181         const uint32_t val = get_value_reply<uint32_t>(p->conn, cookies[c++], XCB_ATOM_CARDINAL, 0);
2182         p->showing_desktop = bool(val);
2183 
2184 #ifdef NETWMDEBUG
2185         fprintf(stderr, "NETRootInfo::update: showing desktop = %d\n", p->showing_desktop);
2186 #endif
2187     }
2188 
2189     if ((dirty & SupportingWMCheck) && p->supportwindow) {
2190         const QByteArray ba = get_string_reply(p->conn, wm_name_cookie, p->atom(UTF8_STRING));
2191         if (ba.length() > 0) {
2192             p->name = nstrndup((const char *)ba.constData(), ba.length());
2193         }
2194 
2195 #ifdef NETWMDEBUG
2196         fprintf(stderr, "NETRootInfo::update: supporting window manager = '%s'\n", p->name);
2197 #endif
2198     }
2199 }
2200 
xcbConnection() const2201 xcb_connection_t *NETRootInfo::xcbConnection() const
2202 {
2203     return p->conn;
2204 }
2205 
rootWindow() const2206 xcb_window_t NETRootInfo::rootWindow() const
2207 {
2208     return p->root;
2209 }
2210 
supportWindow() const2211 xcb_window_t NETRootInfo::supportWindow() const
2212 {
2213     return p->supportwindow;
2214 }
2215 
wmName() const2216 const char *NETRootInfo::wmName() const
2217 {
2218     return p->name;
2219 }
2220 
supportedProperties() const2221 NET::Properties NETRootInfo::supportedProperties() const
2222 {
2223     return p->properties;
2224 }
2225 
supportedProperties2() const2226 NET::Properties2 NETRootInfo::supportedProperties2() const
2227 {
2228     return p->properties2;
2229 }
2230 
supportedStates() const2231 NET::States NETRootInfo::supportedStates() const
2232 {
2233     return p->states;
2234 }
2235 
supportedWindowTypes() const2236 NET::WindowTypes NETRootInfo::supportedWindowTypes() const
2237 {
2238     return p->windowTypes;
2239 }
2240 
supportedActions() const2241 NET::Actions NETRootInfo::supportedActions() const
2242 {
2243     return p->actions;
2244 }
2245 
passedProperties() const2246 NET::Properties NETRootInfo::passedProperties() const
2247 {
2248     return p->role == WindowManager ? p->properties : p->clientProperties;
2249 }
2250 
passedProperties2() const2251 NET::Properties2 NETRootInfo::passedProperties2() const
2252 {
2253     return p->role == WindowManager ? p->properties2 : p->clientProperties2;
2254 }
2255 
passedStates() const2256 NET::States NETRootInfo::passedStates() const
2257 {
2258     return p->role == WindowManager ? p->states : NET::States();
2259 }
2260 
passedWindowTypes() const2261 NET::WindowTypes NETRootInfo::passedWindowTypes() const
2262 {
2263     return p->role == WindowManager ? p->windowTypes : NET::WindowTypes();
2264 }
2265 
passedActions() const2266 NET::Actions NETRootInfo::passedActions() const
2267 {
2268     return p->role == WindowManager ? p->actions : NET::Actions();
2269 }
2270 
setSupported(NET::Property property,bool on)2271 void NETRootInfo::setSupported(NET::Property property, bool on)
2272 {
2273     if (p->role != WindowManager) {
2274         return;
2275     }
2276 
2277     if (on && !isSupported(property)) {
2278         p->properties |= property;
2279         setSupported();
2280     } else if (!on && isSupported(property)) {
2281         p->properties &= ~property;
2282         setSupported();
2283     }
2284 }
2285 
setSupported(NET::Property2 property,bool on)2286 void NETRootInfo::setSupported(NET::Property2 property, bool on)
2287 {
2288     if (p->role != WindowManager) {
2289         return;
2290     }
2291 
2292     if (on && !isSupported(property)) {
2293         p->properties2 |= property;
2294         setSupported();
2295     } else if (!on && isSupported(property)) {
2296         p->properties2 &= ~property;
2297         setSupported();
2298     }
2299 }
2300 
setSupported(NET::WindowTypeMask property,bool on)2301 void NETRootInfo::setSupported(NET::WindowTypeMask property, bool on)
2302 {
2303     if (p->role != WindowManager) {
2304         return;
2305     }
2306 
2307     if (on && !isSupported(property)) {
2308         p->windowTypes |= property;
2309         setSupported();
2310     } else if (!on && isSupported(property)) {
2311         p->windowTypes &= ~property;
2312         setSupported();
2313     }
2314 }
2315 
setSupported(NET::State property,bool on)2316 void NETRootInfo::setSupported(NET::State property, bool on)
2317 {
2318     if (p->role != WindowManager) {
2319         return;
2320     }
2321 
2322     if (on && !isSupported(property)) {
2323         p->states |= property;
2324         setSupported();
2325     } else if (!on && isSupported(property)) {
2326         p->states &= ~property;
2327         setSupported();
2328     }
2329 }
2330 
setSupported(NET::Action property,bool on)2331 void NETRootInfo::setSupported(NET::Action property, bool on)
2332 {
2333     if (p->role != WindowManager) {
2334         return;
2335     }
2336 
2337     if (on && !isSupported(property)) {
2338         p->actions |= property;
2339         setSupported();
2340     } else if (!on && isSupported(property)) {
2341         p->actions &= ~property;
2342         setSupported();
2343     }
2344 }
2345 
isSupported(NET::Property property) const2346 bool NETRootInfo::isSupported(NET::Property property) const
2347 {
2348     return p->properties & property;
2349 }
2350 
isSupported(NET::Property2 property) const2351 bool NETRootInfo::isSupported(NET::Property2 property) const
2352 {
2353     return p->properties2 & property;
2354 }
2355 
isSupported(NET::WindowTypeMask type) const2356 bool NETRootInfo::isSupported(NET::WindowTypeMask type) const
2357 {
2358     return p->windowTypes & type;
2359 }
2360 
isSupported(NET::State state) const2361 bool NETRootInfo::isSupported(NET::State state) const
2362 {
2363     return p->states & state;
2364 }
2365 
isSupported(NET::Action action) const2366 bool NETRootInfo::isSupported(NET::Action action) const
2367 {
2368     return p->actions & action;
2369 }
2370 
clientList() const2371 const xcb_window_t *NETRootInfo::clientList() const
2372 {
2373     return p->clients;
2374 }
2375 
clientListCount() const2376 int NETRootInfo::clientListCount() const
2377 {
2378     return p->clients_count;
2379 }
2380 
clientListStacking() const2381 const xcb_window_t *NETRootInfo::clientListStacking() const
2382 {
2383     return p->stacking;
2384 }
2385 
clientListStackingCount() const2386 int NETRootInfo::clientListStackingCount() const
2387 {
2388     return p->stacking_count;
2389 }
2390 
desktopGeometry() const2391 NETSize NETRootInfo::desktopGeometry() const
2392 {
2393     return p->geometry.width != 0 ? p->geometry : p->rootSize;
2394 }
2395 
desktopViewport(int desktop) const2396 NETPoint NETRootInfo::desktopViewport(int desktop) const
2397 {
2398     if (desktop < 1) {
2399         NETPoint pt; // set to (0,0)
2400         return pt;
2401     }
2402 
2403     return p->viewport[desktop - 1];
2404 }
2405 
workArea(int desktop) const2406 NETRect NETRootInfo::workArea(int desktop) const
2407 {
2408     if (desktop < 1) {
2409         NETRect rt;
2410         return rt;
2411     }
2412 
2413     return p->workarea[desktop - 1];
2414 }
2415 
desktopName(int desktop) const2416 const char *NETRootInfo::desktopName(int desktop) const
2417 {
2418     if (desktop < 1) {
2419         return nullptr;
2420     }
2421 
2422     return p->desktop_names[desktop - 1];
2423 }
2424 
virtualRoots() const2425 const xcb_window_t *NETRootInfo::virtualRoots() const
2426 {
2427     return p->virtual_roots;
2428 }
2429 
virtualRootsCount() const2430 int NETRootInfo::virtualRootsCount() const
2431 {
2432     return p->virtual_roots_count;
2433 }
2434 
desktopLayoutOrientation() const2435 NET::Orientation NETRootInfo::desktopLayoutOrientation() const
2436 {
2437     return p->desktop_layout_orientation;
2438 }
2439 
desktopLayoutColumnsRows() const2440 QSize NETRootInfo::desktopLayoutColumnsRows() const
2441 {
2442     return QSize(p->desktop_layout_columns, p->desktop_layout_rows);
2443 }
2444 
desktopLayoutCorner() const2445 NET::DesktopLayoutCorner NETRootInfo::desktopLayoutCorner() const
2446 {
2447     return p->desktop_layout_corner;
2448 }
2449 
numberOfDesktops(bool ignore_viewport) const2450 int NETRootInfo::numberOfDesktops(bool ignore_viewport) const
2451 {
2452     if (!ignore_viewport && KWindowSystem::mapViewport()) {
2453         return KWindowSystem::numberOfDesktops();
2454     }
2455     return p->number_of_desktops == 0 ? 1 : p->number_of_desktops;
2456 }
2457 
currentDesktop(bool ignore_viewport) const2458 int NETRootInfo::currentDesktop(bool ignore_viewport) const
2459 {
2460     if (!ignore_viewport && KWindowSystem::mapViewport()) {
2461         return KWindowSystem::currentDesktop();
2462     }
2463     return p->current_desktop == 0 ? 1 : p->current_desktop;
2464 }
2465 
activeWindow() const2466 xcb_window_t NETRootInfo::activeWindow() const
2467 {
2468     return p->active;
2469 }
2470 
2471 // NETWinInfo stuffs
2472 
2473 const int NETWinInfo::OnAllDesktops = NET::OnAllDesktops;
2474 
NETWinInfo(xcb_connection_t * connection,xcb_window_t window,xcb_window_t rootWindow,NET::Properties properties,NET::Properties2 properties2,Role role)2475 NETWinInfo::NETWinInfo(xcb_connection_t *connection,
2476                        xcb_window_t window,
2477                        xcb_window_t rootWindow,
2478                        NET::Properties properties,
2479                        NET::Properties2 properties2,
2480                        Role role)
2481 {
2482 #ifdef NETWMDEBUG
2483     fprintf(stderr, "NETWinInfo::NETWinInfo: constructing object with role '%s'\n", (role == WindowManager) ? "WindowManager" : "Client");
2484 #endif
2485 
2486     p = new NETWinInfoPrivate;
2487     p->ref = 1;
2488     p->atoms = atomsForConnection(connection);
2489 
2490     p->conn = connection;
2491     p->window = window;
2492     p->root = rootWindow;
2493     p->mapping_state = Withdrawn;
2494     p->mapping_state_dirty = true;
2495     p->state = NET::States();
2496     p->types[0] = Unknown;
2497     p->name = (char *)nullptr;
2498     p->visible_name = (char *)nullptr;
2499     p->icon_name = (char *)nullptr;
2500     p->visible_icon_name = (char *)nullptr;
2501     p->desktop = p->pid = 0;
2502     p->handled_icons = false;
2503     p->user_time = -1U;
2504     p->startup_id = nullptr;
2505     p->transient_for = XCB_NONE;
2506     p->opacity = 0xffffffffU;
2507     p->window_group = XCB_NONE;
2508     p->icon_pixmap = XCB_PIXMAP_NONE;
2509     p->icon_mask = XCB_PIXMAP_NONE;
2510     p->allowed_actions = NET::Actions();
2511     p->has_net_support = false;
2512     p->class_class = (char *)nullptr;
2513     p->class_name = (char *)nullptr;
2514     p->window_role = (char *)nullptr;
2515     p->client_machine = (char *)nullptr;
2516     p->icon_sizes = nullptr;
2517     p->activities = (char *)nullptr;
2518     p->desktop_file = nullptr;
2519     p->appmenu_object_path = nullptr;
2520     p->appmenu_service_name = nullptr;
2521     p->blockCompositing = false;
2522     p->urgency = false;
2523     p->input = true;
2524     p->initialMappingState = NET::Withdrawn;
2525     p->protocols = NET::NoProtocol;
2526 
2527     // p->strut.left = p->strut.right = p->strut.top = p->strut.bottom = 0;
2528     // p->frame_strut.left = p->frame_strut.right = p->frame_strut.top =
2529     // p->frame_strut.bottom = 0;
2530 
2531     p->properties = properties;
2532     p->properties2 = properties2;
2533 
2534     p->icon_count = 0;
2535 
2536     p->role = role;
2537 
2538     update(p->properties, p->properties2);
2539 }
2540 
2541 #if KWINDOWSYSTEM_BUILD_DEPRECATED_SINCE(5, 0)
NETWinInfo(xcb_connection_t * connection,xcb_window_t window,xcb_window_t rootWindow,NET::Properties properties,Role role)2542 NETWinInfo::NETWinInfo(xcb_connection_t *connection, xcb_window_t window, xcb_window_t rootWindow, NET::Properties properties, Role role)
2543 {
2544 #ifdef NETWMDEBUG
2545     fprintf(stderr, "NETWinInfo::NETWinInfo: constructing object with role '%s'\n", (role == WindowManager) ? "WindowManager" : "Client");
2546 #endif
2547 
2548     p = new NETWinInfoPrivate;
2549     p->ref = 1;
2550     p->atoms = atomsForConnection(connection);
2551 
2552     p->conn = connection;
2553     p->window = window;
2554     p->root = rootWindow;
2555     p->mapping_state = Withdrawn;
2556     p->mapping_state_dirty = true;
2557     p->state = NET::States();
2558     p->types[0] = Unknown;
2559     p->name = (char *)nullptr;
2560     p->visible_name = (char *)nullptr;
2561     p->icon_name = (char *)nullptr;
2562     p->visible_icon_name = (char *)nullptr;
2563     p->desktop = p->pid = 0;
2564     p->handled_icons = false;
2565     p->user_time = -1U;
2566     p->startup_id = nullptr;
2567     p->transient_for = XCB_NONE;
2568     p->opacity = 0xffffffffU;
2569     p->window_group = XCB_NONE;
2570     p->icon_pixmap = XCB_PIXMAP_NONE;
2571     p->icon_mask = XCB_PIXMAP_NONE;
2572     p->allowed_actions = NET::Actions();
2573     p->has_net_support = false;
2574     p->class_class = (char *)nullptr;
2575     p->class_name = (char *)nullptr;
2576     p->window_role = (char *)nullptr;
2577     p->client_machine = (char *)nullptr;
2578     p->icon_sizes = nullptr;
2579     p->activities = (char *)nullptr;
2580     p->desktop_file = nullptr;
2581     p->appmenu_object_path = nullptr;
2582     p->appmenu_service_name = nullptr;
2583     p->blockCompositing = false;
2584     p->urgency = false;
2585     p->input = true;
2586     p->initialMappingState = NET::Withdrawn;
2587     p->protocols = NET::NoProtocol;
2588 
2589     // p->strut.left = p->strut.right = p->strut.top = p->strut.bottom = 0;
2590     // p->frame_strut.left = p->frame_strut.right = p->frame_strut.top =
2591     // p->frame_strut.bottom = 0;
2592 
2593     p->properties = properties;
2594     p->properties2 = NET::Properties2();
2595 
2596     p->icon_count = 0;
2597 
2598     p->role = role;
2599 
2600     update(p->properties);
2601 }
2602 #endif
2603 
NETWinInfo(const NETWinInfo & wininfo)2604 NETWinInfo::NETWinInfo(const NETWinInfo &wininfo)
2605 {
2606     p = wininfo.p;
2607     p->ref++;
2608 }
2609 
~NETWinInfo()2610 NETWinInfo::~NETWinInfo()
2611 {
2612     refdec_nwi(p);
2613 
2614     if (!p->ref) {
2615         delete p;
2616     }
2617 }
2618 
2619 // assignment operator
2620 
operator =(const NETWinInfo & wininfo)2621 const NETWinInfo &NETWinInfo::operator=(const NETWinInfo &wininfo)
2622 {
2623 #ifdef NETWMDEBUG
2624     fprintf(stderr, "NETWinInfo::operator=()\n");
2625 #endif
2626 
2627     if (p != wininfo.p) {
2628         refdec_nwi(p);
2629 
2630         if (!p->ref) {
2631             delete p;
2632         }
2633     }
2634 
2635     p = wininfo.p;
2636     p->ref++;
2637 
2638     return *this;
2639 }
2640 
setIcon(NETIcon icon,bool replace)2641 void NETWinInfo::setIcon(NETIcon icon, bool replace)
2642 {
2643     setIconInternal(p->icons, p->icon_count, p->atom(_NET_WM_ICON), icon, replace);
2644 }
2645 
setIconInternal(NETRArray<NETIcon> & icons,int & icon_count,xcb_atom_t property,NETIcon icon,bool replace)2646 void NETWinInfo::setIconInternal(NETRArray<NETIcon> &icons, int &icon_count, xcb_atom_t property, NETIcon icon, bool replace)
2647 {
2648     if (p->role != Client) {
2649         return;
2650     }
2651 
2652     if (replace) {
2653         for (int i = 0; i < icons.size(); i++) {
2654             delete[] icons[i].data;
2655 
2656             icons[i].data = nullptr;
2657             icons[i].size.width = 0;
2658             icons[i].size.height = 0;
2659         }
2660 
2661         icon_count = 0;
2662     }
2663 
2664     // assign icon
2665     icons[icon_count] = icon;
2666     icon_count++;
2667 
2668     // do a deep copy, we want to own the data
2669     NETIcon &ni = icons[icon_count - 1];
2670     int sz = ni.size.width * ni.size.height;
2671     uint32_t *d = new uint32_t[sz];
2672     ni.data = (unsigned char *)d;
2673     memcpy(d, icon.data, sz * sizeof(uint32_t));
2674 
2675     // compute property length
2676     int proplen = 0;
2677     for (int i = 0; i < icon_count; i++) {
2678         proplen += 2 + (icons[i].size.width * icons[i].size.height);
2679     }
2680 
2681     uint32_t *prop = new uint32_t[proplen];
2682     uint32_t *pprop = prop;
2683     for (int i = 0; i < icon_count; i++) {
2684         // copy size into property
2685         *pprop++ = icons[i].size.width;
2686         *pprop++ = icons[i].size.height;
2687 
2688         // copy data into property
2689         sz = (icons[i].size.width * icons[i].size.height);
2690         uint32_t *d32 = (uint32_t *)icons[i].data;
2691         for (int j = 0; j < sz; j++) {
2692             *pprop++ = *d32++;
2693         }
2694     }
2695 
2696     xcb_change_property(p->conn, XCB_PROP_MODE_REPLACE, p->window, property, XCB_ATOM_CARDINAL, 32, proplen, (const void *)prop);
2697 
2698     delete[] prop;
2699     delete[] p->icon_sizes;
2700     p->icon_sizes = nullptr;
2701 }
2702 
setIconGeometry(NETRect geometry)2703 void NETWinInfo::setIconGeometry(NETRect geometry)
2704 {
2705     if (p->role != Client) {
2706         return;
2707     }
2708 
2709     const qreal scaleFactor = qApp->devicePixelRatio();
2710     geometry.pos.x *= scaleFactor;
2711     geometry.pos.y *= scaleFactor;
2712     geometry.size.width *= scaleFactor;
2713     geometry.size.height *= scaleFactor;
2714 
2715     p->icon_geom = geometry;
2716 
2717     if (geometry.size.width == 0) { // Empty
2718         xcb_delete_property(p->conn, p->window, p->atom(_NET_WM_ICON_GEOMETRY));
2719     } else {
2720         uint32_t data[4];
2721         data[0] = geometry.pos.x;
2722         data[1] = geometry.pos.y;
2723         data[2] = geometry.size.width;
2724         data[3] = geometry.size.height;
2725 
2726         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);
2727     }
2728 }
2729 
setExtendedStrut(const NETExtendedStrut & extended_strut)2730 void NETWinInfo::setExtendedStrut(const NETExtendedStrut &extended_strut)
2731 {
2732     if (p->role != Client) {
2733         return;
2734     }
2735 
2736     p->extended_strut = extended_strut;
2737 
2738     uint32_t data[12];
2739     data[0] = extended_strut.left_width;
2740     data[1] = extended_strut.right_width;
2741     data[2] = extended_strut.top_width;
2742     data[3] = extended_strut.bottom_width;
2743     data[4] = extended_strut.left_start;
2744     data[5] = extended_strut.left_end;
2745     data[6] = extended_strut.right_start;
2746     data[7] = extended_strut.right_end;
2747     data[8] = extended_strut.top_start;
2748     data[9] = extended_strut.top_end;
2749     data[10] = extended_strut.bottom_start;
2750     data[11] = extended_strut.bottom_end;
2751 
2752     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);
2753 }
2754 
setStrut(NETStrut strut)2755 void NETWinInfo::setStrut(NETStrut strut)
2756 {
2757     if (p->role != Client) {
2758         return;
2759     }
2760 
2761     p->strut = strut;
2762 
2763     uint32_t data[4];
2764     data[0] = strut.left;
2765     data[1] = strut.right;
2766     data[2] = strut.top;
2767     data[3] = strut.bottom;
2768 
2769     xcb_change_property(p->conn, XCB_PROP_MODE_REPLACE, p->window, p->atom(_NET_WM_STRUT), XCB_ATOM_CARDINAL, 32, 4, (const void *)data);
2770 }
2771 
setFullscreenMonitors(NETFullscreenMonitors topology)2772 void NETWinInfo::setFullscreenMonitors(NETFullscreenMonitors topology)
2773 {
2774     if (p->role == Client) {
2775         const uint32_t data[5] = {uint32_t(topology.top), uint32_t(topology.bottom), uint32_t(topology.left), uint32_t(topology.right), 1};
2776 
2777         send_client_message(p->conn, netwm_sendevent_mask, p->root, p->window, p->atom(_NET_WM_FULLSCREEN_MONITORS), data);
2778     } else {
2779         p->fullscreen_monitors = topology;
2780 
2781         uint32_t data[4];
2782         data[0] = topology.top;
2783         data[1] = topology.bottom;
2784         data[2] = topology.left;
2785         data[3] = topology.right;
2786 
2787         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);
2788     }
2789 }
2790 
setState(NET::States state,NET::States mask)2791 void NETWinInfo::setState(NET::States state, NET::States mask)
2792 {
2793     if (p->mapping_state_dirty) {
2794         updateWMState();
2795     }
2796 
2797     // setState() needs to know the current state, so read it even if not requested
2798     if ((p->properties & WMState) == 0) {
2799         p->properties |= WMState;
2800 
2801         update(WMState);
2802 
2803         p->properties &= ~WMState;
2804     }
2805 
2806     if (p->role == Client && p->mapping_state != Withdrawn) {
2807 #ifdef NETWMDEBUG
2808         fprintf(stderr, "NETWinInfo::setState (0x%lx, 0x%lx) (Client)\n", state, mask);
2809 #endif // NETWMDEBUG
2810 
2811         xcb_client_message_event_t event;
2812         event.response_type = XCB_CLIENT_MESSAGE;
2813         event.format = 32;
2814         event.sequence = 0;
2815         event.window = p->window;
2816         event.type = p->atom(_NET_WM_STATE);
2817         event.data.data32[3] = 0;
2818         event.data.data32[4] = 0;
2819 
2820         if ((mask & Modal) && ((p->state & Modal) != (state & Modal))) {
2821             event.data.data32[0] = (state & Modal) ? 1 : 0;
2822             event.data.data32[1] = p->atom(_NET_WM_STATE_MODAL);
2823             event.data.data32[2] = 0l;
2824 
2825             xcb_send_event(p->conn, false, p->root, netwm_sendevent_mask, (const char *)&event);
2826         }
2827 
2828         if ((mask & Sticky) && ((p->state & Sticky) != (state & Sticky))) {
2829             event.data.data32[0] = (state & Sticky) ? 1 : 0;
2830             event.data.data32[1] = p->atom(_NET_WM_STATE_STICKY);
2831             event.data.data32[2] = 0l;
2832 
2833             xcb_send_event(p->conn, false, p->root, netwm_sendevent_mask, (const char *)&event);
2834         }
2835 
2836         if ((mask & Max) && (((p->state & mask) & Max) != (state & Max))) {
2837             NET::States wishstate = (p->state & ~mask) | (state & mask);
2838             if (((wishstate & MaxHoriz) != (p->state & MaxHoriz)) && ((wishstate & MaxVert) != (p->state & MaxVert))) {
2839                 if ((wishstate & Max) == Max) {
2840                     event.data.data32[0] = 1;
2841                     event.data.data32[1] = p->atom(_NET_WM_STATE_MAXIMIZED_HORZ);
2842                     event.data.data32[2] = p->atom(_NET_WM_STATE_MAXIMIZED_VERT);
2843                     xcb_send_event(p->conn, false, p->root, netwm_sendevent_mask, (const char *)&event);
2844                 } else if ((wishstate & Max) == 0) {
2845                     event.data.data32[0] = 0;
2846                     event.data.data32[1] = p->atom(_NET_WM_STATE_MAXIMIZED_HORZ);
2847                     event.data.data32[2] = p->atom(_NET_WM_STATE_MAXIMIZED_VERT);
2848                     xcb_send_event(p->conn, false, p->root, netwm_sendevent_mask, (const char *)&event);
2849                 } else {
2850                     event.data.data32[0] = (wishstate & MaxHoriz) ? 1 : 0;
2851                     event.data.data32[1] = p->atom(_NET_WM_STATE_MAXIMIZED_HORZ);
2852                     event.data.data32[2] = 0;
2853                     xcb_send_event(p->conn, false, p->root, netwm_sendevent_mask, (const char *)&event);
2854 
2855                     event.data.data32[0] = (wishstate & MaxVert) ? 1 : 0;
2856                     event.data.data32[1] = p->atom(_NET_WM_STATE_MAXIMIZED_VERT);
2857                     event.data.data32[2] = 0;
2858                     xcb_send_event(p->conn, false, p->root, netwm_sendevent_mask, (const char *)&event);
2859                 }
2860             } else if ((wishstate & MaxVert) != (p->state & MaxVert)) {
2861                 event.data.data32[0] = (wishstate & MaxVert) ? 1 : 0;
2862                 event.data.data32[1] = p->atom(_NET_WM_STATE_MAXIMIZED_VERT);
2863                 event.data.data32[2] = 0;
2864 
2865                 xcb_send_event(p->conn, false, p->root, netwm_sendevent_mask, (const char *)&event);
2866             } else if ((wishstate & MaxHoriz) != (p->state & MaxHoriz)) {
2867                 event.data.data32[0] = (wishstate & MaxHoriz) ? 1 : 0;
2868                 event.data.data32[1] = p->atom(_NET_WM_STATE_MAXIMIZED_HORZ);
2869                 event.data.data32[2] = 0;
2870 
2871                 xcb_send_event(p->conn, false, p->root, netwm_sendevent_mask, (const char *)&event);
2872             }
2873         }
2874 
2875         if ((mask & Shaded) && ((p->state & Shaded) != (state & Shaded))) {
2876             event.data.data32[0] = (state & Shaded) ? 1 : 0;
2877             event.data.data32[1] = p->atom(_NET_WM_STATE_SHADED);
2878             event.data.data32[2] = 0l;
2879 
2880             xcb_send_event(p->conn, false, p->root, netwm_sendevent_mask, (const char *)&event);
2881         }
2882 
2883         if ((mask & SkipTaskbar) && ((p->state & SkipTaskbar) != (state & SkipTaskbar))) {
2884             event.data.data32[0] = (state & SkipTaskbar) ? 1 : 0;
2885             event.data.data32[1] = p->atom(_NET_WM_STATE_SKIP_TASKBAR);
2886             event.data.data32[2] = 0l;
2887 
2888             xcb_send_event(p->conn, false, p->root, netwm_sendevent_mask, (const char *)&event);
2889         }
2890 
2891         if ((mask & SkipPager) && ((p->state & SkipPager) != (state & SkipPager))) {
2892             event.data.data32[0] = (state & SkipPager) ? 1 : 0;
2893             event.data.data32[1] = p->atom(_NET_WM_STATE_SKIP_PAGER);
2894             event.data.data32[2] = 0l;
2895 
2896             xcb_send_event(p->conn, false, p->root, netwm_sendevent_mask, (const char *)&event);
2897         }
2898 
2899         if ((mask & SkipSwitcher) && ((p->state & SkipSwitcher) != (state & SkipSwitcher))) {
2900             event.data.data32[0] = (state & SkipSwitcher) ? 1 : 0;
2901             event.data.data32[1] = p->atom(_KDE_NET_WM_STATE_SKIP_SWITCHER);
2902             event.data.data32[2] = 0l;
2903 
2904             xcb_send_event(p->conn, false, p->root, netwm_sendevent_mask, (const char *)&event);
2905         }
2906 
2907         if ((mask & Hidden) && ((p->state & Hidden) != (state & Hidden))) {
2908             event.data.data32[0] = (state & Hidden) ? 1 : 0;
2909             event.data.data32[1] = p->atom(_NET_WM_STATE_HIDDEN);
2910             event.data.data32[2] = 0l;
2911 
2912             xcb_send_event(p->conn, false, p->root, netwm_sendevent_mask, (const char *)&event);
2913         }
2914 
2915         if ((mask & FullScreen) && ((p->state & FullScreen) != (state & FullScreen))) {
2916             event.data.data32[0] = (state & FullScreen) ? 1 : 0;
2917             event.data.data32[1] = p->atom(_NET_WM_STATE_FULLSCREEN);
2918             event.data.data32[2] = 0l;
2919 
2920             xcb_send_event(p->conn, false, p->root, netwm_sendevent_mask, (const char *)&event);
2921         }
2922 
2923         if ((mask & KeepAbove) && ((p->state & KeepAbove) != (state & KeepAbove))) {
2924             event.data.data32[0] = (state & KeepAbove) ? 1 : 0;
2925             event.data.data32[1] = p->atom(_NET_WM_STATE_ABOVE);
2926             event.data.data32[2] = 0l;
2927 
2928             xcb_send_event(p->conn, false, p->root, netwm_sendevent_mask, (const char *)&event);
2929 
2930             // deprecated variant
2931             event.data.data32[0] = (state & KeepAbove) ? 1 : 0;
2932             event.data.data32[1] = p->atom(_NET_WM_STATE_STAYS_ON_TOP);
2933             event.data.data32[2] = 0l;
2934 
2935             xcb_send_event(p->conn, false, p->root, netwm_sendevent_mask, (const char *)&event);
2936         }
2937 
2938         if ((mask & KeepBelow) && ((p->state & KeepBelow) != (state & KeepBelow))) {
2939             event.data.data32[0] = (state & KeepBelow) ? 1 : 0;
2940             event.data.data32[1] = p->atom(_NET_WM_STATE_BELOW);
2941             event.data.data32[2] = 0l;
2942 
2943             xcb_send_event(p->conn, false, p->root, netwm_sendevent_mask, (const char *)&event);
2944         }
2945 
2946         if ((mask & DemandsAttention) && ((p->state & DemandsAttention) != (state & DemandsAttention))) {
2947             event.data.data32[0] = (state & DemandsAttention) ? 1 : 0;
2948             event.data.data32[1] = p->atom(_NET_WM_STATE_DEMANDS_ATTENTION);
2949             event.data.data32[2] = 0l;
2950 
2951             xcb_send_event(p->conn, false, p->root, netwm_sendevent_mask, (const char *)&event);
2952         }
2953 
2954         // Focused is not added here as it is effectively "read only" set by the WM, a client setting it would be silly
2955     } else {
2956         p->state &= ~mask;
2957         p->state |= state;
2958 
2959         uint32_t data[50];
2960         int count = 0;
2961 
2962         // Hints
2963         if (p->state & Modal) {
2964             data[count++] = p->atom(_NET_WM_STATE_MODAL);
2965         }
2966         if (p->state & MaxVert) {
2967             data[count++] = p->atom(_NET_WM_STATE_MAXIMIZED_VERT);
2968         }
2969         if (p->state & MaxHoriz) {
2970             data[count++] = p->atom(_NET_WM_STATE_MAXIMIZED_HORZ);
2971         }
2972         if (p->state & Shaded) {
2973             data[count++] = p->atom(_NET_WM_STATE_SHADED);
2974         }
2975         if (p->state & Hidden) {
2976             data[count++] = p->atom(_NET_WM_STATE_HIDDEN);
2977         }
2978         if (p->state & FullScreen) {
2979             data[count++] = p->atom(_NET_WM_STATE_FULLSCREEN);
2980         }
2981         if (p->state & DemandsAttention) {
2982             data[count++] = p->atom(_NET_WM_STATE_DEMANDS_ATTENTION);
2983         }
2984         if (p->state & Focused) {
2985             data[count++] = p->atom(_NET_WM_STATE_FOCUSED);
2986         }
2987 
2988         // Policy
2989         if (p->state & KeepAbove) {
2990             data[count++] = p->atom(_NET_WM_STATE_ABOVE);
2991             // deprecated variant
2992             data[count++] = p->atom(_NET_WM_STATE_STAYS_ON_TOP);
2993         }
2994         if (p->state & KeepBelow) {
2995             data[count++] = p->atom(_NET_WM_STATE_BELOW);
2996         }
2997         if (p->state & Sticky) {
2998             data[count++] = p->atom(_NET_WM_STATE_STICKY);
2999         }
3000         if (p->state & SkipTaskbar) {
3001             data[count++] = p->atom(_NET_WM_STATE_SKIP_TASKBAR);
3002         }
3003         if (p->state & SkipPager) {
3004             data[count++] = p->atom(_NET_WM_STATE_SKIP_PAGER);
3005         }
3006         if (p->state & SkipSwitcher) {
3007             data[count++] = p->atom(_KDE_NET_WM_STATE_SKIP_SWITCHER);
3008         }
3009 
3010 #ifdef NETWMDEBUG
3011         fprintf(stderr, "NETWinInfo::setState: setting state property (%d)\n", count);
3012         for (int i = 0; i < count; i++) {
3013             const QByteArray ba = get_atom_name(p->conn, data[i]);
3014             fprintf(stderr, "NETWinInfo::setState:   state %ld '%s'\n", data[i], ba.constData());
3015         }
3016 #endif
3017 
3018         xcb_change_property(p->conn, XCB_PROP_MODE_REPLACE, p->window, p->atom(_NET_WM_STATE), XCB_ATOM_ATOM, 32, count, (const void *)data);
3019     }
3020 }
3021 
setWindowType(WindowType type)3022 void NETWinInfo::setWindowType(WindowType type)
3023 {
3024     if (p->role != Client) {
3025         return;
3026     }
3027 
3028     int len;
3029     uint32_t data[2];
3030 
3031     switch (type) {
3032     case Override:
3033         // spec extension: override window type.  we must comply with the spec
3034         // and provide a fall back (normal seems best)
3035         data[0] = p->atom(_KDE_NET_WM_WINDOW_TYPE_OVERRIDE);
3036         data[1] = p->atom(_NET_WM_WINDOW_TYPE_NORMAL);
3037         len = 2;
3038         break;
3039 
3040     case Dialog:
3041         data[0] = p->atom(_NET_WM_WINDOW_TYPE_DIALOG);
3042         data[1] = XCB_NONE;
3043         len = 1;
3044         break;
3045 
3046     case Menu:
3047         data[0] = p->atom(_NET_WM_WINDOW_TYPE_MENU);
3048         data[1] = XCB_NONE;
3049         len = 1;
3050         break;
3051 
3052     case TopMenu:
3053         // spec extension: override window type.  we must comply with the spec
3054         // and provide a fall back (dock seems best)
3055         data[0] = p->atom(_KDE_NET_WM_WINDOW_TYPE_TOPMENU);
3056         data[1] = p->atom(_NET_WM_WINDOW_TYPE_DOCK);
3057         len = 2;
3058         break;
3059 
3060     case Toolbar:
3061         data[0] = p->atom(_NET_WM_WINDOW_TYPE_TOOLBAR);
3062         data[1] = XCB_NONE;
3063         len = 1;
3064         break;
3065 
3066     case Dock:
3067         data[0] = p->atom(_NET_WM_WINDOW_TYPE_DOCK);
3068         data[1] = XCB_NONE;
3069         len = 1;
3070         break;
3071 
3072     case Desktop:
3073         data[0] = p->atom(_NET_WM_WINDOW_TYPE_DESKTOP);
3074         data[1] = XCB_NONE;
3075         len = 1;
3076         break;
3077 
3078     case Utility:
3079         data[0] = p->atom(_NET_WM_WINDOW_TYPE_UTILITY);
3080         data[1] = p->atom(_NET_WM_WINDOW_TYPE_DIALOG); // fallback for old netwm version
3081         len = 2;
3082         break;
3083 
3084     case Splash:
3085         data[0] = p->atom(_NET_WM_WINDOW_TYPE_SPLASH);
3086         data[1] = p->atom(_NET_WM_WINDOW_TYPE_DOCK); // fallback (dock seems best)
3087         len = 2;
3088         break;
3089 
3090     case DropdownMenu:
3091         data[0] = p->atom(_NET_WM_WINDOW_TYPE_DROPDOWN_MENU);
3092         data[1] = p->atom(_NET_WM_WINDOW_TYPE_MENU); // fallback (tearoff seems to be the best)
3093         len = 1;
3094         break;
3095 
3096     case PopupMenu:
3097         data[0] = p->atom(_NET_WM_WINDOW_TYPE_POPUP_MENU);
3098         data[1] = p->atom(_NET_WM_WINDOW_TYPE_MENU); // fallback (tearoff seems to be the best)
3099         len = 1;
3100         break;
3101 
3102     case Tooltip:
3103         data[0] = p->atom(_NET_WM_WINDOW_TYPE_TOOLTIP);
3104         data[1] = XCB_NONE;
3105         len = 1;
3106         break;
3107 
3108     case Notification:
3109         data[0] = p->atom(_NET_WM_WINDOW_TYPE_NOTIFICATION);
3110         data[1] = p->atom(_NET_WM_WINDOW_TYPE_UTILITY); // fallback (utility seems to be the best)
3111         len = 1;
3112         break;
3113 
3114     case ComboBox:
3115         data[0] = p->atom(_NET_WM_WINDOW_TYPE_COMBO);
3116         data[1] = XCB_NONE;
3117         len = 1;
3118         break;
3119 
3120     case DNDIcon:
3121         data[0] = p->atom(_NET_WM_WINDOW_TYPE_DND);
3122         data[1] = XCB_NONE;
3123         len = 1;
3124         break;
3125 
3126     case OnScreenDisplay:
3127         data[0] = p->atom(_KDE_NET_WM_WINDOW_TYPE_ON_SCREEN_DISPLAY);
3128         data[1] = p->atom(_NET_WM_WINDOW_TYPE_NOTIFICATION);
3129         len = 2;
3130         break;
3131 
3132     case CriticalNotification:
3133         data[0] = p->atom(_KDE_NET_WM_WINDOW_TYPE_CRITICAL_NOTIFICATION);
3134         data[1] = p->atom(_NET_WM_WINDOW_TYPE_NOTIFICATION);
3135         len = 2;
3136         break;
3137 
3138     default:
3139     case Normal:
3140         data[0] = p->atom(_NET_WM_WINDOW_TYPE_NORMAL);
3141         data[1] = XCB_NONE;
3142         len = 1;
3143         break;
3144     }
3145 
3146     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);
3147 }
3148 
setName(const char * name)3149 void NETWinInfo::setName(const char *name)
3150 {
3151     if (p->role != Client) {
3152         return;
3153     }
3154 
3155     delete[] p->name;
3156     p->name = nstrdup(name);
3157 
3158     if (p->name[0] != '\0') {
3159         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);
3160     } else {
3161         xcb_delete_property(p->conn, p->window, p->atom(_NET_WM_NAME));
3162     }
3163 }
3164 
setVisibleName(const char * visibleName)3165 void NETWinInfo::setVisibleName(const char *visibleName)
3166 {
3167     if (p->role != WindowManager) {
3168         return;
3169     }
3170 
3171     delete[] p->visible_name;
3172     p->visible_name = nstrdup(visibleName);
3173 
3174     if (p->visible_name[0] != '\0') {
3175         xcb_change_property(p->conn,
3176                             XCB_PROP_MODE_REPLACE,
3177                             p->window,
3178                             p->atom(_NET_WM_VISIBLE_NAME),
3179                             p->atom(UTF8_STRING),
3180                             8,
3181                             strlen(p->visible_name),
3182                             (const void *)p->visible_name);
3183     } else {
3184         xcb_delete_property(p->conn, p->window, p->atom(_NET_WM_VISIBLE_NAME));
3185     }
3186 }
3187 
setIconName(const char * iconName)3188 void NETWinInfo::setIconName(const char *iconName)
3189 {
3190     if (p->role != Client) {
3191         return;
3192     }
3193 
3194     delete[] p->icon_name;
3195     p->icon_name = nstrdup(iconName);
3196 
3197     if (p->icon_name[0] != '\0') {
3198         xcb_change_property(p->conn,
3199                             XCB_PROP_MODE_REPLACE,
3200                             p->window,
3201                             p->atom(_NET_WM_ICON_NAME),
3202                             p->atom(UTF8_STRING),
3203                             8,
3204                             strlen(p->icon_name),
3205                             (const void *)p->icon_name);
3206     } else {
3207         xcb_delete_property(p->conn, p->window, p->atom(_NET_WM_ICON_NAME));
3208     }
3209 }
3210 
setVisibleIconName(const char * visibleIconName)3211 void NETWinInfo::setVisibleIconName(const char *visibleIconName)
3212 {
3213     if (p->role != WindowManager) {
3214         return;
3215     }
3216 
3217     delete[] p->visible_icon_name;
3218     p->visible_icon_name = nstrdup(visibleIconName);
3219 
3220     if (p->visible_icon_name[0] != '\0') {
3221         xcb_change_property(p->conn,
3222                             XCB_PROP_MODE_REPLACE,
3223                             p->window,
3224                             p->atom(_NET_WM_VISIBLE_ICON_NAME),
3225                             p->atom(UTF8_STRING),
3226                             8,
3227                             strlen(p->visible_icon_name),
3228                             (const void *)p->visible_icon_name);
3229     } else {
3230         xcb_delete_property(p->conn, p->window, p->atom(_NET_WM_VISIBLE_ICON_NAME));
3231     }
3232 }
3233 
setDesktop(int desktop,bool ignore_viewport)3234 void NETWinInfo::setDesktop(int desktop, bool ignore_viewport)
3235 {
3236     if (p->mapping_state_dirty) {
3237         updateWMState();
3238     }
3239 
3240     if (p->role == Client && p->mapping_state != Withdrawn) {
3241         // We only send a ClientMessage if we are 1) a client and 2) managed
3242 
3243         if (desktop == 0) {
3244             return; // We can't do that while being managed
3245         }
3246 
3247         if (!ignore_viewport && KWindowSystem::mapViewport()) {
3248             KWindowSystem::setOnDesktop(p->window, desktop);
3249             return;
3250         }
3251 
3252         const uint32_t data[5] = {desktop == OnAllDesktops ? 0xffffffff : desktop - 1, 0, 0, 0, 0};
3253 
3254         send_client_message(p->conn, netwm_sendevent_mask, p->root, p->window, p->atom(_NET_WM_DESKTOP), data);
3255     } else {
3256         // Otherwise we just set or remove the property directly
3257         p->desktop = desktop;
3258 
3259         if (desktop == 0) {
3260             xcb_delete_property(p->conn, p->window, p->atom(_NET_WM_DESKTOP));
3261         } else {
3262             uint32_t d = (desktop == OnAllDesktops ? 0xffffffff : desktop - 1);
3263             xcb_change_property(p->conn, XCB_PROP_MODE_REPLACE, p->window, p->atom(_NET_WM_DESKTOP), XCB_ATOM_CARDINAL, 32, 1, (const void *)&d);
3264         }
3265     }
3266 }
3267 
setPid(int pid)3268 void NETWinInfo::setPid(int pid)
3269 {
3270     if (p->role != Client) {
3271         return;
3272     }
3273 
3274     p->pid = pid;
3275     uint32_t d = pid;
3276     xcb_change_property(p->conn, XCB_PROP_MODE_REPLACE, p->window, p->atom(_NET_WM_PID), XCB_ATOM_CARDINAL, 32, 1, (const void *)&d);
3277 }
3278 
setHandledIcons(bool handled)3279 void NETWinInfo::setHandledIcons(bool handled)
3280 {
3281     if (p->role != Client) {
3282         return;
3283     }
3284 
3285     p->handled_icons = handled;
3286     uint32_t d = handled;
3287     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);
3288 }
3289 
setStartupId(const char * id)3290 void NETWinInfo::setStartupId(const char *id)
3291 {
3292     if (p->role != Client) {
3293         return;
3294     }
3295 
3296     delete[] p->startup_id;
3297     p->startup_id = nstrdup(id);
3298 
3299     xcb_change_property(p->conn,
3300                         XCB_PROP_MODE_REPLACE,
3301                         p->window,
3302                         p->atom(_NET_STARTUP_ID),
3303                         p->atom(UTF8_STRING),
3304                         8,
3305                         strlen(p->startup_id),
3306                         (const void *)p->startup_id);
3307 }
3308 
setOpacity(unsigned long opacity)3309 void NETWinInfo::setOpacity(unsigned long opacity)
3310 {
3311     //    if (p->role != Client) return;
3312 
3313     p->opacity = opacity;
3314     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);
3315 }
3316 
setOpacityF(qreal opacity)3317 void NETWinInfo::setOpacityF(qreal opacity)
3318 {
3319     setOpacity(static_cast<unsigned long>(opacity * 0xffffffff));
3320 }
3321 
setAllowedActions(NET::Actions actions)3322 void NETWinInfo::setAllowedActions(NET::Actions actions)
3323 {
3324     if (p->role != WindowManager) {
3325         return;
3326     }
3327 
3328     uint32_t data[50];
3329     int count = 0;
3330 
3331     p->allowed_actions = actions;
3332     if (p->allowed_actions & ActionMove) {
3333         data[count++] = p->atom(_NET_WM_ACTION_MOVE);
3334     }
3335     if (p->allowed_actions & ActionResize) {
3336         data[count++] = p->atom(_NET_WM_ACTION_RESIZE);
3337     }
3338     if (p->allowed_actions & ActionMinimize) {
3339         data[count++] = p->atom(_NET_WM_ACTION_MINIMIZE);
3340     }
3341     if (p->allowed_actions & ActionShade) {
3342         data[count++] = p->atom(_NET_WM_ACTION_SHADE);
3343     }
3344     if (p->allowed_actions & ActionStick) {
3345         data[count++] = p->atom(_NET_WM_ACTION_STICK);
3346     }
3347     if (p->allowed_actions & ActionMaxVert) {
3348         data[count++] = p->atom(_NET_WM_ACTION_MAXIMIZE_VERT);
3349     }
3350     if (p->allowed_actions & ActionMaxHoriz) {
3351         data[count++] = p->atom(_NET_WM_ACTION_MAXIMIZE_HORZ);
3352     }
3353     if (p->allowed_actions & ActionFullScreen) {
3354         data[count++] = p->atom(_NET_WM_ACTION_FULLSCREEN);
3355     }
3356     if (p->allowed_actions & ActionChangeDesktop) {
3357         data[count++] = p->atom(_NET_WM_ACTION_CHANGE_DESKTOP);
3358     }
3359     if (p->allowed_actions & ActionClose) {
3360         data[count++] = p->atom(_NET_WM_ACTION_CLOSE);
3361     }
3362 
3363 #ifdef NETWMDEBUG
3364     fprintf(stderr, "NETWinInfo::setAllowedActions: setting property (%d)\n", count);
3365     for (int i = 0; i < count; i++) {
3366         const QByteArray ba = get_atom_name(p->conn, data[i]);
3367         fprintf(stderr, "NETWinInfo::setAllowedActions:   action %ld '%s'\n", data[i], ba.constData());
3368     }
3369 #endif
3370 
3371     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);
3372 }
3373 
setFrameExtents(NETStrut strut)3374 void NETWinInfo::setFrameExtents(NETStrut strut)
3375 {
3376     if (p->role != WindowManager) {
3377         return;
3378     }
3379 
3380     p->frame_strut = strut;
3381 
3382     uint32_t d[4];
3383     d[0] = strut.left;
3384     d[1] = strut.right;
3385     d[2] = strut.top;
3386     d[3] = strut.bottom;
3387 
3388     xcb_change_property(p->conn, XCB_PROP_MODE_REPLACE, p->window, p->atom(_NET_FRAME_EXTENTS), XCB_ATOM_CARDINAL, 32, 4, (const void *)d);
3389     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);
3390 }
3391 
frameExtents() const3392 NETStrut NETWinInfo::frameExtents() const
3393 {
3394     return p->frame_strut;
3395 }
3396 
setFrameOverlap(NETStrut strut)3397 void NETWinInfo::setFrameOverlap(NETStrut strut)
3398 {
3399     if (strut.left != -1 || strut.top != -1 || strut.right != -1 || strut.bottom != -1) {
3400         strut.left = qMax(0, strut.left);
3401         strut.top = qMax(0, strut.top);
3402         strut.right = qMax(0, strut.right);
3403         strut.bottom = qMax(0, strut.bottom);
3404     }
3405 
3406     p->frame_overlap = strut;
3407 
3408     uint32_t d[4];
3409     d[0] = strut.left;
3410     d[1] = strut.right;
3411     d[2] = strut.top;
3412     d[3] = strut.bottom;
3413 
3414     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);
3415 }
3416 
frameOverlap() const3417 NETStrut NETWinInfo::frameOverlap() const
3418 {
3419     return p->frame_overlap;
3420 }
3421 
setGtkFrameExtents(NETStrut strut)3422 void NETWinInfo::setGtkFrameExtents(NETStrut strut)
3423 {
3424     p->gtk_frame_extents = strut;
3425 
3426     uint32_t d[4];
3427     d[0] = strut.left;
3428     d[1] = strut.right;
3429     d[2] = strut.top;
3430     d[3] = strut.bottom;
3431 
3432     xcb_change_property(p->conn, XCB_PROP_MODE_REPLACE, p->window, p->atom(_GTK_FRAME_EXTENTS), XCB_ATOM_CARDINAL, 32, 4, (const void *)d);
3433 }
3434 
gtkFrameExtents() const3435 NETStrut NETWinInfo::gtkFrameExtents() const
3436 {
3437     return p->gtk_frame_extents;
3438 }
3439 
setAppMenuObjectPath(const char * name)3440 void NETWinInfo::setAppMenuObjectPath(const char *name)
3441 {
3442     if (p->role != Client) {
3443         return;
3444     }
3445 
3446     delete[] p->appmenu_object_path;
3447     p->appmenu_object_path = nstrdup(name);
3448 
3449     xcb_change_property(p->conn,
3450                         XCB_PROP_MODE_REPLACE,
3451                         p->window,
3452                         p->atom(_KDE_NET_WM_APPMENU_OBJECT_PATH),
3453                         XCB_ATOM_STRING,
3454                         8,
3455                         strlen(p->appmenu_object_path),
3456                         (const void *)p->appmenu_object_path);
3457 }
3458 
setAppMenuServiceName(const char * name)3459 void NETWinInfo::setAppMenuServiceName(const char *name)
3460 {
3461     if (p->role != Client) {
3462         return;
3463     }
3464 
3465     delete[] p->appmenu_service_name;
3466     p->appmenu_service_name = nstrdup(name);
3467 
3468     xcb_change_property(p->conn,
3469                         XCB_PROP_MODE_REPLACE,
3470                         p->window,
3471                         p->atom(_KDE_NET_WM_APPMENU_SERVICE_NAME),
3472                         XCB_ATOM_STRING,
3473                         8,
3474                         strlen(p->appmenu_service_name),
3475                         (const void *)p->appmenu_service_name);
3476 }
3477 
appMenuObjectPath() const3478 const char *NETWinInfo::appMenuObjectPath() const
3479 {
3480     return p->appmenu_object_path;
3481 }
3482 
appMenuServiceName() const3483 const char *NETWinInfo::appMenuServiceName() const
3484 {
3485     return p->appmenu_service_name;
3486 }
3487 
kdeGeometry(NETRect & frame,NETRect & window)3488 void NETWinInfo::kdeGeometry(NETRect &frame, NETRect &window)
3489 {
3490     if (p->win_geom.size.width == 0 || p->win_geom.size.height == 0) {
3491         const xcb_get_geometry_cookie_t geometry_cookie = xcb_get_geometry(p->conn, p->window);
3492 
3493         const xcb_translate_coordinates_cookie_t translate_cookie = xcb_translate_coordinates(p->conn, p->window, p->root, 0, 0);
3494 
3495         xcb_get_geometry_reply_t *geometry = xcb_get_geometry_reply(p->conn, geometry_cookie, nullptr);
3496         xcb_translate_coordinates_reply_t *translated = xcb_translate_coordinates_reply(p->conn, translate_cookie, nullptr);
3497 
3498         if (geometry && translated) {
3499             p->win_geom.pos.x = translated->dst_x;
3500             p->win_geom.pos.y = translated->dst_y;
3501 
3502             p->win_geom.size.width = geometry->width;
3503             p->win_geom.size.height = geometry->height;
3504         }
3505 
3506         if (geometry) {
3507             free(geometry);
3508         }
3509 
3510         if (translated) {
3511             free(translated);
3512         }
3513     }
3514 
3515     // TODO try to work also without _NET_WM_FRAME_EXTENTS
3516     window = p->win_geom;
3517 
3518     frame.pos.x = window.pos.x - p->frame_strut.left;
3519     frame.pos.y = window.pos.y - p->frame_strut.top;
3520     frame.size.width = window.size.width + p->frame_strut.left + p->frame_strut.right;
3521     frame.size.height = window.size.height + p->frame_strut.top + p->frame_strut.bottom;
3522 }
3523 
icon(int width,int height) const3524 NETIcon NETWinInfo::icon(int width, int height) const
3525 {
3526     return iconInternal(p->icons, p->icon_count, width, height);
3527 }
3528 
iconSizes() const3529 const int *NETWinInfo::iconSizes() const
3530 {
3531     if (p->icon_sizes == nullptr) {
3532         p->icon_sizes = new int[p->icon_count * 2 + 2];
3533         for (int i = 0; i < p->icon_count; ++i) {
3534             p->icon_sizes[i * 2] = p->icons[i].size.width;
3535             p->icon_sizes[i * 2 + 1] = p->icons[i].size.height;
3536         }
3537         p->icon_sizes[p->icon_count * 2] = 0; // terminator
3538         p->icon_sizes[p->icon_count * 2 + 1] = 0;
3539     }
3540     return p->icon_sizes;
3541 }
3542 
iconInternal(NETRArray<NETIcon> & icons,int icon_count,int width,int height) const3543 NETIcon NETWinInfo::iconInternal(NETRArray<NETIcon> &icons, int icon_count, int width, int height) const
3544 {
3545     NETIcon result;
3546 
3547     if (!icon_count) {
3548         result.size.width = 0;
3549         result.size.height = 0;
3550         result.data = nullptr;
3551         return result;
3552     }
3553 
3554     // find the largest icon
3555     result = icons[0];
3556     for (int i = 1; i < icons.size(); i++) {
3557         if (icons[i].size.width >= result.size.width && icons[i].size.height >= result.size.height) {
3558             result = icons[i];
3559         }
3560     }
3561 
3562     // return the largest icon if w and h are -1
3563     if (width == -1 && height == -1) {
3564         return result;
3565     }
3566 
3567     // find the icon that's closest in size to w x h...
3568     for (int i = 0; i < icons.size(); i++) {
3569         if ((icons[i].size.width >= width && icons[i].size.width < result.size.width)
3570             && (icons[i].size.height >= height && icons[i].size.height < result.size.height)) {
3571             result = icons[i];
3572         }
3573     }
3574 
3575     return result;
3576 }
3577 
setUserTime(xcb_timestamp_t time)3578 void NETWinInfo::setUserTime(xcb_timestamp_t time)
3579 {
3580     if (p->role != Client) {
3581         return;
3582     }
3583 
3584     p->user_time = time;
3585     uint32_t d = time;
3586 
3587     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);
3588 }
3589 
event(xcb_generic_event_t * ev)3590 NET::Properties NETWinInfo::event(xcb_generic_event_t *ev)
3591 {
3592     NET::Properties properties;
3593     event(ev, &properties);
3594     return properties;
3595 }
3596 
event(xcb_generic_event_t * event,NET::Properties * properties,NET::Properties2 * properties2)3597 void NETWinInfo::event(xcb_generic_event_t *event, NET::Properties *properties, NET::Properties2 *properties2)
3598 {
3599     NET::Properties dirty;
3600     NET::Properties2 dirty2;
3601     bool do_update = false;
3602     const uint8_t eventType = event->response_type & ~0x80;
3603 
3604     if (p->role == WindowManager && eventType == XCB_CLIENT_MESSAGE && reinterpret_cast<xcb_client_message_event_t *>(event)->format == 32) {
3605         xcb_client_message_event_t *message = reinterpret_cast<xcb_client_message_event_t *>(event);
3606 #ifdef NETWMDEBUG
3607         fprintf(stderr, "NETWinInfo::event: handling ClientMessage event\n");
3608 #endif // NETWMDEBUG
3609 
3610         if (message->type == p->atom(_NET_WM_STATE)) {
3611             dirty = WMState;
3612 
3613             // we need to generate a change mask
3614 
3615 #ifdef NETWMDEBUG
3616             fprintf(stderr, "NETWinInfo::event: state client message, getting new state/mask\n");
3617 #endif
3618 
3619             int i;
3620             NET::States state = NET::States();
3621             NET::States mask = NET::States();
3622 
3623             for (i = 1; i < 3; i++) {
3624 #ifdef NETWMDEBUG
3625                 const QByteArray ba = get_atom_name(p->conn, (xcb_atom_t)message->data.data32[i]);
3626                 fprintf(stderr, "NETWinInfo::event:  message %ld '%s'\n", message->data.data32[i], ba.constData());
3627 #endif
3628 
3629                 if ((xcb_atom_t)message->data.data32[i] == p->atom(_NET_WM_STATE_MODAL)) {
3630                     mask |= Modal;
3631                 } else if ((xcb_atom_t)message->data.data32[i] == p->atom(_NET_WM_STATE_STICKY)) {
3632                     mask |= Sticky;
3633                 } else if ((xcb_atom_t)message->data.data32[i] == p->atom(_NET_WM_STATE_MAXIMIZED_VERT)) {
3634                     mask |= MaxVert;
3635                 } else if ((xcb_atom_t)message->data.data32[i] == p->atom(_NET_WM_STATE_MAXIMIZED_HORZ)) {
3636                     mask |= MaxHoriz;
3637                 } else if ((xcb_atom_t)message->data.data32[i] == p->atom(_NET_WM_STATE_SHADED)) {
3638                     mask |= Shaded;
3639                 } else if ((xcb_atom_t)message->data.data32[i] == p->atom(_NET_WM_STATE_SKIP_TASKBAR)) {
3640                     mask |= SkipTaskbar;
3641                 } else if ((xcb_atom_t)message->data.data32[i] == p->atom(_NET_WM_STATE_SKIP_PAGER)) {
3642                     mask |= SkipPager;
3643                 } else if ((xcb_atom_t)message->data.data32[i] == p->atom(_KDE_NET_WM_STATE_SKIP_SWITCHER)) {
3644                     mask |= SkipSwitcher;
3645                 } else if ((xcb_atom_t)message->data.data32[i] == p->atom(_NET_WM_STATE_HIDDEN)) {
3646                     mask |= Hidden;
3647                 } else if ((xcb_atom_t)message->data.data32[i] == p->atom(_NET_WM_STATE_FULLSCREEN)) {
3648                     mask |= FullScreen;
3649                 } else if ((xcb_atom_t)message->data.data32[i] == p->atom(_NET_WM_STATE_ABOVE)) {
3650                     mask |= KeepAbove;
3651                 } else if ((xcb_atom_t)message->data.data32[i] == p->atom(_NET_WM_STATE_BELOW)) {
3652                     mask |= KeepBelow;
3653                 } else if ((xcb_atom_t)message->data.data32[i] == p->atom(_NET_WM_STATE_DEMANDS_ATTENTION)) {
3654                     mask |= DemandsAttention;
3655                 } else if ((xcb_atom_t)message->data.data32[i] == p->atom(_NET_WM_STATE_STAYS_ON_TOP)) {
3656                     mask |= KeepAbove;
3657                 } else if ((xcb_atom_t)message->data.data32[i] == p->atom(_NET_WM_STATE_FOCUSED)) {
3658                     mask |= Focused;
3659                 }
3660             }
3661 
3662             // when removing, we just leave newstate == 0
3663             switch (message->data.data32[0]) {
3664             case 1: // set
3665                 // to set... the change state should be the same as the mask
3666                 state = mask;
3667                 break;
3668 
3669             case 2: // toggle
3670                 // to toggle, we need to xor the current state with the new state
3671                 state = (p->state & mask) ^ mask;
3672                 break;
3673 
3674             default:
3675                 // to clear state, the new state should stay zero
3676                 ;
3677             }
3678 
3679 #ifdef NETWMDEBUG
3680             fprintf(stderr, "NETWinInfo::event: calling changeState(%lx, %lx)\n", state, mask);
3681 #endif
3682 
3683             changeState(state, mask);
3684         } else if (message->type == p->atom(_NET_WM_DESKTOP)) {
3685             dirty = WMDesktop;
3686 
3687             if (message->data.data32[0] == (unsigned)OnAllDesktops) {
3688                 changeDesktop(OnAllDesktops);
3689             } else {
3690                 changeDesktop(message->data.data32[0] + 1);
3691             }
3692         } else if (message->type == p->atom(_NET_WM_FULLSCREEN_MONITORS)) {
3693             dirty2 = WM2FullscreenMonitors;
3694 
3695             NETFullscreenMonitors topology;
3696             topology.top = message->data.data32[0];
3697             topology.bottom = message->data.data32[1];
3698             topology.left = message->data.data32[2];
3699             topology.right = message->data.data32[3];
3700 
3701 #ifdef NETWMDEBUG
3702             fprintf(stderr,
3703                     "NETWinInfo2::event: calling changeFullscreenMonitors"
3704                     "(%ld, %ld, %ld, %ld, %ld)\n",
3705                     message->window,
3706                     message->data.data32[0],
3707                     message->data.data32[1],
3708                     message->data.data32[2],
3709                     message->data.data32[3]);
3710 #endif
3711             changeFullscreenMonitors(topology);
3712         }
3713     }
3714 
3715     if (eventType == XCB_PROPERTY_NOTIFY) {
3716 #ifdef NETWMDEBUG
3717         fprintf(stderr, "NETWinInfo::event: handling PropertyNotify event\n");
3718 #endif
3719 
3720         xcb_property_notify_event_t *pe = reinterpret_cast<xcb_property_notify_event_t *>(event);
3721 
3722         if (pe->atom == p->atom(_NET_WM_NAME)) {
3723             dirty |= WMName;
3724         } else if (pe->atom == p->atom(_NET_WM_VISIBLE_NAME)) {
3725             dirty |= WMVisibleName;
3726         } else if (pe->atom == p->atom(_NET_WM_DESKTOP)) {
3727             dirty |= WMDesktop;
3728         } else if (pe->atom == p->atom(_NET_WM_WINDOW_TYPE)) {
3729             dirty |= WMWindowType;
3730         } else if (pe->atom == p->atom(_NET_WM_STATE)) {
3731             dirty |= WMState;
3732         } else if (pe->atom == p->atom(_NET_WM_STRUT)) {
3733             dirty |= WMStrut;
3734         } else if (pe->atom == p->atom(_NET_WM_STRUT_PARTIAL)) {
3735             dirty2 |= WM2ExtendedStrut;
3736         } else if (pe->atom == p->atom(_NET_WM_ICON_GEOMETRY)) {
3737             dirty |= WMIconGeometry;
3738         } else if (pe->atom == p->atom(_NET_WM_ICON)) {
3739             dirty |= WMIcon;
3740         } else if (pe->atom == p->atom(_NET_WM_PID)) {
3741             dirty |= WMPid;
3742         } else if (pe->atom == p->atom(_NET_WM_HANDLED_ICONS)) {
3743             dirty |= WMHandledIcons;
3744         } else if (pe->atom == p->atom(_NET_STARTUP_ID)) {
3745             dirty2 |= WM2StartupId;
3746         } else if (pe->atom == p->atom(_NET_WM_WINDOW_OPACITY)) {
3747             dirty2 |= WM2Opacity;
3748         } else if (pe->atom == p->atom(_NET_WM_ALLOWED_ACTIONS)) {
3749             dirty2 |= WM2AllowedActions;
3750         } else if (pe->atom == p->atom(WM_STATE)) {
3751             dirty |= XAWMState;
3752         } else if (pe->atom == p->atom(_NET_FRAME_EXTENTS)) {
3753             dirty |= WMFrameExtents;
3754         } else if (pe->atom == p->atom(_KDE_NET_WM_FRAME_STRUT)) {
3755             dirty |= WMFrameExtents;
3756         } else if (pe->atom == p->atom(_NET_WM_FRAME_OVERLAP)) {
3757             dirty2 |= WM2FrameOverlap;
3758         } else if (pe->atom == p->atom(_NET_WM_ICON_NAME)) {
3759             dirty |= WMIconName;
3760         } else if (pe->atom == p->atom(_NET_WM_VISIBLE_ICON_NAME)) {
3761             dirty |= WMVisibleIconName;
3762         } else if (pe->atom == p->atom(_NET_WM_USER_TIME)) {
3763             dirty2 |= WM2UserTime;
3764         } else if (pe->atom == XCB_ATOM_WM_HINTS) {
3765             dirty2 |= WM2GroupLeader;
3766             dirty2 |= WM2Urgency;
3767             dirty2 |= WM2Input;
3768             dirty2 |= WM2InitialMappingState;
3769             dirty2 |= WM2IconPixmap;
3770         } else if (pe->atom == XCB_ATOM_WM_TRANSIENT_FOR) {
3771             dirty2 |= WM2TransientFor;
3772         } else if (pe->atom == XCB_ATOM_WM_CLASS) {
3773             dirty2 |= WM2WindowClass;
3774         } else if (pe->atom == p->atom(WM_WINDOW_ROLE)) {
3775             dirty2 |= WM2WindowRole;
3776         } else if (pe->atom == XCB_ATOM_WM_CLIENT_MACHINE) {
3777             dirty2 |= WM2ClientMachine;
3778         } else if (pe->atom == p->atom(_KDE_NET_WM_ACTIVITIES)) {
3779             dirty2 |= WM2Activities;
3780         } else if (pe->atom == p->atom(_KDE_NET_WM_BLOCK_COMPOSITING) || pe->atom == p->atom(_NET_WM_BYPASS_COMPOSITOR)) {
3781             dirty2 |= WM2BlockCompositing;
3782         } else if (pe->atom == p->atom(_KDE_NET_WM_SHADOW)) {
3783             dirty2 |= WM2KDEShadow;
3784         } else if (pe->atom == p->atom(WM_PROTOCOLS)) {
3785             dirty2 |= WM2Protocols;
3786         } else if (pe->atom == p->atom(_NET_WM_OPAQUE_REGION)) {
3787             dirty2 |= WM2OpaqueRegion;
3788         } else if (pe->atom == p->atom(_KDE_NET_WM_DESKTOP_FILE)) {
3789             dirty2 = WM2DesktopFileName;
3790         } else if (pe->atom == p->atom(_NET_WM_FULLSCREEN_MONITORS)) {
3791             dirty2 = WM2FullscreenMonitors;
3792         } else if (pe->atom == p->atom(_GTK_FRAME_EXTENTS)) {
3793             dirty2 |= WM2GTKFrameExtents;
3794         } else if (pe->atom == p->atom(_KDE_NET_WM_APPMENU_SERVICE_NAME)) {
3795             dirty2 |= WM2AppMenuServiceName;
3796         } else if (pe->atom == p->atom(_KDE_NET_WM_APPMENU_OBJECT_PATH)) {
3797             dirty2 |= WM2AppMenuObjectPath;
3798         }
3799 
3800         do_update = true;
3801     } else if (eventType == XCB_CONFIGURE_NOTIFY) {
3802 #ifdef NETWMDEBUG
3803         fprintf(stderr, "NETWinInfo::event: handling ConfigureNotify event\n");
3804 #endif
3805 
3806         dirty |= WMGeometry;
3807 
3808         // update window geometry
3809         xcb_configure_notify_event_t *configure = reinterpret_cast<xcb_configure_notify_event_t *>(event);
3810         p->win_geom.pos.x = configure->x;
3811         p->win_geom.pos.y = configure->y;
3812         p->win_geom.size.width = configure->width;
3813         p->win_geom.size.height = configure->height;
3814     }
3815 
3816     if (do_update) {
3817         update(dirty, dirty2);
3818     }
3819 
3820     if (properties) {
3821         *properties = dirty;
3822     }
3823     if (properties2) {
3824         *properties2 = dirty2;
3825     }
3826 }
3827 
3828 #if KWINDOWSYSTEM_BUILD_DEPRECATED_SINCE(5, 0)
event(xcb_generic_event_t * ev,unsigned long * properties,int properties_size)3829 void NETWinInfo::event(xcb_generic_event_t *ev, unsigned long *properties, int properties_size)
3830 {
3831     NET::Properties p;
3832     NET::Properties2 p2;
3833     event(ev, &p, &p2);
3834     unsigned long props[PROPERTIES_SIZE] = {p, p2};
3835     assert(PROPERTIES_SIZE == 2); // add elements above
3836 
3837     if (properties_size > PROPERTIES_SIZE) {
3838         properties_size = PROPERTIES_SIZE;
3839     }
3840     for (int i = 0; i < properties_size; ++i) {
3841         properties[i] = props[i];
3842     }
3843 }
3844 #endif
3845 
updateWMState()3846 void NETWinInfo::updateWMState()
3847 {
3848     update(XAWMState);
3849 }
3850 
update(NET::Properties dirtyProperties,NET::Properties2 dirtyProperties2)3851 void NETWinInfo::update(NET::Properties dirtyProperties, NET::Properties2 dirtyProperties2)
3852 {
3853     Properties dirty = dirtyProperties & p->properties;
3854     Properties2 dirty2 = dirtyProperties2 & p->properties2;
3855 
3856     // We *always* want to update WM_STATE if set in dirty_props
3857     if (dirtyProperties & XAWMState) {
3858         dirty |= XAWMState;
3859     }
3860 
3861     xcb_get_property_cookie_t cookies[255];
3862     int c = 0;
3863 
3864     if (dirty & XAWMState) {
3865         cookies[c++] = xcb_get_property(p->conn, false, p->window, p->atom(WM_STATE), p->atom(WM_STATE), 0, 1);
3866     }
3867 
3868     if (dirty & WMState) {
3869         cookies[c++] = xcb_get_property(p->conn, false, p->window, p->atom(_NET_WM_STATE), XCB_ATOM_ATOM, 0, 2048);
3870     }
3871 
3872     if (dirty & WMDesktop) {
3873         cookies[c++] = xcb_get_property(p->conn, false, p->window, p->atom(_NET_WM_DESKTOP), XCB_ATOM_CARDINAL, 0, 1);
3874     }
3875 
3876     if (dirty & WMName) {
3877         cookies[c++] = xcb_get_property(p->conn, false, p->window, p->atom(_NET_WM_NAME), p->atom(UTF8_STRING), 0, MAX_PROP_SIZE);
3878     }
3879 
3880     if (dirty & WMVisibleName) {
3881         cookies[c++] = xcb_get_property(p->conn, false, p->window, p->atom(_NET_WM_VISIBLE_NAME), p->atom(UTF8_STRING), 0, MAX_PROP_SIZE);
3882     }
3883 
3884     if (dirty & WMIconName) {
3885         cookies[c++] = xcb_get_property(p->conn, false, p->window, p->atom(_NET_WM_ICON_NAME), p->atom(UTF8_STRING), 0, MAX_PROP_SIZE);
3886     }
3887 
3888     if (dirty & WMVisibleIconName) {
3889         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);
3890     }
3891 
3892     if (dirty & WMWindowType) {
3893         cookies[c++] = xcb_get_property(p->conn, false, p->window, p->atom(_NET_WM_WINDOW_TYPE), XCB_ATOM_ATOM, 0, 2048);
3894     }
3895 
3896     if (dirty & WMStrut) {
3897         cookies[c++] = xcb_get_property(p->conn, false, p->window, p->atom(_NET_WM_STRUT), XCB_ATOM_CARDINAL, 0, 4);
3898     }
3899 
3900     if (dirty2 & WM2ExtendedStrut) {
3901         cookies[c++] = xcb_get_property(p->conn, false, p->window, p->atom(_NET_WM_STRUT_PARTIAL), XCB_ATOM_CARDINAL, 0, 12);
3902     }
3903 
3904     if (dirty2 & WM2FullscreenMonitors) {
3905         cookies[c++] = xcb_get_property(p->conn, false, p->window, p->atom(_NET_WM_FULLSCREEN_MONITORS), XCB_ATOM_CARDINAL, 0, 4);
3906     }
3907 
3908     if (dirty & WMIconGeometry) {
3909         cookies[c++] = xcb_get_property(p->conn, false, p->window, p->atom(_NET_WM_ICON_GEOMETRY), XCB_ATOM_CARDINAL, 0, 4);
3910     }
3911 
3912     if (dirty & WMIcon) {
3913         cookies[c++] = xcb_get_property(p->conn, false, p->window, p->atom(_NET_WM_ICON), XCB_ATOM_CARDINAL, 0, 0xffffffff);
3914     }
3915 
3916     if (dirty & WMFrameExtents) {
3917         cookies[c++] = xcb_get_property(p->conn, false, p->window, p->atom(_NET_FRAME_EXTENTS), XCB_ATOM_CARDINAL, 0, 4);
3918         cookies[c++] = xcb_get_property(p->conn, false, p->window, p->atom(_KDE_NET_WM_FRAME_STRUT), XCB_ATOM_CARDINAL, 0, 4);
3919     }
3920 
3921     if (dirty2 & WM2FrameOverlap) {
3922         cookies[c++] = xcb_get_property(p->conn, false, p->window, p->atom(_NET_WM_FRAME_OVERLAP), XCB_ATOM_CARDINAL, 0, 4);
3923     }
3924 
3925     if (dirty2 & WM2Activities) {
3926         cookies[c++] = xcb_get_property(p->conn, false, p->window, p->atom(_KDE_NET_WM_ACTIVITIES), XCB_ATOM_STRING, 0, MAX_PROP_SIZE);
3927     }
3928 
3929     if (dirty2 & WM2BlockCompositing) {
3930         cookies[c++] = xcb_get_property(p->conn, false, p->window, p->atom(_KDE_NET_WM_BLOCK_COMPOSITING), XCB_ATOM_CARDINAL, 0, 1);
3931         cookies[c++] = xcb_get_property(p->conn, false, p->window, p->atom(_NET_WM_BYPASS_COMPOSITOR), XCB_ATOM_CARDINAL, 0, 1);
3932     }
3933 
3934     if (dirty & WMPid) {
3935         cookies[c++] = xcb_get_property(p->conn, false, p->window, p->atom(_NET_WM_PID), XCB_ATOM_CARDINAL, 0, 1);
3936     }
3937 
3938     if (dirty2 & WM2StartupId) {
3939         cookies[c++] = xcb_get_property(p->conn, false, p->window, p->atom(_NET_STARTUP_ID), p->atom(UTF8_STRING), 0, MAX_PROP_SIZE);
3940     }
3941 
3942     if (dirty2 & WM2Opacity) {
3943         cookies[c++] = xcb_get_property(p->conn, false, p->window, p->atom(_NET_WM_WINDOW_OPACITY), XCB_ATOM_CARDINAL, 0, 1);
3944     }
3945 
3946     if (dirty2 & WM2AllowedActions) {
3947         cookies[c++] = xcb_get_property(p->conn, false, p->window, p->atom(_NET_WM_ALLOWED_ACTIONS), XCB_ATOM_ATOM, 0, 2048);
3948     }
3949 
3950     if (dirty2 & WM2UserTime) {
3951         cookies[c++] = xcb_get_property(p->conn, false, p->window, p->atom(_NET_WM_USER_TIME), XCB_ATOM_CARDINAL, 0, 1);
3952     }
3953 
3954     if (dirty2 & WM2TransientFor) {
3955         cookies[c++] = xcb_get_property(p->conn, false, p->window, XCB_ATOM_WM_TRANSIENT_FOR, XCB_ATOM_WINDOW, 0, 1);
3956     }
3957 
3958     if (dirty2 & (WM2GroupLeader | WM2Urgency | WM2Input | WM2InitialMappingState | WM2IconPixmap)) {
3959         cookies[c++] = xcb_get_property(p->conn, false, p->window, XCB_ATOM_WM_HINTS, XCB_ATOM_WM_HINTS, 0, 9);
3960     }
3961 
3962     if (dirty2 & WM2WindowClass) {
3963         cookies[c++] = xcb_get_property(p->conn, false, p->window, XCB_ATOM_WM_CLASS, XCB_ATOM_STRING, 0, MAX_PROP_SIZE);
3964     }
3965 
3966     if (dirty2 & WM2WindowRole) {
3967         cookies[c++] = xcb_get_property(p->conn, false, p->window, p->atom(WM_WINDOW_ROLE), XCB_ATOM_STRING, 0, MAX_PROP_SIZE);
3968     }
3969 
3970     if (dirty2 & WM2ClientMachine) {
3971         cookies[c++] = xcb_get_property(p->conn, false, p->window, XCB_ATOM_WM_CLIENT_MACHINE, XCB_ATOM_STRING, 0, MAX_PROP_SIZE);
3972     }
3973 
3974     if (dirty2 & WM2Protocols) {
3975         cookies[c++] = xcb_get_property(p->conn, false, p->window, p->atom(WM_PROTOCOLS), XCB_ATOM_ATOM, 0, 2048);
3976     }
3977 
3978     if (dirty2 & WM2OpaqueRegion) {
3979         cookies[c++] = xcb_get_property(p->conn, false, p->window, p->atom(_NET_WM_OPAQUE_REGION), XCB_ATOM_CARDINAL, 0, MAX_PROP_SIZE);
3980     }
3981 
3982     if (dirty2 & WM2DesktopFileName) {
3983         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);
3984     }
3985 
3986     if (dirty2 & WM2GTKFrameExtents) {
3987         cookies[c++] = xcb_get_property(p->conn, false, p->window, p->atom(_GTK_FRAME_EXTENTS), XCB_ATOM_CARDINAL, 0, 4);
3988     }
3989 
3990     if (dirty2 & WM2AppMenuObjectPath) {
3991         cookies[c++] = xcb_get_property(p->conn, false, p->window, p->atom(_KDE_NET_WM_APPMENU_OBJECT_PATH), XCB_ATOM_STRING, 0, MAX_PROP_SIZE);
3992     }
3993 
3994     if (dirty2 & WM2AppMenuServiceName) {
3995         cookies[c++] = xcb_get_property(p->conn, false, p->window, p->atom(_KDE_NET_WM_APPMENU_SERVICE_NAME), XCB_ATOM_STRING, 0, MAX_PROP_SIZE);
3996     }
3997 
3998     c = 0;
3999 
4000     if (dirty & XAWMState) {
4001         p->mapping_state = Withdrawn;
4002 
4003         bool success;
4004         uint32_t state = get_value_reply<uint32_t>(p->conn, cookies[c++], p->atom(WM_STATE), 0, &success);
4005 
4006         if (success) {
4007             switch (state) {
4008             case 3: // IconicState
4009                 p->mapping_state = Iconic;
4010                 break;
4011 
4012             case 1: // NormalState
4013                 p->mapping_state = Visible;
4014                 break;
4015 
4016             case 0: // WithdrawnState
4017             default:
4018                 p->mapping_state = Withdrawn;
4019                 break;
4020             }
4021 
4022             p->mapping_state_dirty = false;
4023         }
4024     }
4025 
4026     if (dirty & WMState) {
4027         p->state = NET::States();
4028         const QVector<xcb_atom_t> states = get_array_reply<xcb_atom_t>(p->conn, cookies[c++], XCB_ATOM_ATOM);
4029 
4030 #ifdef NETWMDEBUG
4031         fprintf(stderr, "NETWinInfo::update: updating window state (%ld)\n", states.count());
4032 #endif
4033 
4034         for (const xcb_atom_t state : states) {
4035 #ifdef NETWMDEBUG
4036             const QByteArray ba = get_atom_name(p->conn, state);
4037             fprintf(stderr, "NETWinInfo::update:   adding window state %ld '%s'\n", state, ba.constData());
4038 #endif
4039             if (state == p->atom(_NET_WM_STATE_MODAL)) {
4040                 p->state |= Modal;
4041             }
4042 
4043             else if (state == p->atom(_NET_WM_STATE_STICKY)) {
4044                 p->state |= Sticky;
4045             }
4046 
4047             else if (state == p->atom(_NET_WM_STATE_MAXIMIZED_VERT)) {
4048                 p->state |= MaxVert;
4049             }
4050 
4051             else if (state == p->atom(_NET_WM_STATE_MAXIMIZED_HORZ)) {
4052                 p->state |= MaxHoriz;
4053             }
4054 
4055             else if (state == p->atom(_NET_WM_STATE_SHADED)) {
4056                 p->state |= Shaded;
4057             }
4058 
4059             else if (state == p->atom(_NET_WM_STATE_SKIP_TASKBAR)) {
4060                 p->state |= SkipTaskbar;
4061             }
4062 
4063             else if (state == p->atom(_NET_WM_STATE_SKIP_PAGER)) {
4064                 p->state |= SkipPager;
4065             }
4066 
4067             else if (state == p->atom(_KDE_NET_WM_STATE_SKIP_SWITCHER)) {
4068                 p->state |= SkipSwitcher;
4069             }
4070 
4071             else if (state == p->atom(_NET_WM_STATE_HIDDEN)) {
4072                 p->state |= Hidden;
4073             }
4074 
4075             else if (state == p->atom(_NET_WM_STATE_FULLSCREEN)) {
4076                 p->state |= FullScreen;
4077             }
4078 
4079             else if (state == p->atom(_NET_WM_STATE_ABOVE)) {
4080                 p->state |= KeepAbove;
4081             }
4082 
4083             else if (state == p->atom(_NET_WM_STATE_BELOW)) {
4084                 p->state |= KeepBelow;
4085             }
4086 
4087             else if (state == p->atom(_NET_WM_STATE_DEMANDS_ATTENTION)) {
4088                 p->state |= DemandsAttention;
4089             }
4090 
4091             else if (state == p->atom(_NET_WM_STATE_STAYS_ON_TOP)) {
4092                 p->state |= KeepAbove;
4093             }
4094 
4095             else if (state == p->atom(_NET_WM_STATE_FOCUSED)) {
4096                 p->state |= Focused;
4097             }
4098         }
4099     }
4100 
4101     if (dirty & WMDesktop) {
4102         p->desktop = 0;
4103 
4104         bool success;
4105         uint32_t desktop = get_value_reply<uint32_t>(p->conn, cookies[c++], XCB_ATOM_CARDINAL, 0, &success);
4106 
4107         if (success) {
4108             if (desktop != 0xffffffff) {
4109                 p->desktop = desktop + 1;
4110             } else {
4111                 p->desktop = OnAllDesktops;
4112             }
4113         }
4114     }
4115 
4116     if (dirty & WMName) {
4117         delete[] p->name;
4118         p->name = nullptr;
4119 
4120         const QByteArray str = get_string_reply(p->conn, cookies[c++], p->atom(UTF8_STRING));
4121         if (str.length() > 0) {
4122             p->name = nstrndup(str.constData(), str.length());
4123         }
4124     }
4125 
4126     if (dirty & WMVisibleName) {
4127         delete[] p->visible_name;
4128         p->visible_name = nullptr;
4129 
4130         const QByteArray str = get_string_reply(p->conn, cookies[c++], p->atom(UTF8_STRING));
4131         if (str.length() > 0) {
4132             p->visible_name = nstrndup(str.constData(), str.length());
4133         }
4134     }
4135 
4136     if (dirty & WMIconName) {
4137         delete[] p->icon_name;
4138         p->icon_name = nullptr;
4139 
4140         const QByteArray str = get_string_reply(p->conn, cookies[c++], p->atom(UTF8_STRING));
4141         if (str.length() > 0) {
4142             p->icon_name = nstrndup(str.constData(), str.length());
4143         }
4144     }
4145 
4146     if (dirty & WMVisibleIconName) {
4147         delete[] p->visible_icon_name;
4148         p->visible_icon_name = nullptr;
4149 
4150         const QByteArray str = get_string_reply(p->conn, cookies[c++], p->atom(UTF8_STRING));
4151         if (str.length() > 0) {
4152             p->visible_icon_name = nstrndup(str.constData(), str.length());
4153         }
4154     }
4155 
4156     if (dirty & WMWindowType) {
4157         p->types.reset();
4158         p->types[0] = Unknown;
4159         p->has_net_support = false;
4160 
4161         const QVector<xcb_atom_t> types = get_array_reply<xcb_atom_t>(p->conn, cookies[c++], XCB_ATOM_ATOM);
4162 
4163         if (!types.isEmpty()) {
4164 #ifdef NETWMDEBUG
4165             fprintf(stderr, "NETWinInfo::update: getting window type (%ld)\n", types.count());
4166 #endif
4167             p->has_net_support = true;
4168             int pos = 0;
4169 
4170             for (const xcb_atom_t type : types) {
4171 #ifdef NETWMDEBUG
4172                 const QByteArray name = get_atom_name(p->conn, type);
4173                 fprintf(stderr, "NETWinInfo::update:   examining window type %ld %s\n", type, name.constData());
4174 #endif
4175                 if (type == p->atom(_NET_WM_WINDOW_TYPE_NORMAL)) {
4176                     p->types[pos++] = Normal;
4177                 }
4178 
4179                 else if (type == p->atom(_NET_WM_WINDOW_TYPE_DESKTOP)) {
4180                     p->types[pos++] = Desktop;
4181                 }
4182 
4183                 else if (type == p->atom(_NET_WM_WINDOW_TYPE_DOCK)) {
4184                     p->types[pos++] = Dock;
4185                 }
4186 
4187                 else if (type == p->atom(_NET_WM_WINDOW_TYPE_TOOLBAR)) {
4188                     p->types[pos++] = Toolbar;
4189                 }
4190 
4191                 else if (type == p->atom(_NET_WM_WINDOW_TYPE_MENU)) {
4192                     p->types[pos++] = Menu;
4193                 }
4194 
4195                 else if (type == p->atom(_NET_WM_WINDOW_TYPE_DIALOG)) {
4196                     p->types[pos++] = Dialog;
4197                 }
4198 
4199                 else if (type == p->atom(_NET_WM_WINDOW_TYPE_UTILITY)) {
4200                     p->types[pos++] = Utility;
4201                 }
4202 
4203                 else if (type == p->atom(_NET_WM_WINDOW_TYPE_SPLASH)) {
4204                     p->types[pos++] = Splash;
4205                 }
4206 
4207                 else if (type == p->atom(_NET_WM_WINDOW_TYPE_DROPDOWN_MENU)) {
4208                     p->types[pos++] = DropdownMenu;
4209                 }
4210 
4211                 else if (type == p->atom(_NET_WM_WINDOW_TYPE_POPUP_MENU)) {
4212                     p->types[pos++] = PopupMenu;
4213                 }
4214 
4215                 else if (type == p->atom(_NET_WM_WINDOW_TYPE_TOOLTIP)) {
4216                     p->types[pos++] = Tooltip;
4217                 }
4218 
4219                 else if (type == p->atom(_NET_WM_WINDOW_TYPE_NOTIFICATION)) {
4220                     p->types[pos++] = Notification;
4221                 }
4222 
4223                 else if (type == p->atom(_NET_WM_WINDOW_TYPE_COMBO)) {
4224                     p->types[pos++] = ComboBox;
4225                 }
4226 
4227                 else if (type == p->atom(_NET_WM_WINDOW_TYPE_DND)) {
4228                     p->types[pos++] = DNDIcon;
4229                 }
4230 
4231                 else if (type == p->atom(_KDE_NET_WM_WINDOW_TYPE_OVERRIDE)) {
4232                     p->types[pos++] = Override;
4233                 }
4234 
4235                 else if (type == p->atom(_KDE_NET_WM_WINDOW_TYPE_TOPMENU)) {
4236                     p->types[pos++] = TopMenu;
4237                 }
4238 
4239                 else if (type == p->atom(_KDE_NET_WM_WINDOW_TYPE_ON_SCREEN_DISPLAY)) {
4240                     p->types[pos++] = OnScreenDisplay;
4241                 }
4242 
4243                 else if (type == p->atom(_KDE_NET_WM_WINDOW_TYPE_CRITICAL_NOTIFICATION)) {
4244                     p->types[pos++] = CriticalNotification;
4245                 }
4246             }
4247         }
4248     }
4249 
4250     if (dirty & WMStrut) {
4251         p->strut = NETStrut();
4252 
4253         QVector<uint32_t> data = get_array_reply<uint32_t>(p->conn, cookies[c++], XCB_ATOM_CARDINAL);
4254         if (data.count() == 4) {
4255             p->strut.left = data[0];
4256             p->strut.right = data[1];
4257             p->strut.top = data[2];
4258             p->strut.bottom = data[3];
4259         }
4260     }
4261 
4262     if (dirty2 & WM2ExtendedStrut) {
4263         p->extended_strut = NETExtendedStrut();
4264 
4265         QVector<uint32_t> data = get_array_reply<uint32_t>(p->conn, cookies[c++], XCB_ATOM_CARDINAL);
4266         if (data.count() == 12) {
4267             p->extended_strut.left_width = data[0];
4268             p->extended_strut.right_width = data[1];
4269             p->extended_strut.top_width = data[2];
4270             p->extended_strut.bottom_width = data[3];
4271             p->extended_strut.left_start = data[4];
4272             p->extended_strut.left_end = data[5];
4273             p->extended_strut.right_start = data[6];
4274             p->extended_strut.right_end = data[7];
4275             p->extended_strut.top_start = data[8];
4276             p->extended_strut.top_end = data[9];
4277             p->extended_strut.bottom_start = data[10];
4278             p->extended_strut.bottom_end = data[11];
4279         }
4280     }
4281 
4282     if (dirty2 & WM2FullscreenMonitors) {
4283         p->fullscreen_monitors = NETFullscreenMonitors();
4284 
4285         QVector<uint32_t> data = get_array_reply<uint32_t>(p->conn, cookies[c++], XCB_ATOM_CARDINAL);
4286         if (data.count() == 4) {
4287             p->fullscreen_monitors.top = data[0];
4288             p->fullscreen_monitors.bottom = data[1];
4289             p->fullscreen_monitors.left = data[2];
4290             p->fullscreen_monitors.right = data[3];
4291         }
4292     }
4293 
4294     if (dirty & WMIconGeometry) {
4295         p->icon_geom = NETRect();
4296 
4297         QVector<uint32_t> data = get_array_reply<uint32_t>(p->conn, cookies[c++], XCB_ATOM_CARDINAL);
4298         if (data.count() == 4) {
4299             p->icon_geom.pos.x = data[0];
4300             p->icon_geom.pos.y = data[1];
4301             p->icon_geom.size.width = data[2];
4302             p->icon_geom.size.height = data[3];
4303         }
4304     }
4305 
4306     if (dirty & WMIcon) {
4307         readIcon(p->conn, cookies[c++], p->icons, p->icon_count);
4308         delete[] p->icon_sizes;
4309         p->icon_sizes = nullptr;
4310     }
4311 
4312     if (dirty & WMFrameExtents) {
4313         p->frame_strut = NETStrut();
4314 
4315         QVector<uint32_t> data = get_array_reply<uint32_t>(p->conn, cookies[c++], XCB_ATOM_CARDINAL);
4316 
4317         if (data.isEmpty()) {
4318             data = get_array_reply<uint32_t>(p->conn, cookies[c++], XCB_ATOM_CARDINAL);
4319         } else {
4320             xcb_discard_reply(p->conn, cookies[c++].sequence);
4321         }
4322 
4323         if (data.count() == 4) {
4324             p->frame_strut.left = data[0];
4325             p->frame_strut.right = data[1];
4326             p->frame_strut.top = data[2];
4327             p->frame_strut.bottom = data[3];
4328         }
4329     }
4330 
4331     if (dirty2 & WM2FrameOverlap) {
4332         p->frame_overlap = NETStrut();
4333 
4334         QVector<uint32_t> data = get_array_reply<uint32_t>(p->conn, cookies[c++], XCB_ATOM_CARDINAL);
4335         if (data.count() == 4) {
4336             p->frame_overlap.left = data[0];
4337             p->frame_overlap.right = data[1];
4338             p->frame_overlap.top = data[2];
4339             p->frame_overlap.bottom = data[3];
4340         }
4341     }
4342 
4343     if (dirty2 & WM2Activities) {
4344         delete[] p->activities;
4345         p->activities = nullptr;
4346 
4347         const QByteArray activities = get_string_reply(p->conn, cookies[c++], XCB_ATOM_STRING);
4348         if (activities.length() > 0) {
4349             p->activities = nstrndup(activities.constData(), activities.length());
4350         }
4351     }
4352 
4353     if (dirty2 & WM2BlockCompositing) {
4354         bool success;
4355         p->blockCompositing = false;
4356 
4357         // _KDE_NET_WM_BLOCK_COMPOSITING
4358         uint32_t data = get_value_reply<uint32_t>(p->conn, cookies[c++], XCB_ATOM_CARDINAL, 0, &success);
4359         if (success) {
4360             p->blockCompositing = bool(data);
4361         }
4362 
4363         // _NET_WM_BYPASS_COMPOSITOR
4364         data = get_value_reply<uint32_t>(p->conn, cookies[c++], XCB_ATOM_CARDINAL, 0, &success);
4365         if (success) {
4366             switch (data) {
4367             case 1:
4368                 p->blockCompositing = true;
4369                 break;
4370             case 2:
4371                 p->blockCompositing = false;
4372                 break;
4373             default:
4374                 break; // yes, the standard /is/ that stupid.
4375             }
4376         }
4377     }
4378 
4379     if (dirty & WMPid) {
4380         p->pid = get_value_reply<uint32_t>(p->conn, cookies[c++], XCB_ATOM_CARDINAL, 0);
4381     }
4382 
4383     if (dirty2 & WM2StartupId) {
4384         delete[] p->startup_id;
4385         p->startup_id = nullptr;
4386 
4387         const QByteArray id = get_string_reply(p->conn, cookies[c++], p->atom(UTF8_STRING));
4388         if (id.length() > 0) {
4389             p->startup_id = nstrndup(id.constData(), id.length());
4390         }
4391     }
4392 
4393     if (dirty2 & WM2Opacity) {
4394         p->opacity = get_value_reply<uint32_t>(p->conn, cookies[c++], XCB_ATOM_CARDINAL, 0xffffffff);
4395     }
4396 
4397     if (dirty2 & WM2AllowedActions) {
4398         p->allowed_actions = NET::Actions();
4399 
4400         const QVector<xcb_atom_t> actions = get_array_reply<xcb_atom_t>(p->conn, cookies[c++], XCB_ATOM_ATOM);
4401         if (!actions.isEmpty()) {
4402 #ifdef NETWMDEBUG
4403             fprintf(stderr, "NETWinInfo::update: updating allowed actions (%ld)\n", actions.count());
4404 #endif
4405 
4406             for (const xcb_atom_t action : actions) {
4407 #ifdef NETWMDEBUG
4408                 const QByteArray name = get_atom_name(p->conn, action);
4409                 fprintf(stderr, "NETWinInfo::update:   adding allowed action %ld '%s'\n", action, name.constData());
4410 #endif
4411                 if (action == p->atom(_NET_WM_ACTION_MOVE)) {
4412                     p->allowed_actions |= ActionMove;
4413                 }
4414 
4415                 else if (action == p->atom(_NET_WM_ACTION_RESIZE)) {
4416                     p->allowed_actions |= ActionResize;
4417                 }
4418 
4419                 else if (action == p->atom(_NET_WM_ACTION_MINIMIZE)) {
4420                     p->allowed_actions |= ActionMinimize;
4421                 }
4422 
4423                 else if (action == p->atom(_NET_WM_ACTION_SHADE)) {
4424                     p->allowed_actions |= ActionShade;
4425                 }
4426 
4427                 else if (action == p->atom(_NET_WM_ACTION_STICK)) {
4428                     p->allowed_actions |= ActionStick;
4429                 }
4430 
4431                 else if (action == p->atom(_NET_WM_ACTION_MAXIMIZE_VERT)) {
4432                     p->allowed_actions |= ActionMaxVert;
4433                 }
4434 
4435                 else if (action == p->atom(_NET_WM_ACTION_MAXIMIZE_HORZ)) {
4436                     p->allowed_actions |= ActionMaxHoriz;
4437                 }
4438 
4439                 else if (action == p->atom(_NET_WM_ACTION_FULLSCREEN)) {
4440                     p->allowed_actions |= ActionFullScreen;
4441                 }
4442 
4443                 else if (action == p->atom(_NET_WM_ACTION_CHANGE_DESKTOP)) {
4444                     p->allowed_actions |= ActionChangeDesktop;
4445                 }
4446 
4447                 else if (action == p->atom(_NET_WM_ACTION_CLOSE)) {
4448                     p->allowed_actions |= ActionClose;
4449                 }
4450             }
4451         }
4452     }
4453 
4454     if (dirty2 & WM2UserTime) {
4455         p->user_time = -1U;
4456 
4457         bool success;
4458         uint32_t value = get_value_reply<uint32_t>(p->conn, cookies[c++], XCB_ATOM_CARDINAL, 0, &success);
4459 
4460         if (success) {
4461             p->user_time = value;
4462         }
4463     }
4464 
4465     if (dirty2 & WM2TransientFor) {
4466         p->transient_for = get_value_reply<xcb_window_t>(p->conn, cookies[c++], XCB_ATOM_WINDOW, 0);
4467     }
4468 
4469     if (dirty2 & (WM2GroupLeader | WM2Urgency | WM2Input | WM2InitialMappingState | WM2IconPixmap)) {
4470         xcb_get_property_reply_t *reply = xcb_get_property_reply(p->conn, cookies[c++], nullptr);
4471 
4472         if (reply && reply->format == 32 && reply->value_len == 9 && reply->type == XCB_ATOM_WM_HINTS) {
4473             kde_wm_hints *hints = reinterpret_cast<kde_wm_hints *>(xcb_get_property_value(reply));
4474 
4475             if (hints->flags & (1 << 0) /*Input*/) {
4476                 p->input = hints->input;
4477             }
4478             if (hints->flags & (1 << 1) /*StateHint*/) {
4479                 switch (hints->initial_state) {
4480                 case 3: // IconicState
4481                     p->initialMappingState = Iconic;
4482                     break;
4483 
4484                 case 1: // NormalState
4485                     p->initialMappingState = Visible;
4486                     break;
4487 
4488                 case 0: // WithdrawnState
4489                 default:
4490                     p->initialMappingState = Withdrawn;
4491                     break;
4492                 }
4493             }
4494             if (hints->flags & (1 << 2) /*IconPixmapHint*/) {
4495                 p->icon_pixmap = hints->icon_pixmap;
4496             }
4497             if (hints->flags & (1 << 5) /*IconMaskHint*/) {
4498                 p->icon_mask = hints->icon_mask;
4499             }
4500             if (hints->flags & (1 << 6) /*WindowGroupHint*/) {
4501                 p->window_group = hints->window_group;
4502             }
4503             p->urgency = (hints->flags & (1 << 8) /*UrgencyHint*/);
4504         }
4505 
4506         if (reply) {
4507             free(reply);
4508         }
4509     }
4510 
4511     if (dirty2 & WM2WindowClass) {
4512         delete[] p->class_name;
4513         delete[] p->class_class;
4514         p->class_name = nullptr;
4515         p->class_class = nullptr;
4516 
4517         const QList<QByteArray> list = get_stringlist_reply(p->conn, cookies[c++], XCB_ATOM_STRING);
4518         if (list.count() == 2) {
4519             p->class_name = nstrdup(list.at(0).constData());
4520             p->class_class = nstrdup(list.at(1).constData());
4521         } else if (list.count() == 1) { // Not fully compliant client. Provides a single string
4522             p->class_name = nstrdup(list.at(0).constData());
4523             p->class_class = nstrdup(list.at(0).constData());
4524         }
4525     }
4526 
4527     if (dirty2 & WM2WindowRole) {
4528         delete[] p->window_role;
4529         p->window_role = nullptr;
4530 
4531         const QByteArray role = get_string_reply(p->conn, cookies[c++], XCB_ATOM_STRING);
4532         if (role.length() > 0) {
4533             p->window_role = nstrndup(role.constData(), role.length());
4534         }
4535     }
4536 
4537     if (dirty2 & WM2ClientMachine) {
4538         delete[] p->client_machine;
4539         p->client_machine = nullptr;
4540 
4541         const QByteArray value = get_string_reply(p->conn, cookies[c++], XCB_ATOM_STRING);
4542         if (value.length() > 0) {
4543             p->client_machine = nstrndup(value.constData(), value.length());
4544         }
4545     }
4546 
4547     if (dirty2 & WM2Protocols) {
4548         const QVector<xcb_atom_t> protocols = get_array_reply<xcb_atom_t>(p->conn, cookies[c++], XCB_ATOM_ATOM);
4549         p->protocols = NET::NoProtocol;
4550         for (auto it = protocols.begin(); it != protocols.end(); ++it) {
4551             if ((*it) == p->atom(WM_TAKE_FOCUS)) {
4552                 p->protocols |= TakeFocusProtocol;
4553             } else if ((*it) == p->atom(WM_DELETE_WINDOW)) {
4554                 p->protocols |= DeleteWindowProtocol;
4555             } else if ((*it) == p->atom(_NET_WM_PING)) {
4556                 p->protocols |= PingProtocol;
4557             } else if ((*it) == p->atom(_NET_WM_SYNC_REQUEST)) {
4558                 p->protocols |= SyncRequestProtocol;
4559             } else if ((*it) == p->atom(_NET_WM_CONTEXT_HELP)) {
4560                 p->protocols |= ContextHelpProtocol;
4561             }
4562         }
4563     }
4564 
4565     if (dirty2 & WM2OpaqueRegion) {
4566         const QVector<qint32> values = get_array_reply<qint32>(p->conn, cookies[c++], XCB_ATOM_CARDINAL);
4567         p->opaqueRegion.clear();
4568         p->opaqueRegion.reserve(values.count() / 4);
4569         for (int i = 0; i < values.count() - 3; i += 4) {
4570             NETRect rect;
4571             rect.pos.x = values.at(i);
4572             rect.pos.y = values.at(i + 1);
4573             rect.size.width = values.at(i + 2);
4574             rect.size.height = values.at(i + 3);
4575             p->opaqueRegion.push_back(rect);
4576         }
4577     }
4578 
4579     if (dirty2 & WM2DesktopFileName) {
4580         delete[] p->desktop_file;
4581         p->desktop_file = nullptr;
4582 
4583         const QByteArray id = get_string_reply(p->conn, cookies[c++], p->atom(UTF8_STRING));
4584         if (id.length() > 0) {
4585             p->desktop_file = nstrndup(id.constData(), id.length());
4586         }
4587     }
4588 
4589     if (dirty2 & WM2GTKFrameExtents) {
4590         p->gtk_frame_extents = NETStrut();
4591 
4592         QVector<uint32_t> data = get_array_reply<uint32_t>(p->conn, cookies[c++], XCB_ATOM_CARDINAL);
4593         if (data.count() == 4) {
4594             p->gtk_frame_extents.left = data[0];
4595             p->gtk_frame_extents.right = data[1];
4596             p->gtk_frame_extents.top = data[2];
4597             p->gtk_frame_extents.bottom = data[3];
4598         }
4599     }
4600 
4601     if (dirty2 & WM2AppMenuObjectPath) {
4602         delete[] p->appmenu_object_path;
4603         p->appmenu_object_path = nullptr;
4604 
4605         const QByteArray id = get_string_reply(p->conn, cookies[c++], XCB_ATOM_STRING);
4606         if (id.length() > 0) {
4607             p->appmenu_object_path = nstrndup(id.constData(), id.length());
4608         }
4609     }
4610 
4611     if (dirty2 & WM2AppMenuServiceName) {
4612         delete[] p->appmenu_service_name;
4613         p->appmenu_service_name = nullptr;
4614 
4615         const QByteArray id = get_string_reply(p->conn, cookies[c++], XCB_ATOM_STRING);
4616         if (id.length() > 0) {
4617             p->appmenu_service_name = nstrndup(id.constData(), id.length());
4618         }
4619     }
4620 }
4621 
iconGeometry() const4622 NETRect NETWinInfo::iconGeometry() const
4623 {
4624     return p->icon_geom;
4625 }
4626 
state() const4627 NET::States NETWinInfo::state() const
4628 {
4629     return p->state;
4630 }
4631 
strut() const4632 NETStrut NETWinInfo::strut() const
4633 {
4634     return p->strut;
4635 }
4636 
extendedStrut() const4637 NETExtendedStrut NETWinInfo::extendedStrut() const
4638 {
4639     return p->extended_strut;
4640 }
4641 
fullscreenMonitors() const4642 NETFullscreenMonitors NETWinInfo::fullscreenMonitors() const
4643 {
4644     return p->fullscreen_monitors;
4645 }
4646 
typeMatchesMask(WindowType type,WindowTypes mask)4647 bool NET::typeMatchesMask(WindowType type, WindowTypes mask)
4648 {
4649     switch (type) {
4650         // clang-format off
4651 #define CHECK_TYPE_MASK( type ) \
4652 case type: \
4653     if( mask & type##Mask ) \
4654         return true; \
4655     break;
4656         // clang-format on
4657         CHECK_TYPE_MASK(Normal)
4658         CHECK_TYPE_MASK(Desktop)
4659         CHECK_TYPE_MASK(Dock)
4660         CHECK_TYPE_MASK(Toolbar)
4661         CHECK_TYPE_MASK(Menu)
4662         CHECK_TYPE_MASK(Dialog)
4663         CHECK_TYPE_MASK(Override)
4664         CHECK_TYPE_MASK(TopMenu)
4665         CHECK_TYPE_MASK(Utility)
4666         CHECK_TYPE_MASK(Splash)
4667         CHECK_TYPE_MASK(DropdownMenu)
4668         CHECK_TYPE_MASK(PopupMenu)
4669         CHECK_TYPE_MASK(Tooltip)
4670         CHECK_TYPE_MASK(Notification)
4671         CHECK_TYPE_MASK(ComboBox)
4672         CHECK_TYPE_MASK(DNDIcon)
4673         CHECK_TYPE_MASK(OnScreenDisplay)
4674         CHECK_TYPE_MASK(CriticalNotification)
4675 #undef CHECK_TYPE_MASK
4676     default:
4677         break;
4678     }
4679     return false;
4680 }
4681 
windowType(WindowTypes supported_types) const4682 NET::WindowType NETWinInfo::windowType(WindowTypes supported_types) const
4683 {
4684     for (int i = 0; i < p->types.size(); ++i) {
4685         // return the type only if the application supports it
4686         if (typeMatchesMask(p->types[i], supported_types)) {
4687             return p->types[i];
4688         }
4689     }
4690     return Unknown;
4691 }
4692 
hasWindowType() const4693 bool NETWinInfo::hasWindowType() const
4694 {
4695     return p->types.size() > 0;
4696 }
4697 
name() const4698 const char *NETWinInfo::name() const
4699 {
4700     return p->name;
4701 }
4702 
visibleName() const4703 const char *NETWinInfo::visibleName() const
4704 {
4705     return p->visible_name;
4706 }
4707 
iconName() const4708 const char *NETWinInfo::iconName() const
4709 {
4710     return p->icon_name;
4711 }
4712 
visibleIconName() const4713 const char *NETWinInfo::visibleIconName() const
4714 {
4715     return p->visible_icon_name;
4716 }
4717 
desktop(bool ignore_viewport) const4718 int NETWinInfo::desktop(bool ignore_viewport) const
4719 {
4720     if (!ignore_viewport && KWindowSystem::mapViewport()) {
4721         const KWindowInfo info(p->window, NET::WMDesktop);
4722         return info.desktop();
4723     }
4724     return p->desktop;
4725 }
4726 
pid() const4727 int NETWinInfo::pid() const
4728 {
4729     return p->pid;
4730 }
4731 
userTime() const4732 xcb_timestamp_t NETWinInfo::userTime() const
4733 {
4734     return p->user_time;
4735 }
4736 
startupId() const4737 const char *NETWinInfo::startupId() const
4738 {
4739     return p->startup_id;
4740 }
4741 
opacity() const4742 unsigned long NETWinInfo::opacity() const
4743 {
4744     return p->opacity;
4745 }
4746 
opacityF() const4747 qreal NETWinInfo::opacityF() const
4748 {
4749     if (p->opacity == 0xffffffff) {
4750         return 1.0;
4751     }
4752     return p->opacity * 1.0 / 0xffffffff;
4753 }
4754 
allowedActions() const4755 NET::Actions NETWinInfo::allowedActions() const
4756 {
4757     return p->allowed_actions;
4758 }
4759 
hasNETSupport() const4760 bool NETWinInfo::hasNETSupport() const
4761 {
4762     return p->has_net_support;
4763 }
4764 
transientFor() const4765 xcb_window_t NETWinInfo::transientFor() const
4766 {
4767     return p->transient_for;
4768 }
4769 
groupLeader() const4770 xcb_window_t NETWinInfo::groupLeader() const
4771 {
4772     return p->window_group;
4773 }
4774 
urgency() const4775 bool NETWinInfo::urgency() const
4776 {
4777     return p->urgency;
4778 }
4779 
input() const4780 bool NETWinInfo::input() const
4781 {
4782     return p->input;
4783 }
4784 
initialMappingState() const4785 NET::MappingState NETWinInfo::initialMappingState() const
4786 {
4787     return p->initialMappingState;
4788 }
4789 
icccmIconPixmap() const4790 xcb_pixmap_t NETWinInfo::icccmIconPixmap() const
4791 {
4792     return p->icon_pixmap;
4793 }
4794 
icccmIconPixmapMask() const4795 xcb_pixmap_t NETWinInfo::icccmIconPixmapMask() const
4796 {
4797     return p->icon_mask;
4798 }
4799 
windowClassClass() const4800 const char *NETWinInfo::windowClassClass() const
4801 {
4802     return p->class_class;
4803 }
4804 
windowClassName() const4805 const char *NETWinInfo::windowClassName() const
4806 {
4807     return p->class_name;
4808 }
4809 
windowRole() const4810 const char *NETWinInfo::windowRole() const
4811 {
4812     return p->window_role;
4813 }
4814 
clientMachine() const4815 const char *NETWinInfo::clientMachine() const
4816 {
4817     return p->client_machine;
4818 }
4819 
activities() const4820 const char *NETWinInfo::activities() const
4821 {
4822     return p->activities;
4823 }
4824 
setActivities(const char * activities)4825 void NETWinInfo::setActivities(const char *activities)
4826 {
4827     delete[] p->activities;
4828 
4829     if (activities == (char *)nullptr || activities[0] == '\0') {
4830         // on all activities
4831         static const char nulluuid[] = KDE_ALL_ACTIVITIES_UUID;
4832 
4833         p->activities = nstrdup(nulluuid);
4834 
4835     } else {
4836         p->activities = nstrdup(activities);
4837     }
4838 
4839     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);
4840 }
4841 
setBlockingCompositing(bool active)4842 void NETWinInfo::setBlockingCompositing(bool active)
4843 {
4844     if (p->role != Client) {
4845         return;
4846     }
4847 
4848     p->blockCompositing = active;
4849     if (active) {
4850         uint32_t d = 1;
4851         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);
4852         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);
4853     } else {
4854         xcb_delete_property(p->conn, p->window, p->atom(_KDE_NET_WM_BLOCK_COMPOSITING));
4855         xcb_delete_property(p->conn, p->window, p->atom(_NET_WM_BYPASS_COMPOSITOR));
4856     }
4857 }
4858 
isBlockingCompositing() const4859 bool NETWinInfo::isBlockingCompositing() const
4860 {
4861     return p->blockCompositing;
4862 }
4863 
handledIcons() const4864 bool NETWinInfo::handledIcons() const
4865 {
4866     return p->handled_icons;
4867 }
4868 
passedProperties() const4869 NET::Properties NETWinInfo::passedProperties() const
4870 {
4871     return p->properties;
4872 }
4873 
passedProperties2() const4874 NET::Properties2 NETWinInfo::passedProperties2() const
4875 {
4876     return p->properties2;
4877 }
4878 
mappingState() const4879 NET::MappingState NETWinInfo::mappingState() const
4880 {
4881     return p->mapping_state;
4882 }
4883 
protocols() const4884 NET::Protocols NETWinInfo::protocols() const
4885 {
4886     return p->protocols;
4887 }
4888 
supportsProtocol(NET::Protocol protocol) const4889 bool NETWinInfo::supportsProtocol(NET::Protocol protocol) const
4890 {
4891     return p->protocols.testFlag(protocol);
4892 }
4893 
opaqueRegion() const4894 std::vector<NETRect> NETWinInfo::opaqueRegion() const
4895 {
4896     return p->opaqueRegion;
4897 }
4898 
xcbConnection() const4899 xcb_connection_t *NETWinInfo::xcbConnection() const
4900 {
4901     return p->conn;
4902 }
4903 
setDesktopFileName(const char * name)4904 void NETWinInfo::setDesktopFileName(const char *name)
4905 {
4906     if (p->role != Client) {
4907         return;
4908     }
4909 
4910     delete[] p->desktop_file;
4911     p->desktop_file = nstrdup(name);
4912 
4913     xcb_change_property(p->conn,
4914                         XCB_PROP_MODE_REPLACE,
4915                         p->window,
4916                         p->atom(_KDE_NET_WM_DESKTOP_FILE),
4917                         p->atom(UTF8_STRING),
4918                         8,
4919                         strlen(p->desktop_file),
4920                         (const void *)p->desktop_file);
4921 }
4922 
desktopFileName() const4923 const char *NETWinInfo::desktopFileName() const
4924 {
4925     return p->desktop_file;
4926 }
4927 
virtual_hook(int,void *)4928 void NETRootInfo::virtual_hook(int, void *)
4929 {
4930     /*BASE::virtual_hook( id, data );*/
4931 }
4932 
virtual_hook(int,void *)4933 void NETWinInfo::virtual_hook(int, void *)
4934 {
4935     /*BASE::virtual_hook( id, data );*/
4936 }
4937 
timestampCompare(unsigned long time1,unsigned long time2)4938 int NET::timestampCompare(unsigned long time1, unsigned long time2)
4939 {
4940     return KXUtils::timestampCompare(time1, time2);
4941 }
4942 
timestampDiff(unsigned long time1,unsigned long time2)4943 int NET::timestampDiff(unsigned long time1, unsigned long time2)
4944 {
4945     return KXUtils::timestampDiff(time1, time2);
4946 }
4947 
4948 #endif
4949