[Tablet] tablet mode manager refactoring

平板模式对不能最大化的窗口增加了毛玻璃背景
This commit is contained in:
pangyi 2022-10-26 09:25:44 +08:00 committed by MouseZhang
parent 667ab29467
commit 08b7688125
16 changed files with 1069 additions and 231 deletions

View File

@ -119,6 +119,7 @@ set(kwin_SRCS
surfaceitem_wayland.cpp
surfaceitem_x11.cpp
syncalarmx11filter.cpp
tablet_manager.cpp
tablet_input.cpp
toplevel.cpp
hide_cursor_spy.cpp

View File

@ -96,7 +96,6 @@ AbstractClient::AbstractClient()
connect(ApplicationMenu::self(), &ApplicationMenu::applicationMenuEnabledChanged, this, [this] {
Q_EMIT hasApplicationMenuChanged(hasApplicationMenu());
});
m_bTabletToPcRestoreFlag = false;
}
AbstractClient::~AbstractClient()
@ -271,8 +270,12 @@ Layer AbstractClient::belongsToLayer() const
// and the docks move into the NotificationLayer (which is between Above- and
// ActiveLayer, so that active fullscreen windows will still cover everything)
// Since the desktop is also activated, nothing should be in the ActiveLayer, though
if (isInternal())
if (isInternal()) {
if (keepBelow()) {
return BelowLayer;
}
return UnmanagedLayer;
}
if (isLockScreen())
return UnmanagedLayer;
if (isInputMethod())
@ -933,6 +936,9 @@ bool AbstractClient::startInteractiveMoveResize()
Q_ASSERT(QWidget::keyboardGrabber() == nullptr);
Q_ASSERT(QWidget::mouseGrabber() == nullptr);
stopDelayedInteractiveMoveResize();
if (workspace()->isInTabletMode()) {
return false;
}
if (QApplication::activePopupWidget() != nullptr)
return false; // popups have grab
if (isFullScreen() && (screens()->count() < 2 || !isMovableAcrossScreens()))
@ -1130,9 +1136,6 @@ void AbstractClient::handleInteractiveMoveResize(int x, int y, int x_root, int y
if (isWaitingForInteractiveMoveResizeSync())
return; // we're still waiting for the client or the timeout
if (workspace()->isInTabletMode())
return;
const Gravity gravity = interactiveMoveResizeGravity();
if ((gravity == Gravity::None && !isMovableAcrossScreens())
|| (gravity != Gravity::None && (isShade() || !isResizable()))) {
@ -2390,16 +2393,15 @@ void AbstractClient::processDecorationMove(const QPoint &localPos, const QPoint
bool AbstractClient::processDecorationButtonPress(QMouseEvent *event, bool ignoreMenu)
{
// 标题栏鼠标或手指双击操作
if (workspace()->isInTabletMode()) {
return true;
}
Options::MouseCommand com = Options::MouseNothing;
bool active = isActive();
if (!wantsInput()) // we cannot be active, use it anyway
active = true;
if(workspace()->isInTabletMode()) {
Workspace::self()->performWindowOperation(this, Options::NoOp);
return false;
}
// check whether it is a double click
if (event->button() == Qt::LeftButton && titlebarPositionUnderMouse()) {
if (m_decoration.doubleClickTimer.isValid()) {
@ -2795,51 +2797,6 @@ void AbstractClient::evaluateWindowRules()
applyWindowRules();
}
// 使工作区其他窗口最小化
void AbstractClient::makeOthersMinimize()
{
printf("AbstractClient::makeOthersMinimize, takefocus windowType(0):%d, caption:%s, isModal:%d, isMaximizable:%d\n", windowType(), this->caption().toStdString().c_str(), isModal(), isMaximizable());
qDebug() << "AbstractClient::makeOthersMinimize, takefocus windowType(0): " << windowType()
<< ", caption: " << this->caption()
<< ", isModal: " << isModal()
<< ", isMaximizable: " << isMaximizable();
//平板模式下如果是isModal窗口(如xournal应用); 或者是瞬态的对话框(如归档管理器的新建窗口); 则没必要最小化其他所有未最小化的窗口,或者是新激活的桌面开始菜单和Dock窗口(比如onboard),也不要最小化其他窗口
if (true == this->isUtility()
|| true == this->isModal()
|| (true == this->isDialog() && true == this->isTransient())
|| true == this->isDesktop()
|| true == this->isDock()
|| caption().contains("sogou", Qt::CaseInsensitive)
|| caption().contains("sidebar", Qt::CaseInsensitive)
)
{
return;
}
this->maximize(MaximizeFull);
printf("AbstractClient::makeOthersMinimize, takefocus windowType(1):%d, caption:%s, isdialog:%d, isMaximizable:%d\n", windowType(), this->caption().toStdString().c_str(), isDialog(), isMaximizable());
//锁屏时,如果当前是屏保窗口,对其他窗口不要最小化。解决在平板模式打开应用,锁屏后再进入桌面,应用窗口最小化问题
if (this->resourceClass() == "ukui-screensaver-default") {
return;
}
for (AbstractClient *c : workspace()->allClientList()) {
bool isIgnoreWindow = (this == c || c->isDock() || skipTaskbar() || c->isDialog() || c->isDesktop());
bool isTabletBgWindow = (c->isUtility() && c->caption() == "ukui-kwin_tablet_background" && c->keepBelow());
//除了自己dock和对话框也没必要最小化
if (isIgnoreWindow || isTabletBgWindow) {
continue;
}
if (false == c->isMinimized()){
printf("AbstractClient::makeOthersMinimize, minimize unminimized caption:%s\n", c->caption().toStdString().c_str());
c->minimize(true);
}
}
}
/**
* Returns the list of activities the client window is on.
* if it's on all activities, the list will be empty.

View File

@ -371,12 +371,13 @@ public:
return m_icon;
}
void setTabletToPcRestoreFlag(bool bTabletToPcRestoreFlag){
m_bTabletToPcRestoreFlag = bTabletToPcRestoreFlag;
// only on X11
void setDisableActive() {
Q_ASSERT(!m_diableActive);
m_diableActive = true;
}
bool getTabletToPcRestoreFlag() const {
return m_bTabletToPcRestoreFlag;
bool isDisableActive() const {
return m_diableActive;
}
bool isZombie() const;
@ -578,7 +579,6 @@ public:
void evaluateWindowRules();
virtual void applyWindowRules();
virtual bool takeFocus() = 0;
void makeOthersMinimize();
virtual bool wantsInput() const = 0;
/**
* Whether a dock window wants input.
@ -637,6 +637,7 @@ public:
void keepInArea(QRect area, bool partial = false);
virtual QSize minSize() const;
virtual QSize maxSize() const;
void updateGeometryRestore();
/**
* How to resize the window in order to obey constraints (mainly aspect ratios).
@ -1242,13 +1243,13 @@ private:
bool m_skipPager = false;
bool m_skipSwitcher = false;
QIcon m_icon;
bool m_diableActive = false;
bool m_active = false;
bool m_zombie = false;
bool m_keepAbove = false;
bool m_keepBelow = false;
bool m_demandsAttention = false;
bool m_minimized = false;
bool m_bTabletToPcRestoreFlag; //平板切换pc后如果此时窗口处于最小化则该标志置为true激活后用于将最大化窗口还原再次为false
QTimer *m_autoRaiseTimer = nullptr;
QTimer *m_shadeHoverTimer = nullptr;
ShadeMode m_shadeMode = ShadeNone;
@ -1366,6 +1367,11 @@ inline void AbstractClient::setPendingMoveResizeMode(MoveResizeMode mode)
m_pendingMoveResizeMode = MoveResizeMode(uint(m_pendingMoveResizeMode) | uint(mode));
}
inline void AbstractClient::updateGeometryRestore()
{
setGeometryRestore(frameGeometry());
}
}
Q_DECLARE_METATYPE(KWin::AbstractClient*)

View File

@ -1071,7 +1071,7 @@ void X11Client::focusInEvent(xcb_focus_in_event_t *e)
// check if this client is in should_get_focus list or if activation is allowed
bool activate = workspace()->allowClientActivation(this, -1U, true);
workspace()->gotFocusIn(this); // remove from should_get_focus list
if (activate) {
if (activate && !isDisableActive()) {
setActive(true);
} else {
if (workspace()->restoreFocus()) {

View File

@ -49,6 +49,9 @@ InternalClient::InternalClient(QWindow *window)
setOnAllDesktops(true);
setOpacity(m_internalWindow->opacity());
setSkipCloseAnimation(m_internalWindow->property(s_skipClosePropertyName).toBool());
if (m_internalWindowFlags.testFlag(Qt::WindowStaysOnBottomHint)) {
setKeepBelow(true);
}
// Create scene window, effect window, and update server-side shadow.
setupCompositing();

View File

@ -10,6 +10,7 @@
#include "main.h"
// kwin
#include "tablet_manager.h"
#include "platform.h"
#include "atoms.h"
#ifdef KWIN_BUILD_CMS
@ -149,6 +150,7 @@ void Application::start()
m_kxkbConfig = KSharedConfig::openConfig(QStringLiteral("kxkbrc"), KConfig::NoGlobals);
}
KWin::TabletManager::create(this);
performStartup();
}

View File

@ -259,8 +259,12 @@ void WinInfo::changeState(NET::States state, NET::States mask)
if ((mask & NET::FullScreen) != 0 && (state & NET::FullScreen) == 0)
m_client->setFullScreen(false, false);
if ((mask & NET::Max) == NET::Max)
m_client->setMaximize(state & NET::MaxVert, state & NET::MaxHoriz);
if ((mask & NET::Max) == NET::Max) {
// gtk 应用标题栏下面的菜单栏可拖动区域触摸操作时会触发
if (!workspace()->isInTabletMode()) {
m_client->setMaximize(state & NET::MaxVert, state & NET::MaxHoriz);
}
}
else if (mask & NET::MaxVert)
m_client->setMaximize(state & NET::MaxVert, m_client->maximizeMode() & MaximizeHorizontal);
else if (mask & NET::MaxHoriz)

View File

@ -830,11 +830,10 @@ void AbstractClient::shrinkVertical()
void Workspace::quickTileWindow(QuickTileMode mode)
{
if (!active_client) {
if (workspace()->isInTabletMode()) {
return;
}
if(m_bTabletMode) {
if (!active_client) {
return;
}

807
src/tablet_manager.cpp Normal file
View File

@ -0,0 +1,807 @@
/*
SPDX-FileCopyrightText: 2022 KylinSoft Co., Ltd.
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "tablet_manager.h"
#include "composite.h"
#include "internal_client.h"
#include "main.h"
#include "screens.h"
#include "scene.h"
#include "unmanaged.h"
#include "wayland_server.h"
#include "workspace.h"
#include "x11client.h"
#include "abstract_wayland_output.h"
#include "effects.h"
#include "effectloader.h"
#include <kwineffects.h>
// Qt
#include <QDBusMessage>
#include <QDBusConnection>
#include <QStyleOption>
#include <QPainter>
#include <QX11Info>
#include <QFile>
namespace KWin
{
static const QString BLUR_EFFECT_NAME = "blur";
static const QString TRANSLUCENCY_EFFECT_NAME = "kwin4_effect_translucency";
static const QString SCALEDESKTOP_EFFECT_NAME = "kwin4_effect_scaledesktop";
static const QString KYLIN_STATUSMANAGER_SERVICE = "com.kylin.statusmanager.interface";
static const QString KYLIN_STATUSMANAGER_PATH = "/";
static const QString KYLIN_STATUSMANAGER_INTERFACE = "com.kylin.statusmanager.interface";
KWIN_SINGLETON_FACTORY(TabletManager)
TabletManager::TabletManager(QObject *parent)
: QObject(parent)
, m_background(new TabletBackground())
, m_singleWinManager(nullptr)
, m_bTabletMode(false)
, m_existTabletDesktop(false)
, m_effects(nullptr)
, m_effectloader(nullptr)
{
m_existTabletDesktop = QFile::exists(QStringLiteral("/usr/bin/ukui-tablet-desktop"));
if (kwinApp()->operationMode() == Application::OperationMode::OperationModeX11) {
// TODO: 在这块如果不设置窗口可见和背景模糊的话X 下无法创建成功窗口并受窗管控制
m_background->show();
// KWindowEffects::enableBlurBehind(m_background->windowHandle(), true);
setBlurBehind();
}
// Workspace is created after the wayland server is initialized.
connect(kwinApp(), &Application::workspaceCreated, this, &TabletManager::init);
}
TabletManager::~TabletManager()
{
// TODO: 此处调用析构会导致double free
// if (m_background) {
// delete m_background;
// m_background = nullptr;
// }
if (m_singleWinManager) {
delete m_singleWinManager;
m_singleWinManager = nullptr;
}
m_effectloader = nullptr;
m_effects = nullptr;
}
void TabletManager::init()
{
if (m_background) {
m_background->init();
}
// 延迟一个事件循环再初始化单窗口管理模块类对象因为混成类是在Workspace的构造函数中调用的而混成在启动函数中
// 调用了 Workspace::self() 函数 所以混成类延迟了一个事件循环来保证Worksapce构造完成。因此此处也需要延迟
// 一个事件循环来确保在特效启动过程中模糊特效插件已经加载完成。不然在 X11 环境下太早对背景窗口最小化会导致模糊失败。
// 在这里使用混成的信号 compositingToggled(bool) 不是很合适,因为混成无法开启时,不会触发此信号。
QTimer::singleShot(0, [this]() {
if (!m_singleWinManager) {
m_singleWinManager = new SingleWindowManager(this);
m_singleWinManager->setBlurBackground(m_background);
}
});
connect(Compositor::self(), &Compositor::compositingToggled, [this](bool active) {
if (active) {
m_effects = static_cast<EffectsHandlerImpl*>(effects);
m_effectloader = m_effects->findChild<AbstractEffectLoader*>();
connect(m_effectloader, &AbstractEffectLoader::effectLoaded, [this](KWin::Effect *effect, const QString &name){
Q_UNUSED(effect)
if (name == BLUR_EFFECT_NAME || name == TRANSLUCENCY_EFFECT_NAME) {
m_background->update();
}
});
} else {
m_effectloader = nullptr;
m_effects = nullptr;
}
});
QDBusMessage message = QDBusMessage::createMethodCall(KYLIN_STATUSMANAGER_SERVICE,
KYLIN_STATUSMANAGER_PATH,
KYLIN_STATUSMANAGER_INTERFACE,
QStringLiteral("get_current_tabletmode"));
QDBusMessage response = QDBusConnection::sessionBus().call(message);
if (response.type() == QDBusMessage::ReplyMessage) {
bool bTabletMode = response.arguments().takeFirst().toBool();
slotSwitchTabletMode(bTabletMode);
} else {
printf("TabletMode qdus invalid\n");
}
QDBusConnection::sessionBus().connect(KYLIN_STATUSMANAGER_SERVICE,
KYLIN_STATUSMANAGER_PATH,
KYLIN_STATUSMANAGER_INTERFACE,
QStringLiteral("mode_change_signal"),
this,
SLOT(slotSwitchTabletMode(bool)));
}
void TabletManager::slotSwitchTabletMode(bool bSurfaceMode)
{
if (m_bTabletMode == bSurfaceMode) {
return;
}
m_bTabletMode = bSurfaceMode;
Q_EMIT tabletModeChanged(m_bTabletMode);
if (m_singleWinManager) {
if (KWin::effects) {
EffectsHandlerImpl *effect = static_cast<EffectsHandlerImpl *>(effects);
bool isLoad = effect->isEffectLoaded(SCALEDESKTOP_EFFECT_NAME);
qDebug() << __func__ << "kwin4_effect_scaledesktop is loaded: " << isLoad;
if (isLoad && existTabletDesktop()) {
// 桌面缩放动作需要 1000 ms
QTimer::singleShot(1000, [this]() {
m_singleWinManager->switchTablet(m_bTabletMode);
});
} else {
m_singleWinManager->switchTablet(m_bTabletMode);
}
} else {
qDebug() << __func__ << "KWin::effects pointer is not initialized ! ! !";
m_singleWinManager->switchTablet(m_bTabletMode);
}
}
// 在平板模式启动 kwin 时,此时 effects 对象还未初始化,此时也没有必要进行窗口移动
if (KWin::effects == nullptr) {
return;
}
const auto stacking_order = workspace()->stackingOrder();
for (auto it = stacking_order.rbegin(); it != stacking_order.rend(); ++it) {
AbstractClient *c = qobject_cast<AbstractClient*>(*it);
// 因为现在侧边栏没有设置skipXXX等属性所以暂时使用标题栏名字判断
if (!c || c->isDock() || c->isDesktop()) {
continue;
}
if (m_bTabletMode) {
m_windowDesktopMap.insert(c->internalId(), c->desktop());
// Tablet 模式下所有工作区的窗口合并到一起
moveWindowBetweenVDesktop(c->internalId(), c->desktop(), KWin::effects->currentDesktop());
} else {
if (m_windowDesktopMap.contains(c->internalId())) {
// PC 模式下窗口还原到原来所在的工作区
moveWindowBetweenVDesktop(c->internalId(), KWin::effects->currentDesktop(), m_windowDesktopMap.value(c->internalId()));
}
}
}
if (!m_bTabletMode) {
m_windowDesktopMap.clear();
}
}
void TabletManager::moveWindowBetweenVDesktop(QUuid windowId, int srcDesktopIndex, int destDesktopIndex)
{
auto* window = KWin::effects->findWindow(windowId);
if (window == nullptr || window->isOnAllDesktops()) {
return;
}
auto desktopIndexList = window->desktops();
if (!desktopIndexList.contains(srcDesktopIndex)) {
return;
}
desktopIndexList.remove(desktopIndexList.indexOf(srcDesktopIndex));
if (desktopIndexList.contains(destDesktopIndex)) {
return;
}
desktopIndexList.append(destDesktopIndex);
KWin::effects->windowToDesktops(window, desktopIndexList);
}
void TabletManager::toggleTabletMode()
{
QDBusMessage call = QDBusMessage::createMethodCall(KYLIN_STATUSMANAGER_SERVICE,
KYLIN_STATUSMANAGER_PATH,
KYLIN_STATUSMANAGER_INTERFACE,
QStringLiteral("set_tabletmode"));
call.setArguments({!m_bTabletMode, "kwin", "debug"});
QDBusMessage response = QDBusConnection::sessionBus().call(call);
if (response.type() != QDBusMessage::ReplyMessage) {
qWarning() << "[=== checkmode ===]" << "set_tabletmode dbus not work, change locally";
slotSwitchTabletMode(!m_bTabletMode);
}
}
void TabletManager::setBlurBehind(double radius)
{
if (kwinApp()->operationMode() == Application::OperationMode::OperationModeX11) {
xcb_connection_t *c = QX11Info::connection();
if (!c) {
return;
}
const QByteArray effectName = QByteArrayLiteral("_KDE_NET_WM_BLUR_BEHIND_REGION");
xcb_intern_atom_cookie_t atomCookie = xcb_intern_atom_unchecked(c, false, effectName.length(), effectName.constData());
QScopedPointer<xcb_intern_atom_reply_t, QScopedPointerPodDeleter> atom(xcb_intern_atom_reply(c, atomCookie, nullptr));
if (!atom) {
return;
}
QVector<uint32_t> data;
QRegion region;
data.reserve(region.rectCount() * 4 + 1); // 1 means radius
for (const QRect &r : region) {
// kwin on X uses device pixels, convert from logical
auto dpr = qApp->devicePixelRatio();
data << r.x() * dpr << r.y() * dpr << r.width() * dpr << r.height() * dpr;
}
data << radius;
xcb_change_property(c, XCB_PROP_MODE_REPLACE, m_background->winId(), atom->atom, XCB_ATOM_CARDINAL, 32, data.size(), data.constData());
} else {
if (m_background && m_background->windowHandle()) {
m_background->windowHandle()->setProperty("kwin_blur_strength", radius);
}
}
}
void TabletManager::showBackground(double radius)
{
if (!m_singleWinManager) {
return;
}
m_singleWinManager->showBackground();
setBlurBehind(radius);
}
void TabletManager::hideBackground(double radius)
{
if (!m_singleWinManager) {
return;
}
m_singleWinManager->hideBackground();
setBlurBehind(radius);
}
TabletBackground::TabletBackground()
: QWidget()
{
if (kwinApp()->operationMode() == Application::OperationMode::OperationModeX11) {
setWindowFlags(Qt::WindowStaysOnBottomHint | Qt::FramelessWindowHint);
} else {
setWindowFlags(Qt::BypassWindowManagerHint | Qt::WindowStaysOnBottomHint | Qt::FramelessWindowHint);
}
setAttribute(Qt::WA_ShowWithoutActivating);
setAttribute(Qt::WA_TranslucentBackground);
setFocusPolicy(Qt::NoFocus);
setWindowTitle("tablet_blur_background");
}
TabletBackground::~TabletBackground()
{
qDebug() << __func__;
}
void TabletBackground::init()
{
slotGeometryChanged();
connect(screens(), &Screens::geometryChanged, this, &TabletBackground::slotGeometryChanged);
}
void TabletBackground::paintEvent(QPaintEvent *event)
{
Q_UNUSED(event);
QStyleOption opt;
opt.init(this);
QPainter p(this);
p.setPen(Qt::NoPen);
// QColor color = qApp->palette().color(QPalette::Base);
QColor color(64, 64, 64);
// color.setAlpha(m_alpha);
bool isShouldBlur = false;
if (effects) {
EffectsHandlerImpl *effect = static_cast<EffectsHandlerImpl *>(effects);
bool isLoadBlurEffect = effect->isEffectLoaded(BLUR_EFFECT_NAME);
qDebug() << __func__ << isLoadBlurEffect;
if ( isLoadBlurEffect) {
isShouldBlur = true;
} else {
isShouldBlur = false;
}
}
if (isShouldBlur) {
color.setAlpha(0);
} else {
color.setAlpha(255);
}
QPalette pal(this->palette());
pal.setColor(QPalette::Window, QColor(color));
this->setPalette(pal);
QBrush brush = QBrush(color);
p.setBrush(brush);
p.drawRoundedRect(opt.rect, 0, 0);
style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
}
void TabletBackground::slotGeometryChanged()
{
qDebug() << __func__ << screens()->geometry();
QRect screenGeo = screens()->geometry();
setGeometry(screenGeo);
setFixedSize(screenGeo.size());
}
SingleWindowManager::SingleWindowManager(QObject *parent)
: QObject(parent)
, m_blurHelperWindow(nullptr)
, m_blurBackground(nullptr)
, m_ignorePlaceCenterList()
, m_bTabletMode(false)
{
// 蓝牙、网络工具、搜索不需要进行居中处理
m_ignorePlaceCenterList << "ukui-bluetooth" << "kylin-nm" << "ukui-search";
connect(workspace(), &Workspace::clientAdded, this, &SingleWindowManager::handleClientAdded);
connect(workspace(), &Workspace::clientRemoved, this, &SingleWindowManager::handleClientRemoved);
connect(workspace(), &Workspace::clientActivated, this, &SingleWindowManager::handleClientActivated);
connect(workspace(), &Workspace::clientMinimizedChanged, this, &SingleWindowManager::handleClientMinimizedChanged);
//connect(workspace(), &Workspace::clientDemandsAttentionChanged, this, &SingleWindowManager::handleClientAdded);
connect(screens(), &Screens::geometryChanged, this, &SingleWindowManager::handleScreenGeometryChanged);
}
SingleWindowManager::~SingleWindowManager() = default;
void SingleWindowManager::setBlurBackground(TabletBackground *blurBackground)
{
if (!blurBackground) {
return;
}
m_blurBackground = blurBackground;
if (!m_blurHelperWindow && m_blurBackground) {
const auto stacking_order = workspace()->stackingOrder();
if (!stacking_order.isEmpty()) {
for (int i = stacking_order.count() - 1; i > -1; --i) {
AbstractClient *c = qobject_cast<AbstractClient*>(stacking_order.at(i));
if (c && c->keepBelow() && c->caption().contains("tablet_blur_background")) {
m_blurHelperWindow = c;
// 设置毛玻璃背景窗口为全屏防止在updateClientArea()->checkWorkspacePosition()时毛玻璃窗口被修改位置
m_blurHelperWindow->setFullScreen(true, false);
// only on X11
m_blurHelperWindow->setDisableActive();
m_blurHelperWindow->setSkipSwitcher(true);
m_blurHelperWindow->setSkipPager(true);
m_blurHelperWindow->setOriginalSkipTaskbar(true);
m_blurHelperWindow->minimize(true);
connect(Compositor::self(), &Compositor::compositingToggled, [this](bool active){
if (active) {
if (m_blurHelperWindow->effectWindow()) {
m_blurHelperWindow->effectWindow()->setData(WindowForceBlurRole, QVariant(true));
}
}
});
break;
}
}
}
}
// kwin 启动时如果存在已经存在的窗口,需要进行判断是否需要将顶层最大化或者显示桌面
switchTablet(TabletManager::self()->tabletMode());
}
void SingleWindowManager::switchTablet(bool bTabletMode)
{
m_bTabletMode = bTabletMode;
const auto stacking_order = workspace()->stackingOrder();
if (stacking_order.isEmpty()) {
return;
}
bool isTopWindowFound = false;
bool isTopWindowMaximized = true;
for (int i = stacking_order.count() - 1; i > -1; --i) {
AbstractClient *c = qobject_cast<AbstractClient*>(stacking_order.at(i));
if (!c) {
continue;
}
if (TabletManager::self()->existTabletDesktop() && c->isDesktop()) {
updateDesktopVisibleState(c);
}
// 侧边栏唤醒时不需要显示毛玻璃背景,侧边栏会自己绘制毛玻璃,所以使用标题栏名字将侧边栏移除
if (c->isDock() || c->isDesktop() || c->caption().contains(QStringLiteral("sidebar")) || c->caption() == QStringLiteral("tablet-taskbar")) {
continue;
}
#ifndef QT_NO_DEBUG
qDebug() << "SingleWindowManager::switchTablet, caption:" << c->caption() << m_bTabletMode << "WindowType: " << c->windowType()
<< "isModal: " << c->isModal() << "isDialog: " << c->isDialog() << "isSkipPager:" << c->skipPager()
<< "isSkipSwitcher: " << c->skipSwitcher() << "isSkipTaskbar: " << c->skipSwitcher();
#endif
if (m_bTabletMode) { // PC to Tablet
if (!c->isMinimized()) {
if (!isTopWindowFound) {
// 瞬态窗口
if (c->isTransient()) {
Placement::self()->placeCentered(c, workspace()->activeOutput()->geometry());
// 模式却换时更新一下还原状态的geometry是为了防止居中后在updateClientArea()时又被移动到PC模式保存的位置不再居中显示
c->updateGeometryRestore();
isTopWindowMaximized = false;
if (c->mainClients().size() > 0) {
isTopWindowFound = false;
continue;
}
else {
// 系统应用-文件保护箱的鉴权窗口比较特殊,是文件保护箱自己实现的。鉴权通过后才会显示文件保护箱主界面窗口,显示
// 鉴权窗口时,鉴权窗口的属性为瞬态窗口,但是主界面窗口不存在。所以特种情况,需要将 isTopWindowFound 设为 true。
isTopWindowFound = true;
continue;
}
} else { // 主界面窗口
isTopWindowFound = true;
c->maximize(MaximizeFull); //序号越大,越在顶层,只需要将既不是任务栏又不是桌面的最顶层次未最小化的窗口最大化
if (c->isResizable()) {
isTopWindowMaximized = true;
} else {
// 如果是固定大小的窗口,需要将窗口放到屏幕的正中间
Placement::self()->placeCentered(c, workspace()->activeOutput()->geometry());
c->updateGeometryRestore();
isTopWindowMaximized = false;
}
}
} else {
qDebug() << "SingleWindowManager::switchTablet, minimize() caption:" << c->caption();
c->minimize(true);
}
}
}
}
if (!isTopWindowMaximized && m_bTabletMode) {
showBackground();
} else {
hideBackground();
}
}
template <class T>
void SingleWindowManager::addTransient(QVector<T*> &clients, T *transientClient)
{
clients.append(transientClient);
}
template <class T>
bool SingleWindowManager::removeTransient(QVector<T*> &clients, T *transientClient)
{
const int remove = clients.indexOf(transientClient);
if (remove == -1) {
return false;
}
clients.removeAt(remove);
return true;
}
void SingleWindowManager::updateDesktopVisibleState(AbstractClient *desktop)
{
if (!desktop || !desktop->isDesktop()) {
return;
}
if (desktop->caption().contains(QStringLiteral("ukui-tablet-desktop"))) {
m_tabletDesktop = desktop;
if (effects) {
EffectsHandlerImpl *effect = static_cast<EffectsHandlerImpl *>(effects);
bool isLoad = effect->isEffectLoaded(SCALEDESKTOP_EFFECT_NAME);
// 如果桌面缩放特效加载了,则有特效去处理桌面的显示与隐藏
if (isLoad)
return;
}
qDebug() << __func__ << __LINE__ << "KWin::effects pointer is not initialized ! ! !" << m_bTabletMode;
// 桌面缩放特效未加载,由 SingleWindowManager 管理桌面的显示与隐藏
if (!m_bTabletMode) {
m_tabletDesktop->hideClient();
} else {
m_tabletDesktop->showClient();
}
} else {
m_pcDesktop = desktop;
if (effects) {
EffectsHandlerImpl *effect = static_cast<EffectsHandlerImpl *>(effects);
bool isLoad = effect->isEffectLoaded(SCALEDESKTOP_EFFECT_NAME);
// 如果桌面缩放特效加载了,则有特效去处理桌面的显示与隐藏
if (isLoad)
return;
}
qDebug() << __func__ << __LINE__ << "KWin::effects pointer is not initialized ! ! !" << m_bTabletMode;
// 桌面缩放特效未加载,由 SingleWindowManager 管理桌面的显示与隐藏
if (m_bTabletMode) {
m_pcDesktop->hideClient();
} else {
m_pcDesktop->showClient();
}
}
}
void SingleWindowManager::showBackground()
{
if(kwinApp()->operationMode() == Application::OperationMode::OperationModeX11) {
if (m_blurHelperWindow) {
m_blurHelperWindow->unminimize(true);
}
m_blurBackground->update();
} else {
m_blurBackground->show();
// 对于 Internal window, 直接设置属性即可,没有必要去调用 KWindowEffects 的接口
m_blurBackground->windowHandle()->setProperty("kwin_blur", QRegion());
}
}
void SingleWindowManager::hideBackground()
{
#ifndef QT_NO_DEBUG
qDebug() << __func__ << "Hide Background ! ! !";
#endif
if(kwinApp()->operationMode() == Application::OperationMode::OperationModeX11) {
if (m_blurHelperWindow) {
m_blurHelperWindow->minimize(true);
}
} else {
m_blurBackground->hide();
}
}
void SingleWindowManager::handleClientAdded(AbstractClient *client)
{
if (!client) {
return;
}
// 存在部分窗口,它的主界面窗口还没有 show 出来或者隐藏了,找不到主界面窗口指针,但是该窗口仍为瞬态窗口,这
// 种窗口我们暂时不将其加到瞬态窗口列表里。
if (client->isTransient() && client->mainClients().size() > 0) {
addTransient(m_transientClients, client);
}
if (TabletManager::self()->existTabletDesktop() && client->isDesktop()) {
if (client->caption().contains(QStringLiteral("ukui-tablet-desktop"))) {
qDebug() << __func__ << __LINE__ << "ukui-tablet-desktop add" << m_bTabletMode;
m_tabletDesktop = client;
if (!m_bTabletMode) {
m_tabletDesktop->hideClient();
}
} else {
qDebug() << __func__ << __LINE__ << "peony-qt-desktop add" << m_bTabletMode;
m_pcDesktop = client;
if (m_bTabletMode) {
m_pcDesktop->hideClient();
}
}
}
if (!m_bTabletMode) {
return;
}
#ifndef QT_NO_DEBUG
qDebug() << __func__ << client->caption();
#endif
}
void SingleWindowManager::handleClientRemoved(AbstractClient *client)
{
if (!client) {
return;
}
bool isTransientRemoved = removeTransient(m_transientClients, client);
if (!m_bTabletMode) {
return;
}
#ifndef QT_NO_DEBUG
qDebug() << __func__ << client->caption();
#endif
// 该窗口存在主界面窗口移除此窗口后还有主界面窗口直接return即可不需要处理。但是 client->isTransient() 返回的值永远都
// 是 false因为 workspace 在发送 clientRemoved 信号前,已经将父子窗口全部清空关系了。所以要将瞬态窗口提前保存下来,利用自
// 定义的 removeTransient() 函数来判断是否移除的是瞬态窗口。瞬态窗口不直接return的话可能会导致毛玻璃背景先隐藏再被显示出来。
// wayland 环境下任务栏隐藏也会触发此槽函数直接return即可。
if (isTransientRemoved || client->isDock() || client->caption().contains(QStringLiteral("sidebar"))) {
return;
}
else {
// 窗口可以最大化,意味着毛玻璃背景窗口并没有显示出来
if (client->isMaximizable()) {
return;
} else {
// 窗口不能最大化,意味着毛玻璃背景显示出来了。所以关闭窗口时要隐藏毛玻璃背景窗口。
qDebug() << __func__ << "Hide Background ! ! !";
hideBackground();
}
}
}
void SingleWindowManager::handleClientActivated(AbstractClient *client)
{
if (!m_bTabletMode || !client || client->caption() == QStringLiteral("tablet-taskbar")) {
return;
}
#ifndef QT_NO_DEBUG
// transientFor() 拿到的返回值是不准确的, mainClients() 可以拿到所有的父窗口
// 先不考虑 mainClients() 返回多个父窗口的情况,因为目前不确定是否会存在这种情况
qDebug() << __func__ << "Caption: " << client->caption() << "WindowType: " << client->windowType()
<< "resourceName: " << client->resourceName() << "resourceClass: " << client->resourceClass()
<< "isDialog: " << client->isDialog() << "isUtility: " << client->isUtility()
<< "isMaximizable: " << client->isMaximizable() << "transientFor: " << client->transientFor()
<< "transients:" << client->transients() << "mainClients: " << client->mainClients();
#endif
if (client->isDesktop() || client->isDock() || client == m_blurHelperWindow
/* || client->caption().contains(QStringLiteral("sidebar")) */) {
hideBackground();
return;
}
if (client->caption().contains(QStringLiteral("sidebar"))) {
return;
}
// 鉴权窗口,不需要对其他窗口进行任何处理
QStringList authenticationAgents = {"polkit-ukui-authentication-agent-1"
, "polkit-kde-authentication-agent-1"
, "kylin-installer"};
if (authenticationAgents.contains(client->resourceClass())) {
qDebug() <<__func__ << client->resourceClass();
Placement::self()->placeCentered(client, workspace()->activeOutput()->geometry());
// 某些应用打开前必须鉴权,在显示鉴权窗口时,将毛玻璃背景显示出来。
// 在应用内部操作时激活鉴权窗口时,显示出毛玻璃背景窗口也无可厚非。
showBackground();
return;
}
bool hasMaximizedWindow = true;
if (client->isMaximizable()) {
// 能最大化的窗口直接最大化
client->maximize(MaximizeFull);
} else {
// utility 表示一个小的持久的实用程序窗口,例如调色板或工具箱,用户可能在工作时保持该窗口的打开状态,所以这种类型窗口也不应该被居中
if (!m_ignorePlaceCenterList.contains(client->resourceClass()) && !client->isUtility()) {
// 如果应用不能最大化,则居中显示
Placement::self()->placeCentered(client, workspace()->activeOutput()->geometry());
}
// 继续寻找是否含有主界面窗口
if (client->mainClients().size() > 0) {
AbstractClient *mainInterfaceClient = client->mainClients().at(0);
if (mainInterfaceClient->isMaximizable()) {
// 设置主界面窗口最大化
mainInterfaceClient->maximize(MaximizeFull);
} else {
if (!m_ignorePlaceCenterList.contains(client->resourceClass())) {
// 如果主界面窗口也不能最大化,主界面窗口也需要居中
Placement::self()->placeCentered(mainInterfaceClient, workspace()->activeOutput()->geometry());
}
hasMaximizedWindow = false;
}
// 如果主界面窗口之前被最小化过,则需要单独再取消最小化,否则只会显示子窗口
if (mainInterfaceClient->isMinimized()) {
mainInterfaceClient->unminimize(true);
}
} else {
// 没有主界面窗口,则代表只有一个不能最大化的应用
hasMaximizedWindow = false;
}
}
makeOtherWindowMinimized(client->mainClients().size() > 0 ? client->mainClients().at(0) : client);
if (hasMaximizedWindow) {
qDebug() << __func__ << "Hide Background !!!";
hideBackground();
} else {
qDebug() << __func__ << "Show Background !!!";
showBackground();
}
}
void SingleWindowManager::handleClientMinimizedChanged(AbstractClient *client)
{
if (!m_bTabletMode || !client || !client->isMinimized()) {
return;
}
#ifndef QT_NO_DEBUG
qDebug() << __func__ << client->caption();
#endif
// 该窗口存在主界面窗口最小化此窗口后还有主界面窗口直接return即可不需要处理。
// 存在窗口的主界面窗口还没有 show 出来或者隐藏了,该窗口为瞬态窗口,但是找不到主界面窗口
if ((client->isTransient() && client->mainClients().size() > 0) || client->isSpecialWindow() || client == m_blurHelperWindow) {
return;
}
const auto stacking_order = workspace()->stackingOrder();
if (stacking_order.isEmpty()) {
return;
}
for (int i = stacking_order.count() - 1; i > -1; --i) {
AbstractClient *c = qobject_cast<AbstractClient*>(stacking_order.at(i));
if (!c || c->isSpecialWindow() || c->skipTaskbar() || c->skipSwitcher() || c->skipPager()
|| c->caption().contains(QStringLiteral("sidebar"))) {
continue;
}
if (c == client && !c->isResizable()) {
hideBackground();
} else {
break;
}
}
}
void SingleWindowManager::handleScreenGeometryChanged()
{
if (!m_bTabletMode || !workspace()->activeClient()) {
return;
}
AbstractClient *activeClient = workspace()->activeClient();
if (activeClient->isDock() || activeClient->isDesktop()) {
return;
}
qDebug() << __func__ << activeClient->caption();
if (!activeClient->isMaximizable()) {
Placement::self()->placeCentered(activeClient, workspace()->activeOutput()->geometry());
activeClient->updateGeometryRestore();
}
if (activeClient->mainClients().size() > 0) {
for (auto *c : activeClient->mainClients()) {
if (!c->isMaximizable()) {
Placement::self()->placeCentered(c, workspace()->activeOutput()->geometry());
// 屏幕输出大小改变时更新一下还原状态的geometry是为了防止居中后在updateClientArea()时又被移动到PC模式保存的位置不再居中显示
c->updateGeometryRestore();
}
}
}
}
void SingleWindowManager::makeOtherWindowMinimized(AbstractClient *activeClient)
{
#ifndef QT_NO_DEBUG
qDebug() << __func__ << "Caption: " << activeClient->caption() << "WindowType: " << activeClient->windowType()
<< "isModal: " << activeClient->isModal() << "isDialog: " << activeClient->isDialog();
#endif
if (activeClient->isUtility() || activeClient->isSpecialWindow()
|| activeClient->caption().contains(QStringLiteral("sogouImeService"))
|| m_ignorePlaceCenterList.contains(activeClient->resourceClass())) {
return;
}
bool has_transient_client = (activeClient->transients().size() > 0 ? true : false);
for (Toplevel *client : workspace()->stackingOrder()) {
AbstractClient *c = qobject_cast<AbstractClient*>(client);
if (!c) {
continue;
}
bool isIgnoreWindow = (c->isDock() || c->isDesktop() || c->caption() == QStringLiteral("tablet-taskbar"));
if (isIgnoreWindow) {
continue;
}
if (c == activeClient) {
// 如果窗口有瞬态窗口,也一并拉回上层
if (c->transients().size()) {
for (auto it = c->transients().constBegin(); it != c->transients().constEnd(); ++it) {
(*it)->unminimize(true);
// utility 表示一个小的持久的实用程序窗口,例如调色板或工具箱,用户可能在工作时保持该窗口的打开状态,所以这种类型窗口也不应该被居中
if (!(*it)->isMaximizable() && !(*it)->isUtility()) {
Placement::self()->placeCentered(*it, workspace()->activeOutput()->geometry());
}
}
}
if (!has_transient_client) {
break;
}
}
// 如果激活的窗口存在瞬态窗口,需要将主界面窗口和瞬态窗口之间的其他应用的窗口最小化(存在这种可能性)。
else if (has_transient_client && activeClient->transients().contains(c)) {
break;
}
else if (!c->isMinimized())
{
qDebug() << __func__ << "exec minimize" << c->caption();
c->minimize(true);
}
}
}
}

126
src/tablet_manager.h Normal file
View File

@ -0,0 +1,126 @@
/*
SPDX-FileCopyrightText: 2022 KylinSoft Co., Ltd.
SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef KWIN_TABLET_BACKGROUND_H
#define KWIN_TABLET_BACKGROUND_H
#include <kwin_export.h>
#include <config-kwin.h>
#include <kwinglobals.h>
#include <QVector>
#include <QWidget>
namespace KWin
{
class AbstractClient;
class EffectsHandlerImpl;
class AbstractEffectLoader;
class KWIN_EXPORT TabletBackground : public QWidget
{
Q_OBJECT
public:
TabletBackground();
~TabletBackground() override;
void init();
void autoHide(bool on);
protected:
void paintEvent(QPaintEvent *event) override;
private Q_SLOTS:
void slotGeometryChanged();
private:
};
class KWIN_EXPORT SingleWindowManager : public QObject
{
Q_OBJECT
public:
explicit SingleWindowManager(QObject *parent = nullptr);
~SingleWindowManager() override;
void setBlurBackground(TabletBackground *blurBackground);
void switchTablet(bool bTabletMode);
void showBackground();
void hideBackground();
private Q_SLOTS:
void handleClientAdded(AbstractClient *client);
void handleClientRemoved(AbstractClient *client);
void handleClientActivated(AbstractClient *client);
void handleClientMinimizedChanged(AbstractClient *client);
void handleScreenGeometryChanged();
private:
template <class T>
void addTransient(QVector<T*> &clients, T *transientClient);
template <class T>
bool removeTransient(QVector<T*> &clients, T *transientClient);
void updateDesktopVisibleState(AbstractClient *desktop);
/**
* @brief activeClient
*
* @param activeClient
*/
void makeOtherWindowMinimized(AbstractClient *activeClient);
private:
AbstractClient *m_blurHelperWindow;
TabletBackground *m_blurBackground;
AbstractClient *m_pcDesktop;
AbstractClient *m_tabletDesktop;
QStringList m_ignorePlaceCenterList;
bool m_bTabletMode;
QVector<AbstractClient *> m_transientClients;
};
class KWIN_EXPORT TabletManager: public QObject
{
Q_OBJECT
public:
~TabletManager() override;
void toggleTabletMode();
bool tabletMode() { return m_bTabletMode; }
bool existTabletDesktop() { return m_existTabletDesktop; }
void setBlurBehind(double radius = 4000);
void showBackground(double radius = 4000);
// 外部接口隐藏时设置为4000使平板模式下的毛玻璃足够模糊
void hideBackground(double radius = 4000);
private Q_SLOTS:
void init();
void slotSwitchTabletMode(bool bSurfaceMode);
Q_SIGNALS:
/**
* @brief This signal is emitted when the mode of system has changed.
*
* @param bTabletMode
*/
void tabletModeChanged(bool bTabletMode);
private:
void moveWindowBetweenVDesktop(QUuid windowId, int srcDesktopIndex, int destDesktopIndex);
private:
TabletBackground* m_background;
SingleWindowManager* m_singleWinManager;
bool m_bTabletMode;
bool m_existTabletDesktop;
QMap<QUuid, int> m_windowDesktopMap;
EffectsHandlerImpl *m_effects;
AbstractEffectLoader *m_effectloader;
KWIN_SINGLETON(TabletManager)
};
}
#endif

