x11_platform.cpp 18.2 KB
Newer Older
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
1
2
3
/*
    KWin - the KDE window manager
    This file is part of the KDE project.
4

Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
5
    SPDX-FileCopyrightText: 2016 Martin Gräßlin <mgraesslin@kde.org>
6

Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
7
8
    SPDX-License-Identifier: GPL-2.0-or-later
*/
9
#include "x11_platform.h"
10
#include "x11cursor.h"
11
#include "edge.h"
12
#include "windowselector.h"
13
#include <config-kwin.h>
14
15
16
17
#include <kwinconfig.h>
#if HAVE_EPOXY_GLX
#include "glxbackend.h"
#endif
18
19
20
#if HAVE_X11_XINPUT
#include "xinputintegration.h"
#endif
21
#include "abstract_client.h"
22
#include "effects_x11.h"
23
#include "eglonxbackend.h"
24
#include "keyboard_input.h"
25
#include "logging.h"
26
#include "screens_xrandr.h"
27
#include "screenedges_filter.h"
28
#include "options.h"
29
#include "overlaywindow_x11.h"
30
#include "non_composited_outline.h"
31
#include "workspace.h"
32
#include "x11_decoration_renderer.h"
33
34
#include "x11_output.h"
#include "xcbutils.h"
35

36
37
#include <kwinxrenderutils.h>

38
39
#include <KConfigGroup>
#include <KLocalizedString>
40
#include <KCrash>
41

42
#include <QThread>
43
#include <QOpenGLContext>
44
45
46
47
48
49
50
#include <QX11Info>

