composite.cpp 33.2 KB
Newer Older
1
/********************************************************************
2
3
4
5
6
 KWin - the KDE window manager
 This file is part of the KDE project.

Copyright (C) 2006 Lubos Lunak <l.lunak@kde.org>

7
8
9
10
11
12
13
14
15
16
17
18
19
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/
20
#include "composite.h"
21

22
#include "dbusinterface.h"
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
23
#include "x11client.h"
24
#include "decorations/decoratedclient.h"
25
26
#include "deleted.h"
#include "effects.h"
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
27
#include "internal_client.h"
28
#include "overlaywindow.h"
29
#include "platform.h"
30
#include "scene.h"
31
#include "screens.h"
32
#include "shadow.h"
33
#include "xdgshellclient.h"
34
35
36
#include "unmanaged.h"
#include "useractions.h"
#include "utils.h"
37
#include "wayland_server.h"
38
39
#include "workspace.h"
#include "xcbutils.h"
40

41
42
#include <kwingltexture.h>

43
44
#include <KWayland/Server/surface_interface.h>

45
46
#include <KGlobalAccel>
#include <KLocalizedString>
47
48
#include <KPluginLoader>
#include <KPluginMetaData>
49
#include <KNotification>
50
#include <KSelectionOwner>
51

52
53
54
55
56
57
58
59
60
#include <QDateTime>
#include <QFutureWatcher>
#include <QMenu>
#include <QOpenGLContext>
#include <QQuickWindow>
#include <QtConcurrentRun>
#include <QTextStream>
#include <QTimerEvent>

61
#include <xcb/composite.h>
62
63
#include <xcb/damage.h>

64
65
#include <cstdio>

66
Q_DECLARE_METATYPE(KWin::X11Compositor::SuspendReason)
67

68
69
70
namespace KWin
{

71
72
73
// See main.cpp:
extern int screen_number;

74
extern bool is_multihead;
75
76
extern int currentRefreshRate();

77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
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;
}

98
class CompositorSelectionOwner : public KSelectionOwner
Thomas Lübking's avatar
Thomas Lübking committed
99
{
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
    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;
};
Thomas Lübking's avatar
Thomas Lübking committed
118

119
static inline qint64 milliToNano(int milli) { return qint64(milli) * 1000 * 1000; }
120
121
static inline qint64 nanoToMilli(int nano) { return nano / (1000*1000); }

122
Compositor::Compositor(QObject* workspace)
123
    : QObject(workspace)
124
    , m_state(State::Off)
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
125
    , m_selectionOwner(nullptr)
126
127
128
    , vBlankInterval(0)
    , fpsInterval(0)
    , m_timeSinceLastVBlank(0)
129
    , m_scene(nullptr)
130
131
    , m_bufferSwapPending(false)
    , m_composeAtSwapCompletion(false)
132
{
133
    connect(options, &Options::configChanged, this, &Compositor::configChanged);
134
    connect(options, &Options::animationSpeedChanged, this, &Compositor::configChanged);
Roman Gilg's avatar
Roman Gilg committed
135

136
    m_monotonicClock.start();
137

138
    // 2 sec which should be enough to restart the compositor.
139
140
141
142
    static const int compositorLostMessageDelay = 2000;

    m_releaseSelectionTimer.setSingleShot(true);
    m_releaseSelectionTimer.setInterval(compositorLostMessageDelay);
143
144
    connect(&m_releaseSelectionTimer, &QTimer::timeout,
            this, &Compositor::releaseCompositorSelection);
145
146
147

    m_unusedSupportPropertyTimer.setInterval(compositorLostMessageDelay);
    m_unusedSupportPropertyTimer.setSingleShot(true);
148
149
    connect(&m_unusedSupportPropertyTimer, &QTimer::timeout,
            this, &Compositor::deleteUnusedSupportProperties);
150

151
    // Delay the call to start by one event cycle.
152
153
154
155
    // 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()) {
156
        QTimer::singleShot(0, this, &Compositor::start);
157
    }
158
159
160
    connect(kwinApp()->platform(), &Platform::readyChanged, this,
        [this] (bool ready) {
            if (ready) {
161
                start();
162
            } else {
163
                stop();
164
165
166
            }
        }, Qt::QueuedConnection
    );
167

168
169
170
    if (qEnvironmentVariableIsSet("KWIN_MAX_FRAMES_TESTED"))
       m_framesToTestForSafety = qEnvironmentVariableIntValue("KWIN_MAX_FRAMES_TESTED");

171
172
    // register DBus
    new CompositorDBusInterface(this);
173
174
175
176
}

Compositor::~Compositor()
{
177
    emit aboutToDestroy();
178
    stop();
179
    deleteUnusedSupportProperties();
180
    destroyCompositorSelection();
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
181
    s_compositor = nullptr;
182
183
}

184
bool Compositor::setupStart()
185
{
186
    if (kwinApp()->isTerminating()) {
187
188
        // Don't start while KWin is terminating. An event to restart might be lingering
        // in the event queue due to graphics reset.
189
        return false;
190
    }
191
    if (m_state != State::Off) {
192
        return false;
193
    }
194
    m_state = State::Starting;
195

196
    options->reloadCompositingSettings(true);
197

198
    setupX11Support();
199

200
201
    // There might still be a deleted around, needs to be cleared before
    // creating the scene (BUG 333275).
202
203
204
205
    if (Workspace::self()) {
        while (!Workspace::self()->deletedList().isEmpty()) {
            Workspace::self()->deletedList().first()->discard();
        }
206
207
    }

208
209
    emit aboutToToggleCompositing();

210
    auto supportedCompositors = kwinApp()->platform()->supportedCompositors();
211
212
213
    const auto userConfigIt = std::find(supportedCompositors.begin(), supportedCompositors.end(),
                                        options->compositingMode());

214
215
216
217
    if (userConfigIt != supportedCompositors.end()) {
        supportedCompositors.erase(userConfigIt);
        supportedCompositors.prepend(options->compositingMode());
    } else {
218
219
        qCWarning(KWIN_CORE)
                << "Configured compositor not supported by Platform. Falling back to defaults";
220
221
    }

222
    const auto availablePlugins = KPluginLoader::findPlugins(QStringLiteral("org.kde.kwin.scenes"));
223

Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
224
225
226
227
    for (const KPluginMetaData &pluginMetaData : availablePlugins) {
        qCDebug(KWIN_CORE) << "Available scene plugin:" << pluginMetaData.fileName();
    }

228
    for (auto type : qAsConst(supportedCompositors)) {
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
229
230
231
232
233
234
235
236
237
238
239
240
241
242
        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();
        }
243
244
245
246
247
248
249
250
251
252
253
254
        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()) {
255
256
            std::unique_ptr<SceneFactory>
                    factory{ qobject_cast<SceneFactory*>(pluginIt->instantiate()) };
257
258
259
260
            if (factory) {
                m_scene = factory->create(this);
                if (m_scene) {
                    if (!m_scene->initFailed()) {
261
262
                        qCDebug(KWIN_CORE) << "Instantiated compositing plugin:"
                                           << pluginIt->name();
263
264
265
266
267
                        break;
                    } else {
                        delete m_scene;
                        m_scene = nullptr;
                    }
268
269
270
271
272
                }
            }
        }
    }

Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
273
    if (m_scene == nullptr || m_scene->initFailed()) {
274
        qCCritical(KWIN_CORE) << "Failed to initialize compositing, compositing disabled";
275
276
        m_state = State::Off;

277
        delete m_scene;
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
278
        m_scene = nullptr;
279

280
281
282
        if (m_selectionOwner) {
            m_selectionOwner->setOwning(false);
            m_selectionOwner->release();
283
        }
284
        if (!supportedCompositors.contains(NoCompositing)) {
285
286
            qCCritical(KWIN_CORE) << "The used windowing system requires compositing";
            qCCritical(KWIN_CORE) << "We are going to quit KWin now as it is broken";
287
288
            qApp->quit();
        }
289
        return false;
290
    }
291

292
293
294
295
296
297
    CompositingType compositingType = m_scene->compositingType();
    if (compositingType & OpenGLCompositing) {
        // Override for OpenGl sub-type OpenGL2Compositing.
        compositingType = OpenGLCompositing;
    }
    kwinApp()->platform()->setSelectedCompositor(compositingType);
298

299
    if (!Workspace::self() && m_scene && m_scene->compositingType() == QPainterCompositing) {
300
        // Force Software QtQuick on first startup with QPainter.
301
302
303
        QQuickWindow::setSceneGraphBackend(QSGRendererInterface::Software);
    }

304
    connect(m_scene, &Scene::resetCompositing, this, &Compositor::reinitialize);
305
    emit sceneCreated();
306

307
    return true;
308
309
310
311
}

void Compositor::claimCompositorSelection()
{
312
    if (!m_selectionOwner) {
313
314
        char selection_name[ 100 ];
        sprintf(selection_name, "_NET_WM_CM_S%d", Application::x11ScreenNumber());
315
        m_selectionOwner = new CompositorSelectionOwner(selection_name);
316
317
        connect(m_selectionOwner, &CompositorSelectionOwner::lostOwnership,
                this, &Compositor::stop);
318
    }
Thomas Lübking's avatar
Thomas Lübking committed
319

320
321
    if (!m_selectionOwner) {
        // No X11 yet.
Thomas Lübking's avatar
Thomas Lübking committed
322
        return;
323
    }
324
    if (!m_selectionOwner->owning()) {
325
326
        // Force claim ownership.
        m_selectionOwner->claim(true);
327
        m_selectionOwner->setOwning(true);
328
329
330
    }
}

331
332
void Compositor::setupX11Support()
{
333
334
    auto *con = kwinApp()->x11Connection();
    if (!con) {
335
336
        delete m_selectionOwner;
        m_selectionOwner = nullptr;
337
338
339
        return;
    }
    claimCompositorSelection();
340
341
    xcb_composite_redirect_subwindows(con, kwinApp()->x11RootWindow(),
                                      XCB_COMPOSITE_REDIRECT_MANUAL);
342
343
}

344
345
void Compositor::startupWithWorkspace()
{
346
347
    connect(kwinApp(), &Application::x11ConnectionChanged,
            this, &Compositor::setupX11Support, Qt::UniqueConnection);
348
    Workspace::self()->markXStackingOrderAsDirty();
349
    Q_ASSERT(m_scene);
350

351
    connect(workspace(), &Workspace::destroyed, this, [this] { compositeTimer.stop(); });
352
    setupX11Support();
353
354
    fpsInterval = options->maxFpsInterval();

355
356
357
358
359
360
361
362
    if (m_scene->syncsToVBlank()) {
        // If we do vsync, set the fps to the next multiple of the vblank rate.
        vBlankInterval = milliToNano(1000) / currentRefreshRate();
        fpsInterval = qMax((fpsInterval / vBlankInterval) * vBlankInterval, vBlankInterval);
    } else {
        // No vsync - DO NOT set "0", would cause div-by-zero segfaults.
        vBlankInterval = milliToNano(1);
    }
363
364
365

    // Sets also the 'effects' pointer.
    kwinApp()->platform()->createEffectsHandler(this, m_scene);
366
    connect(Workspace::self(), &Workspace::deletedRemoved, m_scene, &Scene::removeToplevel);
367
    connect(effects, &EffectsHandler::screenGeometryChanged, this, &Compositor::addRepaintFull);
368

Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
369
    for (X11Client *c : Workspace::self()->clientList()) {
370
        c->setupCompositing();
371
        c->updateShadow();
372
    }
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
373
    for (X11Client *c : Workspace::self()->desktopList()) {
374
        c->setupCompositing();
375
376
    }
    for (Unmanaged *c : Workspace::self()->unmanagedList()) {
377
        c->setupCompositing();
378
        c->updateShadow();
379
    }
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
380
381
    for (InternalClient *client : workspace()->internalClients()) {
        client->setupCompositing();
382
        client->updateShadow();
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
383
    }
384
385
386

    if (auto *server = waylandServer()) {
        const auto clients = server->clients();
387
        for (XdgShellClient *c : clients) {
388
            c->setupCompositing();
389
            c->updateShadow();
390
391
        }
    }
392

393
    m_state = State::On;
394
395
    emit compositingToggled(true);

396
397
398
399
    if (m_releaseSelectionTimer.isActive()) {
        m_releaseSelectionTimer.stop();
    }

400
    // Render at least once.
401
    addRepaintFull();
402
    performCompositing();
403
}
404

405
void Compositor::scheduleRepaint()
406
{
407
    if (!compositeTimer.isActive())
408
        setCompositeTimer();
409
410
}

411
void Compositor::stop()
412
{
413
    if (m_state == State::Off || m_state == State::Stopping) {
414
        return;
415
416
    }
    m_state = State::Stopping;
417
418
    emit aboutToToggleCompositing();

419
420
    m_releaseSelectionTimer.start();

421
422
423
424
425
426
    // 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;

427
    if (Workspace::self()) {
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
428
        for (X11Client *c : Workspace::self()->clientList()) {
429
            m_scene->removeToplevel(c);
430
        }
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
431
        for (X11Client *c : Workspace::self()->desktopList()) {
432
            m_scene->removeToplevel(c);
433
434
        }
        for (Unmanaged *c : Workspace::self()->unmanagedList()) {
435
            m_scene->removeToplevel(c);
436
        }
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
437
438
439
        for (InternalClient *client : workspace()->internalClients()) {
            m_scene->removeToplevel(client);
        }
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
440
        for (X11Client *c : Workspace::self()->clientList()) {
441
442
            c->finishCompositing();
        }
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
443
        for (X11Client *c : Workspace::self()->desktopList()) {
444
445
446
447
448
            c->finishCompositing();
        }
        for (Unmanaged *c : Workspace::self()->unmanagedList()) {
            c->finishCompositing();
        }
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
449
450
451
        for (InternalClient *client : workspace()->internalClients()) {
            client->finishCompositing();
        }
452
453
454
455
        if (auto *con = kwinApp()->x11Connection()) {
            xcb_composite_unredirect_subwindows(con, kwinApp()->x11RootWindow(),
                                                XCB_COMPOSITE_REDIRECT_MANUAL);
        }
456
457
458
        while (!workspace()->deletedList().isEmpty()) {
            workspace()->deletedList().first()->discard();
        }
459
    }
460

461
    if (waylandServer()) {
462
        for (XdgShellClient *c : waylandServer()->clients()) {
463
            m_scene->removeToplevel(c);
464
        }
465
        for (XdgShellClient *c : waylandServer()->clients()) {
466
467
468
            c->finishCompositing();
        }
    }
469

470
    delete m_scene;
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
471
    m_scene = nullptr;
472
    compositeTimer.stop();
473
    repaints_region = QRegion();
474
475

    m_state = State::Off;
476
    emit compositingToggled(false);
477
}
478

479
480
481
482
483
484
void Compositor::destroyCompositorSelection()
{
    delete m_selectionOwner;
    m_selectionOwner = nullptr;
}

485
486
void Compositor::releaseCompositorSelection()
{
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
    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.
502
        m_releaseSelectionTimer.start();
503
        break;
504
    }
505
506
}

507
508
509
510
511
512
513
514
515
516
517
518
519
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()
{
520
521
    if (m_state == State::Starting || m_state == State::Stopping) {
        // Currently still maybe restarting the compositor.
522
523
524
        m_unusedSupportPropertyTimer.start();
        return;
    }
525
526
    if (auto *con = kwinApp()->x11Connection()) {
        for (const xcb_atom_t &atom : qAsConst(m_unusedSupportProperties)) {
527
            // remove property from root window
528
            xcb_delete_property(con, kwinApp()->x11RootWindow(), atom);
529
        }
530
        m_unusedSupportProperties.clear();
531
532
533
    }
}

534
void Compositor::configChanged()
535
{
536
537
    reinitialize();
    addRepaintFull();
538
539
}

540
void Compositor::reinitialize()
541
{
542
    // Reparse config. Config options will be reloaded by start()
543
    kwinApp()->config()->reparseConfiguration();
544

545
    // Restart compositing
546
547
    stop();
    start();
548

549
    if (effects) { // start() may fail
550
551
        effects->reconfigure();
    }
552
553
554
}

void Compositor::addRepaint(int x, int y, int w, int h)
555
{
556
    if (m_state != State::On) {
557
        return;
558
    }
559
    repaints_region += QRegion(x, y, w, h);
560
    scheduleRepaint();
561
}
562

563
void Compositor::addRepaint(const QRect& r)
564
{
565
    if (m_state != State::On) {
566
        return;
567
    }
568
    repaints_region += r;
569
    scheduleRepaint();
570
571
}

572
void Compositor::addRepaint(const QRegion& r)
573
{
574
    if (m_state != State::On) {
575
        return;
576
    }
577
    repaints_region += r;
578
    scheduleRepaint();
579
580
}

581
void Compositor::addRepaintFull()
582
{
583
    if (m_state != State::On) {
584
        return;
585
    }
586
587
    const QSize &s = screens()->size();
    repaints_region = QRegion(0, 0, s.width(), s.height());
588
    scheduleRepaint();
589
}
590

591
void Compositor::timerEvent(QTimerEvent *te)
592
{
593
    if (te->timerId() == compositeTimer.timerId()) {
594
        performCompositing();
595
596
    } else
        QObject::timerEvent(te);
597
}
598

599
void Compositor::aboutToSwapBuffers()
600
{
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
601
    Q_ASSERT(!m_bufferSwapPending);
Roman Gilg's avatar
Roman Gilg committed
602

603
604
605
606
607
    m_bufferSwapPending = true;
}

void Compositor::bufferSwapComplete()
{
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
608
    Q_ASSERT(m_bufferSwapPending);
609
    m_bufferSwapPending = false;
610

611
    emit bufferSwapCompleted();
612
613
614
615
616

    if (m_composeAtSwapCompletion) {
        m_composeAtSwapCompletion = false;
        performCompositing();
    }
617
618
}

619
void Compositor::performCompositing()
620
{
621
622
623
    // If a buffer swap is still pending, we return to the event loop and
    // continue processing events until the swap has completed.
    if (m_bufferSwapPending) {
624
625
        m_composeAtSwapCompletion = true;
        compositeTimer.stop();
626
        return;
627
    }
628

629
630
    // If outputs are disabled, we return to the event loop and
    // continue processing events until the outputs are enabled again
631
    if (!kwinApp()->platform()->areOutputsEnabled()) {
632
        compositeTimer.stop();
633
634
635
        return;
    }

636
    // Create a list of all windows in the stacking order
637
638
    QList<Toplevel *> windows = Workspace::self()->xStackingOrder();
    QList<Toplevel *> damaged;
639
640
641

    // Reset the damage state of each window and fetch the damage region
    // without waiting for a reply
642
643
    for (Toplevel *win : windows) {
        if (win->resetAndFetchDamage()) {
644
            damaged << win;
645
        }
646
647
    }

648
649
    if (damaged.count() > 0) {
        m_scene->triggerFence();
650
651
652
        if (auto c = kwinApp()->x11Connection()) {
            xcb_flush(c);
        }
653
    }
654
655

    // Move elevated windows to the top of the stacking order
656
657
    for (EffectWindow *c : static_cast<EffectsHandlerImpl *>(effects)->elevatedWindows()) {
        Toplevel *t = static_cast<EffectWindowImpl *>(c)->window();
658
659
660
661
662
        windows.removeAll(t);
        windows.append(t);
    }

    // Get the replies
663
    for (Toplevel *win : damaged) {
664
665
666
667
668
669
670
671
672
673
674
675
        // 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();
    }

676
    if (repaints_region.isEmpty() && !windowRepaintsPending()) {
677
        m_scene->idle();
678
679
680
681
682
683
        m_timeSinceLastVBlank = fpsInterval - (options->vBlankTime() + 1); // means "start now"
        // Note: It would seem here we should undo suspended unredirect, but when scenes need
        // it for some reason, e.g. transformations or translucency, the next pass that does not
        // need this anymore and paints normally will also reset the suspended unredirect.
        // Otherwise the window would not be painted normally anyway.
        compositeTimer.stop();
684
        return;
685
    }
686

687
688
689
690
691
692
693
694
695
    // 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()) {
            windows.removeAll(win);
696
697
        }
        if (waylandServer() && waylandServer()->isScreenLocked()) {
698
699
            if(!win->isLockScreen() && !win->isInputMethod()) {
                windows.removeAll(win);
700
701
702
            }
        }
    }
Thomas Lübking's avatar
Thomas Lübking committed
703

704
705
706
    QRegion repaints = repaints_region;
    // clear all repaints, so that post-pass can add repaints for the next repaint
    repaints_region = QRegion();
707

708
709
710
    if (m_framesToTestForSafety > 0 && (m_scene->compositingType() & OpenGLCompositing)) {
        kwinApp()->platform()->createOpenGLSafePoint(Platform::OpenGLSafePoint::PreFrame);
    }
711
    m_timeSinceLastVBlank = m_scene->paint(repaints, windows);
712
713
714
715
716
717
    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)) {
718
719
            kwinApp()->platform()->createOpenGLSafePoint(
                Platform::OpenGLSafePoint::PostLastGuardedFrame);
720
721
        }
    }
722

723
    if (waylandServer()) {
724
        const auto currentTime = static_cast<quint32>(m_monotonicClock.elapsed());
725
        for (Toplevel *win : qAsConst(windows)) {
726
            if (auto surface = win->surface()) {
727
                surface->frameRendered(currentTime);
728
729
730
            }
        }
    }
731

732
733
    // Stop here to ensure *we* cause the next repaint schedule - not some effect
    // through m_scene->paint().
734
    compositeTimer.stop();
735
736
737
738
739

    // Trigger at least one more pass even if there would be nothing to paint, so that scene->idle()
    // is called the next time. If there would be nothing pending, it will not restart the timer and
    // scheduleRepaint() would restart it again somewhen later, called from functions that
    // would again add something pending.
740
    if (m_bufferSwapPending && m_scene->syncsToVBlank()) {
741
742
743
744
        m_composeAtSwapCompletion = true;
    } else {
        scheduleRepaint();
    }
745
}
746

747
748
749
template <class T>
static bool repaintsPending(const QList<T*> &windows)
{
750
751
    return std::any_of(windows.begin(), windows.end(),
                       [](T *t) { return !t->repaints().isEmpty(); });
752
753
}

754
bool Compositor::windowRepaintsPending() const
755
{
756
    if (repaintsPending(Workspace::self()->clientList())) {
757
        return true;
758
759
    }
    if (repaintsPending(Workspace::self()->desktopList())) {
760
        return true;
761
762
    }
    if (repaintsPending(Workspace::self()->unmanagedList())) {
763
        return true;
764
765
    }
    if (repaintsPending(Workspace::self()->deletedList())) {
766
        return true;
767
    }
768
769
    if (auto *server = waylandServer()) {
        const auto &clients = server->clients();
770
        auto test = [](XdgShellClient *c) {
771
772
773
774
            return c->readyForPainting() && !c->repaints().isEmpty();
        };
        if (std::any_of(clients.begin(), clients.end(), test)) {
            return true;
775
        }
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
776
777
778
779
780
781
782
    }
    const auto &internalClients = workspace()->internalClients();
    auto internalTest = [] (InternalClient *client) {
        return client->isShown(true) && !client->repaints().isEmpty();
    };
    if (std::any_of(internalClients.begin(), internalClients.end(), internalTest)) {
        return true;
783
    }
784
    return false;
785
}
786

787
void Compositor::setCompositeTimer()
788
{
789
790
791
792
793
794
795
796
797
798
    if (m_state != State::On) {
        return;
    }

    // Don't start the timer if we're waiting for a swap event
    if (m_bufferSwapPending && m_composeAtSwapCompletion)
        return;

    // Don't start the timer if all outputs are disabled
    if (!kwinApp()->platform()->areOutputsEnabled()) {
799
800
801
        return;
    }

802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
    uint waitTime = 1;

    if (m_scene->blocksForRetrace()) {

        // TODO: make vBlankTime dynamic?!
        // It's required because glXWaitVideoSync will *likely* block a full frame if one enters
        // a retrace pass which can last a variable amount of time, depending on the actual screen
        // Now, my ooold 19" CRT can do such retrace so that 2ms are entirely sufficient,
        // while another ooold 15" TFT requires about 6ms

        qint64 padding = m_timeSinceLastVBlank;
        if (padding > fpsInterval) {
            // We're at low repaints or spent more time in painting than the user wanted to wait
            // for that frame. Align to next vblank:
            padding = vBlankInterval - (padding % vBlankInterval);
        } else {
            // Align to the next maxFps tick:
            // "remaining time of the first vsync" + "time for the other vsyncs of the frame"
            padding = ((vBlankInterval - padding % vBlankInterval) +
                       (fpsInterval / vBlankInterval - 1) * vBlankInterval);
        }

        if (padding < options->vBlankTime()) {
            // We'll likely miss this frame so we add one:
            waitTime = nanoToMilli(padding + vBlankInterval - options->vBlankTime());
        } else {
            waitTime = nanoToMilli(padding - options->vBlankTime());
        }
    }
    else { // w/o blocking vsync we just jump to the next demanded tick
        if (fpsInterval > m_timeSinceLastVBlank) {
            waitTime = nanoToMilli(fpsInterval - m_timeSinceLastVBlank);
            if (!waitTime) {
                // Will ensure we don't block out the eventloop - the system's just not faster ...
                waitTime = 1;
            }
        }
839
        /* else if (m_scene->syncsToVBlank() && m_timeSinceLastVBlank - fpsInterval < (vBlankInterval<<1)) {
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
            // NOTICE - "for later" ------------------------------------------------------------------
            // It can happen that we push two frames within one refresh cycle.
            // Swapping will then block even with triple buffering when the GPU does not discard but
            // queues frames
            // now here's the mean part: if we take that as "OMG, we're late - next frame ASAP",
            // there'll immediately be 2 frames in the pipe, swapping will block, we think we're
            // late ... ewww
            // so instead we pad to the clock again and add 2ms safety to ensure the pipe is really
            // free
            // NOTICE: obviously m_timeSinceLastVBlank can be too big because we're too slow as well
            // So if this code was enabled, we'd needlessly half the framerate once more (15 instead of 30)
            waitTime = nanoToMilli(vBlankInterval - (m_timeSinceLastVBlank - fpsInterval)%vBlankInterval) + 2;
        }*/
        else {
            // "0" would be sufficient here, but the compositor isn't the WMs only task.
            waitTime = 1;
        }
    }
