composite.cpp 37.8 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"
23
#include "utils.h"
24
#include <QTextStream>
25
26
27
28
29
#include "workspace.h"
#include "client.h"
#include "unmanaged.h"
#include "deleted.h"
#include "effects.h"
30
#include "overlaywindow.h"
31
#include "scene.h"
32
#include "screens.h"
33
#include "shadow.h"
34
#include "useractions.h"
35
#include "xcbutils.h"
36
#include "platform.h"
37
#include "shell_client.h"
38
#include "wayland_server.h"
39
#include "decorations/decoratedclient.h"
40

41
42
#include <kwingltexture.h>

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

45
46
#include <stdio.h>

47
48
#include <QtConcurrentRun>
#include <QFutureWatcher>
49
#include <QMenu>
50
#include <QTimerEvent>
51
#include <QDateTime>
52
#include <QOpenGLContext>
53
#include <QQuickWindow>
54
55
#include <KGlobalAccel>
#include <KLocalizedString>
56
57
#include <KPluginLoader>
#include <KPluginMetaData>
58
59
#include <KNotification>
#include <KSelectionWatcher>
60

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

64
65
Q_DECLARE_METATYPE(KWin::Compositor::SuspendReason)

66
67
68
namespace KWin
{

69
70
extern int currentRefreshRate();

71
CompositorSelectionOwner::CompositorSelectionOwner(const char *selection) : KSelectionOwner(selection, connection(), rootWindow()), owning(false)
Thomas Lübking's avatar
Thomas Lübking committed
72
73
74
75
76
77
78
79
80
{
    connect (this, SIGNAL(lostOwnership()), SLOT(looseOwnership()));
}

void CompositorSelectionOwner::looseOwnership()
{
    owning = false;
}

81
KWIN_SINGLETON_FACTORY_VARIABLE(Compositor, s_compositor)
82

83
static inline qint64 milliToNano(int milli) { return qint64(milli) * 1000 * 1000; }
84
85
static inline qint64 nanoToMilli(int nano) { return nano / (1000*1000); }

86
Compositor::Compositor(QObject* workspace)
87
    : QObject(workspace)
88
    , m_suspended(options->isUseCompositing() ? NoReasonSuspend : UserSuspend)
89
90
91
    , cm_selection(NULL)
    , vBlankInterval(0)
    , fpsInterval(0)
92
    , m_xrrRefreshRate(0)
93
    , m_finishing(false)
Luboš Luňák's avatar
Luboš Luňák committed
94
    , m_starting(false)
95
    , m_timeSinceLastVBlank(0)
96
    , m_scene(NULL)
97
98
    , m_bufferSwapPending(false)
    , m_composeAtSwapCompletion(false)
99
{
100
    qRegisterMetaType<Compositor::SuspendReason>("Compositor::SuspendReason");
101
    connect(&compositeResetTimer, SIGNAL(timeout()), SLOT(restart()));
102
    connect(options, &Options::configChanged, this, &Compositor::slotConfigChanged);
103
104
    compositeResetTimer.setSingleShot(true);
    nextPaintReference.invalidate(); // Initialize the timer
105
106

    // 2 sec which should be enough to restart the compositor
107
108
109
110
    static const int compositorLostMessageDelay = 2000;

    m_releaseSelectionTimer.setSingleShot(true);
    m_releaseSelectionTimer.setInterval(compositorLostMessageDelay);
111
    connect(&m_releaseSelectionTimer, SIGNAL(timeout()), SLOT(releaseCompositorSelection()));
112
113
114
115

    m_unusedSupportPropertyTimer.setInterval(compositorLostMessageDelay);
    m_unusedSupportPropertyTimer.setSingleShot(true);
    connect(&m_unusedSupportPropertyTimer, SIGNAL(timeout()), SLOT(deleteUnusedSupportProperties()));
116
117
118
119
120
121

    // delay the call to setup 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()) {
Martin Flöser's avatar
Martin Flöser committed
122
        QMetaObject::invokeMethod(this, "setup", Qt::QueuedConnection);
123
    }
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
    connect(kwinApp()->platform(), &Platform::readyChanged, this,
        [this] (bool ready) {
            if (ready) {
                setup();
            } else {
                finish();
            }
        }, Qt::QueuedConnection
    );
    connect(kwinApp(), &Application::x11ConnectionAboutToBeDestroyed, this,
        [this] {
            delete cm_selection;
            cm_selection = nullptr;
        }
    );
139

140
141
142
    if (qEnvironmentVariableIsSet("KWIN_MAX_FRAMES_TESTED"))
       m_framesToTestForSafety = qEnvironmentVariableIntValue("KWIN_MAX_FRAMES_TESTED");

143
144
    // register DBus
    new CompositorDBusInterface(this);
145
146
147
148
}