namespace KWin
{

X11StandalonePlatform::X11StandalonePlatform(QObject *parent)
    : Platform(parent)
51
    , m_x11Display(QX11Info::display())
52
{
53
54
#if HAVE_X11_XINPUT
    if (!qEnvironmentVariableIsSet("KWIN_NO_XI2")) {
55
        m_xinputIntegration = new XInputIntegration(m_x11Display, this);
56
57
58
59
60
61
62
63
64
        m_xinputIntegration->init();
        if (!m_xinputIntegration->hasXinput()) {
            delete m_xinputIntegration;
            m_xinputIntegration = nullptr;
        } else {
            connect(kwinApp(), &Application::workspaceCreated, m_xinputIntegration, &XInputIntegration::startListening);
        }
    }
#endif
65
66

    setSupportsGammaControl(true);
67
68
}

69
70
71
72
73
74
75
X11StandalonePlatform::~X11StandalonePlatform()
{
    if (m_openGLFreezeProtectionThread) {
        m_openGLFreezeProtectionThread->quit();
        m_openGLFreezeProtectionThread->wait();
        delete m_openGLFreezeProtectionThread;
    }
76
77
78
    if (isReady()) {
        XRenderUtils::cleanup();
    }
79
}
80
81
82
83
84
85
86

void X11StandalonePlatform::init()
{
    if (!QX11Info::isPlatformX11()) {
        emit initFailed();
        return;
    }
87
    XRenderUtils::init(kwinApp()->x11Connection(), kwinApp()->x11RootWindow());
88
89
90
91
    setReady(true);
    emit screensQueried();
}

92
93
Screens *X11StandalonePlatform::createScreens(QObject *parent)
{
94
    return new XRandRScreens(this, parent);
95
96
}

97
98
99
100
101
OpenGLBackend *X11StandalonePlatform::createOpenGLBackend()
{
    switch (options->glPlatformInterface()) {
#if HAVE_EPOXY_GLX
    case GlxPlatformInterface:
102
        if (hasGlx()) {
103
            return new GlxBackend(m_x11Display);
104
105
106
        } else {
            qCWarning(KWIN_X11STANDALONE) << "Glx not available, trying EGL instead.";
            // no break, needs fall-through
107
            Q_FALLTHROUGH();
108
        }
109
110
#endif
    case EglPlatformInterface:
111
        return new EglOnXBackend(m_x11Display);
112
113
114
115
116
117
    default:
        // no backend available
        return nullptr;
    }
}

118
119
Edge *X11StandalonePlatform::createScreenEdge(ScreenEdges *edges)
{
120
121
122
    if (m_screenEdgesFilter.isNull()) {
        m_screenEdgesFilter.reset(new ScreenEdgesFilter);
    }
123
124
125
    return new WindowBasedEdge(edges);
}

126
127
void X11StandalonePlatform::createPlatformCursor(QObject *parent)
{
128
129
130
131
132
133
    auto c = new X11Cursor(parent, m_xinputIntegration != nullptr);
#if HAVE_X11_XINPUT
    if (m_xinputIntegration) {
        m_xinputIntegration->setCursor(c);
        // we know we have xkb already
        auto xkb = input()->keyboard()->xkb();
134
        xkb->setConfig(kwinApp()->kxkbConfig());
135
136
137
        xkb->reconfigure();
    }
#endif
138
139
}

140
141
142
143
144
bool X11StandalonePlatform::requiresCompositing() const
{
    return false;
}

145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
bool X11StandalonePlatform::openGLCompositingIsBroken() const
{
    const QString unsafeKey(QLatin1String("OpenGLIsUnsafe") + (kwinApp()->isX11MultiHead() ? QString::number(kwinApp()->x11ScreenNumber()) : QString()));
    return KConfigGroup(kwinApp()->config(), "Compositing").readEntry(unsafeKey, false);
}

QString X11StandalonePlatform::compositingNotPossibleReason() const
{
    // first off, check whether we figured that we'll crash on detection because of a buggy driver
    KConfigGroup gl_workaround_group(kwinApp()->config(), "Compositing");
    const QString unsafeKey(QLatin1String("OpenGLIsUnsafe") + (kwinApp()->isX11MultiHead() ? QString::number(kwinApp()->x11ScreenNumber()) : QString()));
    if (gl_workaround_group.readEntry("Backend", "OpenGL") == QLatin1String("OpenGL") &&
        gl_workaround_group.readEntry(unsafeKey, false))
        return i18n("<b>OpenGL compositing (the default) has crashed KWin in the past.</b><br>"
                    "This was most likely due to a driver bug."
                    "<p>If you think that you have meanwhile upgraded to a stable driver,<br>"
                    "you can reset this protection but <b>be aware that this might result in an immediate crash!</b></p>"
                    "<p>Alternatively, you might want to use the XRender backend instead.</p>");

    if (!Xcb::Extensions::self()->isCompositeAvailable() || !Xcb::Extensions::self()->isDamageAvailable()) {
        return i18n("Required X extensions (XComposite and XDamage) are not available.");
    }
#if !defined( KWIN_HAVE_XRENDER_COMPOSITING )
    if (!hasGlx())
        return i18n("GLX/OpenGL are not available and only OpenGL support is compiled.");
#else
    if (!(hasGlx()
            || (Xcb::Extensions::self()->isRenderAvailable() && Xcb::Extensions::self()->isFixesAvailable()))) {
        return i18n("GLX/OpenGL and XRender/XFixes are not available.");
    }
#endif
    return QString();
}

bool X11StandalonePlatform::compositingPossible() const
{
    // first off, check whether we figured that we'll crash on detection because of a buggy driver
    KConfigGroup gl_workaround_group(kwinApp()->config(), "Compositing");
    const QString unsafeKey(QLatin1String("OpenGLIsUnsafe") + (kwinApp()->isX11MultiHead() ? QString::number(kwinApp()->x11ScreenNumber()) : QString()));
    if (gl_workaround_group.readEntry("Backend", "OpenGL") == QLatin1String("OpenGL") &&
        gl_workaround_group.readEntry(unsafeKey, false))
        return false;


    if (!Xcb::Extensions::self()->isCompositeAvailable()) {
190
        qCDebug(KWIN_X11STANDALONE) << "No composite extension available";
191
192
193
        return false;
    }
    if (!Xcb::Extensions::self()->isDamageAvailable()) {
194
        qCDebug(KWIN_X11STANDALONE) << "No damage extension available";
195
196
197
198
199
200
201
202
203
204
205
206
207
        return false;
    }
    if (hasGlx())
        return true;
#ifdef KWIN_HAVE_XRENDER_COMPOSITING
    if (Xcb::Extensions::self()->isRenderAvailable() && Xcb::Extensions::self()->isFixesAvailable())
        return true;
#endif
    if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES) {
        return true;
    } else if (qstrcmp(qgetenv("KWIN_COMPOSE"), "O2ES") == 0) {
        return true;
    }
208
    qCDebug(KWIN_X11STANDALONE) << "No OpenGL or XRender/XFixes support";
209
210
211
212
213
214
215
    return false;
}

bool X11StandalonePlatform::hasGlx()
{
    return Xcb::Extensions::self()->hasGlx();
}
216

