1 Star 0 Fork 1

Jingling / kwin

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
composite.cpp 32.12 KB
一键复制 编辑 原始数据 按行查看 历史
jingos 提交于 2021-11-30 16:22 . JingOS V1.1.0
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047
/*
KWin - the KDE window manager
This file is part of the KDE project.
SPDX-FileCopyrightText: 2006 Lubos Lunak <l.lunak@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "composite.h"
#include "abstract_output.h"
#include "dbusinterface.h"
#include "x11client.h"
#include "decorations/decoratedclient.h"
#include "deleted.h"
#include "effects.h"
#include "ftrace.h"
#include "internal_client.h"
#include "overlaywindow.h"
#include "platform.h"
#include "renderloop.h"
#include "scene.h"
#include "screens.h"
#include "shadow.h"
#include "unmanaged.h"
#include "useractions.h"
#include "utils.h"
#include "wayland_server.h"
#include "workspace.h"
#include "xcbutils.h"
#include <kwingltexture.h>
#include <KWaylandServer/surface_interface.h>
#include <KGlobalAccel>
#include <KLocalizedString>
#include <KPluginLoader>
#include <KPluginMetaData>
#include <KNotification>
#include <KSelectionOwner>
#include <QDateTime>
#include <QFutureWatcher>
#include <QMenu>
#include <QOpenGLContext>
#include <QQuickWindow>
#include <QtConcurrentRun>
#include <QTextStream>
#include <QTimerEvent>
#include <xcb/composite.h>
#include <xcb/damage.h>
#include <cstdio>
#include "utils.h"
Q_DECLARE_METATYPE(KWin::X11Compositor::SuspendReason)
namespace KWin
{
// See main.cpp:
extern int screen_number;
extern bool is_multihead;
Compositor *Compositor::s_compositor = nullptr;
Compositor *Compositor::self()
{
return s_compositor;
}
WaylandCompositor *WaylandCompositor::create(QObject *parent)
{
Q_ASSERT(!s_compositor);
auto *compositor = new WaylandCompositor(parent);
s_compositor = compositor;
return compositor;
}
X11Compositor *X11Compositor::create(QObject *parent)
{
Q_ASSERT(!s_compositor);
auto *compositor = new X11Compositor(parent);
s_compositor = compositor;
return compositor;
}
class CompositorSelectionOwner : public KSelectionOwner
{
Q_OBJECT
public:
CompositorSelectionOwner(const char *selection)
: KSelectionOwner(selection, connection(), rootWindow())
, m_owning(false)
{
connect (this, &CompositorSelectionOwner::lostOwnership,
this, [this]() { m_owning = false; });
}
bool owning() const {
return m_owning;
}
void setOwning(bool own) {
m_owning = own;
}
private:
bool m_owning;
};
Compositor::Compositor(QObject* workspace)
: QObject(workspace)
, m_state(State::Off)
, m_selectionOwner(nullptr)
, m_scene(nullptr)
{
connect(options, &Options::configChanged, this, &Compositor::configChanged);
connect(options, &Options::animationSpeedChanged, this, &Compositor::configChanged);
// 2 sec which should be enough to restart the compositor.
static const int compositorLostMessageDelay = 2000;
m_releaseSelectionTimer.setSingleShot(true);
m_releaseSelectionTimer.setInterval(compositorLostMessageDelay);
connect(&m_releaseSelectionTimer, &QTimer::timeout,
this, &Compositor::releaseCompositorSelection);
m_unusedSupportPropertyTimer.setInterval(compositorLostMessageDelay);
m_unusedSupportPropertyTimer.setSingleShot(true);
connect(&m_unusedSupportPropertyTimer, &QTimer::timeout,
this, &Compositor::deleteUnusedSupportProperties);
// Delay the call to start by one event cycle.
// The ctor of this class is invoked from the Workspace ctor, that means before
// Workspace is completely constructed, so calling Workspace::self() would result
// in undefined behavior. This is fixed by using a delayed invocation.
if (kwinApp()->platform()->isReady()) {
QTimer::singleShot(0, this, &Compositor::start);
}
connect(kwinApp()->platform(), &Platform::readyChanged, this,
[this] (bool ready) {
if (ready) {
start();
} else {
stop();
}
}, Qt::QueuedConnection
);
if (qEnvironmentVariableIsSet("KWIN_MAX_FRAMES_TESTED"))
m_framesToTestForSafety = qEnvironmentVariableIntValue("KWIN_MAX_FRAMES_TESTED");
// register DBus
new CompositorDBusInterface(this);
FTraceLogger::create();
}
Compositor::~Compositor()
{
emit aboutToDestroy();
stop();
deleteUnusedSupportProperties();
destroyCompositorSelection();
s_compositor = nullptr;
}
bool Compositor::setupStart()
{
if (kwinApp()->isTerminating()) {
// Don't start while KWin is terminating. An event to restart might be lingering
// in the event queue due to graphics reset.
return false;
}
if (m_state != State::Off) {
return false;
}
m_state = State::Starting;
options->reloadCompositingSettings(true);
initializeX11();
// There might still be a deleted around, needs to be cleared before
// creating the scene (BUG 333275).
if (Workspace::self()) {
while (!Workspace::self()->deletedList().isEmpty()) {
Workspace::self()->deletedList().first()->discard();
}
}
emit aboutToToggleCompositing();
auto supportedCompositors = kwinApp()->platform()->supportedCompositors();
const auto userConfigIt = std::find(supportedCompositors.begin(), supportedCompositors.end(),
options->compositingMode());
if (userConfigIt != supportedCompositors.end()) {
supportedCompositors.erase(userConfigIt);
supportedCompositors.prepend(options->compositingMode());
} else {
qCWarning(KWIN_CORE)
<< "Configured compositor not supported by Platform. Falling back to defaults";
}
const auto availablePlugins = KPluginLoader::findPlugins(QStringLiteral("org.kde.kwin.scenes"));
for (const KPluginMetaData &pluginMetaData : availablePlugins) {
qCDebug(KWIN_CORE) << "Available scene plugin:" << pluginMetaData.fileName();
}
for (auto type : qAsConst(supportedCompositors)) {
switch (type) {
case XRenderCompositing:
qCDebug(KWIN_CORE) << "Attempting to load the XRender scene";
break;
case OpenGLCompositing:
case OpenGL2Compositing:
qCDebug(KWIN_CORE) << "Attempting to load the OpenGL scene";
break;
case QPainterCompositing:
qCDebug(KWIN_CORE) << "Attempting to load the QPainter scene";
break;
case NoCompositing:
Q_UNREACHABLE();
}
const auto pluginIt = std::find_if(availablePlugins.begin(), availablePlugins.end(),
[type] (const auto &plugin) {
const auto &metaData = plugin.rawData();
auto it = metaData.find(QStringLiteral("CompositingType"));
if (it != metaData.end()) {
if ((*it).toInt() == int{type}) {
return true;
}
}
return false;
});
if (pluginIt != availablePlugins.end()) {
std::unique_ptr<SceneFactory>
factory{ qobject_cast<SceneFactory*>(pluginIt->instantiate()) };
if (factory) {
m_scene = factory->create(this);
if (m_scene) {
if (!m_scene->initFailed()) {
qCDebug(KWIN_CORE) << "Instantiated compositing plugin:"
<< pluginIt->name();
break;
} else {
delete m_scene;
m_scene = nullptr;
}
}
}
}
}
if (m_scene == nullptr || m_scene->initFailed()) {
qCCritical(KWIN_CORE) << "Failed to initialize compositing, compositing disabled";
m_state = State::Off;
delete m_scene;
m_scene = nullptr;
if (m_selectionOwner) {
m_selectionOwner->setOwning(false);
m_selectionOwner->release();
}
if (!supportedCompositors.contains(NoCompositing)) {
qCCritical(KWIN_CORE) << "The used windowing system requires compositing";
qCCritical(KWIN_CORE) << "We are going to quit KWin now as it is broken";
qApp->quit();
}
return false;
}
CompositingType compositingType = m_scene->compositingType();
if (compositingType & OpenGLCompositing) {
// Override for OpenGl sub-type OpenGL2Compositing.
compositingType = OpenGLCompositing;
}
kwinApp()->platform()->setSelectedCompositor(compositingType);
if (!Workspace::self() && m_scene && m_scene->compositingType() == QPainterCompositing) {
// Force Software QtQuick on first startup with QPainter.
QQuickWindow::setSceneGraphBackend(QSGRendererInterface::Software);
}
connect(m_scene, &Scene::resetCompositing, this, &Compositor::reinitialize);
emit sceneCreated();
return true;
}
void Compositor::initializeX11()
{
xcb_connection_t *connection = kwinApp()->x11Connection();
if (!connection) {
return;
}
if (!m_selectionOwner) {
char selection_name[ 100 ];
sprintf(selection_name, "_NET_WM_CM_S%d", Application::x11ScreenNumber());
m_selectionOwner = new CompositorSelectionOwner(selection_name);
connect(m_selectionOwner, &CompositorSelectionOwner::lostOwnership,
this, &Compositor::stop);
}
if (!m_selectionOwner->owning()) {
// Force claim ownership.
m_selectionOwner->claim(true);
m_selectionOwner->setOwning(true);
}
xcb_composite_redirect_subwindows(connection, kwinApp()->x11RootWindow(),
XCB_COMPOSITE_REDIRECT_MANUAL);
}
void Compositor::cleanupX11()
{
delete m_selectionOwner;
m_selectionOwner = nullptr;
}
void Compositor::startupWithWorkspace()
{
connect(kwinApp(), &Application::x11ConnectionChanged,
this, &Compositor::initializeX11, Qt::UniqueConnection);
connect(kwinApp(), &Application::x11ConnectionAboutToBeDestroyed,
this, &Compositor::cleanupX11, Qt::UniqueConnection);
initializeX11();
Workspace::self()->markXStackingOrderAsDirty();
Q_ASSERT(m_scene);
const Platform *platform = kwinApp()->platform();
kwinApp()->platform()->createTaskManager(this);
if (platform->isPerScreenRenderingEnabled()) {
const QVector<AbstractOutput *> outputs = platform->enabledOutputs();
for (AbstractOutput *output : outputs) {
registerRenderLoop(output->renderLoop(), output);
}
connect(platform, &Platform::outputEnabled,
this, &Compositor::handleOutputEnabled);
connect(platform, &Platform::outputDisabled,
this, &Compositor::handleOutputDisabled);
} else {
registerRenderLoop(platform->renderLoop(), nullptr);
}
// Sets also the 'effects' pointer.
kwinApp()->platform()->createEffectsHandler(this, m_scene);
connect(Workspace::self(), &Workspace::deletedRemoved, m_scene, &Scene::removeToplevel);
connect(effects, &EffectsHandler::screenGeometryChanged, this, &Compositor::addRepaintFull);
for (X11Client *c : Workspace::self()->clientList()) {
c->setupCompositing();
c->updateShadow();
}
for (Unmanaged *c : Workspace::self()->unmanagedList()) {
c->setupCompositing();
c->updateShadow();
}
for (InternalClient *client : workspace()->internalClients()) {
client->setupCompositing();
client->updateShadow();
}
if (auto *server = waylandServer()) {
const auto clients = server->clients();
for (AbstractClient *c : clients) {
c->setupCompositing();
c->updateShadow();
}
}
m_state = State::On;
emit compositingToggled(true);
if (m_releaseSelectionTimer.isActive()) {
m_releaseSelectionTimer.stop();
}
// Render at least once.
addRepaintFull();
}
void Compositor::registerRenderLoop(RenderLoop *renderLoop, AbstractOutput *output)
{
Q_ASSERT(!m_renderLoops.contains(renderLoop));
m_renderLoops.insert(renderLoop, output);
connect(renderLoop, &RenderLoop::frameRequested, this, &Compositor::handleFrameRequested);
}
void Compositor::unregisterRenderLoop(RenderLoop *renderLoop)
{
Q_ASSERT(m_renderLoops.contains(renderLoop));
m_renderLoops.remove(renderLoop);
disconnect(renderLoop, &RenderLoop::frameRequested, this, &Compositor::handleFrameRequested);
}
void Compositor::handleOutputEnabled(AbstractOutput *output)
{
registerRenderLoop(output->renderLoop(), output);
}
void Compositor::handleOutputDisabled(AbstractOutput *output)
{
unregisterRenderLoop(output->renderLoop());
}
int Compositor::screenForRenderLoop(RenderLoop *renderLoop) const
{
Q_ASSERT(m_renderLoops.contains(renderLoop));
AbstractOutput *output = m_renderLoops.value(renderLoop);
if (!output) {
return -1;
}
return kwinApp()->platform()->enabledOutputs().indexOf(output);
}
void Compositor::scheduleRepaint()
{
for (auto it = m_renderLoops.constBegin(); it != m_renderLoops.constEnd(); ++it) {
it.key()->scheduleRepaint();
}
}
void Compositor::stop()
{
if (m_state == State::Off || m_state == State::Stopping) {
return;
}
m_state = State::Stopping;
emit aboutToToggleCompositing();
m_releaseSelectionTimer.start();
// Some effects might need access to effect windows when they are about to
// be destroyed, for example to unreference deleted windows, so we have to
// make sure that effect windows outlive effects.
delete effects;
effects = nullptr;
if (Workspace::self()) {
for (X11Client *c : Workspace::self()->clientList()) {
m_scene->removeToplevel(c);
}
for (Unmanaged *c : Workspace::self()->unmanagedList()) {
m_scene->removeToplevel(c);
}
for (InternalClient *client : workspace()->internalClients()) {
m_scene->removeToplevel(client);
}
for (X11Client *c : Workspace::self()->clientList()) {
c->finishCompositing();
}
for (Unmanaged *c : Workspace::self()->unmanagedList()) {
c->finishCompositing();
}
for (InternalClient *client : workspace()->internalClients()) {
client->finishCompositing();
}
if (auto *con = kwinApp()->x11Connection()) {
xcb_composite_unredirect_subwindows(con, kwinApp()->x11RootWindow(),
XCB_COMPOSITE_REDIRECT_MANUAL);
}
while (!workspace()->deletedList().isEmpty()) {
workspace()->deletedList().first()->discard();
}
}
if (waylandServer()) {
for (AbstractClient *c : waylandServer()->clients()) {
m_scene->removeToplevel(c);
}
for (AbstractClient *c : waylandServer()->clients()) {
c->finishCompositing();
}
}
while (!m_renderLoops.isEmpty()) {
unregisterRenderLoop(m_renderLoops.firstKey());
}
disconnect(kwinApp()->platform(), &Platform::outputEnabled,
this, &Compositor::handleOutputEnabled);
disconnect(kwinApp()->platform(), &Platform::outputDisabled,
this, &Compositor::handleOutputDisabled);
delete m_scene;
m_scene = nullptr;
m_state = State::Off;
emit compositingToggled(false);
}
void Compositor::destroyCompositorSelection()
{
delete m_selectionOwner;
m_selectionOwner = nullptr;
}
void Compositor::releaseCompositorSelection()
{
switch (m_state) {
case State::On:
// We are compositing at the moment. Don't release.
break;
case State::Off:
if (m_selectionOwner) {
qCDebug(KWIN_CORE) << "Releasing compositor selection";
m_selectionOwner->setOwning(false);
m_selectionOwner->release();
}
break;
case State::Starting:
case State::Stopping:
// Still starting or shutting down the compositor. Starting might fail
// or after stopping a restart might follow. So test again later on.
m_releaseSelectionTimer.start();
break;
}
}
void Compositor::keepSupportProperty(xcb_atom_t atom)
{
m_unusedSupportProperties.removeAll(atom);
}
void Compositor::removeSupportProperty(xcb_atom_t atom)
{
m_unusedSupportProperties << atom;
m_unusedSupportPropertyTimer.start();
}
void Compositor::deleteUnusedSupportProperties()
{
if (m_state == State::Starting || m_state == State::Stopping) {
// Currently still maybe restarting the compositor.
m_unusedSupportPropertyTimer.start();
return;
}
if (auto *con = kwinApp()->x11Connection()) {
for (const xcb_atom_t &atom : qAsConst(m_unusedSupportProperties)) {
// remove property from root window
xcb_delete_property(con, kwinApp()->x11RootWindow(), atom);
}
m_unusedSupportProperties.clear();
}
}
void Compositor::configChanged()
{
reinitialize();
addRepaintFull();
}
void Compositor::reinitialize()
{
// Reparse config. Config options will be reloaded by start()
kwinApp()->config()->reparseConfiguration();
// Restart compositing
stop();
start();
if (effects) { // start() may fail
effects->reconfigure();
}
}
void Compositor::addRepaint(int x, int y, int width, int height)
{
addRepaint(QRegion(x, y, width, height));
}
void Compositor::addRepaint(const QRect &rect)
{
addRepaint(QRegion(rect));
}
void Compositor::addRepaint(const QRegion &region)
{
if (m_scene) {
m_scene->addRepaint(region);
}
}
void Compositor::addRepaintFull()
{
addRepaint(screens()->geometry());
}
void Compositor::handleFrameRequested(RenderLoop *renderLoop)
{
// If outputs are disabled, we return to the event loop and
// continue processing events until the outputs are enabled again
if (!kwinApp()->platform()->areOutputsEnabled()) {
return;
}
const int screenId = screenForRenderLoop(renderLoop);
fTraceDuration("Paint (", screens()->name(screenId), ")");
// Create a list of all windows in the stacking order
QList<Toplevel *> windows = Workspace::self()->xStackingOrder();
QList<Toplevel *> damaged;
// Reset the damage state of each window and fetch the damage region
// without waiting for a reply
effects->setPanel(nullptr);
for (Toplevel *win : qAsConst(windows)) {
if (win->resetAndFetchDamage()) {
damaged << win;
}
if (win->isStatusBar() && win->y() == 0 && win->height() < screens()->size().height() / 3) {
effects->setPanelGeometry(win->frameGeometry());
effects->setPanel(win->effectWindow());
}
}
if (damaged.count() > 0) {
m_scene->triggerFence();
if (auto c = kwinApp()->x11Connection()) {
xcb_flush(c);
}
}
// Move elevated windows to the top of the stacking order
for (EffectWindow *c : static_cast<EffectsHandlerImpl *>(effects)->elevatedWindows()) {
Toplevel *t = static_cast<EffectWindowImpl *>(c)->window();
windows.removeAll(t);
windows.append(t);
}
// Get the replies
for (Toplevel *win : qAsConst(damaged)) {
// Discard the cached lanczos texture
if (win->effectWindow()) {
const QVariant texture = win->effectWindow()->data(LanczosCacheRole);
if (texture.isValid()) {
delete static_cast<GLTexture *>(texture.value<void*>());
win->effectWindow()->setData(LanczosCacheRole, QVariant());
}
}
win->getDamageRegionReply();
}
// Skip windows that are not yet ready for being painted and if screen is locked skip windows
// that are neither lockscreen nor inputmethod windows.
//
// TODO? This cannot be used so carelessly - needs protections against broken clients, the
// window should not get focus before it's displayed, handle unredirected windows properly and
// so on.
// Toplevel *systemDialog = nullptr;
// Toplevel *lockScreen = nullptr;
QList<Toplevel*> toRemove;
for (Toplevel *win : windows) {
if (!win->readyForPainting() || !win->visible()) {
toRemove.append(win);
}
// AbstractClient* ac = dynamic_cast<AbstractClient*>(win);
// if (ac &&(ac->jingWindowType() == JingWindowType::TYPE_SYSTEM_DIALOG)) {
// systemDialog = win;
// continue;
// }
// if (waylandServer() && waylandServer()->isScreenLocked()) {
// if(win->isLockScreen()) {
// lockScreen = win;
// continue;
// }
// }
}
for (Toplevel *win : toRemove) {
windows.removeAll(win);
}
// if (lockScreen) {
// windows.removeAll(lockScreen);
// windows.append(lockScreen);
// }
// if (systemDialog) {
// windows.removeAll(systemDialog);
// windows.append(systemDialog);
// }
if (m_framesToTestForSafety > 0 && (m_scene->compositingType() & OpenGLCompositing)) {
kwinApp()->platform()->createOpenGLSafePoint(Platform::OpenGLSafePoint::PreFrame);
}
const QRegion repaints = m_scene->repaints(screenId);
m_scene->resetRepaints(screenId);
m_scene->paint(screenId, repaints, windows, renderLoop);
if (m_framesToTestForSafety > 0) {
if (m_scene->compositingType() & OpenGLCompositing) {
kwinApp()->platform()->createOpenGLSafePoint(Platform::OpenGLSafePoint::PostFrame);
}
m_framesToTestForSafety--;
if (m_framesToTestForSafety == 0 && (m_scene->compositingType() & OpenGLCompositing)) {
kwinApp()->platform()->createOpenGLSafePoint(
Platform::OpenGLSafePoint::PostLastGuardedFrame);
}
}
if (waylandServer()) {
const std::chrono::milliseconds frameTime =
std::chrono::duration_cast<std::chrono::milliseconds>(renderLoop->lastPresentationTimestamp());
for (Toplevel *window : qAsConst(windows)) {
if (!window->readyForPainting()) {
continue;
}
AbstractClient* ac = dynamic_cast<AbstractClient*>(window);
if (ac && ac->isNormalWindow() && ac->jingWindowType() == JingWindowType::TYPE_APPLICATION && ac != workspace()->foregroundTopLevel() && !(window->isLockScreen() || window->isInputMethod() || window->isSystemUI())) {
continue;
}
if (!window->isOnScreen(screenId)) {
continue;
}
if (auto surface = window->surface()) {
surface->frameRendered(frameTime.count());
}
}
if (!kwinApp()->platform()->isCursorHidden()) {
Cursors::self()->currentCursor()->markAsRendered();
}
}
}
void Compositor::runPerformCompositing(int flags, RenderLoop *renderLoop) {
if(flags == 0){
#if HAVE_PERFETTO
qDebug()<<"[PERFETTO]"<<"[STOP]"<<"flags = 0";
#endif
return;
}
// If outputs are disabled, we return to the event loop and
// continue processing events until the outputs are enabled again
if (!kwinApp()->platform()->areOutputsEnabled()) {
return;
}
const int screenId = screenForRenderLoop(renderLoop);
fTraceDuration("Paint (", screens()->name(screenId), ")");
// Create a list of all windows in the stacking order
QList<Toplevel *> windows = Workspace::self()->xStackingOrder();
QList<Toplevel *> damaged;
// Reset the damage state of each window and fetch the damage region
// without waiting for a reply
for (Toplevel *win : qAsConst(windows)) {
if (win->resetAndFetchDamage()) {
damaged << win;
}
}
if (damaged.count() > 0) {
m_scene->triggerFence();
if (auto c = kwinApp()->x11Connection()) {
xcb_flush(c);
}
}
// Move elevated windows to the top of the stacking order
for (EffectWindow *c : static_cast<EffectsHandlerImpl *>(effects)->elevatedWindows()) {
Toplevel *t = static_cast<EffectWindowImpl *>(c)->window();
windows.removeAll(t);
windows.append(t);
}
// Get the replies
for (Toplevel *win : qAsConst(damaged)) {
// Discard the cached lanczos texture
if (win->effectWindow()) {
const QVariant texture = win->effectWindow()->data(LanczosCacheRole);
if (texture.isValid()) {
delete static_cast<GLTexture *>(texture.value<void*>());
win->effectWindow()->setData(LanczosCacheRole, QVariant());
}
}
win->getDamageRegionReply();
}
// Skip windows that are not yet ready for being painted and if screen is locked skip windows
// that are neither lockscreen nor inputmethod windows.
//
// TODO? This cannot be used so carelessly - needs protections against broken clients, the
// window should not get focus before it's displayed, handle unredirected windows properly and
// so on.
for (Toplevel *win : windows) {
if (!win->readyForPainting() || !win->visible()) {
windows.removeAll(win);
}
// if (waylandServer() && waylandServer()->isScreenLocked()) {
// if(!win->isLockScreen() && !win->isInputMethod() && !win->isDesktop()) {
// windows.removeAll(win);
// }
// }
}
if (m_framesToTestForSafety > 0 && (m_scene->compositingType() & OpenGLCompositing)) {
kwinApp()->platform()->createOpenGLSafePoint(Platform::OpenGLSafePoint::PreFrame);
}
const QRegion repaints = m_scene->repaints(screenId);
m_scene->resetRepaints(screenId);
m_scene->paint(screenId, repaints, windows, renderLoop);
if (m_framesToTestForSafety > 0) {
if (m_scene->compositingType() & OpenGLCompositing) {
kwinApp()->platform()->createOpenGLSafePoint(Platform::OpenGLSafePoint::PostFrame);
}
m_framesToTestForSafety--;
if (m_framesToTestForSafety == 0 && (m_scene->compositingType() & OpenGLCompositing)) {
kwinApp()->platform()->createOpenGLSafePoint(
Platform::OpenGLSafePoint::PostLastGuardedFrame);
}
}
if (waylandServer()) {
const std::chrono::milliseconds frameTime =
std::chrono::duration_cast<std::chrono::milliseconds>(renderLoop->lastPresentationTimestamp());
for (Toplevel *window : qAsConst(windows)) {
if (!window->readyForPainting()) {
continue;
}
// if (waylandServer()->isScreenLocked() &&
// !(window->isLockScreen() || window->isInputMethod() || window->isDesktop())) {
// continue;
// }
if (!window->isOnScreen(screenId)) {
continue;
}
if (auto surface = window->surface()) {
surface->frameRendered(frameTime.count());
}
}
if (!kwinApp()->platform()->isCursorHidden()) {
Cursors::self()->currentCursor()->markAsRendered();
}
}
}
bool Compositor::isActive()
{
return m_state == State::On;
}
WaylandCompositor::WaylandCompositor(QObject *parent)
: Compositor(parent)
{
connect(kwinApp(), &Application::x11ConnectionAboutToBeDestroyed,
this, &WaylandCompositor::destroyCompositorSelection);
}
void WaylandCompositor::toggleCompositing()
{
// For the shortcut. Not possible on Wayland because we always composite.
}
void WaylandCompositor::start()
{
if (!Compositor::setupStart()) {
// Internal setup failed, abort.
return;
}
if (Workspace::self()) {
startupWithWorkspace();
} else {
connect(kwinApp(), &Application::workspaceCreated,
this, &WaylandCompositor::startupWithWorkspace);
}
}
X11Compositor::X11Compositor(QObject *parent)
: Compositor(parent)
, m_suspended(options->isUseCompositing() ? NoReasonSuspend : UserSuspend)
{
}
void X11Compositor::toggleCompositing()
{
if (m_suspended) {
// Direct user call; clear all bits.
resume(AllReasonSuspend);
} else {
// But only set the user one (sufficient to suspend).
suspend(UserSuspend);
}
}
void X11Compositor::reinitialize()
{
// Resume compositing if suspended.
m_suspended = NoReasonSuspend;
Compositor::reinitialize();
}
void X11Compositor::configChanged()
{
if (m_suspended) {
stop();
return;
}
Compositor::configChanged();
}
void X11Compositor::suspend(X11Compositor::SuspendReason reason)
{
Q_ASSERT(reason != NoReasonSuspend);
m_suspended |= reason;
if (reason & ScriptSuspend) {
// When disabled show a shortcut how the user can get back compositing.
const auto shortcuts = KGlobalAccel::self()->shortcut(
workspace()->findChild<QAction*>(QStringLiteral("Suspend Compositing")));
if (!shortcuts.isEmpty()) {
// Display notification only if there is the shortcut.
const QString message =
i18n("Desktop effects have been suspended by another application.<br/>"
"You can resume using the '%1' shortcut.",
shortcuts.first().toString(QKeySequence::NativeText));
KNotification::event(QStringLiteral("compositingsuspendeddbus"), message);
}
}
stop();
}
void X11Compositor::resume(X11Compositor::SuspendReason reason)
{
Q_ASSERT(reason != NoReasonSuspend);
m_suspended &= ~reason;
start();
}
void X11Compositor::start()
{
if (m_suspended) {
QStringList reasons;
if (m_suspended & UserSuspend) {
reasons << QStringLiteral("Disabled by User");
}
if (m_suspended & BlockRuleSuspend) {
reasons << QStringLiteral("Disabled by Window");
}
if (m_suspended & ScriptSuspend) {
reasons << QStringLiteral("Disabled by Script");
}
qCDebug(KWIN_CORE) << "Compositing is suspended, reason:" << reasons;
return;
} else if (!kwinApp()->platform()->compositingPossible()) {
qCCritical(KWIN_CORE) << "Compositing is not possible";
return;
}
if (!Compositor::setupStart()) {
// Internal setup failed, abort.
return;
}
startupWithWorkspace();
}
void X11Compositor::handleFrameRequested(RenderLoop *renderLoop)
{
if (scene()->overlayWindow() && !isOverlayWindowVisible()) {
// Return since nothing is visible.
return;
}
Compositor::handleFrameRequested(renderLoop);
}
bool X11Compositor::checkForOverlayWindow(WId w) const
{
if (!scene()) {
// No scene, so it cannot be the overlay window.
return false;
}
if (!scene()->overlayWindow()) {
// No overlay window, it cannot be the overlay.
return false;
}
// Compare the window ID's.
return w == scene()->overlayWindow()->window();
}
bool X11Compositor::isOverlayWindowVisible() const
{
if (!scene()) {
return false;
}
if (!scene()->overlayWindow()) {
return false;
}
return scene()->overlayWindow()->isVisible();
}
void X11Compositor::updateClientCompositeBlocking(X11Client *c)
{
if (c) {
if (c->isBlockingCompositing()) {
// Do NOT attempt to call suspend(true) from within the eventchain!
if (!(m_suspended & BlockRuleSuspend))
QMetaObject::invokeMethod(this, [this]() {
suspend(BlockRuleSuspend);
}, Qt::QueuedConnection);
}
}
else if (m_suspended & BlockRuleSuspend) {
// If !c we just check if we can resume in case a blocking client was lost.
bool shouldResume = true;
for (auto it = Workspace::self()->clientList().constBegin();
it != Workspace::self()->clientList().constEnd(); ++it) {
if ((*it)->isBlockingCompositing()) {
shouldResume = false;
break;
}
}
if (shouldResume) {
// Do NOT attempt to call suspend(false) from within the eventchain!
QMetaObject::invokeMethod(this, [this]() {
resume(BlockRuleSuspend);
}, Qt::QueuedConnection);
}
}
}
X11Compositor *X11Compositor::self()
{
return qobject_cast<X11Compositor *>(Compositor::self());
}
}
// included for CompositorSelectionOwner
#include "composite.moc"
C++
1
https://gitee.com/jingos/kwin.git
git@gitee.com:jingos/kwin.git
jingos
kwin
kwin
master

搜索帮助