KWin
Loading...
Searching...
No Matches
activation.cpp
Go to the documentation of this file.
1/*
2 KWin - the KDE window manager
3 This file is part of the KDE project.
4
5 SPDX-FileCopyrightText: 1999, 2000 Matthias Ettrich <ettrich@kde.org>
6 SPDX-FileCopyrightText: 2003 Lubos Lunak <l.lunak@kde.org>
7
8 SPDX-License-Identifier: GPL-2.0-or-later
9*/
10
11/*
12
13 This file contains things relevant to window activation and focus
14 stealing prevention.
15
16*/
17
18#include "cursor.h"
19#include "focuschain.h"
20#include "netinfo.h"
21#include "workspace.h"
22#include "x11window.h"
23#if KWIN_BUILD_ACTIVITIES
24#include "activities.h"
25#endif
26#include "virtualdesktops.h"
27
28#include <KLocalizedString>
29#include <kstartupinfo.h>
30#include <kstringhandler.h>
31
32#include "atoms.h"
33#include "group.h"
34#include "rules.h"
35#include "useractions.h"
36#include <QDebug>
37
38namespace KWin
39{
40
41/*
42 Prevention of focus stealing:
43
44 KWin tries to prevent unwanted changes of focus, that would result
45 from mapping a new window. Also, some nasty applications may try
46 to force focus change even in cases when ICCCM 4.2.7 doesn't allow it
47 (e.g. they may try to activate their main window because the user
48 definitely "needs" to see something happened - misusing
49 of QWidget::setActiveWindow() may be such case).
50
51 There are 4 ways how a window may become active:
52 - the user changes the active window (e.g. focus follows mouse, clicking
53 on some window's titlebar) - the change of focus will
54 be done by KWin, so there's nothing to solve in this case
55 - the change of active window will be requested using the _NET_ACTIVE_WINDOW
56 message (handled in RootInfo::changeActiveWindow()) - such requests
57 will be obeyed, because this request is meant mainly for e.g. taskbar
58 asking the WM to change the active window as a result of some user action.
59 Normal applications should use this request only rarely in special cases.
60 See also below the discussion of _NET_ACTIVE_WINDOW_TRANSFER.
61 - the change of active window will be done by performing XSetInputFocus()
62 on a window that's not currently active. ICCCM 4.2.7 describes when
63 the application may perform change of input focus. In order to handle
64 misbehaving applications, KWin will try to detect focus changes to
65 windows that don't belong to currently active application, and restore
66 focus back to the currently active window, instead of activating the window
67 that got focus (unfortunately there's no way to FocusChangeRedirect similar
68 to e.g. SubstructureRedirect, so there will be short time when the focus
69 will be changed). The check itself that's done is
70 Workspace::allowWindowActivation() (see below).
71 - a new window will be mapped - this is the most complicated case. If
72 the new window belongs to the currently active application, it may be safely
73 mapped on top and activated. The same if there's no active window,
74 or the active window is the desktop. These checks are done by
75 Workspace::allowWindowActivation().
76 Following checks need to compare times. One time is the timestamp
77 of last user action in the currently active window, the other time is
78 the timestamp of the action that originally caused mapping of the new window
79 (e.g. when the application was started). If the first time is newer than
80 the second one, the window will not be activated, as that indicates
81 futher user actions took place after the action leading to this new
82 mapped window. This check is done by Workspace::allowWindowActivation().
83 There are several ways how to get the timestamp of action that caused
84 the new mapped window (done in X11Window::readUserTimeMapTimestamp()) :
85 - the window may have the _NET_WM_USER_TIME property. This way
86 the application may either explicitly request that the window is not
87 activated (by using 0 timestamp), or the property contains the time
88 of last user action in the application.
89 - KWin itself tries to detect time of last user action in every window,
90 by watching KeyPress and ButtonPress events on windows. This way some
91 events may be missed (if they don't propagate to the toplevel window),
92 but it's good as a fallback for applications that don't provide
93 _NET_WM_USER_TIME, and missing some events may at most lead
94 to unwanted focus stealing.
95 - the timestamp may come from application startup notification.
96 Application startup notification, if it exists for the new mapped window,
97 should include time of the user action that caused it.
98 - if there's no timestamp available, it's checked whether the new window
99 belongs to some already running application - if yes, the timestamp
100 will be 0 (i.e. refuse activation)
101 - if the window is from session restored window, the timestamp will
102 be 0 too, unless this application was the active one at the time
103 when the session was saved, in which case the window will be
104 activated if there wasn't any user interaction since the time
105 KWin was started.
106 - as the last resort, the _KDE_NET_USER_CREATION_TIME timestamp
107 is used. For every toplevel window that is created (see CreateNotify
108 handling), this property is set to the at that time current time.
109 Since at this time it's known that the new window doesn't belong
110 to any existing application (better said, the application doesn't
111 have any other window mapped), it is either the very first window
112 of the application, or it is the only window of the application
113 that was hidden before. The latter case is handled by removing
114 the property from windows before withdrawing them, making
115 the timestamp empty for next mapping of the window. In the sooner
116 case, the timestamp will be used. This helps in case when
117 an application is launched without application startup notification,
118 it creates its mainwindow, and starts its initialization (that
119 may possibly take long time). The timestamp used will be older
120 than any user action done after launching this application.
121 - if no timestamp is found at all, the window is activated.
122 The check whether two windows belong to the same application (same
123 process) is done in X11Window::belongToSameApplication(). Not 100% reliable,
124 but hopefully 99,99% reliable.
125
126 As a somewhat special case, window activation is always enabled when
127 session saving is in progress. When session saving, the session
128 manager allows only one application to interact with the user.
129 Not allowing window activation in such case would result in e.g. dialogs
130 not becoming active, so focus stealing prevention would cause here
131 more harm than good.
132
133 Windows that attempted to become active but KWin prevented this will
134 be marked as demanding user attention. They'll get
135 the _NET_WM_STATE_DEMANDS_ATTENTION state, and the taskbar should mark
136 them specially (blink, etc.). The state will be reset when the window
137 eventually really becomes active.
138
139 There are two more ways how a window can become obtrusive, window stealing
140 focus: By showing above the active window, by either raising itself,
141 or by moving itself on the active desktop.
142 - KWin will refuse raising non-active window above the active one,
143 unless they belong to the same application. Applications shouldn't
144 raise their windows anyway (unless the app wants to raise one
145 of its windows above another of its windows).
146 - KWin activates windows moved to the current desktop (as that seems
147 logical from the user's point of view, after sending the window
148 there directly from KWin, or e.g. using pager). This means
149 applications shouldn't send their windows to another desktop
150 (SELI TODO - but what if they do?)
151
152 Special cases I can think of:
153 - konqueror reusing, i.e. kfmclient tells running Konqueror instance
154 to open new window
155 - without focus stealing prevention - no problem
156 - with ASN (application startup notification) - ASN is forwarded,
157 and because it's newer than the instance's user timestamp,
158 it takes precedence
159 - without ASN - user timestamp needs to be reset, otherwise it would
160 be used, and it's old; moreover this new window mustn't be detected
161 as window belonging to already running application, or it wouldn't
162 be activated - see X11Window::sameAppWindowRoleMatch() for the (rather ugly)
163 hack
164 - konqueror preloading, i.e. window is created in advance, and kfmclient
165 tells this Konqueror instance to show it later
166 - without focus stealing prevention - no problem
167 - with ASN - ASN is forwarded, and because it's newer than the instance's
168 user timestamp, it takes precedence
169 - without ASN - user timestamp needs to be reset, otherwise it would
170 be used, and it's old; also, creation timestamp is changed to
171 the time the instance starts (re-)initializing the window,
172 this ensures creation timestamp will still work somewhat even in this case
173 - KUniqueApplication - when the window is already visible, and the new instance
174 wants it to activate
175 - without focus stealing prevention - _NET_ACTIVE_WINDOW - no problem
176 - with ASN - ASN is forwarded, and set on the already visible window, KWin
177 treats the window as new with that ASN
178 - without ASN - _NET_ACTIVE_WINDOW as application request is used,
179 and there's no really usable timestamp, only timestamp
180 from the time the (new) application instance was started,
181 so KWin will activate the window *sigh*
182 - the bad thing here is that there's absolutely no chance to recognize
183 the case of starting this KUniqueApp from Konsole (and thus wanting
184 the already visible window to become active) from the case
185 when something started this KUniqueApp without ASN (in which case
186 the already visible window shouldn't become active)
187 - the only solution is using ASN for starting applications, at least silent
188 (i.e. without feedback)
189 - when one application wants to activate another application's window (e.g. KMail
190 activating already running KAddressBook window ?)
191 - without focus stealing prevention - _NET_ACTIVE_WINDOW - no problem
192 - with ASN - can't be here, it's the KUniqueApp case then
193 - without ASN - _NET_ACTIVE_WINDOW as application request should be used,
194 KWin will activate the new window depending on the timestamp and
195 whether it belongs to the currently active application
196
197 _NET_ACTIVE_WINDOW usage:
198 data.l[0]= 1 ->app request
199 = 2 ->pager request
200 = 0 - backwards compatibility
201 data.l[1]= timestamp
202*/
203
204//****************************************
205// Workspace
206//****************************************
207
217{
218 if (m_activeWindow == window) {
219 return;
220 }
221
222 if (active_popup && m_activePopupWindow != window && m_setActiveWindowRecursion == 0) {
223 closeActivePopup();
224 }
225 if (m_userActionsMenu->hasWindow() && !m_userActionsMenu->isMenuWindow(window) && m_setActiveWindowRecursion == 0) {
226 m_userActionsMenu->close();
227 }
228 StackingUpdatesBlocker blocker(this);
229 ++m_setActiveWindowRecursion;
230 updateFocusMousePosition(Cursors::self()->mouse()->pos());
231 if (m_activeWindow != nullptr) {
232 // note that this may call setActiveWindow( NULL ), therefore the recursion counter
233 m_activeWindow->setActive(false);
234 }
235 m_activeWindow = window;
236 Q_ASSERT(window == nullptr || window->isActive());
237
238 if (m_activeWindow) {
239 m_lastActiveWindow = m_activeWindow;
240 m_focusChain->update(m_activeWindow, FocusChain::MakeFirst);
241 m_activeWindow->demandAttention(false);
242
243 // activating a client can cause a non active fullscreen window to loose the ActiveLayer status on > 1 screens
244 if (outputs().count() > 1) {
245 for (auto it = m_windows.begin(); it != m_windows.end(); ++it) {
246 if (*it != m_activeWindow && (*it)->layer() == ActiveLayer && (*it)->output() == m_activeWindow->output()) {
247 (*it)->updateLayer();
248 }
249 }
250 }
251 }
252
253 updateToolWindows(false);
254 if (window) {
256 } else {
258 }
259
260 updateStackingOrder(); // e.g. fullscreens have different layer when active/not-active
261
262 if (rootInfo()) {
263 rootInfo()->setActiveClient(m_activeWindow);
264 }
265
266 Q_EMIT windowActivated(m_activeWindow);
267 --m_setActiveWindowRecursion;
268}
269
282void Workspace::activateWindow(Window *window, bool force)
283{
284 if (window == nullptr) {
285 focusToNull();
286 setActiveWindow(nullptr);
287 return;
288 }
289 if (window->isDeleted()) {
290 return;
291 }
292 if (window->isHiddenByShowDesktop()) {
293 ++block_focus;
294 setShowingDesktop(false);
295 --block_focus;
296 }
297 raiseWindow(window);
298 if (!window->isOnCurrentDesktop()) {
299 ++block_focus;
300 switch (options->activationDesktopPolicy()) {
302 VirtualDesktopManager::self()->setCurrent(window->desktops().constLast());
303 break;
305 window->enterDesktop(VirtualDesktopManager::self()->currentDesktop());
306 break;
308 break;
309 }
310 --block_focus;
311 }
312#if KWIN_BUILD_ACTIVITIES
313 if (!window->isOnCurrentActivity()) {
314 ++block_focus;
315 // DBUS!
316 // first isn't necessarily best, but it's easiest
317 m_activities->setCurrent(window->activities().constFirst());
318 --block_focus;
319 }
320#endif
321 if (window->isMinimized()) {
322 window->setMinimized(false);
323 }
324
325 // ensure the window is really visible - could eg. be a hidden utility window, see bug #348083
326 window->setHidden(false);
327
328 // TODO force should perhaps allow this only if the window already contains the mouse
329 if (options->focusPolicyIsReasonable() || force) {
330 requestFocus(window, force);
331 }
332
333 // Don't update user time for windows that have focus stealing workaround.
334 // As they usually belong to the current active window but fail to provide
335 // this information, updating their user time would make the user time
336 // of the currently active window old, and reject further activation for it.
337 // E.g. typing URL in minicli which will show kio_uiserver dialog (with workaround),
338 // and then kdesktop shows dialog about SSL certificate.
339 // This needs also avoiding user creation time in X11Window::readUserTimeMapTimestamp().
340 if (X11Window *x11Window = dynamic_cast<X11Window *>(window)) {
341 // updateUserTime is X11 specific
342 x11Window->updateUserTime();
343 }
344
345 m_quickTileCombineTimer->stop();
346}
347
355bool Workspace::requestFocus(Window *window, bool force)
356{
357 return takeActivity(window, force ? ActivityFocusForce : ActivityFocus);
358}
359
360bool Workspace::takeActivity(Window *window, ActivityFlags flags)
361{
362 // the 'if ( window == m_activeWindow ) return;' optimization mustn't be done here
363 if (!focusChangeEnabled() && (window != m_activeWindow)) {
364 flags &= ~ActivityFocus;
365 }
366
367 if (!window) {
368 focusToNull();
369 return true;
370 }
371
372 if (flags & ActivityFocus) {
373 Window *modal = window->findModal();
374 if (modal != nullptr && modal != window) {
375 if (modal->desktops() != window->desktops()) {
376 modal->setDesktops(window->desktops());
377 }
378 if (!modal->isShown() && !modal->isMinimized()) { // forced desktop or utility window
379 activateWindow(modal); // activating a minimized blocked window will unminimize its modal implicitly
380 }
381 // if the click was inside the window (i.e. handled is set),
382 // but it has a modal, there's no need to use handled mode, because
383 // the modal doesn't get the click anyway
384 // raising of the original window needs to be still done
385 if (flags & ActivityRaise) {
386 raiseWindow(window);
387 }
388 window = modal;
389 }
391 }
392 if (!flags.testFlag(ActivityFocusForce) && (window->isDock() || window->isSplash())) {
393 // toplevel menus and dock windows don't take focus if not forced
394 // and don't have a flag that they take focus
395 if (!window->dockWantsInput()) {
396 flags &= ~ActivityFocus;
397 }
398 }
399 if (window->isShade()) {
400 if (window->wantsInput() && (flags & ActivityFocus)) {
401 // window cannot accept focus, but at least the window should be active (window menu, et. al. )
402 window->setActive(true);
403 focusToNull();
404 }
405 flags &= ~ActivityFocus;
406 }
407 if (!window->isShown()) { // shouldn't happen, call activateWindow() if needed
408 qCWarning(KWIN_CORE) << "takeActivity: not shown";
409 return false;
410 }
411
412 bool ret = true;
413
414 if (flags & ActivityFocus) {
415 ret &= window->takeFocus();
416 }
417 if (flags & ActivityRaise) {
418 workspace()->raiseWindow(window);
419 }
420
421 if (!window->isOnActiveOutput()) {
422 setActiveOutput(window->output());
423 }
424
425 return ret;
426}
427
436{
437 Q_ASSERT(!window->isShown() || !window->isOnCurrentDesktop() || !window->isOnCurrentActivity());
438 activateNextWindow(window);
439}
440
442{
443 auto it = stackingOrder().constEnd();
444 while (it != stackingOrder().constBegin()) {
445 auto window = *(--it);
446 if (!window->isClient()) {
447 continue;
448 }
449
450 // rule out windows which are not really visible.
451 // the screen test is rather superfluous for xrandr & twinview since the geometry would differ -> TODO: might be dropped
452 if (!(window->isShown() && window->isOnCurrentDesktop() && window->isOnCurrentActivity() && window->isOnOutput(output) && !window->isShade())) {
453 continue;
454 }
455
456 if (exclusiveContains(window->frameGeometry(), Cursors::self()->mouse()->pos())) {
457 return window;
458 }
459 }
460 return nullptr;
461}
462
463// deactivates 'window' and activates next window
465{
466 // if 'c' is not the active or the to-become active one, do nothing
467 if (!(window == m_activeWindow || (should_get_focus.count() > 0 && window == should_get_focus.last()))) {
468 return false;
469 }
470
471 closeActivePopup();
472
473 if (window != nullptr) {
474 if (window == m_activeWindow) {
475 setActiveWindow(nullptr);
476 }
477 should_get_focus.removeAll(window);
478 }
479
480 // if blocking focus, move focus to the desktop later if needed
481 // in order to avoid flickering
482 if (!focusChangeEnabled()) {
483 focusToNull();
484 return true;
485 }
486
488 return false;
489 }
490
491 Window *focusCandidate = nullptr;
492
493 VirtualDesktop *desktop = VirtualDesktopManager::self()->currentDesktop();
494
495 if (!focusCandidate && showingDesktop()) {
496 focusCandidate = findDesktop(true, desktop); // to not break the state
497 }
498
499 if (!focusCandidate && options->isNextFocusPrefersMouse()) {
500 focusCandidate = windowUnderMouse(window ? window->output() : workspace()->activeOutput());
501 if (focusCandidate && (focusCandidate == window || focusCandidate->isDesktop())) {
502 // should rather not happen, but it cannot get the focus. rest of usability is tested above
503 focusCandidate = nullptr;
504 }
505 }
506
507 if (!focusCandidate) { // no suitable window under the mouse -> find sth. else
508 // first try to pass the focus to the (former) active clients leader
509 if (window && window->isTransient()) {
510 auto leaders = window->mainWindows();
511 if (leaders.count() == 1 && m_focusChain->isUsableFocusCandidate(leaders.at(0), window)) {
512 focusCandidate = leaders.at(0);
513 raiseWindow(focusCandidate); // also raise - we don't know where it came from
514 }
515 }
516 if (!focusCandidate) {
517 // nope, ask the focus chain for the next candidate
518 focusCandidate = m_focusChain->nextForDesktop(window, desktop);
519 }
520 }
521
522 if (focusCandidate == nullptr) { // last chance: focus the desktop
523 focusCandidate = findDesktop(true, desktop);
524 }
525
526 if (focusCandidate != nullptr) {
527 requestFocus(focusCandidate);
528 } else {
529 focusToNull();
530 }
531
532 return true;
533}
534
536{
538 return;
539 }
540 closeActivePopup();
541 VirtualDesktop *desktop = VirtualDesktopManager::self()->currentDesktop();
542 Window *get_focus = m_focusChain->getForActivation(desktop, output);
543 if (get_focus == nullptr) {
544 get_focus = findDesktop(true, desktop);
545 }
546 if (get_focus != nullptr && get_focus != mostRecentlyActivatedWindow()) {
547 requestFocus(get_focus);
548 }
549 setActiveOutput(output);
550}
551
552void Workspace::gotFocusIn(const Window *window)
553{
554 if (should_get_focus.contains(window)) {
555 // remove also all sooner elements that should have got FocusIn,
556 // but didn't for some reason (and also won't anymore, because they were sooner)
557 while (should_get_focus.first() != window) {
558 should_get_focus.pop_front();
559 }
560 should_get_focus.pop_front(); // remove 'window'
561 }
562}
563
565{
566 should_get_focus.append(window);
567 updateStackingOrder(); // e.g. fullscreens have different layer when active/not-active
568}
569
570// basically the same like allowWindowActivation(), this time allowing
571// a window to be fully raised upon its own request (XRaiseWindow),
572// if refused, it will be raised only on top of windows belonging
573// to the same application
574bool Workspace::allowFullClientRaising(const KWin::Window *window, xcb_timestamp_t time)
575{
576 int level = window->rules()->checkFSP(options->focusStealingPreventionLevel());
577 if (sessionManager()->state() == SessionState::Saving && level <= 2) { // <= normal
578 return true;
579 }
581 if (level == 0) { // none
582 return true;
583 }
584 if (level == 4) { // extreme
585 return false;
586 }
587 if (ac == nullptr || ac->isDesktop()) {
588 qCDebug(KWIN_CORE) << "Raising: No window active, allowing";
589 return true; // no active window -> always allow
590 }
591 // TODO window urgency -> return true?
593 qCDebug(KWIN_CORE) << "Raising: Belongs to active application";
594 return true;
595 }
596 if (level == 3) { // high
597 return false;
598 }
599 xcb_timestamp_t user_time = ac->userTime();
600 qCDebug(KWIN_CORE) << "Raising, compared:" << time << ":" << user_time
601 << ":" << (NET::timestampCompare(time, user_time) >= 0);
602 return NET::timestampCompare(time, user_time) >= 0; // time >= user_time
603}
604
612{
613 // this updateXTime() is necessary - as FocusIn events don't have
614 // a timestamp *sigh*, kwin's timestamp would be older than the timestamp
615 // that was used by whoever caused the focus change, and therefore
616 // the attempt to restore the focus would fail due to old timestamp
617 kwinApp()->updateXTime();
618 if (should_get_focus.count() > 0) {
619 return requestFocus(should_get_focus.last());
620 } else if (m_lastActiveWindow) {
621 return requestFocus(m_lastActiveWindow);
622 }
623 return true;
624}
625
627{
628 if (set) {
629 attention_chain.removeAll(window);
630 attention_chain.prepend(window);
631 } else {
632 attention_chain.removeAll(window);
633 }
634}
635
636//********************************************
637// Client
638//********************************************
639
646void X11Window::updateUserTime(xcb_timestamp_t time)
647{
648 // copied in Group::updateUserTime
649 if (time == XCB_TIME_CURRENT_TIME) {
650 kwinApp()->updateXTime();
651 time = xTime();
652 }
653 if (time != -1U
654 && (m_userTime == XCB_TIME_CURRENT_TIME
655 || NET::timestampCompare(time, m_userTime) > 0)) { // time > user_time
656 m_userTime = time;
657 shade_below = nullptr; // do not hover re-shade a window after it got interaction
658 }
659 group()->updateUserTime(m_userTime);
660}
661
662xcb_timestamp_t X11Window::readUserCreationTime() const
663{
664 Xcb::Property prop(false, window(), atoms->kde_net_wm_user_creation_time, XCB_ATOM_CARDINAL, 0, 1);
665 return prop.value<xcb_timestamp_t>(-1);
666}
667
668xcb_timestamp_t X11Window::readUserTimeMapTimestamp(const KStartupInfoId *asn_id, const KStartupInfoData *asn_data,
669 bool session) const
670{
671 xcb_timestamp_t time = info->userTime();
672 // qDebug() << "User timestamp, initial:" << time;
673 //^^ this deadlocks kwin --replace sometimes.
674
675 // newer ASN timestamp always replaces user timestamp, unless user timestamp is 0
676 // helps e.g. with konqy reusing
677 if (asn_data != nullptr && time != 0) {
678 if (asn_id->timestamp() != 0
679 && (time == -1U || NET::timestampCompare(asn_id->timestamp(), time) > 0)) {
680 time = asn_id->timestamp();
681 }
682 }
683 qCDebug(KWIN_CORE) << "User timestamp, ASN:" << time;
684 if (time == -1U) {
685 // The window doesn't have any timestamp.
686 // If it's the first window for its application
687 // (i.e. there's no other window from the same app),
688 // use the _KDE_NET_WM_USER_CREATION_TIME trick.
689 // Otherwise, refuse activation of a window
690 // from already running application if this application
691 // is not the active one (unless focus stealing prevention is turned off).
692 X11Window *act = dynamic_cast<X11Window *>(workspace()->mostRecentlyActivatedWindow());
693 if (act != nullptr && !belongToSameApplication(act, this, SameApplicationCheck::RelaxedForActive)) {
694 bool first_window = true;
695 auto sameApplicationActiveHackPredicate = [this](const X11Window *cl) {
696 // ignore already existing splashes, toolbars, utilities and menus,
697 // as the app may show those before the main window
698 return !cl->isSplash() && !cl->isToolbar() && !cl->isUtility() && !cl->isMenu()
700 };
701 if (isTransient()) {
702 auto clientMainClients = [this]() {
703 QList<X11Window *> ret;
704 const auto mcs = mainWindows();
705 for (auto mc : mcs) {
706 if (X11Window *c = dynamic_cast<X11Window *>(mc)) {
707 ret << c;
708 }
709 }
710 return ret;
711 };
712 if (act->hasTransient(this, true)) {
713 ; // is transient for currently active window, even though it's not
714 // the same app (e.g. kcookiejar dialog) -> allow activation
715 } else if (groupTransient() && findInList<X11Window, X11Window>(clientMainClients(), sameApplicationActiveHackPredicate) == nullptr) {
716 ; // standalone transient
717 } else {
718 first_window = false;
719 }
720 } else {
721 if (workspace()->findClient(sameApplicationActiveHackPredicate)) {
722 first_window = false;
723 }
724 }
725 // don't refuse if focus stealing prevention is turned off
726 if (!first_window && rules()->checkFSP(options->focusStealingPreventionLevel()) > 0) {
727 qCDebug(KWIN_CORE) << "User timestamp, already exists:" << 0;
728 return 0; // refuse activation
729 }
730 }
731 // Creation time would just mess things up during session startup,
732 // as possibly many apps are started up at the same time.
733 // If there's no active window yet, no timestamp will be needed,
734 // as plain Workspace::allowWindowActivation() will return true
735 // in such case. And if there's already active window,
736 // it's better not to activate the new one.
737 // Unless it was the active window at the time
738 // of session saving and there was no user interaction yet,
739 // this check will be done in manage().
740 if (session) {
741 return -1U;
742 }
743 time = readUserCreationTime();
744 }
745 qCDebug(KWIN_CORE) << "User timestamp, final:" << this << ":" << time;
746 return time;
747}
748
749xcb_timestamp_t X11Window::userTime() const
750{
751 xcb_timestamp_t time = m_userTime;
752 if (time == 0) { // doesn't want focus after showing
753 return 0;
754 }
755 Q_ASSERT(group() != nullptr);
756 if (time == -1U
757 || (group()->userTime() != -1U
758 && NET::timestampCompare(group()->userTime(), time) > 0)) {
759 time = group()->userTime();
760 }
761 return time;
762}
763
765{
766 updateUrgency(); // demand attention again if it's still urgent
767 info->setState(isActive() ? NET::Focused : NET::States(), NET::Focused);
768}
769
770void X11Window::startupIdChanged()
771{
772 KStartupInfoId asn_id;
773 KStartupInfoData asn_data;
774 bool asn_valid = workspace()->checkStartupNotification(window(), asn_id, asn_data);
775 if (!asn_valid) {
776 return;
777 }
778 // If the ASN contains desktop, move it to the desktop, otherwise move it to the current
779 // desktop (since the new ASN should make the window act like if it's a new application
780 // launched). However don't affect the window's desktop if it's set to be on all desktops.
781
782 if (asn_data.desktop() != 0 && !isOnAllDesktops()) {
783 if (asn_data.desktop() == -1) {
784 workspace()->sendWindowToDesktops(this, {}, true);
785 } else {
786 if (VirtualDesktop *desktop = VirtualDesktopManager::self()->desktopForX11Id(asn_data.desktop())) {
787 workspace()->sendWindowToDesktops(this, {desktop}, true);
788 }
789 }
790 }
791
792 if (asn_data.xinerama() != -1) {
793 Output *output = workspace()->xineramaIndexToOutput(asn_data.xinerama());
794 if (output) {
796 }
797 }
798 const xcb_timestamp_t timestamp = asn_id.timestamp();
799 if (timestamp != 0) {
800 bool activate = allowWindowActivation(timestamp);
801 if (activate) {
802 workspace()->activateWindow(this);
803 } else {
805 }
806 }
807}
808
809void X11Window::updateUrgency()
810{
811 if (info->urgency()) {
813 }
814}
815
816namespace FSP
817{
825}
826
827// focus_in -> the window got FocusIn event
828bool X11Window::allowWindowActivation(xcb_timestamp_t time, bool focus_in)
829{
830 auto window = this;
831 // options->focusStealingPreventionLevel :
832 // 0 - none - old KWin behaviour, new windows always get focus
833 // 1 - low - focus stealing prevention is applied normally, when unsure, activation is allowed
834 // 2 - normal - focus stealing prevention is applied normally, when unsure, activation is not allowed,
835 // this is the default
836 // 3 - high - new window gets focus only if it belongs to the active application,
837 // or when no window is currently active
838 // 4 - extreme - no window gets focus without user intervention
839 if (time == -1U) {
840 time = window->userTime();
841 }
842 const FSP::Level level = (FSP::Level)window->rules()->checkFSP(options->focusStealingPreventionLevel());
843 if (workspace()->sessionManager()->state() == SessionState::Saving && level <= FSP::Medium) { // <= normal
844 return true;
845 }
847 if (focus_in) {
848 if (workspace()->inShouldGetFocus(window)) {
849 return true; // FocusIn was result of KWin's action
850 }
851 // Before getting FocusIn, the active Client already
852 // got FocusOut, and therefore got deactivated.
853 ac = workspace()->lastActiveWindow();
854 }
855 if (time == 0) { // explicitly asked not to get focus
856 if (!window->rules()->checkAcceptFocus(false)) {
857 return false;
858 }
859 }
860 const FSP::Level protection = (FSP::Level)(ac ? ac->rules()->checkFPP(2) : FSP::None);
861
862 // stealing is unconditionally allowed (NETWM behavior)
863 if (level == FSP::None || protection == FSP::None) {
864 return true;
865 }
866
867 // The active window "grabs" the focus or stealing is generally forbidden
868 if (level == FSP::Extreme || protection == FSP::Extreme) {
869 return false;
870 }
871
872 // No active window, it's ok to pass focus
873 // NOTICE that extreme protection needs to be handled before to allow protection on unmanged windows
874 if (ac == nullptr || ac->isDesktop()) {
875 qCDebug(KWIN_CORE) << "Activation: No window active, allowing";
876 return true; // no active window -> always allow
877 }
878
879 // TODO window urgency -> return true?
880
881 // Unconditionally allow intra-window passing around for lower stealing protections
882 // unless the active window has High interest
884 qCDebug(KWIN_CORE) << "Activation: Belongs to active application";
885 return true;
886 }
887
888 // High FPS, not intr-window change. Only allow if the active window has only minor interest
889 if (level > FSP::Medium && protection > FSP::Low) {
890 return false;
891 }
892
893 if (time == -1U) { // no time known
894 qCDebug(KWIN_CORE) << "Activation: No timestamp at all";
895 // Only allow for Low protection unless active window has High interest in focus
896 if (level < FSP::Medium && protection < FSP::High) {
897 return true;
898 }
899 // no timestamp at all, don't activate - because there's also creation timestamp
900 // done on CreateNotify, this case should happen only in case application
901 // maps again already used window, i.e. this won't happen after app startup
902 return false;
903 }
904
905 // Low or medium FSP, usertime comparism is possible
906 const xcb_timestamp_t user_time = ac->userTime();
907 qCDebug(KWIN_CORE) << "Activation, compared:" << window << ":" << time << ":" << user_time
908 << ":" << (NET::timestampCompare(time, user_time) >= 0);
909 return NET::timestampCompare(time, user_time) >= 0; // time >= user_time
910}
911
912//****************************************
913// Group
914//****************************************
915
916void Group::startupIdChanged()
917{
918 KStartupInfoId asn_id;
919 KStartupInfoData asn_data;
920 bool asn_valid = workspace()->checkStartupNotification(leader_wid, asn_id, asn_data);
921 if (!asn_valid) {
922 return;
923 }
924 if (asn_id.timestamp() != 0 && user_time != -1U
925 && NET::timestampCompare(asn_id.timestamp(), user_time) > 0) {
926 user_time = asn_id.timestamp();
927 }
928}
929
930void Group::updateUserTime(xcb_timestamp_t time)
931{
932 // copy of X11Window::updateUserTime
933 if (time == XCB_CURRENT_TIME) {
934 kwinApp()->updateXTime();
935 time = xTime();
936 }
937 if (time != -1U
938 && (user_time == XCB_CURRENT_TIME
939 || NET::timestampCompare(time, user_time) > 0)) { // time > user_time
940 user_time = time;
941 }
942}
943
944} // namespace
Xcb::Atom kde_net_wm_user_creation_time
Definition atoms.h:39
QPointF pos()
Definition cursor.cpp:204
static Cursors * self()
Definition cursor.cpp:35
Cursor * mouse() const
Definition cursor.h:266
xcb_timestamp_t userTime() const
Definition group.h:73
void updateUserTime(xcb_timestamp_t time)
int focusStealingPreventionLevel
Definition options.h:137
ActivationDesktopPolicy activationDesktopPolicy
Definition options.h:112
@ SwitchToOtherDesktop
Definition options.h:339
@ BringToCurrentDesktop
Definition options.h:340
bool focusPolicyIsReasonable
Definition options.h:113
bool isNextFocusPrefersMouse() const
Definition options.h:248
void setActiveClient(Window *client)
Definition netinfo.cpp:237
SessionState state() const
Definition sm.cpp:361
bool isMenuWindow(const Window *window) const
void setMinimized(bool set)
Definition window.cpp:984
virtual QList< Window * > mainWindows() const
Definition window.cpp:2256
virtual bool isTransient() const
Definition window.cpp:2290
bool isShade() const
Definition window.h:1034
bool isShown() const
Definition window.cpp:4232
bool isOnCurrentActivity() const
Definition window.cpp:3097
static bool belongToSameApplication(const Window *c1, const Window *c2, SameApplicationChecks checks=SameApplicationChecks())
Definition window.cpp:426
void demandAttention(bool set=true)
Definition window.cpp:708
bool isHiddenByShowDesktop() const
Definition window.cpp:4257
void setActive(bool)
Definition window.cpp:499
bool isOnAllDesktops() const
Definition window.h:2032
virtual bool takeFocus()=0
bool isOnCurrentDesktop() const
Definition window.cpp:848
void setDesktops(QList< VirtualDesktop * > desktops)
Definition window.cpp:726
bool wantsInput
Definition window.h:409
bool isMinimized() const
Definition window.h:988
void setHidden(bool hidden)
Definition window.cpp:4242
bool isActive() const
Definition window.h:882
bool isDesktop() const
Definition window.h:1922
QList< KWin::VirtualDesktop * > desktops
Definition window.h:295
void enterDesktop(VirtualDesktop *desktop)
Definition window.cpp:786
virtual bool dockWantsInput() const
Definition window.cpp:2944
QStringList activities
Definition window.h:305
const WindowRules * rules() const
Definition window.h:1048
bool isSplash() const
Definition window.h:1942
virtual xcb_timestamp_t userTime() const
Definition window.cpp:431
bool isOnActiveOutput() const
Definition window.cpp:249
bool isDock() const
Definition window.h:1927
KWin::Layer layer
Definition window.h:529
KWin::Output * output
Definition window.h:111
virtual Window * findModal(bool allow_itself=false)=0
bool isDeleted() const
Definition window.cpp:540
bool checkDisableGlobalShortcuts(bool disable) const
int checkFSP(int fsp) const
int checkFPP(int fpp) const
void windowAttentionChanged(Window *, bool set)
Window * mostRecentlyActivatedWindow() const
Definition workspace.h:772
void windowHidden(Window *)
void activateWindow(Window *window, bool force=false)
const QList< Window * > & stackingOrder() const
Definition workspace.h:788
void sendWindowToOutput(Window *window, Output *output)
Window * windowUnderMouse(Output *output) const
Window * findDesktop(bool topmost, VirtualDesktop *desktop) const
Definition layers.cpp:253
Output * activeOutput() const
friend Workspace * workspace()
Definition workspace.h:830
void gotFocusIn(const Window *window)
void switchToOutput(Output *output)
void updateStackingOrder(bool propagate_new_windows=false)
Definition layers.cpp:93
void raiseWindow(Window *window, bool nogroup=false)
Definition layers.cpp:354
void windowActivated(KWin::Window *)
Window * lastActiveWindow() const
Definition workspace.h:422
bool checkStartupNotification(xcb_window_t w, KStartupInfoId &id, KStartupInfoData &data)
void updateFocusMousePosition(const QPointF &pos)
Definition workspace.h:820
bool showingDesktop() const
Definition workspace.h:804
QList< Output * > outputs() const
Definition workspace.h:762
void setActiveWindow(Window *window)
void sendWindowToDesktops(Window *window, const QList< VirtualDesktop * > &desktops, bool dont_activate)
bool activateNextWindow(Window *window)
bool requestFocus(Window *window, bool force=false)
void setActiveOutput(Output *output)
void cancelDelayFocus()
bool takeActivity(Window *window, ActivityFlags flags)
void setShowingDesktop(bool showing, bool animated=true)
SessionManager * sessionManager() const
Definition workspace.h:799
void disableGlobalShortcutsForClient(bool disable)
Output * xineramaIndexToOutput(int index) const
bool focusChangeEnabled()
Definition workspace.h:213
void setShouldGetFocus(Window *window)
QList< Window * > mainWindows() const override
bool isTransient() const override
Definition x11window.h:560
void doSetActive() override
void updateUserTime(xcb_timestamp_t time=XCB_TIME_CURRENT_TIME)
xcb_window_t window() const
bool allowWindowActivation(xcb_timestamp_t time=-1U, bool focus_in=false)
const Group * group() const override
Definition x11window.h:565
static bool belongToSameApplication(const X11Window *c1, const X11Window *c2, SameApplicationChecks checks=SameApplicationChecks())
xcb_timestamp_t userTime() const override
bool groupTransient() const override
Definition x11window.h:555
RootInfo * rootInfo()
Definition netinfo.h:59
KWIN_EXPORT xcb_timestamp_t xTime()
Definition xcb.h:29
Workspace * workspace()
Definition workspace.h:830
Options * options
Definition main.cpp:73
@ ActiveLayer
Definition globals.h:170
KWIN_EXPORT Atoms * atoms
Definition main.cpp:74