217
218
219
220
221
222
223
void X11StandalonePlatform::createOpenGLSafePoint(OpenGLSafePoint safePoint)
{
    const QString unsafeKey(QLatin1String("OpenGLIsUnsafe") + (kwinApp()->isX11MultiHead() ? QString::number(kwinApp()->x11ScreenNumber()) : QString()));
    auto group = KConfigGroup(kwinApp()->config(), "Compositing");
    switch (safePoint) {
    case OpenGLSafePoint::PreInit:
        group.writeEntry(unsafeKey, true);
224
225
        group.sync();
        // Deliberately continue with PreFrame
226
        Q_FALLTHROUGH();
227
228
229
230
231
232
233
234
235
236
    case OpenGLSafePoint::PreFrame:
        if (m_openGLFreezeProtectionThread == nullptr) {
            Q_ASSERT(m_openGLFreezeProtection == nullptr);
            m_openGLFreezeProtectionThread = new QThread(this);
            m_openGLFreezeProtectionThread->setObjectName("FreezeDetector");
            m_openGLFreezeProtectionThread->start();
            m_openGLFreezeProtection = new QTimer;
            m_openGLFreezeProtection->setInterval(15000);
            m_openGLFreezeProtection->setSingleShot(true);
            m_openGLFreezeProtection->start();
237
            const QString configName = kwinApp()->config()->name();
238
239
            m_openGLFreezeProtection->moveToThread(m_openGLFreezeProtectionThread);
            connect(m_openGLFreezeProtection, &QTimer::timeout, m_openGLFreezeProtection,
240
                [configName] {
241
                    const QString unsafeKey(QLatin1String("OpenGLIsUnsafe") + (kwinApp()->isX11MultiHead() ? QString::number(kwinApp()->x11ScreenNumber()) : QString()));
242
                    auto group = KConfigGroup(KSharedConfig::openConfig(configName), "Compositing");
243
244
                    group.writeEntry(unsafeKey, true);
                    group.sync();
245
                    KCrash::setDrKonqiEnabled(false);
246
247
248
249
250
251
                    qFatal("Freeze in OpenGL initialization detected");
                }, Qt::DirectConnection);
        } else {
            Q_ASSERT(m_openGLFreezeProtection);
            QMetaObject::invokeMethod(m_openGLFreezeProtection, "start", Qt::QueuedConnection);
        }
252
253
254
        break;
    case OpenGLSafePoint::PostInit:
        group.writeEntry(unsafeKey, false);
255
256
        group.sync();
        // Deliberately continue with PostFrame
257
        Q_FALLTHROUGH();
258
259
260
261
262
263
264
265
266
267
    case OpenGLSafePoint::PostFrame:
        QMetaObject::invokeMethod(m_openGLFreezeProtection, "stop", Qt::QueuedConnection);
        break;
    case OpenGLSafePoint::PostLastGuardedFrame:
        m_openGLFreezeProtection->deleteLater();
        m_openGLFreezeProtection = nullptr;
        m_openGLFreezeProtectionThread->quit();
        m_openGLFreezeProtectionThread->wait();
        delete m_openGLFreezeProtectionThread;
        m_openGLFreezeProtectionThread = nullptr;
268
269
270
271
        break;
    }
}

272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
PlatformCursorImage X11StandalonePlatform::cursorImage() const
{
    auto c = kwinApp()->x11Connection();
    QScopedPointer<xcb_xfixes_get_cursor_image_reply_t, QScopedPointerPodDeleter> cursor(
        xcb_xfixes_get_cursor_image_reply(c,
                                          xcb_xfixes_get_cursor_image_unchecked(c),
                                          nullptr));
    if (cursor.isNull()) {
        return PlatformCursorImage();
    }

    QImage qcursorimg((uchar *) xcb_xfixes_get_cursor_image_cursor_image(cursor.data()), cursor->width, cursor->height,
                      QImage::Format_ARGB32_Premultiplied);
    // deep copy of image as the data is going to be freed
    return PlatformCursorImage(qcursorimg.copy(), QPoint(cursor->xhot, cursor->yhot));
}

289
290
291
292
293
294
295
296
297
298
void X11StandalonePlatform::doHideCursor()
{
    xcb_xfixes_hide_cursor(kwinApp()->x11Connection(), kwinApp()->x11RootWindow());
}

void X11StandalonePlatform::doShowCursor()
{
    xcb_xfixes_show_cursor(kwinApp()->x11Connection(), kwinApp()->x11RootWindow());
}

