KWindowSystem

kstartupinfo.cpp
1/*
2 SPDX-FileCopyrightText: 2001-2003 Lubos Lunak <l.lunak@kde.org>
3
4 SPDX-License-Identifier: MIT
5*/
6
7// qDebug() can't be turned off in kdeinit
8#if 0
9#define KSTARTUPINFO_ALL_DEBUG
10#ifdef __GNUC__
11#warning Extra KStartupInfo debug messages enabled.
12#endif
13#endif
14
15#ifdef QT_NO_CAST_FROM_ASCII
16#undef QT_NO_CAST_FROM_ASCII
17#endif
18
19#include "kstartupinfo.h"
20#include "kwindowsystem_debug.h"
21
22#include <QDateTime>
23
24// need to resolve INT32(qglobal.h)<>INT32(Xlibint.h) conflict
25#ifndef QT_CLEAN_NAMESPACE
26#define QT_CLEAN_NAMESPACE
27#endif
28
29#include <QTimer>
30#include <netwm.h>
31#include <stdlib.h>
32#include <sys/time.h>
33#include <unistd.h>
34
35#include <QCoreApplication>
36#include <QDebug>
37#include <QStandardPaths>
38#include <X11/Xlib.h>
39#include <fixx11h.h>
40#include <kwindowsystem.h>
41#include <kx11extras.h>
42#include <kxmessages.h>
43#include <private/qtx11extras_p.h>
44#include <signal.h>
45
46static const char NET_STARTUP_MSG[] = "_NET_STARTUP_INFO";
47
48// DESKTOP_STARTUP_ID is used also in kinit/wrapper.c ,
49// kdesu in both kdelibs and kdebase and who knows where else
50static const char NET_STARTUP_ENV[] = "DESKTOP_STARTUP_ID";
51
52static QByteArray s_startup_id;
53
54static long get_num(const QString &item_P);
55static QString get_str(const QString &item_P);
56static QByteArray get_cstr(const QString &item_P);
57static QStringList get_fields(const QString &txt_P);
58static QString escape_str(const QString &str_P);
59
60class Q_DECL_HIDDEN KStartupInfo::Data : public KStartupInfoData
61{
62public:
63 Data()
64 : age(0)
65 {
66 } // just because it's in a QMap
67 Data(const QString &txt_P)
68 : KStartupInfoData(txt_P)
69 , age(0)
70 {
71 }
72 unsigned int age;
73};
74
75struct Q_DECL_HIDDEN KStartupInfoId::Private {
76 Private()
77 : id("")
78 {
79 }
80
81 QString to_text() const;
82
83 QByteArray id; // id
84};
85
86struct Q_DECL_HIDDEN KStartupInfoData::Private {
87 Private()
88 : desktop(0)
89 , wmclass("")
90 , hostname("")
91 , silent(KStartupInfoData::Unknown)
92 , screen(-1)
93 , xinerama(-1)
94 {
95 }
96
97 QString to_text() const;
98 void remove_pid(pid_t pid);
99
100 QString bin;
104 int desktop;
106 QByteArray wmclass;
108 KStartupInfoData::TriState silent;
109 int screen;
110 int xinerama;
111 QString application_id;
112};
113
114class Q_DECL_HIDDEN KStartupInfo::Private
115{
116public:
117 // private slots
118 void startups_cleanup();
119 void startups_cleanup_no_age();
120 void got_message(const QString &msg);
121 void window_added(WId w);
122 void slot_window_added(WId w);
123
124 void init(int flags);
125 void got_startup_info(const QString &msg_P, bool update_only_P);
126 void got_remove_startup_info(const QString &msg_P);
127 void new_startup_info_internal(const KStartupInfoId &id_P, Data &data_P, bool update_only_P);
128 void removeAllStartupInfoInternal(const KStartupInfoId &id_P);
129 /**
130 * Emits the gotRemoveStartup signal and erases the @p it from the startups map.
131 * @returns Iterator to next item in the startups map.
132 **/
134 void remove_startup_pids(const KStartupInfoId &id, const KStartupInfoData &data);
135 void remove_startup_pids(const KStartupInfoData &data);
136 startup_t check_startup_internal(WId w, KStartupInfoId *id, KStartupInfoData *data);
137 bool find_id(const QByteArray &id_P, KStartupInfoId *id_O, KStartupInfoData *data_O);
138 bool find_pid(pid_t pid_P, const QByteArray &hostname, KStartupInfoId *id_O, KStartupInfoData *data_O);
139 bool find_wclass(const QByteArray &res_name_P, const QByteArray &res_class_P, KStartupInfoId *id_O, KStartupInfoData *data_O);
140 void startups_cleanup_internal(bool age_P);
141 void clean_all_noncompliant();
142 static QString check_required_startup_fields(const QString &msg, const KStartupInfoData &data, int screen);
143 static void setWindowStartupId(WId w_P, const QByteArray &id_P);
144
145 KStartupInfo *q;
146 unsigned int timeout;
148 // contains silenced ASN's only if !AnnounceSilencedChanges
150 // contains ASN's that had change: but no new: yet
152 KXMessages msgs;
153 QTimer *cleanup;
154 int flags;
155
156 Private(int flags_P, KStartupInfo *qq)
157 : q(qq)
158 , timeout(60)
159 , msgs(NET_STARTUP_MSG)
160 , cleanup(nullptr)
161 , flags(flags_P)
162 {
163 }
164
165 void createConnections()
166 {
167 // d == nullptr means "disabled"
168 if (!QX11Info::isPlatformX11() || !QX11Info::display()) {
169 return;
170 }
171
172 if (!(flags & DisableKWinModule)) {
173 QObject::connect(KX11Extras::self(), SIGNAL(windowAdded(WId)), q, SLOT(slot_window_added(WId)));
174 }
175 QObject::connect(&msgs, SIGNAL(gotMessage(QString)), q, SLOT(got_message(QString)));
176 cleanup = new QTimer(q);
177 QObject::connect(cleanup, SIGNAL(timeout()), q, SLOT(startups_cleanup()));
178 }
179};
180
181KStartupInfo::KStartupInfo(int flags_P, QObject *parent_P)
182 : QObject(parent_P)
183 , d(new Private(flags_P, this))
184{
185 d->createConnections();
186}
187
188KStartupInfo::~KStartupInfo()
189{
190 delete d;
191}
192
193void KStartupInfo::Private::got_message(const QString &msg_P)
194{
195 // TODO do something with SCREEN= ?
196 // qCDebug(LOG_KWINDOWSYSTEM) << "got:" << msg_P;
197 QString msg = msg_P.trimmed();
198 if (msg.startsWith(QLatin1String("new:"))) { // must match length below
199 got_startup_info(msg.mid(4), false);
200 } else if (msg.startsWith(QLatin1String("change:"))) { // must match length below
201 got_startup_info(msg.mid(7), true);
202 } else if (msg.startsWith(QLatin1String("remove:"))) { // must match length below
203 got_remove_startup_info(msg.mid(7));
204 }
205}
206
207// if the application stops responding for a while, KWindowSystem may get
208// the information about the already mapped window before KXMessages
209// actually gets the info about the started application (depends
210// on their order in the native x11 event filter)
211// simply delay info from KWindowSystem a bit
212// SELI???
213namespace
214{
215class DelayedWindowEvent : public QEvent
216{
217public:
218 DelayedWindowEvent(WId w_P)
219 : QEvent(uniqueType())
220 , w(w_P)
221 {
222 }
223 Window w;
224 static Type uniqueType()
225 {
226 return Type(QEvent::User + 15);
227 }
228};
229}
230
231void KStartupInfo::Private::slot_window_added(WId w_P)
232{
233 qApp->postEvent(q, new DelayedWindowEvent(w_P));
234}
235
236void KStartupInfo::customEvent(QEvent *e_P)
237{
238 if (e_P->type() == DelayedWindowEvent::uniqueType()) {
239 d->window_added(static_cast<DelayedWindowEvent *>(e_P)->w);
240 } else
242}
243
244void KStartupInfo::Private::window_added(WId w_P)
245{
247 KStartupInfoData data;
248 startup_t ret = check_startup_internal(w_P, &id, &data);
249 switch (ret) {
250 case Match:
251 // qCDebug(LOG_KWINDOWSYSTEM) << "new window match";
252 break;
253 case NoMatch:
254 break; // nothing
255 case CantDetect:
256 if (flags & CleanOnCantDetect) {
257 clean_all_noncompliant();
258 }
259 break;
260 }
261}
262
263void KStartupInfo::Private::got_startup_info(const QString &msg_P, bool update_P)
264{
265 KStartupInfoId id(msg_P);
266 if (id.isNull()) {
267 return;
268 }
269 KStartupInfo::Data data(msg_P);
270 new_startup_info_internal(id, data, update_P);
271}
272
273void KStartupInfo::Private::new_startup_info_internal(const KStartupInfoId &id_P, KStartupInfo::Data &data_P, bool update_P)
274{
275 if (id_P.isNull()) {
276 return;
277 }
278 if (startups.contains(id_P)) {
279 // already reported, update
280 startups[id_P].update(data_P);
281 startups[id_P].age = 0; // CHECKME
282 // qCDebug(LOG_KWINDOWSYSTEM) << "updating";
283 if (startups[id_P].silent() == KStartupInfo::Data::Yes && !(flags & AnnounceSilenceChanges)) {
284 silent_startups[id_P] = startups[id_P];
285 startups.remove(id_P);
286 Q_EMIT q->gotRemoveStartup(id_P, silent_startups[id_P]);
287 return;
288 }
289 Q_EMIT q->gotStartupChange(id_P, startups[id_P]);
290 return;
291 }
292 if (silent_startups.contains(id_P)) {
293 // already reported, update
294 silent_startups[id_P].update(data_P);
295 silent_startups[id_P].age = 0; // CHECKME
296 // qCDebug(LOG_KWINDOWSYSTEM) << "updating silenced";
297 if (silent_startups[id_P].silent() != Data::Yes) {
298 startups[id_P] = silent_startups[id_P];
299 silent_startups.remove(id_P);
300 q->Q_EMIT gotNewStartup(id_P, startups[id_P]);
301 return;
302 }
303 Q_EMIT q->gotStartupChange(id_P, silent_startups[id_P]);
304 return;
305 }
306 if (uninited_startups.contains(id_P)) {
307 uninited_startups[id_P].update(data_P);
308 // qCDebug(LOG_KWINDOWSYSTEM) << "updating uninited";
309 if (!update_P) { // uninited finally got new:
310 startups[id_P] = uninited_startups[id_P];
311 uninited_startups.remove(id_P);
312 Q_EMIT q->gotNewStartup(id_P, startups[id_P]);
313 return;
314 }
315 // no change announce, it's still uninited
316 return;
317 }
318 if (update_P) { // change: without any new: first
319 // qCDebug(LOG_KWINDOWSYSTEM) << "adding uninited";
320 uninited_startups.insert(id_P, data_P);
321 } else if (data_P.silent() != Data::Yes || flags & AnnounceSilenceChanges) {
322 // qCDebug(LOG_KWINDOWSYSTEM) << "adding";
323 startups.insert(id_P, data_P);
324 Q_EMIT q->gotNewStartup(id_P, data_P);
325 } else { // new silenced, and silent shouldn't be announced
326 // qCDebug(LOG_KWINDOWSYSTEM) << "adding silent";
327 silent_startups.insert(id_P, data_P);
328 }
329 cleanup->start(1000); // 1 sec
330}
331
332void KStartupInfo::Private::got_remove_startup_info(const QString &msg_P)
333{
334 KStartupInfoId id(msg_P);
335 KStartupInfoData data(msg_P);
336 if (!data.pids().isEmpty()) {
337 if (!id.isNull()) {
338 remove_startup_pids(id, data);
339 } else {
340 remove_startup_pids(data);
341 }
342 return;
343 }
344 removeAllStartupInfoInternal(id);
345}
346
347void KStartupInfo::Private::removeAllStartupInfoInternal(const KStartupInfoId &id_P)
348{
349 auto it = startups.find(id_P);
350 if (it != startups.end()) {
351 // qCDebug(LOG_KWINDOWSYSTEM) << "removing";
352 Q_EMIT q->gotRemoveStartup(it.key(), it.value());
353 startups.erase(it);
354 return;
355 }
356 it = silent_startups.find(id_P);
357 if (it != silent_startups.end()) {
358 silent_startups.erase(it);
359 return;
360 }
361 it = uninited_startups.find(id_P);
362 if (it != uninited_startups.end()) {
363 uninited_startups.erase(it);
364 }
365}
366
367QMap<KStartupInfoId, KStartupInfo::Data>::iterator KStartupInfo::Private::removeStartupInfoInternal(QMap<KStartupInfoId, Data>::iterator it)
368{
369 Q_EMIT q->gotRemoveStartup(it.key(), it.value());
370 return startups.erase(it);
371}
372
373void KStartupInfo::Private::remove_startup_pids(const KStartupInfoData &data_P)
374{
375 // first find the matching info
376 for (QMap<KStartupInfoId, KStartupInfo::Data>::Iterator it = startups.begin(); it != startups.end(); ++it) {
377 if ((*it).hostname() != data_P.hostname()) {
378 continue;
379 }
380 if (!(*it).is_pid(data_P.pids().first())) {
381 continue; // not the matching info
382 }
383 remove_startup_pids(it.key(), data_P);
384 break;
385 }
386}
387
388void KStartupInfo::Private::remove_startup_pids(const KStartupInfoId &id_P, const KStartupInfoData &data_P)
389{
390 if (data_P.pids().isEmpty()) {
391 qFatal("data_P.pids().isEmpty()");
392 }
393 Data *data = nullptr;
394 if (startups.contains(id_P)) {
395 data = &startups[id_P];
396 } else if (silent_startups.contains(id_P)) {
397 data = &silent_startups[id_P];
398 } else if (uninited_startups.contains(id_P)) {
399 data = &uninited_startups[id_P];
400 } else {
401 return;
402 }
403 const auto pids = data_P.pids();
404 for (auto pid : pids) {
405 data->d->remove_pid(pid); // remove all pids from the info
406 }
407 if (data->pids().isEmpty()) { // all pids removed -> remove info
408 removeAllStartupInfoInternal(id_P);
409 }
410}
411
413{
414 if (id_P.isNull()) {
415 return false;
416 }
417 return sendStartupXcb(QX11Info::connection(), QX11Info::appScreen(), id_P, data_P);
418}
419
420bool KStartupInfo::sendStartupXcb(xcb_connection_t *conn, int screen, const KStartupInfoId &id_P, const KStartupInfoData &data_P)
421{
422 if (id_P.isNull()) {
423 return false;
424 }
425 QString msg = QStringLiteral("new: %1 %2").arg(id_P.d->to_text(), data_P.d->to_text());
426 msg = Private::check_required_startup_fields(msg, data_P, screen);
427#ifdef KSTARTUPINFO_ALL_DEBUG
428 qCDebug(LOG_KWINDOWSYSTEM) << "sending " << msg;
429#endif
430 return KXMessages::broadcastMessageX(conn, NET_STARTUP_MSG, msg, screen);
431}
432
433QString KStartupInfo::Private::check_required_startup_fields(const QString &msg, const KStartupInfoData &data_P, int screen)
434{
435 QString ret = msg;
436 if (data_P.name().isEmpty()) {
437 // qWarning() << "NAME not specified in initial startup message";
438 QString name = data_P.bin();
439 if (name.isEmpty()) {
440 name = QStringLiteral("UNKNOWN");
441 }
442 ret += QStringLiteral(" NAME=\"%1\"").arg(escape_str(name));
443 }
444 if (data_P.screen() == -1) { // add automatically if needed
445 ret += QStringLiteral(" SCREEN=%1").arg(screen);
446 }
447 return ret;
448}
449
451{
452 if (id_P.isNull()) {
453 return false;
454 }
455 return sendChangeXcb(QX11Info::connection(), QX11Info::appScreen(), id_P, data_P);
456}
457
458bool KStartupInfo::sendChangeXcb(xcb_connection_t *conn, int screen, const KStartupInfoId &id_P, const KStartupInfoData &data_P)
459{
460 if (id_P.isNull()) {
461 return false;
462 }
463 QString msg = QStringLiteral("change: %1 %2").arg(id_P.d->to_text(), data_P.d->to_text());
464#ifdef KSTARTUPINFO_ALL_DEBUG
465 qCDebug(LOG_KWINDOWSYSTEM) << "sending " << msg;
466#endif
467 return KXMessages::broadcastMessageX(conn, NET_STARTUP_MSG, msg, screen);
468}
469
471{
472 if (id_P.isNull()) {
473 return false;
474 }
475 return sendFinishXcb(QX11Info::connection(), QX11Info::appScreen(), id_P);
476}
477
478bool KStartupInfo::sendFinishXcb(xcb_connection_t *conn, int screen, const KStartupInfoId &id_P)
479{
480 if (id_P.isNull()) {
481 return false;
482 }
483 QString msg = QStringLiteral("remove: %1").arg(id_P.d->to_text());
484#ifdef KSTARTUPINFO_ALL_DEBUG
485 qCDebug(LOG_KWINDOWSYSTEM) << "sending " << msg;
486#endif
487 return KXMessages::broadcastMessageX(conn, NET_STARTUP_MSG, msg, screen);
488}
489
491{
492 // if( id_P.isNull()) // id may be null, the pids and hostname matter then
493 // return false;
494 return sendFinishXcb(QX11Info::connection(), QX11Info::appScreen(), id_P, data_P);
495}
496
497bool KStartupInfo::sendFinishXcb(xcb_connection_t *conn, int screen, const KStartupInfoId &id_P, const KStartupInfoData &data_P)
498{
499 // if( id_P.isNull()) // id may be null, the pids and hostname matter then
500 // return false;
501 QString msg = QStringLiteral("remove: %1 %2").arg(id_P.d->to_text(), data_P.d->to_text());
502#ifdef KSTARTUPINFO_ALL_DEBUG
503 qCDebug(LOG_KWINDOWSYSTEM) << "sending " << msg;
504#endif
505 return KXMessages::broadcastMessageX(conn, NET_STARTUP_MSG, msg, screen);
506}
507
509{
510 QByteArray startupId = s_startup_id;
511
512 if (startupId.isEmpty()) {
513 startupId = QX11Info::nextStartupId();
514 }
515
516 appStarted(startupId);
517 setStartupId("0"); // reset the id, no longer valid (must use clearStartupId() to avoid infinite loop)
518}
519
521{
523 id.initId(startup_id);
524 if (id.isNull()) {
525 return;
526 }
527 if (QX11Info::isPlatformX11() && !qEnvironmentVariableIsEmpty("DISPLAY")) { // don't rely on QX11Info::display()
529 }
530}
531
533{
534 if (startup_id == s_startup_id) {
535 return;
536 }
537 if (startup_id.isEmpty()) {
538 s_startup_id = "0";
539 } else {
540 s_startup_id = startup_id;
541 if (QX11Info::isPlatformX11()) {
543 id.initId(startup_id);
544 long timestamp = id.timestamp();
545 if (timestamp != 0) {
546 if (QX11Info::appUserTime() == 0 || NET::timestampCompare(timestamp, QX11Info::appUserTime()) > 0) { // time > appUserTime
547 QX11Info::setAppUserTime(timestamp);
548 }
549 if (QX11Info::appTime() == 0 || NET::timestampCompare(timestamp, QX11Info::appTime()) > 0) { // time > appTime
550 QX11Info::setAppTime(timestamp);
551 }
552 }
553 }
554 }
555}
556
557void KStartupInfo::setNewStartupId(QWindow *window, const QByteArray &startup_id)
558{
559 Q_ASSERT(window);
560 setStartupId(startup_id);
561 bool activate = true;
562 if (window != nullptr && QX11Info::isPlatformX11()) {
563 if (!startup_id.isEmpty() && startup_id != "0") {
564 NETRootInfo i(QX11Info::connection(), NET::Supported);
565 if (i.isSupported(NET::WM2StartupId)) {
566 KStartupInfo::Private::setWindowStartupId(window->winId(), startup_id);
567 activate = false; // WM will take care of it
568 }
569 }
570 if (activate) {
572 // This is not very nice, but there's no way how to get any
573 // usable timestamp without ASN, so force activating the window.
574 // And even with ASN, it's not possible to get the timestamp here,
575 // so if the WM doesn't have support for ASN, it can't be used either.
577 }
578 }
579}
580
582{
583 return d->check_startup_internal(w_P, &id_O, &data_O);
584}
585
587{
588 return d->check_startup_internal(w_P, &id_O, nullptr);
589}
590
592{
593 return d->check_startup_internal(w_P, nullptr, &data_O);
594}
595
597{
598 return d->check_startup_internal(w_P, nullptr, nullptr);
599}
600
601KStartupInfo::startup_t KStartupInfo::Private::check_startup_internal(WId w_P, KStartupInfoId *id_O, KStartupInfoData *data_O)
602{
603 if (startups.isEmpty()) {
604 return NoMatch; // no startups
605 }
606 // Strategy:
607 //
608 // Is this a compliant app ?
609 // - Yes - test for match
610 // - No - Is this a NET_WM compliant app ?
611 // - Yes - test for pid match
612 // - No - test for WM_CLASS match
613 qCDebug(LOG_KWINDOWSYSTEM) << "check_startup";
614 QByteArray id = windowStartupId(w_P);
615 if (!id.isNull()) {
616 if (id.isEmpty() || id == "0") { // means ignore this window
617 qCDebug(LOG_KWINDOWSYSTEM) << "ignore";
618 return NoMatch;
619 }
620 return find_id(id, id_O, data_O) ? Match : NoMatch;
621 }
622 if (!QX11Info::isPlatformX11()) {
623 qCDebug(LOG_KWINDOWSYSTEM) << "check_startup:cantdetect";
624 return CantDetect;
625 }
626 NETWinInfo info(QX11Info::connection(),
627 w_P,
628 QX11Info::appRootWindow(),
629 NET::WMWindowType | NET::WMPid | NET::WMState,
630 NET::WM2WindowClass | NET::WM2ClientMachine | NET::WM2TransientFor);
631 pid_t pid = info.pid();
632 if (pid > 0) {
633 QByteArray hostname = info.clientMachine();
634 if (!hostname.isEmpty() && find_pid(pid, hostname, id_O, data_O)) {
635 return Match;
636 }
637 // try XClass matching , this PID stuff sucks :(
638 }
639 if (find_wclass(info.windowClassName(), info.windowClassClass(), id_O, data_O)) {
640 return Match;
641 }
642 // ignore NET::Tool and other special window types, if they can't be matched
645 if (type != NET::Normal && type != NET::Override && type != NET::Unknown && type != NET::Dialog && type != NET::Utility)
646 // && type != NET::Dock ) why did I put this here?
647 {
648 return NoMatch;
649 }
650 // lets see if this is a transient
651 xcb_window_t transient_for = info.transientFor();
652 if (transient_for != QX11Info::appRootWindow() && transient_for != XCB_WINDOW_NONE) {
653 return NoMatch;
654 }
655 qCDebug(LOG_KWINDOWSYSTEM) << "check_startup:cantdetect";
656 return CantDetect;
657}
658
659bool KStartupInfo::Private::find_id(const QByteArray &id_P, KStartupInfoId *id_O, KStartupInfoData *data_O)
660{
661 // qCDebug(LOG_KWINDOWSYSTEM) << "find_id:" << id_P;
663 id.initId(id_P);
664 if (startups.contains(id)) {
665 if (id_O != nullptr) {
666 *id_O = id;
667 }
668 if (data_O != nullptr) {
669 *data_O = startups[id];
670 }
671 // qCDebug(LOG_KWINDOWSYSTEM) << "check_startup_id:match";
672 return true;
673 }
674 return false;
675}
676
677bool KStartupInfo::Private::find_pid(pid_t pid_P, const QByteArray &hostname_P, KStartupInfoId *id_O, KStartupInfoData *data_O)
678{
679 // qCDebug(LOG_KWINDOWSYSTEM) << "find_pid:" << pid_P;
680 for (QMap<KStartupInfoId, KStartupInfo::Data>::Iterator it = startups.begin(); it != startups.end(); ++it) {
681 if ((*it).is_pid(pid_P) && (*it).hostname() == hostname_P) {
682 // Found it !
683 if (id_O != nullptr) {
684 *id_O = it.key();
685 }
686 if (data_O != nullptr) {
687 *data_O = *it;
688 }
689 // non-compliant, remove on first match
690 removeStartupInfoInternal(it);
691 // qCDebug(LOG_KWINDOWSYSTEM) << "check_startup_pid:match";
692 return true;
693 }
694 }
695 return false;
696}
697
698bool KStartupInfo::Private::find_wclass(const QByteArray &_res_name, const QByteArray &_res_class, KStartupInfoId *id_O, KStartupInfoData *data_O)
699{
700 QByteArray res_name = _res_name.toLower();
701 QByteArray res_class = _res_class.toLower();
702 // qCDebug(LOG_KWINDOWSYSTEM) << "find_wclass:" << res_name << ":" << res_class;
703 for (QMap<KStartupInfoId, Data>::Iterator it = startups.begin(); it != startups.end(); ++it) {
704 const QByteArray wmclass = (*it).findWMClass();
705 if (wmclass.toLower() == res_name || wmclass.toLower() == res_class) {
706 // Found it !
707 if (id_O != nullptr) {
708 *id_O = it.key();
709 }
710 if (data_O != nullptr) {
711 *data_O = *it;
712 }
713 // non-compliant, remove on first match
714 removeStartupInfoInternal(it);
715 // qCDebug(LOG_KWINDOWSYSTEM) << "check_startup_wclass:match";
716 return true;
717 }
718 }
719 return false;
720}
721
723{
724 if (!QX11Info::isPlatformX11()) {
725 return QByteArray();
726 }
727 NETWinInfo info(QX11Info::connection(), w_P, QX11Info::appRootWindow(), NET::Properties(), NET::WM2StartupId | NET::WM2GroupLeader);
728 QByteArray ret = info.startupId();
729 if (ret.isEmpty() && info.groupLeader() != XCB_WINDOW_NONE) {
730 // retry with window group leader, as the spec says
731 NETWinInfo groupLeaderInfo(QX11Info::connection(), info.groupLeader(), QX11Info::appRootWindow(), NET::Properties(), NET::Properties2());
732 ret = groupLeaderInfo.startupId();
733 }
734 return ret;
735}
736
737void KStartupInfo::Private::setWindowStartupId(WId w_P, const QByteArray &id_P)
738{
739 if (!QX11Info::isPlatformX11()) {
740 return;
741 }
742 if (id_P.isNull()) {
743 return;
744 }
745 NETWinInfo info(QX11Info::connection(), w_P, QX11Info::appRootWindow(), NET::Properties(), NET::Properties2());
746 info.setStartupId(id_P.constData());
747}
748
749void KStartupInfo::setTimeout(unsigned int secs_P)
750{
751 d->timeout = secs_P;
752 // schedule removing entries that are older than the new timeout
753 QTimer::singleShot(0, this, SLOT(startups_cleanup_no_age()));
754}
755
756void KStartupInfo::Private::startups_cleanup_no_age()
757{
758 startups_cleanup_internal(false);
759}
760
761void KStartupInfo::Private::startups_cleanup()
762{
763 if (startups.isEmpty() && silent_startups.isEmpty() && uninited_startups.isEmpty()) {
764 cleanup->stop();
765 return;
766 }
767 startups_cleanup_internal(true);
768}
769
770void KStartupInfo::Private::startups_cleanup_internal(bool age_P)
771{
772 auto checkCleanup = [this, age_P](QMap<KStartupInfoId, KStartupInfo::Data> &s, bool doEmit) {
773 auto it = s.begin();
774 while (it != s.end()) {
775 if (age_P) {
776 (*it).age++;
777 }
778 unsigned int tout = timeout;
779 if ((*it).silent() == KStartupInfo::Data::Yes) {
780 // give kdesu time to get a password
781 tout *= 20;
782 }
783 const QByteArray timeoutEnvVariable = qgetenv("KSTARTUPINFO_TIMEOUT");
784 if (!timeoutEnvVariable.isNull()) {
785 tout = timeoutEnvVariable.toUInt();
786 }
787 if ((*it).age >= tout) {
788 if (doEmit) {
789 Q_EMIT q->gotRemoveStartup(it.key(), it.value());
790 }
791 it = s.erase(it);
792 } else {
793 ++it;
794 }
795 }
796 };
797 checkCleanup(startups, true);
798 checkCleanup(silent_startups, false);
799 checkCleanup(uninited_startups, false);
800}
801
802void KStartupInfo::Private::clean_all_noncompliant()
803{
804 for (QMap<KStartupInfoId, KStartupInfo::Data>::Iterator it = startups.begin(); it != startups.end();) {
805 if ((*it).WMClass() != "0") {
806 ++it;
807 continue;
808 }
809 it = removeStartupInfoInternal(it);
810 }
811}
812
814{
815 quint32 timestamp = 0;
816 if (QX11Info::isPlatformX11()) {
817 timestamp = QX11Info::getTimestamp();
818 }
820}
821
823{
824 // Assign a unique id, use hostname+time+pid, that should be 200% unique.
825 // Also append the user timestamp (for focus stealing prevention).
826 struct timeval tm;
827 gettimeofday(&tm, nullptr);
828 char hostname[256];
829 hostname[0] = '\0';
830 if (!gethostname(hostname, 255)) {
831 hostname[sizeof(hostname) - 1] = '\0';
832 }
833 QByteArray id = QStringLiteral("%1;%2;%3;%4_TIME%5").arg(hostname).arg(tm.tv_sec).arg(tm.tv_usec).arg(getpid()).arg(timestamp).toUtf8();
834 // qCDebug(LOG_KWINDOWSYSTEM) << "creating: " << id << ":" << (qApp ? qAppName() : QString("unnamed app") /* e.g. kdeinit */);
835 return id;
836}
837
839{
840 return d->id;
841}
842
843QString KStartupInfoId::Private::to_text() const
844{
845 return QStringLiteral(" ID=\"%1\" ").arg(escape_str(id));
846}
847
849 : d(new Private)
850{
851 const QStringList items = get_fields(txt_P);
852 for (QStringList::ConstIterator it = items.begin(); it != items.end(); ++it) {
853 if ((*it).startsWith(QLatin1String("ID="))) {
854 d->id = get_cstr(*it);
855 }
856 }
857}
858
860{
861 if (!id_P.isEmpty()) {
862 d->id = id_P;
863#ifdef KSTARTUPINFO_ALL_DEBUG
864 qCDebug(LOG_KWINDOWSYSTEM) << "using: " << d->id;
865#endif
866 return;
867 }
868 const QByteArray startup_env = qgetenv(NET_STARTUP_ENV);
869 if (!startup_env.isEmpty()) {
870 // already has id
871 d->id = startup_env;
872#ifdef KSTARTUPINFO_ALL_DEBUG
873 qCDebug(LOG_KWINDOWSYSTEM) << "reusing: " << d->id;
874#endif
875 return;
876 }
878}
879
881{
882 if (isNull()) {
883 qunsetenv(NET_STARTUP_ENV);
884 return false;
885 }
886 return !qputenv(NET_STARTUP_ENV, id()) == 0;
887}
888
890{
891 qunsetenv(NET_STARTUP_ENV);
892}
893
895 : d(new Private)
896{
897}
898
899KStartupInfoId::~KStartupInfoId()
900{
901 delete d;
902}
903
905 : d(new Private(*id_P.d))
906{
907}
908
909KStartupInfoId &KStartupInfoId::operator=(const KStartupInfoId &id_P)
910{
911 if (&id_P == this) {
912 return *this;
913 }
914 *d = *id_P.d;
915 return *this;
916}
917
919{
920 return id() == id_P.id();
921}
922
924{
925 return !(*this == id_P);
926}
927
928// needed for QMap
929bool KStartupInfoId::operator<(const KStartupInfoId &id_P) const
930{
931 return id() < id_P.id();
932}
933
935{
936 return d->id.isEmpty() || d->id == "0";
937}
938
939unsigned long KStartupInfoId::timestamp() const
940{
941 if (isNull()) {
942 return 0;
943 }
944 // As per the spec, the ID must contain the _TIME followed by the timestamp
945 int pos = d->id.lastIndexOf("_TIME");
946 if (pos >= 0) {
947 bool ok;
948 unsigned long time = QString(d->id.mid(pos + 5)).toULong(&ok);
949 if (!ok && d->id[pos + 5] == '-') { // try if it's as a negative signed number perhaps
950 time = QString(d->id.mid(pos + 5)).toLong(&ok);
951 }
952 if (ok) {
953 return time;
954 }
955 }
956 return 0;
957}
958
959QString KStartupInfoData::Private::to_text() const
960{
961 QString ret;
962 // prepare some space which should be always enough.
963 // No need to squeze at the end, as the result is only used as intermediate string
964 ret.reserve(256);
965 if (!bin.isEmpty()) {
966 ret += QStringLiteral(" BIN=\"%1\"").arg(escape_str(bin));
967 }
968 if (!name.isEmpty()) {
969 ret += QStringLiteral(" NAME=\"%1\"").arg(escape_str(name));
970 }
971 if (!description.isEmpty()) {
972 ret += QStringLiteral(" DESCRIPTION=\"%1\"").arg(escape_str(description));
973 }
974 if (!icon.isEmpty()) {
975 ret += QStringLiteral(" ICON=\"%1\"").arg(icon);
976 }
977 if (desktop != 0) {
978 ret += QStringLiteral(" DESKTOP=%1").arg(desktop == NET::OnAllDesktops ? NET::OnAllDesktops : desktop - 1); // spec counts from 0
979 }
980 if (!wmclass.isEmpty()) {
981 ret += QStringLiteral(" WMCLASS=\"%1\"").arg(QString(wmclass));
982 }
983 if (!hostname.isEmpty()) {
984 ret += QStringLiteral(" HOSTNAME=%1").arg(QString(hostname));
985 }
986 for (QList<pid_t>::ConstIterator it = pids.begin(); it != pids.end(); ++it) {
987 ret += QStringLiteral(" PID=%1").arg(*it);
988 }
989 if (silent != KStartupInfoData::Unknown) {
990 ret += QStringLiteral(" SILENT=%1").arg(silent == KStartupInfoData::Yes ? 1 : 0);
991 }
992 if (screen != -1) {
993 ret += QStringLiteral(" SCREEN=%1").arg(screen);
994 }
995 if (xinerama != -1) {
996 ret += QStringLiteral(" XINERAMA=%1").arg(xinerama);
997 }
998 if (!application_id.isEmpty()) {
999 ret += QStringLiteral(" APPLICATION_ID=\"%1\"").arg(application_id);
1000 }
1001 return ret;
1002}
1003
1005 : d(new Private)
1006{
1007 const QStringList items = get_fields(txt_P);
1008 for (QStringList::ConstIterator it = items.begin(); it != items.end(); ++it) {
1009 if ((*it).startsWith(QLatin1String("BIN="))) {
1010 d->bin = get_str(*it);
1011 } else if ((*it).startsWith(QLatin1String("NAME="))) {
1012 d->name = get_str(*it);
1013 } else if ((*it).startsWith(QLatin1String("DESCRIPTION="))) {
1014 d->description = get_str(*it);
1015 } else if ((*it).startsWith(QLatin1String("ICON="))) {
1016 d->icon = get_str(*it);
1017 } else if ((*it).startsWith(QLatin1String("DESKTOP="))) {
1018 d->desktop = get_num(*it);
1019 if (d->desktop != NET::OnAllDesktops) {
1020 ++d->desktop; // spec counts from 0
1021 }
1022 } else if ((*it).startsWith(QLatin1String("WMCLASS="))) {
1023 d->wmclass = get_cstr(*it);
1024 } else if ((*it).startsWith(QLatin1String("HOSTNAME="))) { // added to version 1 (2014)
1025 d->hostname = get_cstr(*it);
1026 } else if ((*it).startsWith(QLatin1String("PID="))) { // added to version 1 (2014)
1027 addPid(get_num(*it));
1028 } else if ((*it).startsWith(QLatin1String("SILENT="))) {
1029 d->silent = get_num(*it) != 0 ? Yes : No;
1030 } else if ((*it).startsWith(QLatin1String("SCREEN="))) {
1031 d->screen = get_num(*it);
1032 } else if ((*it).startsWith(QLatin1String("XINERAMA="))) {
1033 d->xinerama = get_num(*it);
1034 } else if ((*it).startsWith(QLatin1String("APPLICATION_ID="))) {
1035 d->application_id = get_str(*it);
1036 }
1037 }
1038}
1039
1041 : d(new Private(*data.d))
1042{
1043}
1044
1045KStartupInfoData &KStartupInfoData::operator=(const KStartupInfoData &data)
1046{
1047 if (&data == this) {
1048 return *this;
1049 }
1050 *d = *data.d;
1051 return *this;
1052}
1053
1055{
1056 if (!data_P.bin().isEmpty()) {
1057 d->bin = data_P.bin();
1058 }
1059 if (!data_P.name().isEmpty() && name().isEmpty()) { // don't overwrite
1060 d->name = data_P.name();
1061 }
1062 if (!data_P.description().isEmpty() && description().isEmpty()) { // don't overwrite
1063 d->description = data_P.description();
1064 }
1065 if (!data_P.icon().isEmpty() && icon().isEmpty()) { // don't overwrite
1066 d->icon = data_P.icon();
1067 }
1068 if (data_P.desktop() != 0 && desktop() == 0) { // don't overwrite
1069 d->desktop = data_P.desktop();
1070 }
1071 if (!data_P.d->wmclass.isEmpty()) {
1072 d->wmclass = data_P.d->wmclass;
1073 }
1074 if (!data_P.d->hostname.isEmpty()) {
1075 d->hostname = data_P.d->hostname;
1076 }
1077 for (QList<pid_t>::ConstIterator it = data_P.d->pids.constBegin(); it != data_P.d->pids.constEnd(); ++it) {
1078 addPid(*it);
1079 }
1080 if (data_P.silent() != Unknown) {
1081 d->silent = data_P.silent();
1082 }
1083 if (data_P.screen() != -1) {
1084 d->screen = data_P.screen();
1085 }
1086 if (data_P.xinerama() != -1 && xinerama() != -1) { // don't overwrite
1087 d->xinerama = data_P.xinerama();
1088 }
1089 if (!data_P.applicationId().isEmpty() && applicationId().isEmpty()) { // don't overwrite
1090 d->application_id = data_P.applicationId();
1091 }
1092}
1093
1095 : d(new Private)
1096{
1097}
1098
1099KStartupInfoData::~KStartupInfoData()
1100{
1101 delete d;
1102}
1103
1105{
1106 d->bin = bin_P;
1107}
1108
1110{
1111 return d->bin;
1112}
1113
1115{
1116 d->name = name_P;
1117}
1118
1120{
1121 return d->name;
1122}
1123
1125{
1126 if (!name().isEmpty()) {
1127 return name();
1128 }
1129 return bin();
1130}
1131
1133{
1134 d->description = desc_P;
1135}
1136
1138{
1139 return d->description;
1140}
1141
1143{
1144 if (!description().isEmpty()) {
1145 return description();
1146 }
1147 return name();
1148}
1149
1151{
1152 d->icon = icon_P;
1153}
1154
1156{
1157 if (!icon().isEmpty()) {
1158 return icon();
1159 }
1160 return bin();
1161}
1162
1164{
1165 return d->icon;
1166}
1167
1169{
1170 d->desktop = desktop_P;
1171}
1172
1174{
1175 return d->desktop;
1176}
1177
1179{
1180 d->wmclass = wmclass_P;
1181}
1182
1184{
1185 if (!WMClass().isEmpty() && WMClass() != "0") {
1186 return WMClass();
1187 }
1188 return bin().toUtf8();
1189}
1190
1192{
1193 return d->wmclass;
1194}
1195
1197{
1198 if (!hostname_P.isNull()) {
1199 d->hostname = hostname_P;
1200 } else {
1201 char tmp[256];
1202 tmp[0] = '\0';
1203 if (!gethostname(tmp, 255)) {
1204 tmp[sizeof(tmp) - 1] = '\0';
1205 }
1206 d->hostname = tmp;
1207 }
1208}
1209
1211{
1212 return d->hostname;
1213}
1214
1216{
1217 if (!d->pids.contains(pid_P)) {
1218 d->pids.append(pid_P);
1219 }
1220}
1221
1222void KStartupInfoData::Private::remove_pid(pid_t pid_P)
1223{
1224 pids.removeAll(pid_P);
1225}
1226
1228{
1229 return d->pids;
1230}
1231
1232bool KStartupInfoData::is_pid(pid_t pid_P) const
1233{
1234 return d->pids.contains(pid_P);
1235}
1236
1237void KStartupInfoData::setSilent(TriState state_P)
1238{
1239 d->silent = state_P;
1240}
1241
1242KStartupInfoData::TriState KStartupInfoData::silent() const
1243{
1244 return d->silent;
1245}
1246
1248{
1249 d->screen = _screen;
1250}
1251
1253{
1254 return d->screen;
1255}
1256
1258{
1259 d->xinerama = xinerama;
1260}
1261
1263{
1264 return d->xinerama;
1265}
1266
1268{
1269 if (desktop.startsWith(QLatin1Char('/'))) {
1270 d->application_id = desktop;
1271 return;
1272 }
1273 // the spec requires this is always a full path, in order for everyone to be able to find it
1275 if (desk.isEmpty()) {
1276 return;
1277 }
1278 d->application_id = desk;
1279}
1280
1282{
1283 return d->application_id;
1284}
1285
1286static long get_num(const QString &item_P)
1287{
1288 unsigned int pos = item_P.indexOf(QLatin1Char('='));
1289 return item_P.mid(pos + 1).toLong();
1290}
1291
1292static QString get_str(const QString &item_P)
1293{
1294 int pos = item_P.indexOf(QLatin1Char('='));
1295 return item_P.mid(pos + 1);
1296}
1297
1298static QByteArray get_cstr(const QString &item_P)
1299{
1300 return get_str(item_P).toUtf8();
1301}
1302
1303static QStringList get_fields(const QString &txt_P)
1304{
1305 QString txt = txt_P.simplified();
1306 QStringList ret;
1307 QString item;
1308 bool in = false;
1309 bool escape = false;
1310 for (int pos = 0; pos < txt.length(); ++pos) {
1311 if (escape) {
1312 item += txt[pos];
1313 escape = false;
1314 } else if (txt[pos] == QLatin1Char('\\')) {
1315 escape = true;
1316 } else if (txt[pos] == QLatin1Char('\"')) {
1317 in = !in;
1318 } else if (txt[pos] == QLatin1Char(' ') && !in) {
1319 ret.append(item);
1320 item = QString();
1321 } else {
1322 item += txt[pos];
1323 }
1324 }
1325 ret.append(item);
1326 return ret;
1327}
1328
1329static QString escape_str(const QString &str_P)
1330{
1331 QString ret;
1332 // prepare some space which should be always enough.
1333 // No need to squeze at the end, as the result is only used as intermediate string
1334 ret.reserve(str_P.size() * 2);
1335 for (int pos = 0; pos < str_P.length(); ++pos) {
1336 if (str_P[pos] == QLatin1Char('\\') || str_P[pos] == QLatin1Char('"')) {
1337 ret += QLatin1Char('\\');
1338 }
1339 ret += str_P[pos];
1340 }
1341 return ret;
1342}
1343
1344#include "moc_kstartupinfo.cpp"
Class representing data about an application startup notification.
const QString & findDescription() const
Returns the description of the startup notification.
const QString & description() const
Returns the name of the startup notification, or empty if not available.
QByteArray WMClass() const
Returns the WM_CLASS value for the startup notification, or empty if not available.
void update(const KStartupInfoData &data)
Updates the notification data from the given data.
void setHostname(const QByteArray &hostname=QByteArray())
Sets the hostname on which the application is starting.
TriState silent() const
Return the silence status for the startup notification.
void setDesktop(int desktop)
Sets the desktop for the startup notification (i.e. the desktop on which the starting application sho...
QString applicationId() const
The .desktop file used to initiate this startup notification, or empty.
const QString & findIcon() const
Returns the icon of the startup notification, and if it's not available, tries to get it from the bin...
QList< pid_t > pids() const
Returns all PIDs for the startup notification.
const QString & name() const
Returns the name of the startup notification, or empty if not available.
const QString & bin() const
Returns the binary name of the starting application.
void addPid(pid_t pid)
Adds a PID to the list of processes that belong to the startup notification.
void setName(const QString &name)
Sets the name for the notification (e.g. 'Control Center').
void setXinerama(int xinerama)
Sets the Xinerama screen for the startup notification ( i.e.
int screen() const
The X11 screen on which the startup notification is happening, -1 if unknown.
void setWMClass(const QByteArray &wmclass)
Sets a WM_CLASS value for the startup notification, it may be used for increasing the chance that the...
void setIcon(const QString &icon)
Sets the icon for the startup notification (e.g. 'kcontrol').
int xinerama() const
The Xinerama screen for the startup notification, -1 if unknown.
void setBin(const QString &bin)
Sets the binary name of the application (e.g. 'kcontrol').
KStartupInfoData()
Constructor.
void setDescription(const QString &descr)
Sets the description for the notification (e.g. 'Launching Control Center').
const QString & icon() const
Returns the icon of the startup notification, or empty if not available.
const QByteArray findWMClass() const
Returns the WM_CLASS value for the startup notification, or binary name if not available.
void setSilent(TriState state)
Sets whether the visual feedback for this startup notification should be silenced (temporarily suspen...
bool is_pid(pid_t pid) const
Checks whether the given pid is in the list of PIDs for startup notification.
QByteArray hostname() const
Returns the hostname for the startup notification.
int desktop() const
Returns the desktop for the startup notification.
void setApplicationId(const QString &desktop)
Sets the .desktop file that was used to initiate the startup notification.
void setScreen(int screen)
Sets the X11 screen on which the startup notification should happen.
const QString & findName() const
Returns the name of the startup notification.
Class representing an identification of application startup notification.
void initId(const QByteArray &id="")
Initializes this object with the given identification ( which may be also "0" for no notification ),...
bool setupStartupEnv() const
Sets the startup notification environment variable to this identification.
bool isNull() const
Checks whether the identifier is valid.
unsigned long timestamp() const
Return the user timestamp for the startup notification, or 0 if no timestamp is set.
KStartupInfoId()
Creates an empty identification.
bool operator!=(const KStartupInfoId &id) const
Overloaded operator.
bool operator==(const KStartupInfoId &id) const
Overloaded operator.
const QByteArray & id() const
Returns the notification identifier as string.
Class for manipulating the application startup notification.
void setTimeout(unsigned int secs)
Sets the timeout for notifications, after this timeout a notification is removed.
static void setNewStartupId(QWindow *window, const QByteArray &startup_id)
Use this function if the application got a request with startup notification from outside (for exampl...
static void appStarted()
Manual notification that the application has started.
KStartupInfo(int flags, QObject *parent=nullptr)
Creates an instance that will receive the startup notifications.
static bool sendStartup(const KStartupInfoId &id, const KStartupInfoData &data)
Sends given notification data about started application with the given startup identification.
startup_t checkStartup(WId w)
Checks if the given windows matches any existing startup notification.
static QByteArray windowStartupId(WId w)
Returns startup notification identification of the given window.
static bool sendChange(const KStartupInfoId &id, const KStartupInfoData &data)
Sends given notification data about started application with the given startup identification.
static bool sendStartupXcb(xcb_connection_t *conn, int screen, const KStartupInfoId &id, const KStartupInfoData &data)
Like sendStartup , uses conn instead of QX11Info::connection() for sending the info.
static void resetStartupEnv()
Unsets the startup notification environment variable.
static QByteArray createNewStartupIdForTimestamp(quint32 timestamp)
Creates and returns new startup id with timestamp as user timestamp part.
static bool sendChangeXcb(xcb_connection_t *conn, int screen, const KStartupInfoId &id, const KStartupInfoData &data)
Like sendChange , uses conn instead of QX11Info::connection() for sending the info.
void gotRemoveStartup(const KStartupInfoId &id, const KStartupInfoData &data)
Emitted when a startup notification is removed (either because it was detected that the application i...
void gotStartupChange(const KStartupInfoId &id, const KStartupInfoData &data)
Emitted when a startup notification changes.
void gotNewStartup(const KStartupInfoId &id, const KStartupInfoData &data)
Emitted when a new startup notification is created (i.e. a new application is being started).
static void setStartupId(const QByteArray &startup_id)
Sets a new value for the application startup notification window property for newly created toplevel ...
static bool sendFinish(const KStartupInfoId &id)
Ends startup notification with the given identification.
static bool sendFinishXcb(xcb_connection_t *conn, int screen, const KStartupInfoId &id)
Like sendFinish , uses conn instead of QX11Info::connection() for sending the info.
static QByteArray createNewStartupId()
Creates and returns new startup id.
static void forceActiveWindow(WId win, long time=0)
Sets window win to be the active window.
static int currentDesktop()
Returns the current virtual desktop.
static void setOnDesktop(WId win, int desktop)
Moves window win to desktop desktop.
Sending string messages to other applications using the X Client Messages.
Definition kxmessages.h:33
static bool broadcastMessageX(xcb_connection_t *c, const char *msg_type, const QString &message, int screenNumber)
Broadcasts the given message with the given message type.
Common API for root window properties/protocols.
Definition netwm.h:41
bool isSupported(NET::Property property) const
Returns true if the given property is supported by the window manager.
Definition netwm.cpp:2379
Common API for application window properties/protocols.
Definition netwm.h:967
const char * startupId() const
Returns the startup notification id of the window.
Definition netwm.cpp:4719
xcb_window_t groupLeader() const
Returns the leader window for the group the window is in, if any.
Definition netwm.cpp:4752
@ DialogMask
Definition netwm_def.h:459
@ MenuMask
Definition netwm_def.h:458
@ SplashMask
Definition netwm_def.h:463
@ UtilityMask
Definition netwm_def.h:462
@ OverrideMask
Definition netwm_def.h:460
@ ToolbarMask
Definition netwm_def.h:457
@ DockMask
Definition netwm_def.h:456
@ NormalMask
Definition netwm_def.h:454
@ DesktopMask
Definition netwm_def.h:455
@ TopMenuMask
Definition netwm_def.h:461
WindowType
Window type.
Definition netwm_def.h:357
@ Override
Definition netwm_def.h:394
@ Normal
indicates that this is a normal, top-level window
Definition netwm_def.h:365
@ Unknown
indicates that the window did not define a window type.
Definition netwm_def.h:361
@ Dialog
indicates that this is a dialog window
Definition netwm_def.h:388
@ Utility
indicates a utility window
Definition netwm_def.h:403
static int timestampCompare(unsigned long time1, unsigned long time2)
Compares two X timestamps, taking into account wrapping and 64bit architectures.
Definition netwm.cpp:4925
void init(KXmlGuiWindow *window, KGameDifficulty *difficulty=nullptr)
QString name(GameStandardAction id)
VehicleSection::Type type(QStringView coachNumber, QStringView coachClassification)
NETWORKMANAGERQT_EXPORT QString hostname()
const char * constData() const const
bool isEmpty() const const
bool isNull() const const
qsizetype lastIndexOf(QByteArrayView bv) const const
QByteArray mid(qsizetype pos, qsizetype len) const const
QByteArray toLower() const const
uint toUInt(bool *ok, int base) const const
Type type() const const
void append(QList< T > &&value)
iterator begin()
const_iterator constBegin() const const
const_iterator constEnd() const const
bool contains(const AT &value) const const
iterator end()
T & first()
bool isEmpty() const const
iterator begin()
bool contains(const Key &key) const const
iterator end()
iterator erase(const_iterator first, const_iterator last)
iterator find(const Key &key)
iterator insert(const Key &key, const T &value)
bool isEmpty() const const
size_type remove(const Key &key)
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
virtual void customEvent(QEvent *event)
QString locate(StandardLocation type, const QString &fileName, LocateOptions options)
QString arg(Args &&... args) const const
qsizetype indexOf(QChar ch, qsizetype from, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
qsizetype length() const const
QString mid(qsizetype position, qsizetype n) const const
void reserve(qsizetype size)
QString simplified() const const
qsizetype size() const const
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
long toLong(bool *ok, int base) const const
ulong toULong(bool *ok, int base) const const
QByteArray toUtf8() const const
QString trimmed() const const
void start()
void stop()
WId winId() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Nov 8 2024 11:58:44 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.