858
859
    // Force 4fps minimum:
    compositeTimer.start(qMin(waitTime, 250u), this);
860
}
861

862
bool Compositor::isActive()
863
{
864
    return m_state == State::On;
865
}
866

867
868
869
870
871
872
873
874
875
WaylandCompositor::WaylandCompositor(QObject *parent)
    : Compositor(parent)
{
    connect(kwinApp(), &Application::x11ConnectionAboutToBeDestroyed,
            this, &WaylandCompositor::destroyCompositorSelection);
}

void WaylandCompositor::toggleCompositing()
{
876
    // For the shortcut. Not possible on Wayland because we always composite.
877
878
879
880
881
882
883
884
}

void WaylandCompositor::start()
{
    if (!Compositor::setupStart()) {
        // Internal setup failed, abort.
        return;
    }
885

886
887
888
    if (Workspace::self()) {
        startupWithWorkspace();
    } else {
889
890
        connect(kwinApp(), &Application::workspaceCreated,
                this, &WaylandCompositor::startupWithWorkspace);
891
892
893
894
895
    }
}

int WaylandCompositor::refreshRate() const
{
896
897
898
899
    // TODO: This makes no sense on Wayland. First step would be to atleast
    //       set the refresh rate to the highest available one. Second step
    //       would be to not use a uniform value at all but per screen.
    return KWin::currentRefreshRate();
900
901
902
903
904
905
906
907
908
909
910
}

X11Compositor::X11Compositor(QObject *parent)
    : Compositor(parent)
    , m_suspended(options->isUseCompositing() ? NoReasonSuspend : UserSuspend)
    , m_xrrRefreshRate(0)
{
}

void X11Compositor::toggleCompositing()
{
911
912
    if (m_suspended) {
        // Direct user call; clear all bits.
913
        resume(AllReasonSuspend);
914
915
    } else {
        // But only set the user one (sufficient to suspend).
916
917
918
919
920
921
        suspend(UserSuspend);
    }
}

void X11Compositor::reinitialize()
{
922
    // Resume compositing if suspended.
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
    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;
940

941
    if (reason & ScriptSuspend) {
942
943
944
        // When disabled show a shortcut how the user can get back compositing.
        const auto shortcuts = KGlobalAccel::self()->shortcut(
            workspace()->findChild<QAction*>(QStringLiteral("Suspend Compositing")));
945
        if (!shortcuts.isEmpty()) {
946
947
948
949
950
            // 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));
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
            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;