Compositor::~Compositor()
{
149
    emit aboutToDestroy();
150
    finish();
151
    deleteUnusedSupportProperties();
152
    delete cm_selection;
153
    s_compositor = NULL;
154
155
156
}


157
void Compositor::setup()
158
{
159
    if (hasScene())
160
        return;
161
    if (m_suspended) {
162
163
164
165
166
167
168
169
170
171
        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");
        }
172
        qCDebug(KWIN_CORE) << "Compositing is suspended, reason:" << reasons;
173
        return;
174
    } else if (!kwinApp()->platform()->compositingPossible()) {
175
        qCCritical(KWIN_CORE) << "Compositing is not possible";
176
        return;
177
    }
178
    m_starting = true;
179

180
    if (!options->isCompositingInitialized()) {
181
        options->reloadCompositingSettings(true);
182
    }
Rohan Garg's avatar
Rohan Garg committed
183
    slotCompositingOptionsInitialized();
184
}
185

Thomas Lübking's avatar
Thomas Lübking committed
186
extern int screen_number; // main.cpp
187
extern bool is_multihead;
Thomas Lübking's avatar
Thomas Lübking committed
188

189
void Compositor::slotCompositingOptionsInitialized()
190
{
191
    setupX11Support();
192
193

    // There might still be a deleted around, needs to be cleared before creating the scene (BUG 333275)
194
195
196
197
    if (Workspace::self()) {
        while (!Workspace::self()->deletedList().isEmpty()) {
            Workspace::self()->deletedList().first()->discard();
        }
198
199
    }

200
201
202
203
204
205
206
207
208
    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";
    }

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

211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
    for (auto type : qAsConst(supportedCompositors)) {
        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;
                    }
235
236
237
238
239
                }
            }
        }
    }

240
    if (m_scene == NULL || m_scene->initFailed()) {
241
        qCCritical(KWIN_CORE) << "Failed to initialize compositing, compositing disabled";
242
243
        delete m_scene;
        m_scene = NULL;
244
        m_starting = false;
245
246
247
248
        if (cm_selection) {
            cm_selection->owning = false;
            cm_selection->release();
        }
249
        if (!supportedCompositors.contains(NoCompositing)) {
250
251
            qCCritical(KWIN_CORE) << "The used windowing system requires compositing";
            qCCritical(KWIN_CORE) << "We are going to quit KWin now as it is broken";
252
253
            qApp->quit();
        }
254
        return;
255
    }
256
257
258
259
260
261

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

262
    connect(m_scene, &Scene::resetCompositing, this, &Compositor::restart);
263
    emit sceneCreated();
264
265
266
267
268
269
270
271
272
273

    if (Workspace::self()) {
        startupWithWorkspace();
    } else {
        connect(kwinApp(), &Application::workspaceCreated, this, &Compositor::startupWithWorkspace);
    }
}