299
300
301
302
303
304
305
306
void X11StandalonePlatform::startInteractiveWindowSelection(std::function<void(KWin::Toplevel*)> callback, const QByteArray &cursorName)
{
    if (m_windowSelector.isNull()) {
        m_windowSelector.reset(new WindowSelector);
    }
    m_windowSelector->start(callback, cursorName);
}

307
308
309
310
311
312
313
314
void X11StandalonePlatform::startInteractivePositionSelection(std::function<void (const QPoint &)> callback)
{
    if (m_windowSelector.isNull()) {
        m_windowSelector.reset(new WindowSelector);
    }
    m_windowSelector->start(callback);
}

315
316
317
318
319
320
321
322
323
324
325
326
void X11StandalonePlatform::setupActionForGlobalAccel(QAction *action)
{
    connect(action, &QAction::triggered, kwinApp(), [action] {
        QVariant timestamp = action->property("org.kde.kglobalaccel.activationTimestamp");
        bool ok = false;
        const quint32 t = timestamp.toULongLong(&ok);
        if (ok) {
            kwinApp()->setX11Time(t);
        }
    });
}

327
328
329
330
331
OverlayWindow *X11StandalonePlatform::createOverlayWindow()
{
    return new OverlayWindowX11();
}

332
333
334
335
336
337
338
339
340
OutlineVisual *X11StandalonePlatform::createOutline(Outline *outline)
{
    // first try composited Outline
    auto ret = Platform::createOutline(outline);
    if (!ret) {
        ret = new NonCompositedOutlineVisual(outline);
    }
    return ret;
}
341

342
343
344
345
346
347
348
349
350
Decoration::Renderer *X11StandalonePlatform::createDecorationRenderer(Decoration::DecoratedClientImpl *client)
{
    auto renderer = Platform::createDecorationRenderer(client);
    if (!renderer) {
        renderer = new Decoration::X11Renderer(client);
    }
    return renderer;
}

351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
void X11StandalonePlatform::invertScreen()
{
    using namespace Xcb::RandR;
    bool succeeded = false;

    if (Xcb::Extensions::self()->isRandrAvailable()) {
        const auto active_client = workspace()->activeClient();
        ScreenResources res((active_client && active_client->window() != XCB_WINDOW_NONE) ? active_client->window() : rootWindow());

        if (!res.isNull()) {
            for (int j = 0; j < res->num_crtcs; ++j) {
                auto crtc = res.crtcs()[j];
                CrtcGamma gamma(crtc);
                if (gamma.isNull()) {
                    continue;
                }
                if (gamma->size) {
368
                    qCDebug(KWIN_X11STANDALONE) << "inverting screen using xcb_randr_set_crtc_gamma";
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
                    const int half = gamma->size / 2 + 1;

                    uint16_t *red = gamma.red();
                    uint16_t *green = gamma.green();
                    uint16_t *blue = gamma.blue();
                    for (int i = 0; i < half; ++i) {
                        auto invert = [&gamma, i](uint16_t *ramp) {
                            qSwap(ramp[i], ramp[gamma->size - 1 - i]);
                        };
                        invert(red);
                        invert(green);
                        invert(blue);
                    }
                    xcb_randr_set_crtc_gamma(connection(), crtc, gamma->size, red, green, blue);
                    succeeded = true;
                }
            }
        }
    }
    if (!succeeded) {
        Platform::invertScreen();
    }
}

393
394
395
396
397
void X11StandalonePlatform::createEffectsHandler(Compositor *compositor, Scene *scene)
{
    new EffectsHandlerImplX11(compositor, scene);
}

398
399
400
401
402
403
404
405
406
407
408
409
410
QVector<CompositingType> X11StandalonePlatform::supportedCompositors() const
{
    QVector<CompositingType> compositors;
#if HAVE_EPOXY_GLX
    compositors << OpenGLCompositing;
#endif
#ifdef KWIN_HAVE_XRENDER_COMPOSITING
    compositors << XRenderCompositing;
#endif
    compositors << NoCompositing;
    return compositors;
}

411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
void X11StandalonePlatform::initOutputs()
{
    doUpdateOutputs<Xcb::RandR::ScreenResources>();
}

void X11StandalonePlatform::updateOutputs()
{
    doUpdateOutputs<Xcb::RandR::CurrentResources>();
}