View File

@ -1369,9 +1369,9 @@ void Workspace::slotWindowToPrevScreen()
*/
void Workspace::slotWindowMaximize()
{
if(m_bTabletMode)
if(workspace()->isInTabletMode()) {
return;
}
if (USABLE_ACTIVE_CLIENT)
performWindowOperation(active_client, Options::MaximizeOp);
}
@ -1501,11 +1501,7 @@ void Workspace::slotToggleShowDesktop()
*/
void Workspace::slotAlwaysShowDesktop()
{
if(isInTabletMode()) {
active_client->minimize(false);
} else {
setShowingDesktop(true);
}
setShowingDesktop(true);
}
/**

View File

@ -53,6 +53,7 @@
#include "main.h"
#include "decorations/decorationbridge.h"
#include "xwaylandclient.h"
#include "tablet_manager.h"
// KDE
#include <KConfig>
#include <KConfigGroup>
@ -127,7 +128,6 @@ Workspace::Workspace()
, set_active_client_recursion(0)
, block_stacking_updates(0)
, m_sessionManager(new SessionManager(this))
, m_bTabletMode(false)
{
// If KWin was already running it saved its configuration after loosing the selection -> Reread
QFuture<void> reparseConfigFuture = QtConcurrent::run(options, &Options::reparseConfiguration);
@ -279,28 +279,6 @@ void Workspace::init()
connect(server, &WaylandServer::shellClientRemoved, this, &Workspace::removeShellClient);
}
//kwin启动时获取运行模式: 0:PC模式, 1:平板模式
QDBusMessage message = QDBusMessage::createMethodCall("com.kylin.statusmanager.interface",
"/",
"com.kylin.statusmanager.interface",
"get_current_tabletmode");
QDBusMessage response = QDBusConnection::sessionBus().call(message);
if (response.type() == QDBusMessage::ReplyMessage) {
m_bTabletMode = response.arguments().takeFirst().toBool();
slotSwitchTabletMode(m_bTabletMode);
} else {
printf("TabletMode qdus invalid\n");
}
//通过dbus监听PC-平板模式切换
QDBusConnection::sessionBus().connect("com.kylin.statusmanager.interface",
"/",
"com.kylin.statusmanager.interface",
"mode_change_signal",
this,
SLOT(slotSwitchTabletMode(bool)));
// SELI TODO: This won't work with unreasonable focus policies,
// and maybe in rare cases also if the selected client doesn't
// want focus
@ -312,58 +290,6 @@ void Workspace::init()
// TODO: ungrabXServer()
}
void Workspace::slotSwitchTabletMode(bool bSurfaceMode)
{
if (m_bTabletMode == bSurfaceMode)
return;
m_bTabletMode = bSurfaceMode;
switchTablet(m_bTabletMode);
}
void Workspace::switchTablet(bool bOn)
{
bool bTopClientMaximize = true; //顶层窗口最大化标志
for (int i = stacking_order.count() - 1; i > -1; --i) {
AbstractClient *c = qobject_cast<AbstractClient*>(stacking_order.at(i));
if (!c || c->isDock() || c->isDesktop() || c->caption().contains("sidebar", Qt::CaseInsensitive))
continue;
//m_bTabletMode为true表示pc切平板
if (m_bTabletMode) {
if (c->maximizeMode() == MaximizeRestore) {
c->setTabletToPcRestoreFlag(true); //切平板前,如果是还原状态,则记录
}
//并且该窗口不是最小化时
if(!c->isMinimized()) {
if(bTopClientMaximize) {
bTopClientMaximize = false;
c->maximize(MaximizeFull); //序号越大,越在顶层,只需要将既不是任务栏又不是桌面的最顶层次未最小化的窗口最大化
if (false == c->isResizable()) {
if (!showingDesktop()) {
// 如果是固定大小的窗口,需要将窗口放到屏幕的正中间
Placement::self()->placeCentered(c, workspace()->activeOutput()->geometry());
}
}
} else {
printf("Workspace::switchTablet, minimize unminimized caption:%s\n", c->caption().toStdString().c_str());
c->minimize(true);
}
}
} else {
//m_bTabletMode为false表示平板切pc顶层最大化窗口如果之前是还原状态则需还原
if (MaximizeFull == c->maximizeMode() && true == bTopClientMaximize) {
bTopClientMaximize = false;
if (c->getTabletToPcRestoreFlag()) { //如果pc切平板前记录的之前是还原状态则当前激活窗口在切回pc后需还原否则不需要处理
c->maximize(MaximizeRestore);
c->setTabletToPcRestoreFlag(false); //切回pc后由于当前激活窗口已还原则以后该窗口无需再做还原处理
}
}
}
}
}
void Workspace::initializeX11()
{
if (!kwinApp()->x11Connection()) {
@ -731,9 +657,6 @@ X11Client *Workspace::createClient(xcb_window_t w, bool is_mapped)
return nullptr;
}
addClient(c);
if(m_bTabletMode) //当是平板模式时,窗口默认最大化,其他窗口最小化/*,并且设置平板标签*/
c->makeOthersMinimize();
return c;
}
@ -935,8 +858,13 @@ void Workspace::updateToolWindows(bool also_hide)
{
// TODO: What if Client's transiency/group changes? should this be called too? (I'm paranoid, am I not?)
if (!options->isHideUtilityWindowsForInactive()) {
for (auto it = m_x11Clients.constBegin(); it != m_x11Clients.constEnd(); ++it)
for (auto it = m_x11Clients.constBegin(); it != m_x11Clients.constEnd(); ++it) {
// 处于隐藏的窗口不应该被显示出来
if ((*it)->isHiddenInternal()) {
continue;
}
(*it)->showClient();
}
return;
}
const Group* group = nullptr;
@ -1481,15 +1409,31 @@ void Workspace::setShowingDesktop(bool showing)
} // ~StackingUpdatesBlocker
if (showing_desktop && topDesk) {
requestFocus(topDesk);
// 平板模式下,只需要把当前激活窗口最小化
if (isInTabletMode()) {
AbstractClient *tmpActiveClient = activeClient();
if (tmpActiveClient) {
if (tmpActiveClient->transientFor()) {
tmpActiveClient->transientFor()->minimize(false);
}
else if (tmpActiveClient->isDesktop() || tmpActiveClient->isDock())
return;
tmpActiveClient->minimize(false);
}
} else {
requestFocus(topDesk);
}
} else if (!showing_desktop && changed) {
const auto client = FocusChain::self()->getForActivation(VirtualDesktopManager::self()->currentDesktop());
if (client) {
activateClient(client);
if (!isInTabletMode()) {
const auto client = FocusChain::self()->getForActivation(VirtualDesktopManager::self()->currentDesktop());
if (client) {
activateClient(client);
}
}
}
if (changed)
if (changed && !isInTabletMode()) {
Q_EMIT showingDesktopChanged(showing);
}
}
void Workspace::disableGlobalShortcutsForClient(bool disable)
@ -2016,6 +1960,11 @@ void Workspace::removeInternalClient(InternalClient *client)
Q_EMIT internalClientRemoved(client);
}
bool Workspace::isInTabletMode()
{
return TabletManager::self() ? TabletManager::self()->tabletMode() : false;
}
Group* Workspace::findGroup(xcb_window_t leader) const
{
Q_ASSERT(leader != XCB_WINDOW_NONE);
@ -2320,17 +2269,6 @@ void Workspace::updateClientArea()
}
}
//当屏幕旋转时,那些固定大小的窗口需要再次进行一次居中设置(除了任务栏和桌面)
if (m_bTabletMode) {
for (auto it = m_x11Clients.constBegin(); it != m_x11Clients.constEnd(); ++it) {
if (true == (*it)->isResizable() || true == (*it)->isDock() || true == (*it)->isDesktop()) {
continue;
}
Placement::self()->placeCentered(*it, workspace()->activeOutput()->geometry());
}
}
if (m_workAreas != workAreas || m_restrictedAreas != restrictedAreas || m_screenAreas != screenAreas) {
m_workAreas = workAreas;
m_screenAreas = screenAreas;

View File

@ -416,8 +416,7 @@ public:
*/
void removeInternalClient(InternalClient *client);
bool isInTabletMode() {return m_bTabletMode;}
void switchTablet(bool bOn);
bool isInTabletMode();
public Q_SLOTS:
void performWindowOperation(KWin::AbstractClient* c, Options::WindowOperation op);
@ -436,8 +435,6 @@ public Q_SLOTS:
void slotAlwaysShowDesktop();
void slotAlwaysRestoreDesktop();
void slotSwitchTabletMode(bool bSurfaceMode);
void slotWindowMaximize();
void slotWindowMaximizeVertical();
void slotWindowMaximizeHorizontal();
@ -669,7 +666,6 @@ private:
static Workspace* _self;
bool workspaceInit;
bool m_bTabletMode;
QScopedPointer<KStartupInfo> m_startup;
QScopedPointer<ColorMapper> m_colorMapper;

View File

@ -1770,24 +1770,41 @@ void X11Client::closeWindow()
if (!isCloseable())
return;
bool doClose = true;
// doClose default to true,
// because there is no need to call AppManager on PC mode.
if (workspace()->isInTabletMode()) {
doClose = false;
QDBusMessage message = QDBusMessage::createMethodCall("com.kylin.AppManager",
"/com/kylin/AppManager",
"com.kylin.AppManager",
"ActiveProcessByWid");
QList<QVariant> args;
args.append(this->window());
message.setArguments(args);
QDBusMessage response = QDBusConnection::sessionBus().call(message);
if (response.type() == QDBusMessage::ReplyMessage) {
doClose = response.arguments().takeFirst().toBool();
qDebug() << "[DEBUG]: AppManager return " << doClose;
} else {
qDebug() << "[DEBUG]: ActiveProcessByWid qdus invalid.";
return;
}
}
// Update user time, because the window may create a confirming dialog.
updateUserTime();
QDBusMessage message = QDBusMessage::createMethodCall("com.kylin.AppManager",
"/com/kylin/AppManager",
"com.kylin.AppManager",
"ActiveProcessByWid");
QList<QVariant> args;
args.append(this->window());
message.setArguments(args);
QDBusConnection::sessionBus().send(message);
if (info->supportsProtocol(NET::DeleteWindowProtocol)) {
sendClientMessage(window(), atoms->wm_protocols, atoms->wm_delete_window);
pingWindow();
} else // Client will not react on wm_delete_window. We have not choice
// but destroy his connection to the XServer.
killWindow();
if (doClose)
{
if (info->supportsProtocol(NET::DeleteWindowProtocol)) {
sendClientMessage(window(), atoms->wm_protocols, atoms->wm_delete_window);
pingWindow();
} else // Client will not react on wm_delete_window. We have not choice
// but destroy his connection to the XServer.
killWindow();
}
}
@ -1979,21 +1996,6 @@ bool X11Client::takeFocus()
}
workspace()->setShouldGetFocus(this);
//如果是平板模式,则其他所有窗口均最小化
if (Workspace::self()->isInTabletMode()){
// 平板模式下,当窗口获得焦点时,如果是固定大小的窗口(除了任务栏),需要将窗口放到屏幕的正中间
if (false == this->isResizable() && false == isDock() && false == isDesktop()) {
Placement::self()->placeCentered(this, workspace()->activeOutput()->geometry());
}
makeOthersMinimize();
} else {
//如果pc切平板前该窗口是还原状态平板切pc后的窗口激活时需将该窗口从最大化切换还原
if (getTabletToPcRestoreFlag()) {
setTabletToPcRestoreFlag(false);
this->maximize(MaximizeRestore);
}
}
bool breakShowingDesktop = !keepAbove();
if (breakShowingDesktop) {
const auto members = group()->members();

View File

@ -536,7 +536,6 @@ private:
QRect m_lastClientGeometry;
QScopedPointer<X11DecorationRenderer> m_decorationRenderer;
bool bUnMakeOthersMinimizeFlag; //不最小化其他窗体标志
unsigned int m_specialFlags = 0;
};

