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"