File indexing completed on 2024-11-10 04:56:34

0001 /*
0002     KWin - the KDE window manager
0003     This file is part of the KDE project.
0004 
0005     SPDX-FileCopyrightText: 2016 Martin Gräßlin <mgraesslin@kde.org>
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 #include "x11_standalone_backend.h"
0010 
0011 #include "config-kwin.h"
0012 
0013 #include "atoms.h"
0014 #include "core/session.h"
0015 #include "x11_standalone_cursor.h"
0016 #include "x11_standalone_edge.h"
0017 #include "x11_standalone_placeholderoutput.h"
0018 #include "x11_standalone_windowselector.h"
0019 #if HAVE_GLX
0020 #include "x11_standalone_glx_backend.h"
0021 #endif
0022 #if HAVE_X11_XINPUT
0023 #include "x11_standalone_xinputintegration.h"
0024 #endif
0025 #include "core/renderloop.h"
0026 #include "keyboard_input.h"
0027 #include "options.h"
0028 #include "utils/c_ptr.h"
0029 #include "utils/edid.h"
0030 #include "utils/xcbutils.h"
0031 #include "window.h"
0032 #include "workspace.h"
0033 #include "x11_standalone_effects.h"
0034 #include "x11_standalone_egl_backend.h"
0035 #include "x11_standalone_keyboard.h"
0036 #include "x11_standalone_logging.h"
0037 #include "x11_standalone_non_composited_outline.h"
0038 #include "x11_standalone_output.h"
0039 #include "x11_standalone_screenedges_filter.h"
0040 #include "xkb.h"
0041 
0042 #include "../common/kwinxrenderutils.h"
0043 
0044 #include <KConfigGroup>
0045 #include <KLocalizedString>
0046 
0047 #include <QOpenGLContext>
0048 #include <QThread>
0049 #include <private/qtx11extras_p.h>
0050 
0051 #include <span>
0052 
0053 namespace KWin
0054 {
0055 
0056 class XrandrEventFilter : public X11EventFilter
0057 {
0058 public:
0059     explicit XrandrEventFilter(X11StandaloneBackend *backend);
0060 
0061     bool event(xcb_generic_event_t *event) override;
0062 
0063 private:
0064     X11StandaloneBackend *m_backend;
0065 };
0066 
0067 XrandrEventFilter::XrandrEventFilter(X11StandaloneBackend *backend)
0068     : X11EventFilter(Xcb::Extensions::self()->randrNotifyEvent())
0069     , m_backend(backend)
0070 {
0071 }
0072 
0073 bool XrandrEventFilter::event(xcb_generic_event_t *event)
0074 {
0075     Q_ASSERT((event->response_type & ~0x80) == Xcb::Extensions::self()->randrNotifyEvent());
0076     // let's try to gather a few XRandR events, unlikely that there is just one
0077     m_backend->scheduleUpdateOutputs();
0078 
0079     // update default screen
0080     auto *xrrEvent = reinterpret_cast<xcb_randr_screen_change_notify_event_t *>(event);
0081     xcb_screen_t *screen = Xcb::defaultScreen();
0082     if (xrrEvent->rotation & (XCB_RANDR_ROTATION_ROTATE_90 | XCB_RANDR_ROTATION_ROTATE_270)) {
0083         screen->width_in_pixels = xrrEvent->height;
0084         screen->height_in_pixels = xrrEvent->width;
0085         screen->width_in_millimeters = xrrEvent->mheight;
0086         screen->height_in_millimeters = xrrEvent->mwidth;
0087     } else {
0088         screen->width_in_pixels = xrrEvent->width;
0089         screen->height_in_pixels = xrrEvent->height;
0090         screen->width_in_millimeters = xrrEvent->mwidth;
0091         screen->height_in_millimeters = xrrEvent->mheight;
0092     }
0093 
0094     return false;
0095 }
0096 
0097 X11StandaloneBackend::X11StandaloneBackend(QObject *parent)
0098     : OutputBackend(parent)
0099     , m_updateOutputsTimer(std::make_unique<QTimer>())
0100     , m_x11Display(QX11Info::display())
0101     , m_renderLoop(std::make_unique<RenderLoop>(nullptr))
0102 {
0103 #if HAVE_X11_XINPUT
0104     if (!qEnvironmentVariableIsSet("KWIN_NO_XI2")) {
0105         m_xinputIntegration = std::make_unique<XInputIntegration>(m_x11Display, this);
0106         m_xinputIntegration->init();
0107         if (!m_xinputIntegration->hasXinput()) {
0108             m_xinputIntegration.reset();
0109         } else {
0110             connect(kwinApp(), &Application::workspaceCreated, m_xinputIntegration.get(), &XInputIntegration::startListening);
0111         }
0112     }
0113 #endif
0114 
0115     m_updateOutputsTimer->setSingleShot(true);
0116     connect(m_updateOutputsTimer.get(), &QTimer::timeout, this, &X11StandaloneBackend::updateOutputs);
0117 
0118     m_keyboard = std::make_unique<X11Keyboard>();
0119 }
0120 
0121 X11StandaloneBackend::~X11StandaloneBackend()
0122 {
0123     m_eglDisplay.reset();
0124     XRenderUtils::cleanup();
0125 }
0126 
0127 ::Display *X11StandaloneBackend::display() const
0128 {
0129     return m_x11Display;
0130 }
0131 
0132 xcb_connection_t *X11StandaloneBackend::connection() const
0133 {
0134     return kwinApp()->x11Connection();
0135 }
0136 
0137 xcb_window_t X11StandaloneBackend::rootWindow() const
0138 {
0139     return kwinApp()->x11RootWindow();
0140 }
0141 
0142 bool X11StandaloneBackend::initialize()
0143 {
0144     if (!QX11Info::isPlatformX11()) {
0145         return false;
0146     }
0147     XRenderUtils::init(kwinApp()->x11Connection(), kwinApp()->x11RootWindow());
0148     initOutputs();
0149 
0150     if (Xcb::Extensions::self()->isRandrAvailable()) {
0151         m_randrEventFilter = std::make_unique<XrandrEventFilter>(this);
0152     }
0153     connect(Cursors::self(), &Cursors::hiddenChanged, this, &X11StandaloneBackend::updateCursor);
0154     return true;
0155 }
0156 
0157 std::unique_ptr<OpenGLBackend> X11StandaloneBackend::createOpenGLBackend()
0158 {
0159     switch (options->glPlatformInterface()) {
0160 #if HAVE_GLX
0161     case GlxPlatformInterface:
0162         if (hasGlx()) {
0163             return std::make_unique<GlxBackend>(m_x11Display, this);
0164         } else {
0165             qCWarning(KWIN_X11STANDALONE) << "Glx not available, trying EGL instead.";
0166             // no break, needs fall-through
0167             Q_FALLTHROUGH();
0168         }
0169 #endif
0170     case EglPlatformInterface:
0171         return std::make_unique<EglBackend>(m_x11Display, this);
0172     default:
0173         // no backend available
0174         return nullptr;
0175     }
0176 }
0177 
0178 std::unique_ptr<Edge> X11StandaloneBackend::createScreenEdge(ScreenEdges *edges)
0179 {
0180     if (!m_screenEdgesFilter) {
0181         m_screenEdgesFilter = std::make_unique<ScreenEdgesFilter>();
0182     }
0183     return std::make_unique<WindowBasedEdge>(edges);
0184 }
0185 
0186 std::unique_ptr<Cursor> X11StandaloneBackend::createPlatformCursor()
0187 {
0188 #if HAVE_X11_XINPUT
0189     auto c = std::make_unique<X11Cursor>(m_xinputIntegration != nullptr);
0190     if (m_xinputIntegration) {
0191         m_xinputIntegration->setCursor(c.get());
0192         // we know we have xkb already
0193         auto xkb = input()->keyboard()->xkb();
0194         xkb->setConfig(kwinApp()->kxkbConfig());
0195         xkb->reconfigure();
0196     }
0197     return c;
0198 #else
0199     return std::make_unique<X11Cursor>(false);
0200 #endif
0201 }
0202 
0203 bool X11StandaloneBackend::hasGlx()
0204 {
0205     return Xcb::Extensions::self()->hasGlx();
0206 }
0207 
0208 PlatformCursorImage X11StandaloneBackend::cursorImage() const
0209 {
0210     auto c = kwinApp()->x11Connection();
0211     UniqueCPtr<xcb_xfixes_get_cursor_image_reply_t> cursor(
0212         xcb_xfixes_get_cursor_image_reply(c,
0213                                           xcb_xfixes_get_cursor_image_unchecked(c),
0214                                           nullptr));
0215     if (!cursor) {
0216         return PlatformCursorImage();
0217     }
0218 
0219     QImage qcursorimg((uchar *)xcb_xfixes_get_cursor_image_cursor_image(cursor.get()), cursor->width, cursor->height,
0220                       QImage::Format_ARGB32_Premultiplied);
0221     // deep copy of image as the data is going to be freed
0222     return PlatformCursorImage(qcursorimg.copy(), QPoint(cursor->xhot, cursor->yhot));
0223 }
0224 
0225 void X11StandaloneBackend::updateCursor()
0226 {
0227     if (Cursors::self()->isCursorHidden()) {
0228         xcb_xfixes_hide_cursor(kwinApp()->x11Connection(), kwinApp()->x11RootWindow());
0229     } else {
0230         xcb_xfixes_show_cursor(kwinApp()->x11Connection(), kwinApp()->x11RootWindow());
0231     }
0232 }
0233 
0234 void X11StandaloneBackend::startInteractiveWindowSelection(std::function<void(KWin::Window *)> callback, const QByteArray &cursorName)
0235 {
0236     if (!m_windowSelector) {
0237         m_windowSelector = std::make_unique<WindowSelector>();
0238     }
0239     m_windowSelector->start(callback, cursorName);
0240 }
0241 
0242 void X11StandaloneBackend::startInteractivePositionSelection(std::function<void(const QPointF &)> callback)
0243 {
0244     if (!m_windowSelector) {
0245         m_windowSelector = std::make_unique<WindowSelector>();
0246     }
0247     m_windowSelector->start(callback);
0248 }
0249 
0250 std::unique_ptr<OutlineVisual> X11StandaloneBackend::createOutline(Outline *outline)
0251 {
0252     return std::make_unique<NonCompositedOutlineVisual>(outline);
0253 }
0254 
0255 void X11StandaloneBackend::createEffectsHandler(Compositor *compositor, WorkspaceScene *scene)
0256 {
0257     new EffectsHandlerX11(compositor, scene);
0258 }
0259 
0260 QList<CompositingType> X11StandaloneBackend::supportedCompositors() const
0261 {
0262     QList<CompositingType> compositors;
0263 #if HAVE_GLX
0264     compositors << OpenGLCompositing;
0265 #endif
0266     compositors << NoCompositing;
0267     return compositors;
0268 }
0269 
0270 void X11StandaloneBackend::initOutputs()
0271 {
0272     doUpdateOutputs<Xcb::RandR::ScreenResources>();
0273     updateRefreshRate();
0274 }
0275 
0276 void X11StandaloneBackend::scheduleUpdateOutputs()
0277 {
0278     m_updateOutputsTimer->start();
0279 }
0280 
0281 void X11StandaloneBackend::updateOutputs()
0282 {
0283     doUpdateOutputs<Xcb::RandR::CurrentResources>();
0284     updateRefreshRate();
0285 }
0286 
0287 template<typename T>
0288 void X11StandaloneBackend::doUpdateOutputs()
0289 {
0290     QList<Output *> changed;
0291     QList<Output *> added;
0292     QList<Output *> removed = m_outputs;
0293 
0294     if (Xcb::Extensions::self()->isRandrAvailable()) {
0295         T resources(rootWindow());
0296         if (!resources.isNull()) {
0297 
0298             std::span crtcs(resources.crtcs(), resources->num_crtcs);
0299             for (auto crtc : crtcs) {
0300                 Xcb::RandR::CrtcInfo info(crtc, resources->config_timestamp);
0301 
0302                 const QRect geometry = info.rect();
0303                 if (!geometry.isValid()) {
0304                     continue;
0305                 }
0306 
0307                 float refreshRate = -1.0f;
0308 
0309                 for (auto mode : std::span(resources.modes(), resources->num_modes)) {
0310                     if (info->mode == mode.id) {
0311                         if (mode.htotal != 0 && mode.vtotal != 0) { // BUG 313996
0312                             // refresh rate calculation - WTF was wikipedia 1998 when I needed it?
0313                             int dotclock = mode.dot_clock,
0314                                 vtotal = mode.vtotal;
0315                             if (mode.mode_flags & XCB_RANDR_MODE_FLAG_INTERLACE) {
0316                                 dotclock *= 2;
0317                             }
0318                             if (mode.mode_flags & XCB_RANDR_MODE_FLAG_DOUBLE_SCAN) {
0319                                 vtotal *= 2;
0320                             }
0321                             refreshRate = dotclock / float(mode.htotal * vtotal);
0322                         }
0323                         break; // found mode
0324                     }
0325                 }
0326 
0327                 for (auto xcbOutput : std::span(info.outputs(), info->num_outputs)) {
0328                     Xcb::RandR::OutputInfo outputInfo(xcbOutput, resources->config_timestamp);
0329                     if (outputInfo->crtc != crtc) {
0330                         continue;
0331                     }
0332 
0333                     X11Output *output = findX11Output(outputInfo.name());
0334                     if (output) {
0335                         changed.append(output);
0336                         removed.removeOne(output);
0337                     } else {
0338                         output = new X11Output(this);
0339                         added.append(output);
0340                     }
0341 
0342                     // TODO: Perhaps the output has to save the inherited gamma ramp and
0343                     // restore it during tear down. Currently neither standalone x11 nor
0344                     // drm platform do this.
0345                     Xcb::RandR::CrtcGamma gamma(crtc);
0346 
0347                     output->setRenderLoop(m_renderLoop.get());
0348                     output->setCrtc(crtc);
0349                     output->setGammaRampSize(gamma.isNull() ? 0 : gamma->size);
0350                     auto it = std::find(crtcs.begin(), crtcs.end(), crtc);
0351                     int crtcIndex = std::distance(crtcs.begin(), it);
0352                     output->setXineramaNumber(crtcIndex);
0353 
0354                     QSize physicalSize(outputInfo->mm_width, outputInfo->mm_height);
0355                     switch (info->rotation) {
0356                     case XCB_RANDR_ROTATION_ROTATE_0:
0357                     case XCB_RANDR_ROTATION_ROTATE_180:
0358                         break;
0359                     case XCB_RANDR_ROTATION_ROTATE_90:
0360                     case XCB_RANDR_ROTATION_ROTATE_270:
0361                         physicalSize.transpose();
0362                         break;
0363                     case XCB_RANDR_ROTATION_REFLECT_X:
0364                     case XCB_RANDR_ROTATION_REFLECT_Y:
0365                         break;
0366                     }
0367 
0368                     X11Output::Information information{
0369                         .name = outputInfo.name(),
0370                         .physicalSize = physicalSize,
0371                     };
0372 
0373                     auto edidProperty = Xcb::RandR::OutputProperty(xcbOutput, atoms->edid, XCB_ATOM_INTEGER, 0, 100, false, false);
0374                     bool ok;
0375                     if (auto data = edidProperty.toByteArray(&ok); ok && !data.isEmpty()) {
0376                         if (auto edid = Edid(data, edidProperty.data()->num_items); edid.isValid()) {
0377                             information.manufacturer = edid.manufacturerString();
0378                             information.model = edid.monitorName();
0379                             information.serialNumber = edid.serialNumber();
0380                             information.edid = edid;
0381                         }
0382                     }
0383 
0384                     auto mode = std::make_shared<OutputMode>(geometry.size(), refreshRate * 1000);
0385 
0386                     X11Output::State state = output->m_state;
0387                     state.modes = {mode};
0388                     state.currentMode = mode;
0389                     state.position = geometry.topLeft();
0390 
0391                     output->setInformation(information);
0392                     output->setState(state);
0393                     break;
0394                 }
0395             }
0396         }
0397     }
0398 
0399     // The workspace handles having no outputs poorly. If the last output is about to be
0400     // removed, create a dummy output to avoid crashing.
0401     if (changed.isEmpty() && added.isEmpty()) {
0402         auto dummyOutput = new X11PlaceholderOutput(this);
0403         m_outputs << dummyOutput;
0404         Q_EMIT outputAdded(dummyOutput);
0405         dummyOutput->updateEnabled(true);
0406     }
0407 
0408     // Process new outputs. Note new outputs must be introduced before removing any other outputs.
0409     for (Output *output : std::as_const(added)) {
0410         m_outputs.append(output);
0411         Q_EMIT outputAdded(output);
0412         if (auto placeholderOutput = qobject_cast<X11PlaceholderOutput *>(output)) {
0413             placeholderOutput->updateEnabled(true);
0414         } else if (auto nativeOutput = qobject_cast<X11Output *>(output)) {
0415             nativeOutput->updateEnabled(true);
0416         }
0417     }
0418 
0419     // Outputs have to be removed last to avoid the case where there are no enabled outputs.
0420     for (Output *output : std::as_const(removed)) {
0421         m_outputs.removeOne(output);
0422         if (auto placeholderOutput = qobject_cast<X11PlaceholderOutput *>(output)) {
0423             placeholderOutput->updateEnabled(false);
0424         } else if (auto nativeOutput = qobject_cast<X11Output *>(output)) {
0425             nativeOutput->updateEnabled(false);
0426         }
0427         Q_EMIT outputRemoved(output);
0428         output->unref();
0429     }
0430 
0431     // Make sure that the position of an output in m_outputs matches its xinerama index, there
0432     // are X11 protocols that use xinerama indices to identify outputs.
0433     std::sort(m_outputs.begin(), m_outputs.end(), [](const Output *a, const Output *b) {
0434         const auto xa = qobject_cast<const X11Output *>(a);
0435         if (!xa) {
0436             return false;
0437         }
0438         const auto xb = qobject_cast<const X11Output *>(b);
0439         if (!xb) {
0440             return true;
0441         }
0442         return xa->xineramaNumber() < xb->xineramaNumber();
0443     });
0444 
0445     Q_EMIT outputsQueried();
0446 }
0447 
0448 X11Output *X11StandaloneBackend::findX11Output(const QString &name) const
0449 {
0450     for (Output *output : m_outputs) {
0451         if (output->name() == name) {
0452             return qobject_cast<X11Output *>(output);
0453         }
0454     }
0455     return nullptr;
0456 }
0457 
0458 Outputs X11StandaloneBackend::outputs() const
0459 {
0460     return m_outputs;
0461 }
0462 
0463 X11Keyboard *X11StandaloneBackend::keyboard() const
0464 {
0465     return m_keyboard.get();
0466 }
0467 
0468 RenderLoop *X11StandaloneBackend::renderLoop() const
0469 {
0470     return m_renderLoop.get();
0471 }
0472 
0473 static bool refreshRate_compare(const Output *first, const Output *smallest)
0474 {
0475     return first->refreshRate() < smallest->refreshRate();
0476 }
0477 
0478 static int currentRefreshRate()
0479 {
0480     static const int refreshRate = qEnvironmentVariableIntValue("KWIN_X11_REFRESH_RATE");
0481     if (refreshRate) {
0482         return refreshRate;
0483     }
0484 
0485     const QList<Output *> outputs = kwinApp()->outputBackend()->outputs();
0486     if (outputs.isEmpty()) {
0487         return 60000;
0488     }
0489 
0490     static const QString syncDisplayDevice = qEnvironmentVariable("__GL_SYNC_DISPLAY_DEVICE");
0491     if (!syncDisplayDevice.isEmpty()) {
0492         for (const Output *output : outputs) {
0493             if (output->name() == syncDisplayDevice) {
0494                 return output->refreshRate();
0495             }
0496         }
0497     }
0498 
0499     auto syncIt = std::min_element(outputs.begin(), outputs.end(), refreshRate_compare);
0500     return (*syncIt)->refreshRate();
0501 }
0502 
0503 void X11StandaloneBackend::updateRefreshRate()
0504 {
0505     int refreshRate = currentRefreshRate();
0506     if (refreshRate <= 0) {
0507         qCWarning(KWIN_X11STANDALONE) << "Bogus refresh rate" << refreshRate;
0508         refreshRate = 60000;
0509     }
0510 
0511     m_renderLoop->setRefreshRate(refreshRate);
0512 }
0513 
0514 void X11StandaloneBackend::setEglDisplay(std::unique_ptr<EglDisplay> &&display)
0515 {
0516     m_eglDisplay = std::move(display);
0517 }
0518 
0519 EglDisplay *X11StandaloneBackend::sceneEglDisplayObject() const
0520 {
0521     return m_eglDisplay.get();
0522 }
0523 }
0524 
0525 #include "moc_x11_standalone_backend.cpp"