Changeset View
Changeset View
Standalone View
Standalone View
group.cpp
Show All 24 Lines | |||||
25 | 25 | | |||
26 | */ | 26 | */ | ||
27 | 27 | | |||
28 | //#define QT_CLEAN_NAMESPACE | 28 | //#define QT_CLEAN_NAMESPACE | ||
29 | 29 | | |||
30 | #include "group.h" | 30 | #include "group.h" | ||
31 | #include <QTextStream> | 31 | #include <QTextStream> | ||
32 | #include "workspace.h" | 32 | #include "workspace.h" | ||
33 | #include "client.h" | 33 | #include "x11client.h" | ||
34 | #include "effects.h" | 34 | #include "effects.h" | ||
35 | 35 | | |||
36 | #include <kstartupinfo.h> | 36 | #include <kstartupinfo.h> | ||
37 | #include <KWindowSystem> | 37 | #include <KWindowSystem> | ||
38 | #include <QDebug> | 38 | #include <QDebug> | ||
39 | 39 | | |||
40 | /* | 40 | /* | ||
41 | TODO | 41 | TODO | ||
▲ Show 20 Lines • Show All 48 Lines • ▼ Show 20 Line(s) | 79 | else if (leader_wid != XCB_WINDOW_NONE) { | |||
90 | readIcon(48, false); | 90 | readIcon(48, false); | ||
91 | readIcon(64, false); | 91 | readIcon(64, false); | ||
92 | readIcon(128, false); | 92 | readIcon(128, false); | ||
93 | return ic; | 93 | return ic; | ||
94 | } | 94 | } | ||
95 | return QIcon(); | 95 | return QIcon(); | ||
96 | } | 96 | } | ||
97 | 97 | | |||
98 | void Group::addMember(Client* member_P) | 98 | void Group::addMember(X11Client *member_P) | ||
99 | { | 99 | { | ||
100 | _members.append(member_P); | 100 | _members.append(member_P); | ||
101 | // qDebug() << "GROUPADD:" << this << ":" << member_P; | 101 | // qDebug() << "GROUPADD:" << this << ":" << member_P; | ||
102 | // qDebug() << kBacktrace(); | 102 | // qDebug() << kBacktrace(); | ||
103 | } | 103 | } | ||
104 | 104 | | |||
105 | void Group::removeMember(Client* member_P) | 105 | void Group::removeMember(X11Client *member_P) | ||
106 | { | 106 | { | ||
107 | // qDebug() << "GROUPREMOVE:" << this << ":" << member_P; | 107 | // qDebug() << "GROUPREMOVE:" << this << ":" << member_P; | ||
108 | // qDebug() << kBacktrace(); | 108 | // qDebug() << kBacktrace(); | ||
109 | Q_ASSERT(_members.contains(member_P)); | 109 | Q_ASSERT(_members.contains(member_P)); | ||
110 | _members.removeAll(member_P); | 110 | _members.removeAll(member_P); | ||
111 | // there are cases when automatic deleting of groups must be delayed, | 111 | // there are cases when automatic deleting of groups must be delayed, | ||
112 | // e.g. when removing a member and doing some operation on the possibly | 112 | // e.g. when removing a member and doing some operation on the possibly | ||
113 | // other members of the group (which would be however deleted already | 113 | // other members of the group (which would be however deleted already | ||
Show All 12 Lines | |||||
126 | void Group::deref() | 126 | void Group::deref() | ||
127 | { | 127 | { | ||
128 | if (--refcount == 0 && _members.isEmpty()) { | 128 | if (--refcount == 0 && _members.isEmpty()) { | ||
129 | workspace()->removeGroup(this); | 129 | workspace()->removeGroup(this); | ||
130 | delete this; | 130 | delete this; | ||
131 | } | 131 | } | ||
132 | } | 132 | } | ||
133 | 133 | | |||
134 | void Group::gotLeader(Client* leader_P) | 134 | void Group::gotLeader(X11Client *leader_P) | ||
135 | { | 135 | { | ||
136 | Q_ASSERT(leader_P->window() == leader_wid); | 136 | Q_ASSERT(leader_P->window() == leader_wid); | ||
137 | leader_client = leader_P; | 137 | leader_client = leader_P; | ||
138 | } | 138 | } | ||
139 | 139 | | |||
140 | void Group::lostLeader() | 140 | void Group::lostLeader() | ||
141 | { | 141 | { | ||
142 | Q_ASSERT(!_members.contains(leader_client)); | 142 | Q_ASSERT(!_members.contains(leader_client)); | ||
Show All 16 Lines | 157 | for (GroupList::ConstIterator it = groups.constBegin(); | |||
159 | ++it) | 159 | ++it) | ||
160 | if ((*it)->leader() == leader) | 160 | if ((*it)->leader() == leader) | ||
161 | return *it; | 161 | return *it; | ||
162 | return nullptr; | 162 | return nullptr; | ||
163 | } | 163 | } | ||
164 | 164 | | |||
165 | // Client is group transient, but has no group set. Try to find | 165 | // Client is group transient, but has no group set. Try to find | ||
166 | // group with windows with the same client leader. | 166 | // group with windows with the same client leader. | ||
167 | Group* Workspace::findClientLeaderGroup(const Client* c) const | 167 | Group* Workspace::findClientLeaderGroup(const X11Client *c) const | ||
168 | { | 168 | { | ||
169 | Group* ret = nullptr; | 169 | Group* ret = nullptr; | ||
170 | for (ClientList::ConstIterator it = clients.constBegin(); | 170 | for (ClientList::ConstIterator it = clients.constBegin(); | ||
171 | it != clients.constEnd(); | 171 | it != clients.constEnd(); | ||
172 | ++it) { | 172 | ++it) { | ||
173 | if (*it == c) | 173 | if (*it == c) | ||
174 | continue; | 174 | continue; | ||
175 | if ((*it)->wmClientLeader() == c->wmClientLeader()) { | 175 | if ((*it)->wmClientLeader() == c->wmClientLeader()) { | ||
176 | if (ret == nullptr || ret == (*it)->group()) | 176 | if (ret == nullptr || ret == (*it)->group()) | ||
177 | ret = (*it)->group(); | 177 | ret = (*it)->group(); | ||
178 | else { | 178 | else { | ||
179 | // There are already two groups with the same client leader. | 179 | // There are already two groups with the same client leader. | ||
180 | // This most probably means the app uses group transients without | 180 | // This most probably means the app uses group transients without | ||
181 | // setting group for its windows. Merging the two groups is a bad | 181 | // setting group for its windows. Merging the two groups is a bad | ||
182 | // hack, but there's no really good solution for this case. | 182 | // hack, but there's no really good solution for this case. | ||
183 | ClientList old_group = (*it)->group()->members(); | 183 | ClientList old_group = (*it)->group()->members(); | ||
184 | // old_group autodeletes when being empty | 184 | // old_group autodeletes when being empty | ||
185 | for (int pos = 0; | 185 | for (int pos = 0; | ||
186 | pos < old_group.count(); | 186 | pos < old_group.count(); | ||
187 | ++pos) { | 187 | ++pos) { | ||
188 | Client* tmp = old_group[ pos ]; | 188 | X11Client *tmp = old_group[ pos ]; | ||
189 | if (tmp != c) | 189 | if (tmp != c) | ||
190 | tmp->changeClientLeaderGroup(ret); | 190 | tmp->changeClientLeaderGroup(ret); | ||
191 | } | 191 | } | ||
192 | } | 192 | } | ||
193 | } | 193 | } | ||
194 | } | 194 | } | ||
195 | return ret; | 195 | return ret; | ||
196 | } | 196 | } | ||
▲ Show 20 Lines • Show All 69 Lines • ▼ Show 20 Line(s) | 265 | { | |||
266 | return c1->resourceClass() == c2->resourceClass(); | 266 | return c1->resourceClass() == c2->resourceClass(); | ||
267 | } | 267 | } | ||
268 | 268 | | |||
269 | 269 | | |||
270 | //**************************************** | 270 | //**************************************** | ||
271 | // Client | 271 | // Client | ||
272 | //**************************************** | 272 | //**************************************** | ||
273 | 273 | | |||
274 | bool Client::belongToSameApplication(const Client* c1, const Client* c2, SameApplicationChecks checks) | 274 | bool X11Client::belongToSameApplication(const X11Client *c1, const X11Client *c2, SameApplicationChecks checks) | ||
275 | { | 275 | { | ||
276 | bool same_app = false; | 276 | bool same_app = false; | ||
277 | 277 | | |||
278 | // tests that definitely mean they belong together | 278 | // tests that definitely mean they belong together | ||
279 | if (c1 == c2) | 279 | if (c1 == c2) | ||
280 | same_app = true; | 280 | same_app = true; | ||
281 | else if (c1->isTransient() && c2->hasTransient(c1, true)) | 281 | else if (c1->isTransient() && c2->hasTransient(c1, true)) | ||
282 | same_app = true; // c1 has c2 as mainwindow | 282 | same_app = true; // c1 has c2 as mainwindow | ||
Show All 34 Lines | |||||
317 | // the window role is exactly the same). KMainWindow sets | 317 | // the window role is exactly the same). KMainWindow sets | ||
318 | // window role this way by default, and different KMainWindow | 318 | // window role this way by default, and different KMainWindow | ||
319 | // usually "are" different application from user's point of view. | 319 | // usually "are" different application from user's point of view. | ||
320 | // This help with no-focus-stealing for e.g. konqy reusing. | 320 | // This help with no-focus-stealing for e.g. konqy reusing. | ||
321 | // On the other hand, if one of the windows is active, they are | 321 | // On the other hand, if one of the windows is active, they are | ||
322 | // considered belonging to the same application. This is for | 322 | // considered belonging to the same application. This is for | ||
323 | // the cases when opening new mainwindow directly from the application, | 323 | // the cases when opening new mainwindow directly from the application, | ||
324 | // e.g. 'Open New Window' in konqy ( active_hack == true ). | 324 | // e.g. 'Open New Window' in konqy ( active_hack == true ). | ||
325 | bool Client::sameAppWindowRoleMatch(const Client* c1, const Client* c2, bool active_hack) | 325 | bool X11Client::sameAppWindowRoleMatch(const X11Client *c1, const X11Client *c2, bool active_hack) | ||
326 | { | 326 | { | ||
327 | if (c1->isTransient()) { | 327 | if (c1->isTransient()) { | ||
328 | while (const Client *t = dynamic_cast<const Client*>(c1->transientFor())) | 328 | while (const X11Client *t = dynamic_cast<const X11Client *>(c1->transientFor())) | ||
329 | c1 = t; | 329 | c1 = t; | ||
330 | if (c1->groupTransient()) | 330 | if (c1->groupTransient()) | ||
331 | return c1->group() == c2->group(); | 331 | return c1->group() == c2->group(); | ||
332 | #if 0 | 332 | #if 0 | ||
333 | // if a group transient is in its own group, it didn't possibly have a group, | 333 | // if a group transient is in its own group, it didn't possibly have a group, | ||
334 | // and therefore should be considered belonging to the same app like | 334 | // and therefore should be considered belonging to the same app like | ||
335 | // all other windows from the same app | 335 | // all other windows from the same app | ||
336 | || c1->group()->leaderClient() == c1 || c2->group()->leaderClient() == c2; | 336 | || c1->group()->leaderClient() == c1 || c2->group()->leaderClient() == c2; | ||
337 | #endif | 337 | #endif | ||
338 | } | 338 | } | ||
339 | if (c2->isTransient()) { | 339 | if (c2->isTransient()) { | ||
340 | while (const Client *t = dynamic_cast<const Client*>(c2->transientFor())) | 340 | while (const X11Client *t = dynamic_cast<const X11Client *>(c2->transientFor())) | ||
341 | c2 = t; | 341 | c2 = t; | ||
342 | if (c2->groupTransient()) | 342 | if (c2->groupTransient()) | ||
343 | return c1->group() == c2->group(); | 343 | return c1->group() == c2->group(); | ||
344 | #if 0 | 344 | #if 0 | ||
345 | || c1->group()->leaderClient() == c1 || c2->group()->leaderClient() == c2; | 345 | || c1->group()->leaderClient() == c1 || c2->group()->leaderClient() == c2; | ||
346 | #endif | 346 | #endif | ||
347 | } | 347 | } | ||
348 | int pos1 = c1->windowRole().indexOf('#'); | 348 | int pos1 = c1->windowRole().indexOf('#'); | ||
Show All 14 Lines | |||||
363 | Transiency stuff: ICCCM 4.1.2.6, NETWM 7.3 | 363 | Transiency stuff: ICCCM 4.1.2.6, NETWM 7.3 | ||
364 | 364 | | |||
365 | WM_TRANSIENT_FOR is basically means "this is my mainwindow". | 365 | WM_TRANSIENT_FOR is basically means "this is my mainwindow". | ||
366 | For NET::Unknown windows, transient windows are considered to be NET::Dialog | 366 | For NET::Unknown windows, transient windows are considered to be NET::Dialog | ||
367 | windows, for compatibility with non-NETWM clients. KWin may adjust the value | 367 | windows, for compatibility with non-NETWM clients. KWin may adjust the value | ||
368 | of this property in some cases (window pointing to itself or creating a loop, | 368 | of this property in some cases (window pointing to itself or creating a loop, | ||
369 | keeping NET::Splash windows above other windows from the same app, etc.). | 369 | keeping NET::Splash windows above other windows from the same app, etc.). | ||
370 | 370 | | |||
371 | Client::transient_for_id is the value of the WM_TRANSIENT_FOR property, after | 371 | X11Client::transient_for_id is the value of the WM_TRANSIENT_FOR property, after | ||
372 | possibly being adjusted by KWin. Client::transient_for points to the Client | 372 | possibly being adjusted by KWin. X11Client::transient_for points to the Client | ||
373 | this Client is transient for, or is NULL. If Client::transient_for_id is | 373 | this Client is transient for, or is NULL. If X11Client::transient_for_id is | ||
374 | poiting to the root window, the window is considered to be transient | 374 | poiting to the root window, the window is considered to be transient | ||
375 | for the whole window group, as suggested in NETWM 7.3. | 375 | for the whole window group, as suggested in NETWM 7.3. | ||
376 | 376 | | |||
377 | In the case of group transient window, Client::transient_for is NULL, | 377 | In the case of group transient window, X11Client::transient_for is NULL, | ||
378 | and Client::groupTransient() returns true. Such window is treated as | 378 | and X11Client::groupTransient() returns true. Such window is treated as | ||
379 | if it were transient for every window in its window group that has been | 379 | if it were transient for every window in its window group that has been | ||
380 | mapped _before_ it (or, to be exact, was added to the same group before it). | 380 | mapped _before_ it (or, to be exact, was added to the same group before it). | ||
381 | Otherwise two group transients can create loops, which can lead very very | 381 | Otherwise two group transients can create loops, which can lead very very | ||
382 | nasty things (bug #67914 and all its dupes). | 382 | nasty things (bug #67914 and all its dupes). | ||
383 | 383 | | |||
384 | Client::original_transient_for_id is the value of the property, which | 384 | X11Client::original_transient_for_id is the value of the property, which | ||
385 | may be different if Client::transient_for_id if e.g. forcing NET::Splash | 385 | may be different if X11Client::transient_for_id if e.g. forcing NET::Splash | ||
386 | to be kept on top of its window group, or when the mainwindow is not mapped | 386 | to be kept on top of its window group, or when the mainwindow is not mapped | ||
387 | yet, in which case the window is temporarily made group transient, | 387 | yet, in which case the window is temporarily made group transient, | ||
388 | and when the mainwindow is mapped, transiency is re-evaluated. | 388 | and when the mainwindow is mapped, transiency is re-evaluated. | ||
389 | 389 | | |||
390 | This can get a bit complicated with with e.g. two Konqueror windows created | 390 | This can get a bit complicated with with e.g. two Konqueror windows created | ||
391 | by the same process. They should ideally appear like two independent applications | 391 | by the same process. They should ideally appear like two independent applications | ||
392 | to the user. This should be accomplished by all windows in the same process | 392 | to the user. This should be accomplished by all windows in the same process | ||
393 | having the same window group (needs to be changed in Qt at the moment), and | 393 | having the same window group (needs to be changed in Qt at the moment), and | ||
394 | using non-group transients poiting to their relevant mainwindow for toolwindows | 394 | using non-group transients poiting to their relevant mainwindow for toolwindows | ||
395 | etc. KWin should handle both group and non-group transient dialogs well. | 395 | etc. KWin should handle both group and non-group transient dialogs well. | ||
396 | 396 | | |||
397 | In other words: | 397 | In other words: | ||
398 | - non-transient windows : isTransient() == false | 398 | - non-transient windows : isTransient() == false | ||
399 | - normal transients : transientFor() != NULL | 399 | - normal transients : transientFor() != NULL | ||
400 | - group transients : groupTransient() == true | 400 | - group transients : groupTransient() == true | ||
401 | 401 | | |||
402 | - list of mainwindows : mainClients() (call once and loop over the result) | 402 | - list of mainwindows : mainClients() (call once and loop over the result) | ||
403 | - list of transients : transients() | 403 | - list of transients : transients() | ||
404 | - every window in the group : group()->members() | 404 | - every window in the group : group()->members() | ||
405 | */ | 405 | */ | ||
406 | 406 | | |||
407 | Xcb::TransientFor Client::fetchTransient() const | 407 | Xcb::TransientFor X11Client::fetchTransient() const | ||
408 | { | 408 | { | ||
409 | return Xcb::TransientFor(window()); | 409 | return Xcb::TransientFor(window()); | ||
410 | } | 410 | } | ||
411 | 411 | | |||
412 | void Client::readTransientProperty(Xcb::TransientFor &transientFor) | 412 | void X11Client::readTransientProperty(Xcb::TransientFor &transientFor) | ||
413 | { | 413 | { | ||
414 | xcb_window_t new_transient_for_id = XCB_WINDOW_NONE; | 414 | xcb_window_t new_transient_for_id = XCB_WINDOW_NONE; | ||
415 | if (transientFor.getTransientFor(&new_transient_for_id)) { | 415 | if (transientFor.getTransientFor(&new_transient_for_id)) { | ||
416 | m_originalTransientForId = new_transient_for_id; | 416 | m_originalTransientForId = new_transient_for_id; | ||
417 | new_transient_for_id = verifyTransientFor(new_transient_for_id, true); | 417 | new_transient_for_id = verifyTransientFor(new_transient_for_id, true); | ||
418 | } else { | 418 | } else { | ||
419 | m_originalTransientForId = XCB_WINDOW_NONE; | 419 | m_originalTransientForId = XCB_WINDOW_NONE; | ||
420 | new_transient_for_id = verifyTransientFor(XCB_WINDOW_NONE, false); | 420 | new_transient_for_id = verifyTransientFor(XCB_WINDOW_NONE, false); | ||
421 | } | 421 | } | ||
422 | setTransient(new_transient_for_id); | 422 | setTransient(new_transient_for_id); | ||
423 | } | 423 | } | ||
424 | 424 | | |||
425 | void Client::readTransient() | 425 | void X11Client::readTransient() | ||
426 | { | 426 | { | ||
427 | Xcb::TransientFor transientFor = fetchTransient(); | 427 | Xcb::TransientFor transientFor = fetchTransient(); | ||
428 | readTransientProperty(transientFor); | 428 | readTransientProperty(transientFor); | ||
429 | } | 429 | } | ||
430 | 430 | | |||
431 | void Client::setTransient(xcb_window_t new_transient_for_id) | 431 | void X11Client::setTransient(xcb_window_t new_transient_for_id) | ||
432 | { | 432 | { | ||
433 | if (new_transient_for_id != m_transientForId) { | 433 | if (new_transient_for_id != m_transientForId) { | ||
434 | removeFromMainClients(); | 434 | removeFromMainClients(); | ||
435 | Client *transient_for = nullptr; | 435 | X11Client *transient_for = nullptr; | ||
436 | m_transientForId = new_transient_for_id; | 436 | m_transientForId = new_transient_for_id; | ||
437 | if (m_transientForId != XCB_WINDOW_NONE && !groupTransient()) { | 437 | if (m_transientForId != XCB_WINDOW_NONE && !groupTransient()) { | ||
438 | transient_for = workspace()->findClient(Predicate::WindowMatch, m_transientForId); | 438 | transient_for = workspace()->findClient(Predicate::WindowMatch, m_transientForId); | ||
439 | Q_ASSERT(transient_for != nullptr); // verifyTransient() had to check this | 439 | Q_ASSERT(transient_for != nullptr); // verifyTransient() had to check this | ||
440 | transient_for->addTransient(this); | 440 | transient_for->addTransient(this); | ||
441 | } // checkGroup() will check 'check_active_modal' | 441 | } // checkGroup() will check 'check_active_modal' | ||
442 | setTransientFor(transient_for); | 442 | setTransientFor(transient_for); | ||
443 | checkGroup(nullptr, true); // force, because transiency has changed | 443 | checkGroup(nullptr, true); // force, because transiency has changed | ||
444 | workspace()->updateClientLayer(this); | 444 | workspace()->updateClientLayer(this); | ||
445 | workspace()->resetUpdateToolWindowsTimer(); | 445 | workspace()->resetUpdateToolWindowsTimer(); | ||
446 | emit transientChanged(); | 446 | emit transientChanged(); | ||
447 | } | 447 | } | ||
448 | } | 448 | } | ||
449 | 449 | | |||
450 | void Client::removeFromMainClients() | 450 | void X11Client::removeFromMainClients() | ||
451 | { | 451 | { | ||
452 | if (transientFor()) | 452 | if (transientFor()) | ||
453 | transientFor()->removeTransient(this); | 453 | transientFor()->removeTransient(this); | ||
454 | if (groupTransient()) { | 454 | if (groupTransient()) { | ||
455 | for (ClientList::ConstIterator it = group()->members().constBegin(); | 455 | for (ClientList::ConstIterator it = group()->members().constBegin(); | ||
456 | it != group()->members().constEnd(); | 456 | it != group()->members().constEnd(); | ||
457 | ++it) | 457 | ++it) | ||
458 | (*it)->removeTransient(this); | 458 | (*it)->removeTransient(this); | ||
459 | } | 459 | } | ||
460 | } | 460 | } | ||
461 | 461 | | |||
462 | // *sigh* this transiency handling is madness :( | 462 | // *sigh* this transiency handling is madness :( | ||
463 | // This one is called when destroying/releasing a window. | 463 | // This one is called when destroying/releasing a window. | ||
464 | // It makes sure this client is removed from all grouping | 464 | // It makes sure this client is removed from all grouping | ||
465 | // related lists. | 465 | // related lists. | ||
466 | void Client::cleanGrouping() | 466 | void X11Client::cleanGrouping() | ||
467 | { | 467 | { | ||
468 | // qDebug() << "CLEANGROUPING:" << this; | 468 | // qDebug() << "CLEANGROUPING:" << this; | ||
469 | // for ( ClientList::ConstIterator it = group()->members().begin(); | 469 | // for ( ClientList::ConstIterator it = group()->members().begin(); | ||
470 | // it != group()->members().end(); | 470 | // it != group()->members().end(); | ||
471 | // ++it ) | 471 | // ++it ) | ||
472 | // qDebug() << "CL:" << *it; | 472 | // qDebug() << "CL:" << *it; | ||
473 | // ClientList mains; | 473 | // ClientList mains; | ||
474 | // mains = mainClients(); | 474 | // mains = mainClients(); | ||
▲ Show 20 Lines • Show All 50 Lines • ▼ Show 20 Line(s) | |||||
525 | // qDebug() << "CL4:" << *it; | 525 | // qDebug() << "CL4:" << *it; | ||
526 | m_transientForId = XCB_WINDOW_NONE; | 526 | m_transientForId = XCB_WINDOW_NONE; | ||
527 | } | 527 | } | ||
528 | 528 | | |||
529 | // Make sure that no group transient is considered transient | 529 | // Make sure that no group transient is considered transient | ||
530 | // for a window that is (directly or indirectly) transient for it | 530 | // for a window that is (directly or indirectly) transient for it | ||
531 | // (including another group transients). | 531 | // (including another group transients). | ||
532 | // Non-group transients not causing loops are checked in verifyTransientFor(). | 532 | // Non-group transients not causing loops are checked in verifyTransientFor(). | ||
533 | void Client::checkGroupTransients() | 533 | void X11Client::checkGroupTransients() | ||
534 | { | 534 | { | ||
535 | for (ClientList::ConstIterator it1 = group()->members().constBegin(); | 535 | for (ClientList::ConstIterator it1 = group()->members().constBegin(); | ||
536 | it1 != group()->members().constEnd(); | 536 | it1 != group()->members().constEnd(); | ||
537 | ++it1) { | 537 | ++it1) { | ||
538 | if (!(*it1)->groupTransient()) // check all group transients in the group | 538 | if (!(*it1)->groupTransient()) // check all group transients in the group | ||
539 | continue; // TODO optimize to check only the changed ones? | 539 | continue; // TODO optimize to check only the changed ones? | ||
540 | for (ClientList::ConstIterator it2 = group()->members().constBegin(); | 540 | for (ClientList::ConstIterator it2 = group()->members().constBegin(); | ||
541 | it2 != group()->members().constEnd(); | 541 | it2 != group()->members().constEnd(); | ||
Show All 35 Lines | |||||
577 | } | 577 | } | ||
578 | } | 578 | } | ||
579 | } | 579 | } | ||
580 | } | 580 | } | ||
581 | 581 | | |||
582 | /** | 582 | /** | ||
583 | * Check that the window is not transient for itself, and similar nonsense. | 583 | * Check that the window is not transient for itself, and similar nonsense. | ||
584 | */ | 584 | */ | ||
585 | xcb_window_t Client::verifyTransientFor(xcb_window_t new_transient_for, bool set) | 585 | xcb_window_t X11Client::verifyTransientFor(xcb_window_t new_transient_for, bool set) | ||
586 | { | 586 | { | ||
587 | xcb_window_t new_property_value = new_transient_for; | 587 | xcb_window_t new_property_value = new_transient_for; | ||
588 | // make sure splashscreens are shown above all their app's windows, even though | 588 | // make sure splashscreens are shown above all their app's windows, even though | ||
589 | // they're in Normal layer | 589 | // they're in Normal layer | ||
590 | if (isSplash() && new_transient_for == XCB_WINDOW_NONE) | 590 | if (isSplash() && new_transient_for == XCB_WINDOW_NONE) | ||
591 | new_transient_for = rootWindow(); | 591 | new_transient_for = rootWindow(); | ||
592 | if (new_transient_for == XCB_WINDOW_NONE) { | 592 | if (new_transient_for == XCB_WINDOW_NONE) { | ||
593 | if (set) // sometimes WM_TRANSIENT_FOR is set to None, instead of root window | 593 | if (set) // sometimes WM_TRANSIENT_FOR is set to None, instead of root window | ||
Show All 14 Lines | 607 | while (new_transient_for != XCB_WINDOW_NONE | |||
608 | && new_transient_for != rootWindow() | 608 | && new_transient_for != rootWindow() | ||
609 | && !workspace()->findClient(Predicate::WindowMatch, new_transient_for)) { | 609 | && !workspace()->findClient(Predicate::WindowMatch, new_transient_for)) { | ||
610 | Xcb::Tree tree(new_transient_for); | 610 | Xcb::Tree tree(new_transient_for); | ||
611 | if (tree.isNull()) { | 611 | if (tree.isNull()) { | ||
612 | break; | 612 | break; | ||
613 | } | 613 | } | ||
614 | new_transient_for = tree->parent; | 614 | new_transient_for = tree->parent; | ||
615 | } | 615 | } | ||
616 | if (Client* new_transient_for_client = workspace()->findClient(Predicate::WindowMatch, new_transient_for)) { | 616 | if (X11Client *new_transient_for_client = workspace()->findClient(Predicate::WindowMatch, new_transient_for)) { | ||
617 | if (new_transient_for != before_search) { | 617 | if (new_transient_for != before_search) { | ||
618 | qCDebug(KWIN_CORE) << "Client " << this << " has WM_TRANSIENT_FOR poiting to non-toplevel window " | 618 | qCDebug(KWIN_CORE) << "Client " << this << " has WM_TRANSIENT_FOR poiting to non-toplevel window " | ||
619 | << before_search << ", child of " << new_transient_for_client << ", adjusting."; | 619 | << before_search << ", child of " << new_transient_for_client << ", adjusting."; | ||
620 | new_property_value = new_transient_for; // also fix the property | 620 | new_property_value = new_transient_for; // also fix the property | ||
621 | } | 621 | } | ||
622 | } else | 622 | } else | ||
623 | new_transient_for = before_search; // nice try | 623 | new_transient_for = before_search; // nice try | ||
624 | // loop detection | 624 | // loop detection | ||
625 | // group transients cannot cause loops, because they're considered transient only for non-transient | 625 | // group transients cannot cause loops, because they're considered transient only for non-transient | ||
626 | // windows in the group | 626 | // windows in the group | ||
627 | int count = 20; | 627 | int count = 20; | ||
628 | xcb_window_t loop_pos = new_transient_for; | 628 | xcb_window_t loop_pos = new_transient_for; | ||
629 | while (loop_pos != XCB_WINDOW_NONE && loop_pos != rootWindow()) { | 629 | while (loop_pos != XCB_WINDOW_NONE && loop_pos != rootWindow()) { | ||
630 | Client* pos = workspace()->findClient(Predicate::WindowMatch, loop_pos); | 630 | X11Client *pos = workspace()->findClient(Predicate::WindowMatch, loop_pos); | ||
631 | if (pos == nullptr) | 631 | if (pos == nullptr) | ||
632 | break; | 632 | break; | ||
633 | loop_pos = pos->m_transientForId; | 633 | loop_pos = pos->m_transientForId; | ||
634 | if (--count == 0 || pos == this) { | 634 | if (--count == 0 || pos == this) { | ||
635 | qCWarning(KWIN_CORE) << "Client " << this << " caused WM_TRANSIENT_FOR loop." ; | 635 | qCWarning(KWIN_CORE) << "Client " << this << " caused WM_TRANSIENT_FOR loop." ; | ||
636 | new_transient_for = rootWindow(); | 636 | new_transient_for = rootWindow(); | ||
637 | } | 637 | } | ||
638 | } | 638 | } | ||
639 | if (new_transient_for != rootWindow() | 639 | if (new_transient_for != rootWindow() | ||
640 | && workspace()->findClient(Predicate::WindowMatch, new_transient_for) == nullptr) { | 640 | && workspace()->findClient(Predicate::WindowMatch, new_transient_for) == nullptr) { | ||
641 | // it's transient for a specific window, but that window is not mapped | 641 | // it's transient for a specific window, but that window is not mapped | ||
642 | new_transient_for = rootWindow(); | 642 | new_transient_for = rootWindow(); | ||
643 | } | 643 | } | ||
644 | if (new_property_value != m_originalTransientForId) | 644 | if (new_property_value != m_originalTransientForId) | ||
645 | Xcb::setTransientFor(window(), new_property_value); | 645 | Xcb::setTransientFor(window(), new_property_value); | ||
646 | return new_transient_for; | 646 | return new_transient_for; | ||
647 | } | 647 | } | ||
648 | 648 | | |||
649 | void Client::addTransient(AbstractClient* cl) | 649 | void X11Client::addTransient(AbstractClient* cl) | ||
650 | { | 650 | { | ||
651 | AbstractClient::addTransient(cl); | 651 | AbstractClient::addTransient(cl); | ||
652 | if (workspace()->mostRecentlyActivatedClient() == this && cl->isModal()) | 652 | if (workspace()->mostRecentlyActivatedClient() == this && cl->isModal()) | ||
653 | check_active_modal = true; | 653 | check_active_modal = true; | ||
654 | // qDebug() << "ADDTRANS:" << this << ":" << cl; | 654 | // qDebug() << "ADDTRANS:" << this << ":" << cl; | ||
655 | // qDebug() << kBacktrace(); | 655 | // qDebug() << kBacktrace(); | ||
656 | // for ( ClientList::ConstIterator it = transients_list.begin(); | 656 | // for ( ClientList::ConstIterator it = transients_list.begin(); | ||
657 | // it != transients_list.end(); | 657 | // it != transients_list.end(); | ||
658 | // ++it ) | 658 | // ++it ) | ||
659 | // qDebug() << "AT:" << (*it); | 659 | // qDebug() << "AT:" << (*it); | ||
660 | } | 660 | } | ||
661 | 661 | | |||
662 | void Client::removeTransient(AbstractClient* cl) | 662 | void X11Client::removeTransient(AbstractClient* cl) | ||
663 | { | 663 | { | ||
664 | // qDebug() << "REMOVETRANS:" << this << ":" << cl; | 664 | // qDebug() << "REMOVETRANS:" << this << ":" << cl; | ||
665 | // qDebug() << kBacktrace(); | 665 | // qDebug() << kBacktrace(); | ||
666 | // cl is transient for this, but this is going away | 666 | // cl is transient for this, but this is going away | ||
667 | // make cl group transient | 667 | // make cl group transient | ||
668 | AbstractClient::removeTransient(cl); | 668 | AbstractClient::removeTransient(cl); | ||
669 | if (cl->transientFor() == this) { | 669 | if (cl->transientFor() == this) { | ||
670 | if (Client *c = dynamic_cast<Client*>(cl)) { | 670 | if (X11Client *c = dynamic_cast<X11Client *>(cl)) { | ||
671 | c->m_transientForId = XCB_WINDOW_NONE; | 671 | c->m_transientForId = XCB_WINDOW_NONE; | ||
672 | c->setTransientFor(nullptr); // SELI | 672 | c->setTransientFor(nullptr); // SELI | ||
673 | // SELI cl->setTransient( rootWindow()); | 673 | // SELI cl->setTransient( rootWindow()); | ||
674 | c->setTransient(XCB_WINDOW_NONE); | 674 | c->setTransient(XCB_WINDOW_NONE); | ||
675 | } | 675 | } | ||
676 | } | 676 | } | ||
677 | } | 677 | } | ||
678 | 678 | | |||
679 | // A new window has been mapped. Check if it's not a mainwindow for this already existing window. | 679 | // A new window has been mapped. Check if it's not a mainwindow for this already existing window. | ||
680 | void Client::checkTransient(xcb_window_t w) | 680 | void X11Client::checkTransient(xcb_window_t w) | ||
681 | { | 681 | { | ||
682 | if (m_originalTransientForId != w) | 682 | if (m_originalTransientForId != w) | ||
683 | return; | 683 | return; | ||
684 | w = verifyTransientFor(w, true); | 684 | w = verifyTransientFor(w, true); | ||
685 | setTransient(w); | 685 | setTransient(w); | ||
686 | } | 686 | } | ||
687 | 687 | | |||
688 | // returns true if cl is the transient_for window for this client, | 688 | // returns true if cl is the transient_for window for this client, | ||
689 | // or recursively the transient_for window | 689 | // or recursively the transient_for window | ||
690 | bool Client::hasTransient(const AbstractClient* cl, bool indirect) const | 690 | bool X11Client::hasTransient(const AbstractClient* cl, bool indirect) const | ||
691 | { | 691 | { | ||
692 | if (const Client *c = dynamic_cast<const Client*>(cl)) { | 692 | if (const X11Client *c = dynamic_cast<const X11Client *>(cl)) { | ||
693 | // checkGroupTransients() uses this to break loops, so hasTransient() must detect them | 693 | // checkGroupTransients() uses this to break loops, so hasTransient() must detect them | ||
694 | ConstClientList set; | 694 | ConstClientList set; | ||
695 | return hasTransientInternal(c, indirect, set); | 695 | return hasTransientInternal(c, indirect, set); | ||
696 | } | 696 | } | ||
697 | return false; | 697 | return false; | ||
698 | } | 698 | } | ||
699 | 699 | | |||
700 | bool Client::hasTransientInternal(const Client* cl, bool indirect, ConstClientList& set) const | 700 | bool X11Client::hasTransientInternal(const X11Client *cl, bool indirect, ConstClientList& set) const | ||
701 | { | 701 | { | ||
702 | if (const Client *t = dynamic_cast<const Client*>(cl->transientFor())) { | 702 | if (const X11Client *t = dynamic_cast<const X11Client *>(cl->transientFor())) { | ||
703 | if (t == this) | 703 | if (t == this) | ||
704 | return true; | 704 | return true; | ||
705 | if (!indirect) | 705 | if (!indirect) | ||
706 | return false; | 706 | return false; | ||
707 | if (set.contains(cl)) | 707 | if (set.contains(cl)) | ||
708 | return false; | 708 | return false; | ||
709 | set.append(cl); | 709 | set.append(cl); | ||
710 | return hasTransientInternal(t, indirect, set); | 710 | return hasTransientInternal(t, indirect, set); | ||
711 | } | 711 | } | ||
712 | if (!cl->isTransient()) | 712 | if (!cl->isTransient()) | ||
713 | return false; | 713 | return false; | ||
714 | if (group() != cl->group()) | 714 | if (group() != cl->group()) | ||
715 | return false; | 715 | return false; | ||
716 | // cl is group transient, search from top | 716 | // cl is group transient, search from top | ||
717 | if (transients().contains(const_cast< Client* >(cl))) | 717 | if (transients().contains(const_cast< X11Client *>(cl))) | ||
718 | return true; | 718 | return true; | ||
719 | if (!indirect) | 719 | if (!indirect) | ||
720 | return false; | 720 | return false; | ||
721 | if (set.contains(this)) | 721 | if (set.contains(this)) | ||
722 | return false; | 722 | return false; | ||
723 | set.append(this); | 723 | set.append(this); | ||
724 | for (auto it = transients().constBegin(); | 724 | for (auto it = transients().constBegin(); | ||
725 | it != transients().constEnd(); | 725 | it != transients().constEnd(); | ||
726 | ++it) { | 726 | ++it) { | ||
727 | const Client *c = qobject_cast<const Client *>(*it); | 727 | const X11Client *c = qobject_cast<const X11Client *>(*it); | ||
728 | if (!c) { | 728 | if (!c) { | ||
729 | continue; | 729 | continue; | ||
730 | } | 730 | } | ||
731 | if (c->hasTransientInternal(cl, indirect, set)) | 731 | if (c->hasTransientInternal(cl, indirect, set)) | ||
732 | return true; | 732 | return true; | ||
733 | } | 733 | } | ||
734 | return false; | 734 | return false; | ||
735 | } | 735 | } | ||
736 | 736 | | |||
737 | QList<AbstractClient*> Client::mainClients() const | 737 | QList<AbstractClient*> X11Client::mainClients() const | ||
738 | { | 738 | { | ||
739 | if (!isTransient()) | 739 | if (!isTransient()) | ||
740 | return QList<AbstractClient*>(); | 740 | return QList<AbstractClient*>(); | ||
741 | if (const AbstractClient *t = transientFor()) | 741 | if (const AbstractClient *t = transientFor()) | ||
742 | return QList<AbstractClient*>{const_cast< AbstractClient* >(t)}; | 742 | return QList<AbstractClient*>{const_cast< AbstractClient* >(t)}; | ||
743 | QList<AbstractClient*> result; | 743 | QList<AbstractClient*> result; | ||
744 | Q_ASSERT(group()); | 744 | Q_ASSERT(group()); | ||
745 | for (ClientList::ConstIterator it = group()->members().constBegin(); | 745 | for (ClientList::ConstIterator it = group()->members().constBegin(); | ||
746 | it != group()->members().constEnd(); | 746 | it != group()->members().constEnd(); | ||
747 | ++it) | 747 | ++it) | ||
748 | if ((*it)->hasTransient(this, false)) | 748 | if ((*it)->hasTransient(this, false)) | ||
749 | result.append(*it); | 749 | result.append(*it); | ||
750 | return result; | 750 | return result; | ||
751 | } | 751 | } | ||
752 | 752 | | |||
753 | AbstractClient* Client::findModal(bool allow_itself) | 753 | AbstractClient* X11Client::findModal(bool allow_itself) | ||
754 | { | 754 | { | ||
755 | for (auto it = transients().constBegin(); | 755 | for (auto it = transients().constBegin(); | ||
756 | it != transients().constEnd(); | 756 | it != transients().constEnd(); | ||
757 | ++it) | 757 | ++it) | ||
758 | if (AbstractClient* ret = (*it)->findModal(true)) | 758 | if (AbstractClient* ret = (*it)->findModal(true)) | ||
759 | return ret; | 759 | return ret; | ||
760 | if (isModal() && allow_itself) | 760 | if (isModal() && allow_itself) | ||
761 | return this; | 761 | return this; | ||
762 | return nullptr; | 762 | return nullptr; | ||
763 | } | 763 | } | ||
764 | 764 | | |||
765 | // Client::window_group only holds the contents of the hint, | 765 | // X11Client::window_group only holds the contents of the hint, | ||
766 | // but it should be used only to find the group, not for anything else | 766 | // but it should be used only to find the group, not for anything else | ||
767 | // Argument is only when some specific group needs to be set. | 767 | // Argument is only when some specific group needs to be set. | ||
768 | void Client::checkGroup(Group* set_group, bool force) | 768 | void X11Client::checkGroup(Group* set_group, bool force) | ||
769 | { | 769 | { | ||
770 | Group* old_group = in_group; | 770 | Group* old_group = in_group; | ||
771 | if (old_group != nullptr) | 771 | if (old_group != nullptr) | ||
772 | old_group->ref(); // turn off automatic deleting | 772 | old_group->ref(); // turn off automatic deleting | ||
773 | if (set_group != nullptr) { | 773 | if (set_group != nullptr) { | ||
774 | if (set_group != in_group) { | 774 | if (set_group != in_group) { | ||
775 | if (in_group != nullptr) | 775 | if (in_group != nullptr) | ||
776 | in_group->removeMember(this); | 776 | in_group->removeMember(this); | ||
777 | in_group = set_group; | 777 | in_group = set_group; | ||
778 | in_group->addMember(this); | 778 | in_group->addMember(this); | ||
779 | } | 779 | } | ||
780 | } else if (info->groupLeader() != XCB_WINDOW_NONE) { | 780 | } else if (info->groupLeader() != XCB_WINDOW_NONE) { | ||
781 | Group* new_group = workspace()->findGroup(info->groupLeader()); | 781 | Group* new_group = workspace()->findGroup(info->groupLeader()); | ||
782 | Client *t = qobject_cast<Client*>(transientFor()); | 782 | X11Client *t = qobject_cast<X11Client *>(transientFor()); | ||
783 | if (t != nullptr && t->group() != new_group) { | 783 | if (t != nullptr && t->group() != new_group) { | ||
784 | // move the window to the right group (e.g. a dialog provided | 784 | // move the window to the right group (e.g. a dialog provided | ||
785 | // by different app, but transient for this one, so make it part of that group) | 785 | // by different app, but transient for this one, so make it part of that group) | ||
786 | new_group = t->group(); | 786 | new_group = t->group(); | ||
787 | } | 787 | } | ||
788 | if (new_group == nullptr) // doesn't exist yet | 788 | if (new_group == nullptr) // doesn't exist yet | ||
789 | new_group = new Group(info->groupLeader()); | 789 | new_group = new Group(info->groupLeader()); | ||
790 | if (new_group != in_group) { | 790 | if (new_group != in_group) { | ||
791 | if (in_group != nullptr) | 791 | if (in_group != nullptr) | ||
792 | in_group->removeMember(this); | 792 | in_group->removeMember(this); | ||
793 | in_group = new_group; | 793 | in_group = new_group; | ||
794 | in_group->addMember(this); | 794 | in_group->addMember(this); | ||
795 | } | 795 | } | ||
796 | } else { | 796 | } else { | ||
797 | if (Client *t = qobject_cast<Client*>(transientFor())) { | 797 | if (X11Client *t = qobject_cast<X11Client *>(transientFor())) { | ||
798 | // doesn't have window group set, but is transient for something | 798 | // doesn't have window group set, but is transient for something | ||
799 | // so make it part of that group | 799 | // so make it part of that group | ||
800 | Group* new_group = t->group(); | 800 | Group* new_group = t->group(); | ||
801 | if (new_group != in_group) { | 801 | if (new_group != in_group) { | ||
802 | if (in_group != nullptr) | 802 | if (in_group != nullptr) | ||
803 | in_group->removeMember(this); | 803 | in_group->removeMember(this); | ||
804 | in_group = t->group(); | 804 | in_group = t->group(); | ||
805 | in_group->addMember(this); | 805 | in_group->addMember(this); | ||
▲ Show 20 Lines • Show All 73 Lines • ▼ Show 20 Line(s) | |||||
879 | if (old_group != nullptr) | 879 | if (old_group != nullptr) | ||
880 | old_group->deref(); // can be now deleted if empty | 880 | old_group->deref(); // can be now deleted if empty | ||
881 | checkGroupTransients(); | 881 | checkGroupTransients(); | ||
882 | checkActiveModal(); | 882 | checkActiveModal(); | ||
883 | workspace()->updateClientLayer(this); | 883 | workspace()->updateClientLayer(this); | ||
884 | } | 884 | } | ||
885 | 885 | | |||
886 | // used by Workspace::findClientLeaderGroup() | 886 | // used by Workspace::findClientLeaderGroup() | ||
887 | void Client::changeClientLeaderGroup(Group* gr) | 887 | void X11Client::changeClientLeaderGroup(Group* gr) | ||
888 | { | 888 | { | ||
889 | // transientFor() != NULL are in the group of their mainwindow, so keep them there | 889 | // transientFor() != NULL are in the group of their mainwindow, so keep them there | ||
890 | if (transientFor() != nullptr) | 890 | if (transientFor() != nullptr) | ||
891 | return; | 891 | return; | ||
892 | // also don't change the group for window which have group set | 892 | // also don't change the group for window which have group set | ||
893 | if (info->groupLeader()) | 893 | if (info->groupLeader()) | ||
894 | return; | 894 | return; | ||
895 | checkGroup(gr); // change group | 895 | checkGroup(gr); // change group | ||
896 | } | 896 | } | ||
897 | 897 | | |||
898 | bool Client::check_active_modal = false; | 898 | bool X11Client::check_active_modal = false; | ||
899 | 899 | | |||
900 | void Client::checkActiveModal() | 900 | void X11Client::checkActiveModal() | ||
901 | { | 901 | { | ||
902 | // if the active window got new modal transient, activate it. | 902 | // if the active window got new modal transient, activate it. | ||
903 | // cannot be done in AddTransient(), because there may temporarily | 903 | // cannot be done in AddTransient(), because there may temporarily | ||
904 | // exist loops, breaking findModal | 904 | // exist loops, breaking findModal | ||
905 | Client* check_modal = dynamic_cast<Client*>(workspace()->mostRecentlyActivatedClient()); | 905 | X11Client *check_modal = dynamic_cast<X11Client *>(workspace()->mostRecentlyActivatedClient()); | ||
906 | if (check_modal != nullptr && check_modal->check_active_modal) { | 906 | if (check_modal != nullptr && check_modal->check_active_modal) { | ||
907 | Client* new_modal = dynamic_cast<Client*>(check_modal->findModal()); | 907 | X11Client *new_modal = dynamic_cast<X11Client *>(check_modal->findModal()); | ||
908 | if (new_modal != nullptr && new_modal != check_modal) { | 908 | if (new_modal != nullptr && new_modal != check_modal) { | ||
909 | if (!new_modal->isManaged()) | 909 | if (!new_modal->isManaged()) | ||
910 | return; // postpone check until end of manage() | 910 | return; // postpone check until end of manage() | ||
911 | workspace()->activateClient(new_modal); | 911 | workspace()->activateClient(new_modal); | ||
912 | } | 912 | } | ||
913 | check_modal->check_active_modal = false; | 913 | check_modal->check_active_modal = false; | ||
914 | } | 914 | } | ||
915 | } | 915 | } | ||
916 | 916 | | |||
917 | } // namespace | 917 | } // namespace |