void Compositor::claimCompositorSelection()
{
274
    if (!cm_selection) {
275
276
277
278
279
        char selection_name[ 100 ];
        sprintf(selection_name, "_NET_WM_CM_S%d", Application::x11ScreenNumber());
        cm_selection = new CompositorSelectionOwner(selection_name);
        connect(cm_selection, SIGNAL(lostOwnership()), SLOT(finish()));
    }
Thomas Lübking's avatar
Thomas Lübking committed
280
281
282
283

    if (!cm_selection) // no X11 yet
        return;

284
285
286
287
288
289
    if (!cm_selection->owning) {
        cm_selection->claim(true);   // force claiming
        cm_selection->owning = true;
    }
}

290
291
292
293
294
295
296
297
298
299
300
301
void Compositor::setupX11Support()
{
    auto c = kwinApp()->x11Connection();
    if (!c) {
        delete cm_selection;
        cm_selection = nullptr;
        return;
    }
    claimCompositorSelection();
    xcb_composite_redirect_subwindows(c, kwinApp()->x11RootWindow(), XCB_COMPOSITE_REDIRECT_MANUAL);
}

302
303
304
305
306
void Compositor::startupWithWorkspace()
{
    if (!m_starting) {
        return;
    }
307
    connect(kwinApp(), &Application::x11ConnectionChanged, this, &Compositor::setupX11Support, Qt::UniqueConnection);
308
    Workspace::self()->markXStackingOrderAsDirty();
309
    Q_ASSERT(m_scene);
310
    connect(workspace(), &Workspace::destroyed, this, [this] { compositeTimer.stop(); });
311
    setupX11Support();
312
    m_xrrRefreshRate = KWin::currentRefreshRate();
313
    fpsInterval = options->maxFpsInterval();
314
    if (m_scene->syncsToVBlank()) {  // if we do vsync, set the fps to the next multiple of the vblank rate
315
        vBlankInterval = milliToNano(1000) / m_xrrRefreshRate;
316
        fpsInterval = qMax((fpsInterval / vBlankInterval) * vBlankInterval, vBlankInterval);
317
    } else
318
319
        vBlankInterval = milliToNano(1); // no sync - DO NOT set "0", would cause div-by-zero segfaults.
    m_timeSinceLastVBlank = fpsInterval - (options->vBlankTime() + 1); // means "start now" - we don't have even a slight idea when the first vsync will occur
320
    scheduleRepaint();
321
    kwinApp()->platform()->createEffectsHandler(this, m_scene);   // sets also the 'effects' pointer
322
    connect(Workspace::self(), &Workspace::deletedRemoved, m_scene, &Scene::windowDeleted);
323
    connect(effects, SIGNAL(screenGeometryChanged(QSize)), SLOT(addRepaintFull()));
324
    addRepaintFull();
325
    foreach (Client * c, Workspace::self()->clientList()) {
326
        c->setupCompositing();
327
328
        c->getShadow();
    }
329
    foreach (Client * c,  Workspace::self()->desktopList())
330
        c->setupCompositing();
331
    foreach (Unmanaged * c, Workspace::self()->unmanagedList()) {
332
        c->setupCompositing();
333
334
        c->getShadow();
    }
335
336
337
338
339
340
    if (auto w = waylandServer()) {
        const auto clients = w->clients();
        for (auto c : clients) {
            c->setupCompositing();
            c->getShadow();
        }
341
342
343
344
345
        const auto internalClients = w->internalClients();
        for (auto c : internalClients) {
            c->setupCompositing();
            c->getShadow();
        }
346
    }
347

348
349
    emit compositingToggled(true);

350
351
352
353
354
    m_starting = false;
    if (m_releaseSelectionTimer.isActive()) {
        m_releaseSelectionTimer.stop();
    }

355
356
    // render at least once
    performCompositing();
357
}
358

359
void Compositor::scheduleRepaint()
360
361
362
363
364
{
    if (!compositeTimer.isActive())
        setCompositeTimer();
}

