2020-08-02 22:22:19 +00:00
|
|
|
/*
|
|
|
|
KWin - the KDE window manager
|
|
|
|
This file is part of the KDE project.
|
2012-11-20 16:26:50 +00:00
|
|
|
|
2020-08-02 22:22:19 +00:00
|
|
|
SPDX-FileCopyrightText: 2012 Martin Gräßlin <mgraesslin@kde.org>
|
2012-11-20 16:26:50 +00:00
|
|
|
|
2020-08-02 22:22:19 +00:00
|
|
|
SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
*/
|
2012-11-20 16:26:50 +00:00
|
|
|
#include "focuschain.h"
|
2015-03-05 09:24:54 +00:00
|
|
|
#include "abstract_client.h"
|
2021-08-28 18:58:29 +00:00
|
|
|
#include "workspace.h"
|
2012-11-20 16:26:50 +00:00
|
|
|
|
|
|
|
namespace KWin
|
|
|
|
{
|
|
|
|
|
2013-04-05 07:41:25 +00:00
|
|
|
KWIN_SINGLETON_FACTORY_VARIABLE(FocusChain, s_manager)
|
2012-11-20 16:26:50 +00:00
|
|
|
|
|
|
|
FocusChain::FocusChain(QObject *parent)
|
|
|
|
: QObject(parent)
|
|
|
|
, m_separateScreenFocus(false)
|
Use nullptr everywhere
Summary:
Because KWin is a very old project, we use three kinds of null pointer
literals: 0, NULL, and nullptr. Since C++11, it's recommended to use
nullptr keyword.
This change converts all usages of 0 and NULL literal to nullptr. Even
though it breaks git history, we need to do it in order to have consistent
code as well to ease code reviews (it's very tempting for some people to
add unrelated changes to their patches, e.g. converting NULL to nullptr).
Test Plan: Compiles.
Reviewers: #kwin, davidedmundson, romangg
Reviewed By: #kwin, davidedmundson, romangg
Subscribers: romangg, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D23618
2019-09-19 14:46:54 +00:00
|
|
|
, m_activeClient(nullptr)
|
2012-11-20 16:26:50 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
FocusChain::~FocusChain()
|
|
|
|
{
|
Use nullptr everywhere
Summary:
Because KWin is a very old project, we use three kinds of null pointer
literals: 0, NULL, and nullptr. Since C++11, it's recommended to use
nullptr keyword.
This change converts all usages of 0 and NULL literal to nullptr. Even
though it breaks git history, we need to do it in order to have consistent
code as well to ease code reviews (it's very tempting for some people to
add unrelated changes to their patches, e.g. converting NULL to nullptr).
Test Plan: Compiles.
Reviewers: #kwin, davidedmundson, romangg
Reviewed By: #kwin, davidedmundson, romangg
Subscribers: romangg, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D23618
2019-09-19 14:46:54 +00:00
|
|
|
s_manager = nullptr;
|
2012-11-20 16:26:50 +00:00
|
|
|
}
|
|
|
|
|
2015-03-05 09:24:54 +00:00
|
|
|
void FocusChain::remove(AbstractClient *client)
|
2012-11-20 16:26:50 +00:00
|
|
|
{
|
2018-07-27 19:43:09 +00:00
|
|
|
for (auto it = m_desktopFocusChains.begin();
|
2012-11-20 16:26:50 +00:00
|
|
|
it != m_desktopFocusChains.end();
|
|
|
|
++it) {
|
|
|
|
it.value().removeAll(client);
|
|
|
|
}
|
|
|
|
m_mostRecentlyUsed.removeAll(client);
|
|
|
|
}
|
|
|
|
|
2021-08-16 12:07:43 +00:00
|
|
|
void FocusChain::addDesktop(VirtualDesktop *desktop)
|
2012-11-20 16:26:50 +00:00
|
|
|
{
|
2021-08-16 12:07:43 +00:00
|
|
|
m_desktopFocusChains.insert(desktop, Chain());
|
|
|
|
}
|
|
|
|
|
|
|
|
void FocusChain::removeDesktop(VirtualDesktop *desktop)
|
|
|
|
{
|
|
|
|
if (m_currentDesktop == desktop) {
|
|
|
|
m_currentDesktop = nullptr;
|
2012-11-20 16:26:50 +00:00
|
|
|
}
|
2021-08-16 12:07:43 +00:00
|
|
|
m_desktopFocusChains.remove(desktop);
|
2012-11-20 16:26:50 +00:00
|
|
|
}
|
|
|
|
|
2021-08-16 12:07:43 +00:00
|
|
|
AbstractClient *FocusChain::getForActivation(VirtualDesktop *desktop) const
|
2012-11-20 16:26:50 +00:00
|
|
|
{
|
2021-08-28 18:58:29 +00:00
|
|
|
return getForActivation(desktop, workspace()->activeOutput());
|
2012-11-20 16:26:50 +00:00
|
|
|
}
|
|
|
|
|
2021-08-28 14:48:12 +00:00
|
|
|
AbstractClient *FocusChain::getForActivation(VirtualDesktop *desktop, AbstractOutput *output) const
|
2012-11-20 16:26:50 +00:00
|
|
|
{
|
2018-07-27 19:43:09 +00:00
|
|
|
auto it = m_desktopFocusChains.constFind(desktop);
|
2012-11-20 16:26:50 +00:00
|
|
|
if (it == m_desktopFocusChains.constEnd()) {
|
Use nullptr everywhere
Summary:
Because KWin is a very old project, we use three kinds of null pointer
literals: 0, NULL, and nullptr. Since C++11, it's recommended to use
nullptr keyword.
This change converts all usages of 0 and NULL literal to nullptr. Even
though it breaks git history, we need to do it in order to have consistent
code as well to ease code reviews (it's very tempting for some people to
add unrelated changes to their patches, e.g. converting NULL to nullptr).
Test Plan: Compiles.
Reviewers: #kwin, davidedmundson, romangg
Reviewed By: #kwin, davidedmundson, romangg
Subscribers: romangg, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D23618
2019-09-19 14:46:54 +00:00
|
|
|
return nullptr;
|
2012-11-20 16:26:50 +00:00
|
|
|
}
|
2015-03-05 09:24:54 +00:00
|
|
|
const auto &chain = it.value();
|
2012-11-20 16:26:50 +00:00
|
|
|
for (int i = chain.size() - 1; i >= 0; --i) {
|
2015-03-05 09:24:54 +00:00
|
|
|
auto tmp = chain.at(i);
|
2012-11-20 16:26:50 +00:00
|
|
|
// TODO: move the check into Client
|
|
|
|
if (tmp->isShown(false) && tmp->isOnCurrentActivity()
|
2021-08-28 14:48:12 +00:00
|
|
|
&& ( !m_separateScreenFocus || tmp->output() == output)) {
|
2015-03-12 12:43:39 +00:00
|
|
|
return tmp;
|
2012-11-20 16:26:50 +00:00
|
|
|
}
|
|
|
|
}
|
Use nullptr everywhere
Summary:
Because KWin is a very old project, we use three kinds of null pointer
literals: 0, NULL, and nullptr. Since C++11, it's recommended to use
nullptr keyword.
This change converts all usages of 0 and NULL literal to nullptr. Even
though it breaks git history, we need to do it in order to have consistent
code as well to ease code reviews (it's very tempting for some people to
add unrelated changes to their patches, e.g. converting NULL to nullptr).
Test Plan: Compiles.
Reviewers: #kwin, davidedmundson, romangg
Reviewed By: #kwin, davidedmundson, romangg
Subscribers: romangg, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D23618
2019-09-19 14:46:54 +00:00
|
|
|
return nullptr;
|
2012-11-20 16:26:50 +00:00
|
|
|
}
|
|
|
|
|
2015-03-05 09:24:54 +00:00
|
|
|
void FocusChain::update(AbstractClient *client, FocusChain::Change change)
|
2012-11-20 16:26:50 +00:00
|
|
|
{
|
|
|
|
if (!client->wantsTabFocus()) {
|
|
|
|
// Doesn't want tab focus, remove
|
|
|
|
remove(client);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (client->isOnAllDesktops()) {
|
|
|
|
// Now on all desktops, add it to focus chains it is not already in
|
2018-07-27 19:43:09 +00:00
|
|
|
for (auto it = m_desktopFocusChains.begin();
|
2012-11-20 16:26:50 +00:00
|
|
|
it != m_desktopFocusChains.end();
|
|
|
|
++it) {
|
2015-03-05 09:24:54 +00:00
|
|
|
auto &chain = it.value();
|
2012-11-20 16:26:50 +00:00
|
|
|
// Making first/last works only on current desktop, don't affect all desktops
|
|
|
|
if (it.key() == m_currentDesktop
|
|
|
|
&& (change == MakeFirst || change == MakeLast)) {
|
|
|
|
if (change == MakeFirst) {
|
|
|
|
makeFirstInChain(client, chain);
|
|
|
|
} else {
|
|
|
|
makeLastInChain(client, chain);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
insertClientIntoChain(client, chain);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Now only on desktop, remove it anywhere else
|
2018-07-27 19:43:09 +00:00
|
|
|
for (auto it = m_desktopFocusChains.begin();
|
2012-11-20 16:26:50 +00:00
|
|
|
it != m_desktopFocusChains.end();
|
|
|
|
++it) {
|
2015-03-05 09:24:54 +00:00
|
|
|
auto &chain = it.value();
|
2012-11-20 16:26:50 +00:00
|
|
|
if (client->isOnDesktop(it.key())) {
|
|
|
|
updateClientInChain(client, change, chain);
|
|
|
|
} else {
|
|
|
|
chain.removeAll(client);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// add for most recently used chain
|
|
|
|
updateClientInChain(client, change, m_mostRecentlyUsed);
|
|
|
|
}
|
|
|
|
|
2018-07-27 19:43:09 +00:00
|
|
|
void FocusChain::updateClientInChain(AbstractClient *client, FocusChain::Change change, Chain &chain)
|
2012-11-20 16:26:50 +00:00
|
|
|
{
|
|
|
|
if (change == MakeFirst) {
|
|
|
|
makeFirstInChain(client, chain);
|
|
|
|
} else if (change == MakeLast) {
|
|
|
|
makeLastInChain(client, chain);
|
|
|
|
} else {
|
|
|
|
insertClientIntoChain(client, chain);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-27 19:43:09 +00:00
|
|
|
void FocusChain::insertClientIntoChain(AbstractClient *client, Chain &chain)
|
2012-11-20 16:26:50 +00:00
|
|
|
{
|
|
|
|
if (chain.contains(client)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (m_activeClient && m_activeClient != client &&
|
2013-03-02 11:10:56 +00:00
|
|
|
!chain.empty() && chain.last() == m_activeClient) {
|
2012-11-20 16:26:50 +00:00
|
|
|
// Add it after the active client
|
|
|
|
chain.insert(chain.size() - 1, client);
|
|
|
|
} else {
|
|
|
|
// Otherwise add as the first one
|
|
|
|
chain.append(client);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-05 09:24:54 +00:00
|
|
|
void FocusChain::moveAfterClient(AbstractClient *client, AbstractClient *reference)
|
2012-11-20 16:26:50 +00:00
|
|
|
{
|
|
|
|
if (!client->wantsTabFocus()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-07-27 19:43:09 +00:00
|
|
|
for (auto it = m_desktopFocusChains.begin();
|
2012-11-20 16:26:50 +00:00
|
|
|
it != m_desktopFocusChains.end();
|
|
|
|
++it) {
|
|
|
|
if (!client->isOnDesktop(it.key())) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
moveAfterClientInChain(client, reference, it.value());
|
|
|
|
}
|
|
|
|
moveAfterClientInChain(client, reference, m_mostRecentlyUsed);
|
|
|
|
}
|
|
|
|
|
2018-07-27 19:43:09 +00:00
|
|
|
void FocusChain::moveAfterClientInChain(AbstractClient *client, AbstractClient *reference, Chain &chain)
|
2012-11-20 16:26:50 +00:00
|
|
|
{
|
|
|
|
if (!chain.contains(reference)) {
|
|
|
|
return;
|
|
|
|
}
|
2015-03-05 09:24:54 +00:00
|
|
|
if (AbstractClient::belongToSameApplication(reference, client)) {
|
2012-11-20 16:26:50 +00:00
|
|
|
chain.removeAll(client);
|
|
|
|
chain.insert(chain.indexOf(reference), client);
|
|
|
|
} else {
|
|
|
|
chain.removeAll(client);
|
|
|
|
for (int i = chain.size() - 1; i >= 0; --i) {
|
2015-03-05 09:24:54 +00:00
|
|
|
if (AbstractClient::belongToSameApplication(reference, chain.at(i))) {
|
2012-11-20 16:26:50 +00:00
|
|
|
chain.insert(i, client);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-05 09:24:54 +00:00
|
|
|
AbstractClient *FocusChain::firstMostRecentlyUsed() const
|
2012-11-20 16:26:50 +00:00
|
|
|
{
|
|
|
|
if (m_mostRecentlyUsed.isEmpty()) {
|
Use nullptr everywhere
Summary:
Because KWin is a very old project, we use three kinds of null pointer
literals: 0, NULL, and nullptr. Since C++11, it's recommended to use
nullptr keyword.
This change converts all usages of 0 and NULL literal to nullptr. Even
though it breaks git history, we need to do it in order to have consistent
code as well to ease code reviews (it's very tempting for some people to
add unrelated changes to their patches, e.g. converting NULL to nullptr).
Test Plan: Compiles.
Reviewers: #kwin, davidedmundson, romangg
Reviewed By: #kwin, davidedmundson, romangg
Subscribers: romangg, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D23618
2019-09-19 14:46:54 +00:00
|
|
|
return nullptr;
|
2012-11-20 16:26:50 +00:00
|
|
|
}
|
|
|
|
return m_mostRecentlyUsed.first();
|
|
|
|
}
|
|
|
|
|
2015-03-05 09:24:54 +00:00
|
|
|
AbstractClient *FocusChain::nextMostRecentlyUsed(AbstractClient *reference) const
|
2012-11-20 16:26:50 +00:00
|
|
|
{
|
|
|
|
if (m_mostRecentlyUsed.isEmpty()) {
|
Use nullptr everywhere
Summary:
Because KWin is a very old project, we use three kinds of null pointer
literals: 0, NULL, and nullptr. Since C++11, it's recommended to use
nullptr keyword.
This change converts all usages of 0 and NULL literal to nullptr. Even
though it breaks git history, we need to do it in order to have consistent
code as well to ease code reviews (it's very tempting for some people to
add unrelated changes to their patches, e.g. converting NULL to nullptr).
Test Plan: Compiles.
Reviewers: #kwin, davidedmundson, romangg
Reviewed By: #kwin, davidedmundson, romangg
Subscribers: romangg, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D23618
2019-09-19 14:46:54 +00:00
|
|
|
return nullptr;
|
2012-11-20 16:26:50 +00:00
|
|
|
}
|
|
|
|
const int index = m_mostRecentlyUsed.indexOf(reference);
|
2014-11-06 21:58:53 +00:00
|
|
|
if (index == -1) {
|
|
|
|
return m_mostRecentlyUsed.first();
|
|
|
|
}
|
|
|
|
if (index == 0) {
|
2012-11-20 16:26:50 +00:00
|
|
|
return m_mostRecentlyUsed.last();
|
|
|
|
}
|
|
|
|
return m_mostRecentlyUsed.at(index - 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
// copied from activation.cpp
|
2015-03-05 09:24:54 +00:00
|
|
|
bool FocusChain::isUsableFocusCandidate(AbstractClient *c, AbstractClient *prev) const
|
2012-11-20 16:26:50 +00:00
|
|
|
{
|
|
|
|
return c != prev &&
|
|
|
|
c->isShown(false) && c->isOnCurrentDesktop() && c->isOnCurrentActivity() &&
|
2021-08-28 18:58:29 +00:00
|
|
|
(!m_separateScreenFocus || c->isOnOutput(prev ? prev->output() : workspace()->activeOutput()));
|
2012-11-20 16:26:50 +00:00
|
|
|
}
|
|
|
|
|
2021-08-16 12:07:43 +00:00
|
|
|
AbstractClient *FocusChain::nextForDesktop(AbstractClient *reference, VirtualDesktop *desktop) const
|
2012-11-20 16:26:50 +00:00
|
|
|
{
|
2018-07-27 19:43:09 +00:00
|
|
|
auto it = m_desktopFocusChains.constFind(desktop);
|
|
|
|
if (it == m_desktopFocusChains.constEnd()) {
|
Use nullptr everywhere
Summary:
Because KWin is a very old project, we use three kinds of null pointer
literals: 0, NULL, and nullptr. Since C++11, it's recommended to use
nullptr keyword.
This change converts all usages of 0 and NULL literal to nullptr. Even
though it breaks git history, we need to do it in order to have consistent
code as well to ease code reviews (it's very tempting for some people to
add unrelated changes to their patches, e.g. converting NULL to nullptr).
Test Plan: Compiles.
Reviewers: #kwin, davidedmundson, romangg
Reviewed By: #kwin, davidedmundson, romangg
Subscribers: romangg, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D23618
2019-09-19 14:46:54 +00:00
|
|
|
return nullptr;
|
2012-11-20 16:26:50 +00:00
|
|
|
}
|
2015-03-05 09:24:54 +00:00
|
|
|
const auto &chain = it.value();
|
2012-11-20 16:26:50 +00:00
|
|
|
for (int i = chain.size() - 1; i >= 0; --i) {
|
2015-03-05 09:24:54 +00:00
|
|
|
auto client = chain.at(i);
|
2012-11-20 16:26:50 +00:00
|
|
|
if (isUsableFocusCandidate(client, reference)) {
|
2015-03-12 12:43:39 +00:00
|
|
|
return client;
|
2012-11-20 16:26:50 +00:00
|
|
|
}
|
|
|
|
}
|
Use nullptr everywhere
Summary:
Because KWin is a very old project, we use three kinds of null pointer
literals: 0, NULL, and nullptr. Since C++11, it's recommended to use
nullptr keyword.
This change converts all usages of 0 and NULL literal to nullptr. Even
though it breaks git history, we need to do it in order to have consistent
code as well to ease code reviews (it's very tempting for some people to
add unrelated changes to their patches, e.g. converting NULL to nullptr).
Test Plan: Compiles.
Reviewers: #kwin, davidedmundson, romangg
Reviewed By: #kwin, davidedmundson, romangg
Subscribers: romangg, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D23618
2019-09-19 14:46:54 +00:00
|
|
|
return nullptr;
|
2012-11-20 16:26:50 +00:00
|
|
|
}
|
|
|
|
|
2018-07-27 19:43:09 +00:00
|
|
|
void FocusChain::makeFirstInChain(AbstractClient *client, Chain &chain)
|
2012-11-20 16:26:50 +00:00
|
|
|
{
|
|
|
|
chain.removeAll(client);
|
2020-10-20 20:15:46 +00:00
|
|
|
if (options->moveMinimizedWindowsToEndOfTabBoxFocusChain()) {
|
|
|
|
if (client->isMinimized()) { // add it before the first minimized ...
|
|
|
|
for (int i = chain.count()-1; i >= 0; --i) {
|
|
|
|
if (chain.at(i)->isMinimized()) {
|
|
|
|
chain.insert(i+1, client);
|
|
|
|
return;
|
|
|
|
}
|
2020-10-20 20:05:33 +00:00
|
|
|
}
|
2020-10-20 20:15:46 +00:00
|
|
|
chain.prepend(client); // ... or at end of chain
|
|
|
|
} else {
|
|
|
|
chain.append(client);
|
2020-10-20 20:05:33 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
chain.append(client);
|
|
|
|
}
|
2012-11-20 16:26:50 +00:00
|
|
|
}
|
|
|
|
|
2018-07-27 19:43:09 +00:00
|
|
|
void FocusChain::makeLastInChain(AbstractClient *client, Chain &chain)
|
2012-11-20 16:26:50 +00:00
|
|
|
{
|
|
|
|
chain.removeAll(client);
|
|
|
|
chain.prepend(client);
|
|
|
|
}
|
|
|
|
|
2021-08-16 12:07:43 +00:00
|
|
|
bool FocusChain::contains(AbstractClient *client, VirtualDesktop *desktop) const
|
2012-11-20 16:26:50 +00:00
|
|
|
{
|
2018-07-27 19:43:09 +00:00
|
|
|
auto it = m_desktopFocusChains.constFind(desktop);
|
|
|
|
if (it == m_desktopFocusChains.constEnd()) {
|
2012-11-20 16:26:50 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return it.value().contains(client);
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace
|