template <typename T>
void X11StandalonePlatform::doUpdateOutputs()
{
    auto fallback = [this]() {
        auto *o = new X11Output(this);
426
        o->setGammaRampSize(0);
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
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
477
478
479
480
481
482
483
484
        o->setRefreshRate(-1.0f);
        o->setName(QStringLiteral("Xinerama"));
        m_outputs << o;
    };

    // TODO: instead of resetting all outputs, check if new output is added/removed
    //       or still available and leave still available outputs in m_outputs
    //       untouched (like in DRM backend)
    qDeleteAll(m_outputs);
    m_outputs.clear();

    if (!Xcb::Extensions::self()->isRandrAvailable()) {
        fallback();
        return;
    }
    T resources(rootWindow());
    if (resources.isNull()) {
        fallback();
        return;
    }
    xcb_randr_crtc_t *crtcs = resources.crtcs();
    xcb_randr_mode_info_t *modes = resources.modes();

    QVector<Xcb::RandR::CrtcInfo> infos(resources->num_crtcs);
    for (int i = 0; i < resources->num_crtcs; ++i) {
        infos[i] = Xcb::RandR::CrtcInfo(crtcs[i], resources->config_timestamp);
    }

    for (int i = 0; i < resources->num_crtcs; ++i) {
        Xcb::RandR::CrtcInfo info(infos.at(i));

        xcb_randr_output_t *outputs = info.outputs();
        QVector<Xcb::RandR::OutputInfo> outputInfos(outputs ? resources->num_outputs : 0);
        if (outputs) {
            for (int i = 0; i < resources->num_outputs; ++i) {
                outputInfos[i] = Xcb::RandR::OutputInfo(outputs[i], resources->config_timestamp);
            }
        }

        float refreshRate = -1.0f;
        for (int j = 0; j < resources->num_modes; ++j) {
            if (info->mode == modes[j].id) {
                if (modes[j].htotal != 0 && modes[j].vtotal != 0) { // BUG 313996
                    // refresh rate calculation - WTF was wikipedia 1998 when I needed it?
                    int dotclock = modes[j].dot_clock,
                          vtotal = modes[j].vtotal;
                    if (modes[j].mode_flags & XCB_RANDR_MODE_FLAG_INTERLACE)
                        dotclock *= 2;
                    if (modes[j].mode_flags & XCB_RANDR_MODE_FLAG_DOUBLE_SCAN)
                        vtotal *= 2;
                    refreshRate = dotclock/float(modes[j].htotal*vtotal);
                }
                break; // found mode
            }
        }

        const QRect geo = info.rect();
        if (geo.isValid()) {
485
486
487
488
489
490
491
            xcb_randr_crtc_t crtc = crtcs[i];

            // TODO: Perhaps the output has to save the inherited gamma ramp and
            // restore it during tear down. Currently neither standalone x11 nor
            // drm platform do this.
            Xcb::RandR::CrtcGamma gamma(crtc);

492
            auto *o = new X11Output(this);
493
494
            o->setCrtc(crtc);
            o->setGammaRampSize(gamma.isNull() ? 0 : gamma->size);
495
            o->setGeometry(geo);
496
            o->setRefreshRate(refreshRate * 1000);
497
498
499

            for (int j = 0; j < info->num_outputs; ++j) {
                Xcb::RandR::OutputInfo outputInfo(outputInfos.at(j));
500
501
502
503
504
505
506
507
508
509
510
511
512
513
                if (outputInfo->crtc != crtc) {
                    continue;
                }
                QSize physicalSize(outputInfo->mm_width, outputInfo->mm_height);
                switch (info->rotation) {
                case XCB_RANDR_ROTATION_ROTATE_0:
                case XCB_RANDR_ROTATION_ROTATE_180:
                    break;
                case XCB_RANDR_ROTATION_ROTATE_90:
                case XCB_RANDR_ROTATION_ROTATE_270:
                    physicalSize.transpose();
                    break;
                case XCB_RANDR_ROTATION_REFLECT_X:
                case XCB_RANDR_ROTATION_REFLECT_Y:
514
515
                    break;
                }
516
517
518
                o->setName(outputInfo.name());
                o->setPhysicalSize(physicalSize);
                break;
519
            }
520

521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
            m_outputs << o;
        }
    }

    if (m_outputs.isEmpty()) {
        fallback();
    }
}

Outputs X11StandalonePlatform::outputs() const
{
    return m_outputs;
}

Outputs X11StandalonePlatform::enabledOutputs() const
{
    return m_outputs;
}

540
}