365
void Compositor::finish()
366
{
367
    if (!hasScene())
368
        return;
369
    m_finishing = true;
370
    m_releaseSelectionTimer.start();
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
    if (Workspace::self()) {
        foreach (Client * c, Workspace::self()->clientList())
            m_scene->windowClosed(c, NULL);
        foreach (Client * c, Workspace::self()->desktopList())
            m_scene->windowClosed(c, NULL);
        foreach (Unmanaged * c, Workspace::self()->unmanagedList())
            m_scene->windowClosed(c, NULL);
        foreach (Deleted * c, Workspace::self()->deletedList())
            m_scene->windowDeleted(c);
        foreach (Client * c, Workspace::self()->clientList())
        c->finishCompositing();
        foreach (Client * c, Workspace::self()->desktopList())
        c->finishCompositing();
        foreach (Unmanaged * c, Workspace::self()->unmanagedList())
        c->finishCompositing();
        foreach (Deleted * c, Workspace::self()->deletedList())
        c->finishCompositing();
388
389
390
        if (auto c = kwinApp()->x11Connection()) {
            xcb_composite_unredirect_subwindows(c, kwinApp()->x11RootWindow(), XCB_COMPOSITE_REDIRECT_MANUAL);
        }
391
    }
392
393
394
395
396
397
398
399
400
401
402
403
404
405
    if (waylandServer()) {
        foreach (ShellClient *c, waylandServer()->clients()) {
            m_scene->windowClosed(c, nullptr);
        }
        foreach (ShellClient *c, waylandServer()->internalClients()) {
            m_scene->windowClosed(c, nullptr);
        }
        foreach (ShellClient *c, waylandServer()->clients()) {
            c->finishCompositing();
        }
        foreach (ShellClient *c, waylandServer()->internalClients()) {
            c->finishCompositing();
        }
    }
406
407
    delete effects;
    effects = NULL;
408
409
    delete m_scene;
    m_scene = NULL;
410
    compositeTimer.stop();
411
    repaints_region = QRegion();
412
413
414
415
416
417
418
419
420
    if (Workspace::self()) {
        for (ClientList::ConstIterator it = Workspace::self()->clientList().constBegin();
                it != Workspace::self()->clientList().constEnd();
                ++it) {
            // forward all opacity values to the frame in case there'll be other CM running
            if ((*it)->opacity() != 1.0) {
                NETWinInfo i(connection(), (*it)->frameId(), rootWindow(), 0, 0);
                i.setOpacity(static_cast< unsigned long >((*it)->opacity() * 0xffffffff));
            }
421
        }
422
423
424
        // discard all Deleted windows (#152914)
        while (!Workspace::self()->deletedList().isEmpty())
            Workspace::self()->deletedList().first()->discard();
425
    }
426
    m_finishing = false;
427
    emit compositingToggled(false);
428
}
429

430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
void Compositor::releaseCompositorSelection()
{
    if (hasScene() && !m_finishing) {
        // compositor is up and running again, no need to release the selection
        return;
    }
    if (m_starting) {
        // currently still starting the compositor, it might fail, so restart the timer to test again
        m_releaseSelectionTimer.start();
        return;
    }

    if (m_finishing) {
        // still shutting down, a restart might follow, so restart the timer to test again
        m_releaseSelectionTimer.start();
        return;
    }
447
    qCDebug(KWIN_CORE) << "Releasing compositor selection";
448
449
450
451
    if (cm_selection) {
        cm_selection->owning = false;
        cm_selection->release();
    }
452
453
}

454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
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_starting) {
        // currently still starting the compositor
        m_unusedSupportPropertyTimer.start();
        return;
    }
    if (m_finishing) {
        // still shutting down, a restart might follow
        m_unusedSupportPropertyTimer.start();
        return;
    }
477
    if (const auto c = kwinApp()->x11Connection()) {
478
479
        foreach (const xcb_atom_t &atom, m_unusedSupportProperties) {
            // remove property from root window
480
            xcb_delete_property(c, kwinApp()->x11RootWindow(), atom);
481
        }
482
483
484
    }
}

485
486
void Compositor::slotConfigChanged()
{
487
488
    if (!m_suspended) {
        setup();
489
490
491
492
        if (effects)   // setupCompositing() may fail
            effects->reconfigure();
        addRepaintFull();
    } else
493
        finish();
494
495
496
497
}