View File

@ -826,17 +826,34 @@ void XdgToplevelClient::showOnScreenEdge()
void XdgToplevelClient::closeWindow()
{
if (isCloseable()) {
QDBusMessage message = QDBusMessage::createMethodCall("com.kylin.AppManager",
"/com/kylin/AppManager",
"com.kylin.AppManager",
"ActiveProcessByWid");
QList<QVariant> args;
args.append(this->window());
message.setArguments(args);
QDBusConnection::sessionBus().send(message);
bool doClose = true;
// doClose default to true,
// because there is no need to call AppManager on PC mode.
if (workspace()->isInTabletMode()) {
doClose = false;
QDBusMessage message = QDBusMessage::createMethodCall("com.kylin.AppManager",
"/com/kylin/AppManager",
"com.kylin.AppManager",
"ActiveProcessByWid");
QList<QVariant> args;
args.append(this->window());
message.setArguments(args);
QDBusMessage response = QDBusConnection::sessionBus().call(message);
sendPing(PingReason::CloseWindow);
m_shellSurface->sendClose();
if (response.type() == QDBusMessage::ReplyMessage) {
doClose = response.arguments().takeFirst().toBool();
qDebug() << "[DEBUG]: AppManager return " << doClose;
} else {
qDebug() << "[DEBUG]: ActiveProcessByWid qdus invalid.";
return;
}
}
// TODO: 暂时上面ActiveProcessByWid在wayland下无法使用先全部允许关闭
doClose = true;
if (doClose) {
sendPing(PingReason::CloseWindow);
m_shellSurface->sendClose();
}
}
}
@ -1019,21 +1036,6 @@ bool XdgToplevelClient::takeFocus()
setActive(true);
}
//如果是平板模式,则其他所有窗口均最小化
if (Workspace::self()->isInTabletMode()){
// 平板模式下,当窗口获得焦点时,如果是固定大小的窗口(除了任务栏),需要将窗口放到屏幕的正中间
if (false == this->isResizable() && false == isDock() && false == isDesktop()) {
Placement::self()->placeCentered(this, workspace()->activeOutput()->geometry());
}
makeOthersMinimize();
} else {
//如果pc切平板前该窗口是还原状态平板切pc后的窗口激活时需将该窗口从最大化切换还原
if (getTabletToPcRestoreFlag()) {
setTabletToPcRestoreFlag(false);
this->maximize(MaximizeRestore);
}
}
if (!keepAbove() && !isOnScreenDisplay() && !belongsToDesktop()) {
workspace()->setShowingDesktop(false);
}