void Compositor::slotReinitialize()
{
498
    // Reparse config. Config options will be reloaded by setup()
499
    kwinApp()->config()->reparseConfiguration();
500

501
    // Restart compositing
502
    finish();
503
    // resume compositing if suspended
504
    m_suspended = NoReasonSuspend;
505
    options->setCompositingInitialized(false);
506
    setup();
507
508
509
510

    if (effects) { // setup() may fail
        effects->reconfigure();
    }
511
512
}

513
// for the shortcut
514
void Compositor::slotToggleCompositing()
515
{
516
    if (kwinApp()->platform()->requiresCompositing()) {
517
518
519
        // we are not allowed to turn on/off compositing
        return;
    }
520
521
522
523
524
    if (m_suspended) { // direct user call; clear all bits
        resume(AllReasonSuspend);
    } else { // but only set the user one (sufficient to suspend)
        suspend(UserSuspend);
    }
525
}
526

527
528
529
530
531
void Compositor::updateCompositeBlocking()
{
    updateCompositeBlocking(NULL);
}

532
void Compositor::updateCompositeBlocking(Client *c)
533
{
534
    if (kwinApp()->platform()->requiresCompositing()) {
535
536
        return;
    }
537
538
    if (c) { // if c == 0 we just check if we can resume
        if (c->isBlockingCompositing()) {
539
540
            if (!(m_suspended & BlockRuleSuspend)) // do NOT attempt to call suspend(true); from within the eventchain!
                QMetaObject::invokeMethod(this, "suspend", Qt::QueuedConnection, Q_ARG(Compositor::SuspendReason, BlockRuleSuspend));
541
542
        }
    }
543
    else if (m_suspended & BlockRuleSuspend) {  // lost a client and we're blocked - can we resume?
544
        bool resume = true;
545
        for (ClientList::ConstIterator it = Workspace::self()->clientList().constBegin(); it != Workspace::self()->clientList().constEnd(); ++it) {
546
547
548
549
550
            if ((*it)->isBlockingCompositing()) {
                resume = false;
                break;
            }
        }
551
        if (resume) { // do NOT attempt to call suspend(false); from within the eventchain!
552
            QMetaObject::invokeMethod(this, "resume", Qt::QueuedConnection, Q_ARG(Compositor::SuspendReason, BlockRuleSuspend));
553
554
555
556
        }
    }
}

557
void Compositor::suspend(Compositor::SuspendReason reason)
558
{
559
    if (kwinApp()->platform()->requiresCompositing()) {
560
561
        return;
    }
562
563
    Q_ASSERT(reason != NoReasonSuspend);
    m_suspended |= reason;
564
565
566
567
568
569
570
571
572
573
    if (reason & KWin::Compositor::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);
        }
    }
574
    finish();
575
576
}

577
void Compositor::resume(Compositor::SuspendReason reason)
578
{
579
580
581
    Q_ASSERT(reason != NoReasonSuspend);
    m_suspended &= ~reason;
    setup(); // signal "toggled" is eventually emitted from within setup
582
583
}

584
void Compositor::restart()
585
{
586
    if (hasScene()) {
587
588
        finish();
        QTimer::singleShot(0, this, SLOT(setup()));
589
    }
590
}
591

592
void Compositor::addRepaint(int x, int y, int w, int h)
593
{
594
    if (!hasScene())
595
        return;
596
    repaints_region += QRegion(x, y, w, h);
597
    scheduleRepaint();
598
}
599

600
void Compositor::addRepaint(const QRect& r)
601
{
602
    if (!hasScene())
603
604
        return;
    repaints_region += r;
605
    scheduleRepaint();
606
607
}

608
void Compositor::addRepaint(const QRegion& r)
609
{
610
    if (!hasScene())
611
612
        return;
    repaints_region += r;
613
    scheduleRepaint();
614
615
}

616
void Compositor::addRepaintFull()
617
{
618
    if (!hasScene())
619
        return;
620
621
    const QSize &s = screens()->size();
    repaints_region = QRegion(0, 0, s.width(), s.height());
622
    scheduleRepaint();
623
}
624

625
void Compositor::timerEvent(QTimerEvent *te)
626
{
627
    if (te->timerId() == compositeTimer.timerId()) {
628
        performCompositing();
629
630
    } else
        QObject::timerEvent(te);
631
}
632

633
void Compositor::aboutToSwapBuffers()
634
{
635
636
637
638
639
640
641
642
643
644
645
646
647
    assert(!m_bufferSwapPending);

    m_bufferSwapPending = true;
}

void Compositor::bufferSwapComplete()
{
    assert(m_bufferSwapPending);
    m_bufferSwapPending = false;

    if (m_composeAtSwapCompletion) {
        m_composeAtSwapCompletion = false;
        performCompositing();
648
649
650
    }
}

651
void Compositor::performCompositing()
652
{
653
    if (m_scene->usesOverlayWindow() && !isOverlayWindowVisible())
654
        return; // nothing is visible anyway
655
656
657
658
659
660
661

    // 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) {
        m_composeAtSwapCompletion = true;
        compositeTimer.stop();
        return;
662
    }
663

664
665
    // If outputs are disabled, we return to the event loop and
    // continue processing events until the outputs are enabled again
666
    if (!kwinApp()->platform()->areOutputsEnabled()) {
667
668
669
670
        compositeTimer.stop();
        return;
    }

671
672
673
674
675
676
677
678
679
680
681
    // Create a list of all windows in the stacking order
    ToplevelList windows = Workspace::self()->xStackingOrder();
    ToplevelList damaged;

    // Reset the damage state of each window and fetch the damage region
    // without waiting for a reply
    foreach (Toplevel *win, windows) {
        if (win->resetAndFetchDamage())
            damaged << win;
    }

682
683
    if (damaged.count() > 0) {
        m_scene->triggerFence();
684
685
686
        if (auto c = kwinApp()->x11Connection()) {
            xcb_flush(c);
        }
687
    }
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709

    // Move elevated windows to the top of the stacking order
    foreach (EffectWindow *c, static_cast<EffectsHandlerImpl *>(effects)->elevatedWindows()) {
        Toplevel* t = static_cast< EffectWindowImpl* >(c)->window();
        windows.removeAll(t);
        windows.append(t);
    }

    // Get the replies
    foreach (Toplevel *win, 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();
    }

710
    if (repaints_region.isEmpty() && !windowRepaintsPending()) {
711
        m_scene->idle();
Thomas Lübking's avatar
Thomas Lübking committed
712
        m_timeSinceLastVBlank = fpsInterval - (options->vBlankTime() + 1); // means "start now"
713
        m_timeSinceStart += m_timeSinceLastVBlank;
714
715
716
717
        // 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.
Thomas Lübking's avatar
Thomas Lübking committed
718
        compositeTimer.stop();
719
        return;
720
    }
721

722
723
    // skip windows that are not yet ready for being painted and if screen is locked skip windows that are
    // neither lockscreen nor inputmethod windows
Thomas Lübking's avatar
Thomas Lübking committed
724
    // TODO ?
725
    // this cannot be used so carelessly - needs protections against broken clients, the window
726
    // should not get focus before it's displayed, handle unredirected windows properly and so on.
727
728
    foreach (Toplevel *t, windows) {
        if (!t->readyForPainting()) {
Thomas Lübking's avatar
Thomas Lübking committed
729
            windows.removeAll(t);
730
731
732
733
734
735
736
        }
        if (waylandServer() && waylandServer()->isScreenLocked()) {
            if(!t->isLockScreen() && !t->isInputMethod()) {
                windows.removeAll(t);
            }
        }
    }
Thomas Lübking's avatar
Thomas Lübking committed
737

738
739
740
    QRegion repaints = repaints_region;
    // clear all repaints, so that post-pass can add repaints for the next repaint
    repaints_region = QRegion();
741

742
743
744
    if (m_framesToTestForSafety > 0 && (m_scene->compositingType() & OpenGLCompositing)) {
        kwinApp()->platform()->createOpenGLSafePoint(Platform::OpenGLSafePoint::PreFrame);
    }
745
    m_timeSinceLastVBlank = m_scene->paint(repaints, windows);
746
747
748
749
750
751
752
753
754
    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);
        }
    }
755
756
    m_timeSinceStart += m_timeSinceLastVBlank;

757
    if (waylandServer()) {
758
        for (Toplevel *win : qAsConst(damaged)) {
759
760
761
762
763
            if (auto surface = win->surface()) {
                surface->frameRendered(m_timeSinceStart);
            }
        }
    }
764

Thomas Lübking's avatar
Thomas Lübking committed
765
766
    compositeTimer.stop(); // stop here to ensure *we* cause the next repaint schedule - not some effect through m_scene->paint()

767
768
    // 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
769
    // scheduleRepaint() would restart it again somewhen later, called from functions that
770
    // would again add something pending.
771
772
773
774
775
    if (m_bufferSwapPending && m_scene->syncsToVBlank()) {
        m_composeAtSwapCompletion = true;
    } else {
        scheduleRepaint();
    }
776
}
777

778
779
780
781
782
783
template <class T>
static bool repaintsPending(const QList<T*> &windows)
{
    return std::any_of(windows.begin(), windows.end(), [] (T *t) { return !t->repaints().isEmpty(); });
}

784
bool Compositor::windowRepaintsPending() const
785
{
786
    if (repaintsPending(Workspace::self()->clientList())) {
787
        return true;
788
789
    }
    if (repaintsPending(Workspace::self()->desktopList())) {
790
        return true;
791
792
    }
    if (repaintsPending(Workspace::self()->unmanagedList())) {
793
        return true;
794
795
    }
    if (repaintsPending(Workspace::self()->deletedList())) {
796
        return true;
797
    }
798
799
    if (auto w = waylandServer()) {
        const auto &clients = w->clients();
800
801
802
803
804
        auto test = [] (ShellClient *c) {
            return c->readyForPainting() && !c->repaints().isEmpty();
        };
        if (std::any_of(clients.begin(), clients.end(), test)) {
            return true;
805
806
        }
        const auto &internalClients = w->internalClients();
807
808
809
810
811
        auto internalTest = [] (ShellClient *c) {
            return c->isShown(true) && !c->repaints().isEmpty();
        };
        if (std::any_of(internalClients.begin(), internalClients.end(), internalTest)) {
            return true;
812
813
        }
    }
814
    return false;
815
}
816

817
818
819
820
821
822
void Compositor::setCompositeResetTimer(int msecs)
{
    compositeResetTimer.start(msecs);
}

void Compositor::setCompositeTimer()
823
{
824
    if (!hasScene())  // should not really happen, but there may be e.g. some damage events still pending
825
        return;
826
    if (m_starting || !Workspace::self()) {
827
828
        return;
    }
829

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

834
    // Don't start the timer if all outputs are disabled
835
    if (!kwinApp()->platform()->areOutputsEnabled()) {
836
837
838
        return;
    }

839
    uint waitTime = 1;
840

841
    if (m_scene->blocksForRetrace()) {
842
843
844
845
846
847
848

        // 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

849
        qint64 padding = m_timeSinceLastVBlank;
850
851
852
853
854
855
        if (padding > fpsInterval) {
            // we're at low repaints or spent more time in painting than the user wanted to wait for that frame
            padding = vBlankInterval - (padding%vBlankInterval); // -> align to next vblank
        } else {  // -> align to the next maxFps tick
            padding = ((vBlankInterval - padding%vBlankInterval) + (fpsInterval/vBlankInterval-1)*vBlankInterval);
            //               "remaining time of the first vsync" + "time for the other vsyncs of the frame"
856
        }
857

858
        if (padding < options->vBlankTime()) { // we'll likely miss this frame
859
            waitTime = nanoToMilli(padding + vBlankInterval - options->vBlankTime()); // so we add one
860
        } else {
861
            waitTime = nanoToMilli(padding - options->vBlankTime());
862
863
        }
    }
Thomas Lübking's avatar
Thomas Lübking committed
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
    else { // w/o blocking vsync we just jump to the next demanded tick
        if (fpsInterval > m_timeSinceLastVBlank) {
            waitTime = nanoToMilli(fpsInterval - m_timeSinceLastVBlank);
            if (!waitTime) {
                waitTime = 1; // will ensure we don't block out the eventloop - the system's just not faster ...
            }
        }/* else if (m_scene->syncsToVBlank() && m_timeSinceLastVBlank - fpsInterval < (vBlankInterval<<1)) {
            // 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 {
            waitTime = 1; // ... "0" would be sufficient, but the compositor isn't the WMs only task
        }
    }
887
    compositeTimer.start(qMin(waitTime, 250u), this); // force 4fps minimum
888
}
889

890
bool Compositor::isActive()
891
{
892
    return !m_finishing && hasScene();
893
}
894

895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
bool Compositor::checkForOverlayWindow(WId w) const
{
    if (!hasScene()) {
        // no scene, so it cannot be the overlay window
        return false;
    }
    if (!m_scene->overlayWindow()) {
        // no overlay window, it cannot be the overlay
        return false;
    }
    // and compare the window ID's
    return w == m_scene->overlayWindow()->window();
}

bool Compositor::isOverlayWindowVisible() const
{
    if (!hasScene()) {
        return false;
    }
    if (!m_scene->overlayWindow()) {
        return false;
    }
    return m_scene->overlayWindow()->isVisible();
}

920
921
922
923
/*****************************************************
 * Workspace
 ****************************************************/

924
925
926
927
928
bool Workspace::compositing() const
{
    return m_compositor && m_compositor->hasScene();
}

929
930
931
932
//****************************************
// Toplevel
//****************************************

933
bool Toplevel::setupCompositing()
934
935
{
    if (!compositing())
936
        return false;
937

938
    if (damage_handle != XCB_NONE)
939
        return false;
940

941
    if (kwinApp()->operationMode() == Application::OperationModeX11 && !surface()) {
942
943
944
        damage_handle = xcb_generate_id(connection());
        xcb_damage_create(connection(), damage_handle, frameId(), XCB_DAMAGE_REPORT_LEVEL_NON_EMPTY);
    }
945

946
    damage_region = QRegion(0, 0, width(), height());
947
    effect_window = new EffectWindowImpl(this);
948

949
    Compositor::self()->scene()->windowAdded(this);
950
951
952
953
954
955
956
957

    // With unmanaged windows there is a race condition between the client painting the window
    // and us setting up damage tracking.  If the client wins we won't get a damage event even
    // though the window has been painted.  To avoid this we mark the whole window as damaged
    // and schedule a repaint immediately after creating the damage object.
    if (dynamic_cast<Unmanaged*>(this))
        addDamageFull();

958
    return true;
959
}
960

961
void Toplevel::finishCompositing(ReleaseReason releaseReason)
962
{
963
    if (kwinApp()->operationMode() == Application::OperationModeX11 && damage_handle == XCB_NONE)
964
        return;
965
    if (effect_window->window() == this) { // otherwise it's already passed to Deleted, don't free data
966
967
        discardWindowPixmap();
        delete effect_window;
968
    }
969

970
    if (damage_handle != XCB_NONE &&
971
            releaseReason != ReleaseReason::Destroyed) {
972
973
        xcb_damage_destroy(connection(), damage_handle);
    }
974
975

    damage_handle = XCB_NONE;
976
977
978
    damage_region = QRegion();
    repaints_region = QRegion();
    effect_window = NULL;
979
}
980
981

void Toplevel::discardWindowPixmap()
982
{
983
    addDa