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

0001 /*
0002     KWin - the KDE window manager
0003     This file is part of the KDE project.
0004 
0005     SPDX-FileCopyrightText: 2015 Martin Gräßlin <mgraesslin@kde.org>
0006     SPDX-FileCopyrightText: 2023 Harald Sitter <sitter@kde.org>
0007 
0008     SPDX-License-Identifier: GPL-2.0-or-later
0009 */
0010 #include "x11_windowed_backend.h"
0011 #include "../common/kwinxrenderutils.h"
0012 
0013 #include <config-kwin.h>
0014 
0015 #include "utils/xcbutils.h"
0016 #include "x11_windowed_egl_backend.h"
0017 #include "x11_windowed_logging.h"
0018 #include "x11_windowed_output.h"
0019 #include "x11_windowed_qpainter_backend.h"
0020 #include <pointer_input.h>
0021 // KDE
0022 #include <KLocalizedString>
0023 #include <QAbstractEventDispatcher>
0024 #include <QCoreApplication>
0025 #include <QSocketNotifier>
0026 // xcb
0027 #include <xcb/dri3.h>
0028 #include <xcb/xcb_keysyms.h>
0029 #include <xcb/present.h>
0030 #include <xcb/shm.h>
0031 // X11
0032 #include <X11/Xlib-xcb.h>
0033 #include <fixx11h.h>
0034 #if HAVE_X11_XINPUT
0035 #include <X11/extensions/XI2proto.h>
0036 #include <X11/extensions/XInput2.h>
0037 #endif
0038 // system
0039 #include <X11/Xlib-xcb.h>
0040 #include <X11/keysym.h>
0041 #include <drm_fourcc.h>
0042 #include <fcntl.h>
0043 #include <gbm.h>
0044 #include <linux/input.h>
0045 
0046 namespace KWin
0047 {
0048 
0049 void X11WindowedInputDevice::setPointer(bool set)
0050 {
0051     m_pointer = set;
0052 }
0053 
0054 void X11WindowedInputDevice::setKeyboard(bool set)
0055 {
0056     m_keyboard = set;
0057 }
0058 
0059 void X11WindowedInputDevice::setTouch(bool set)
0060 {
0061     m_touch = set;
0062 }
0063 
0064 void X11WindowedInputDevice::setName(const QString &name)
0065 {
0066     m_name = name;
0067 }
0068 
0069 QString X11WindowedInputDevice::sysName() const
0070 {
0071     return QString();
0072 }
0073 
0074 QString X11WindowedInputDevice::name() const
0075 {
0076     return m_name;
0077 }
0078 
0079 bool X11WindowedInputDevice::isEnabled() const
0080 {
0081     return true;
0082 }
0083 
0084 void X11WindowedInputDevice::setEnabled(bool enabled)
0085 {
0086 }
0087 
0088 LEDs X11WindowedInputDevice::leds() const
0089 {
0090     return LEDs();
0091 }
0092 
0093 void X11WindowedInputDevice::setLeds(LEDs leds)
0094 {
0095 }
0096 
0097 bool X11WindowedInputDevice::isKeyboard() const
0098 {
0099     return m_keyboard;
0100 }
0101 
0102 bool X11WindowedInputDevice::isAlphaNumericKeyboard() const
0103 {
0104     return m_keyboard;
0105 }
0106 
0107 bool X11WindowedInputDevice::isPointer() const
0108 {
0109     return m_pointer;
0110 }
0111 
0112 bool X11WindowedInputDevice::isTouchpad() const
0113 {
0114     return false;
0115 }
0116 
0117 bool X11WindowedInputDevice::isTouch() const
0118 {
0119     return m_touch;
0120 }
0121 
0122 bool X11WindowedInputDevice::isTabletTool() const
0123 {
0124     return false;
0125 }
0126 
0127 bool X11WindowedInputDevice::isTabletPad() const
0128 {
0129     return false;
0130 }
0131 
0132 bool X11WindowedInputDevice::isTabletModeSwitch() const
0133 {
0134     return false;
0135 }
0136 
0137 bool X11WindowedInputDevice::isLidSwitch() const
0138 {
0139     return false;
0140 }
0141 
0142 X11WindowedInputBackend::X11WindowedInputBackend(X11WindowedBackend *backend)
0143     : m_backend(backend)
0144 {
0145 }
0146 
0147 void X11WindowedInputBackend::initialize()
0148 {
0149     if (m_backend->pointerDevice()) {
0150         Q_EMIT deviceAdded(m_backend->pointerDevice());
0151     }
0152     if (m_backend->keyboardDevice()) {
0153         Q_EMIT deviceAdded(m_backend->keyboardDevice());
0154     }
0155     if (m_backend->touchDevice()) {
0156         Q_EMIT deviceAdded(m_backend->touchDevice());
0157     }
0158 }
0159 
0160 X11WindowedBackend::X11WindowedBackend(const X11WindowedBackendOptions &options)
0161     : m_options(options)
0162 {
0163 }
0164 
0165 X11WindowedBackend::~X11WindowedBackend()
0166 {
0167     destroyOutputs();
0168     m_pointerDevice.reset();
0169     m_keyboardDevice.reset();
0170     m_touchDevice.reset();
0171     m_eglDisplay.reset();
0172 
0173     if (m_gbmDevice) {
0174         gbm_device_destroy(m_gbmDevice);
0175     }
0176 
0177     if (m_connection) {
0178         if (m_keySymbols) {
0179             xcb_key_symbols_free(m_keySymbols);
0180         }
0181         xcb_disconnect(m_connection);
0182         m_connection = nullptr;
0183     }
0184 }
0185 
0186 bool X11WindowedBackend::initialize()
0187 {
0188     m_display = XOpenDisplay(m_options.display.toLatin1().constData());
0189     if (!m_display) {
0190         return false;
0191     }
0192 
0193     m_connection = XGetXCBConnection(m_display);
0194     m_screenNumber = XDefaultScreen(m_display);
0195     XSetEventQueueOwner(m_display, XCBOwnsEventQueue);
0196 
0197     int screen = m_screenNumber;
0198     for (xcb_screen_iterator_t it = xcb_setup_roots_iterator(xcb_get_setup(m_connection));
0199             it.rem;
0200             --screen, xcb_screen_next(&it)) {
0201         if (screen == m_screenNumber) {
0202             m_screen = it.data;
0203         }
0204     }
0205 
0206     const xcb_query_extension_reply_t *presentExtension = xcb_get_extension_data(m_connection, &xcb_present_id);
0207     if (presentExtension && presentExtension->present) {
0208         m_presentOpcode = presentExtension->major_opcode;
0209         xcb_present_query_version_cookie_t cookie = xcb_present_query_version(m_connection, 1, 2);
0210         UniqueCPtr<xcb_present_query_version_reply_t> reply(xcb_present_query_version_reply(m_connection, cookie, nullptr));
0211         if (!reply) {
0212             qCWarning(KWIN_X11WINDOWED) << "Requested Present extension version is unsupported";
0213             return false;
0214         }
0215         m_presentMajorVersion = reply->major_version;
0216         m_presentMinorVersion = reply->minor_version;
0217     } else {
0218         qCWarning(KWIN_X11WINDOWED) << "Present X11 extension is unavailable";
0219         return false;
0220     }
0221 
0222     const xcb_query_extension_reply_t *shmExtension = xcb_get_extension_data(m_connection, &xcb_shm_id);
0223     if (shmExtension && shmExtension->present) {
0224         xcb_shm_query_version_cookie_t cookie = xcb_shm_query_version(m_connection);
0225         UniqueCPtr<xcb_shm_query_version_reply_t> reply(xcb_shm_query_version_reply(m_connection, cookie, nullptr));
0226         if (!reply) {
0227             qCWarning(KWIN_X11WINDOWED) << "Requested SHM extension version is unsupported";
0228         } else {
0229             if (!reply->shared_pixmaps) {
0230                 qCWarning(KWIN_X11WINDOWED) << "X server supports SHM extension but not shared pixmaps";
0231             } else {
0232                 m_hasShm = true;
0233             }
0234         }
0235     }
0236 
0237     const xcb_query_extension_reply_t *driExtension = xcb_get_extension_data(m_connection, &xcb_dri3_id);
0238     if (driExtension && driExtension->present) {
0239         xcb_dri3_query_version_cookie_t cookie = xcb_dri3_query_version(m_connection, 1, 2);
0240         UniqueCPtr<xcb_dri3_query_version_reply_t> reply(xcb_dri3_query_version_reply(m_connection, cookie, nullptr));
0241         if (reply) {
0242             m_hasDri = true;
0243             m_driMajorVersion = reply->major_version;
0244             m_driMinorVersion = reply->minor_version;
0245         } else {
0246             qCWarning(KWIN_X11WINDOWED) << "Requested DRI3 extension version is unsupported";
0247         }
0248     }
0249 
0250     initXInput();
0251     initDri3();
0252 
0253     XRenderUtils::init(m_connection, m_screen->root);
0254     createOutputs();
0255 
0256     m_pointerDevice = std::make_unique<X11WindowedInputDevice>();
0257     m_pointerDevice->setPointer(true);
0258     m_keyboardDevice = std::make_unique<X11WindowedInputDevice>();
0259     m_keyboardDevice->setKeyboard(true);
0260     if (m_hasXInput) {
0261         m_touchDevice = std::make_unique<X11WindowedInputDevice>();
0262         m_touchDevice->setTouch(true);
0263     }
0264 
0265     m_eventNotifier = std::make_unique<QSocketNotifier>(xcb_get_file_descriptor(m_connection), QSocketNotifier::Read);
0266     auto processXcbEvents = [this] {
0267         while (auto event = xcb_poll_for_event(m_connection)) {
0268             handleEvent(event);
0269             free(event);
0270         }
0271         xcb_flush(m_connection);
0272     };
0273     connect(m_eventNotifier.get(), &QSocketNotifier::activated, this, processXcbEvents);
0274     connect(QCoreApplication::eventDispatcher(), &QAbstractEventDispatcher::aboutToBlock, this, processXcbEvents);
0275     connect(QCoreApplication::eventDispatcher(), &QAbstractEventDispatcher::awake, this, processXcbEvents);
0276 
0277     Q_EMIT outputsQueried();
0278     return true;
0279 }
0280 
0281 void X11WindowedBackend::initXInput()
0282 {
0283 #if HAVE_X11_XINPUT
0284     int xi_opcode, event, error;
0285     // init XInput extension
0286     if (!XQueryExtension(m_display, "XInputExtension", &xi_opcode, &event, &error)) {
0287         qCDebug(KWIN_X11WINDOWED) << "XInputExtension not present";
0288         return;
0289     }
0290 
0291     // verify that the XInput extension is at at least version 2.0
0292     int major = 2, minor = 2;
0293     int result = XIQueryVersion(m_display, &major, &minor);
0294     if (result != Success) {
0295         qCDebug(KWIN_X11WINDOWED) << "Failed to init XInput 2.2, trying 2.0";
0296         minor = 0;
0297         if (XIQueryVersion(m_display, &major, &minor) != Success) {
0298             qCDebug(KWIN_X11WINDOWED) << "Failed to init XInput";
0299             return;
0300         }
0301     }
0302     m_xiOpcode = xi_opcode;
0303     m_majorVersion = major;
0304     m_minorVersion = minor;
0305     m_hasXInput = m_majorVersion >= 2 && m_minorVersion >= 2;
0306 #endif
0307 }
0308 
0309 void X11WindowedBackend::initDri3()
0310 {
0311     if (m_hasDri) {
0312         xcb_dri3_open_cookie_t cookie = xcb_dri3_open(m_connection, m_screen->root, 0);
0313         UniqueCPtr<xcb_dri3_open_reply_t> reply(xcb_dri3_open_reply(m_connection, cookie, nullptr));
0314         if (reply && reply->nfd == 1) {
0315             int fd = xcb_dri3_open_reply_fds(m_connection, reply.get())[0];
0316             fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
0317             m_drmFileDescriptor = FileDescriptor{fd};
0318             m_gbmDevice = gbm_create_device(m_drmFileDescriptor.get());
0319         }
0320     }
0321 
0322     xcb_depth_iterator_t it = xcb_screen_allowed_depths_iterator(m_screen);
0323     while (it.rem > 0) {
0324         uint32_t format = driFormatForDepth(it.data->depth);
0325         if (format) {
0326             QList<uint64_t> &mods = m_driFormats[format];
0327 
0328             if (m_driMajorVersion > 1 || m_driMinorVersion >= 2) {
0329                 xcb_dri3_get_supported_modifiers_cookie_t cookie = xcb_dri3_get_supported_modifiers(m_connection, m_screen->root, it.data->depth, 32);
0330                 UniqueCPtr<xcb_dri3_get_supported_modifiers_reply_t> reply(xcb_dri3_get_supported_modifiers_reply(m_connection, cookie, nullptr));
0331                 if (reply) {
0332                     const uint64_t *modifiers = xcb_dri3_get_supported_modifiers_screen_modifiers(reply.get());
0333                     const int modifierCount = xcb_dri3_get_supported_modifiers_screen_modifiers_length(reply.get());
0334                     for (int i = 0; i < modifierCount; ++i) {
0335                         mods.append(modifiers[i]);
0336                     }
0337                 }
0338             }
0339 
0340             if (mods.isEmpty()) {
0341                 mods.append(DRM_FORMAT_MOD_INVALID);
0342             }
0343         }
0344 
0345         xcb_depth_next(&it);
0346     }
0347 }
0348 
0349 X11WindowedOutput *X11WindowedBackend::findOutput(xcb_window_t window) const
0350 {
0351     auto it = std::find_if(m_outputs.constBegin(), m_outputs.constEnd(),
0352                            [window](X11WindowedOutput *output) {
0353                                return output->window() == window;
0354                            });
0355     if (it != m_outputs.constEnd()) {
0356         return *it;
0357     }
0358     return nullptr;
0359 }
0360 
0361 void X11WindowedBackend::createOutputs()
0362 {
0363     Xcb::Atom protocolsAtom(QByteArrayLiteral("WM_PROTOCOLS"), false, m_connection);
0364     Xcb::Atom deleteWindowAtom(QByteArrayLiteral("WM_DELETE_WINDOW"), false, m_connection);
0365 
0366     // we need to multiply the initial window size with the scale in order to
0367     // create an output window of this size in the end
0368     const QSize pixelSize = m_options.outputSize * m_options.outputScale;
0369     for (int i = 0; i < m_options.outputCount; ++i) {
0370         auto *output = new X11WindowedOutput(this);
0371         output->init(pixelSize, m_options.outputScale);
0372 
0373         m_protocols = protocolsAtom;
0374         m_deleteWindowProtocol = deleteWindowAtom;
0375 
0376         xcb_change_property(m_connection,
0377                             XCB_PROP_MODE_REPLACE,
0378                             output->window(),
0379                             m_protocols,
0380                             XCB_ATOM_ATOM,
0381                             32, 1,
0382                             &m_deleteWindowProtocol);
0383 
0384         m_outputs << output;
0385         Q_EMIT outputAdded(output);
0386         output->updateEnabled(true);
0387     }
0388 
0389     updateWindowTitle();
0390 
0391     xcb_flush(m_connection);
0392 }
0393 
0394 #if HAVE_X11_XINPUT
0395 
0396 static inline qreal fixed1616ToReal(FP1616 val)
0397 {
0398     return (val)*1.0 / (1 << 16);
0399 }
0400 #endif
0401 
0402 void X11WindowedBackend::handleEvent(xcb_generic_event_t *e)
0403 {
0404     const uint8_t eventType = e->response_type & ~0x80;
0405     switch (eventType) {
0406     case XCB_BUTTON_PRESS:
0407     case XCB_BUTTON_RELEASE:
0408         handleButtonPress(reinterpret_cast<xcb_button_press_event_t *>(e));
0409         break;
0410     case XCB_MOTION_NOTIFY: {
0411         auto event = reinterpret_cast<xcb_motion_notify_event_t *>(e);
0412         const X11WindowedOutput *output = findOutput(event->event);
0413         if (!output) {
0414             break;
0415         }
0416         const QPointF position = output->mapFromGlobal(QPointF(event->root_x, event->root_y));
0417         Q_EMIT m_pointerDevice->pointerMotionAbsolute(position, std::chrono::milliseconds(event->time), m_pointerDevice.get());
0418         Q_EMIT m_pointerDevice->pointerFrame(m_pointerDevice.get());
0419     } break;
0420     case XCB_KEY_PRESS:
0421     case XCB_KEY_RELEASE: {
0422         auto event = reinterpret_cast<xcb_key_press_event_t *>(e);
0423         if (eventType == XCB_KEY_PRESS) {
0424             if (!m_keySymbols) {
0425                 m_keySymbols = xcb_key_symbols_alloc(m_connection);
0426             }
0427             const xcb_keysym_t kc = xcb_key_symbols_get_keysym(m_keySymbols, event->detail, 0);
0428             if (kc == XK_Control_R) {
0429                 grabKeyboard(event->time);
0430             }
0431             Q_EMIT m_keyboardDevice->keyChanged(event->detail - 8,
0432                                                 InputRedirection::KeyboardKeyPressed,
0433                                                 std::chrono::milliseconds(event->time),
0434                                                 m_keyboardDevice.get());
0435         } else {
0436             Q_EMIT m_keyboardDevice->keyChanged(event->detail - 8,
0437                                                 InputRedirection::KeyboardKeyReleased,
0438                                                 std::chrono::milliseconds(event->time),
0439                                                 m_keyboardDevice.get());
0440         }
0441     } break;
0442     case XCB_CONFIGURE_NOTIFY:
0443         updateSize(reinterpret_cast<xcb_configure_notify_event_t *>(e));
0444         break;
0445     case XCB_ENTER_NOTIFY: {
0446         auto event = reinterpret_cast<xcb_enter_notify_event_t *>(e);
0447         const X11WindowedOutput *output = findOutput(event->event);
0448         if (!output) {
0449             break;
0450         }
0451         const QPointF position = output->mapFromGlobal(QPointF(event->root_x, event->root_y));
0452         Q_EMIT m_pointerDevice->pointerMotionAbsolute(position, std::chrono::milliseconds(event->time), m_pointerDevice.get());
0453     } break;
0454     case XCB_CLIENT_MESSAGE:
0455         handleClientMessage(reinterpret_cast<xcb_client_message_event_t *>(e));
0456         break;
0457     case XCB_EXPOSE:
0458         handleExpose(reinterpret_cast<xcb_expose_event_t *>(e));
0459         break;
0460     case XCB_MAPPING_NOTIFY:
0461         if (m_keySymbols) {
0462             xcb_refresh_keyboard_mapping(m_keySymbols, reinterpret_cast<xcb_mapping_notify_event_t *>(e));
0463         }
0464         break;
0465     case XCB_GE_GENERIC: {
0466         xcb_ge_generic_event_t *ev = reinterpret_cast<xcb_ge_generic_event_t *>(e);
0467         if (ev->extension == m_presentOpcode) {
0468             handlePresentEvent(ev);
0469         } else if (ev->extension == m_xiOpcode) {
0470             handleXinputEvent(ev);
0471         }
0472         break;
0473     }
0474     default:
0475         break;
0476     }
0477 }
0478 
0479 void X11WindowedBackend::grabKeyboard(xcb_timestamp_t time)
0480 {
0481     const bool oldState = m_keyboardGrabbed;
0482     if (m_keyboardGrabbed) {
0483         xcb_ungrab_keyboard(m_connection, time);
0484         xcb_ungrab_pointer(m_connection, time);
0485         m_keyboardGrabbed = false;
0486     } else {
0487         const X11WindowedOutput *output = static_cast<X11WindowedOutput *>(m_outputs[0]);
0488         const auto c = xcb_grab_keyboard_unchecked(m_connection, false, output->window(), time,
0489                                                    XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC);
0490         UniqueCPtr<xcb_grab_keyboard_reply_t> grab(xcb_grab_keyboard_reply(m_connection, c, nullptr));
0491         if (!grab) {
0492             return;
0493         }
0494         if (grab->status == XCB_GRAB_STATUS_SUCCESS) {
0495             const auto c = xcb_grab_pointer_unchecked(m_connection, false, output->window(),
0496                                                       XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE | XCB_EVENT_MASK_POINTER_MOTION | XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_LEAVE_WINDOW,
0497                                                       XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC,
0498                                                       output->window(), XCB_CURSOR_NONE, time);
0499             UniqueCPtr<xcb_grab_pointer_reply_t> grab(xcb_grab_pointer_reply(m_connection, c, nullptr));
0500             if (!grab || grab->status != XCB_GRAB_STATUS_SUCCESS) {
0501                 xcb_ungrab_keyboard(m_connection, time);
0502                 return;
0503             }
0504             m_keyboardGrabbed = true;
0505         }
0506     }
0507     if (oldState != m_keyboardGrabbed) {
0508         updateWindowTitle();
0509         xcb_flush(m_connection);
0510     }
0511 }
0512 
0513 void X11WindowedBackend::updateWindowTitle()
0514 {
0515     const QString grab = m_keyboardGrabbed ? i18n("Press right control to ungrab input") : i18n("Press right control key to grab input");
0516     const QString title = QStringLiteral("%1 - %2").arg(i18n("KDE Wayland Compositor"), grab);
0517     for (auto it = m_outputs.constBegin(); it != m_outputs.constEnd(); ++it) {
0518         (*it)->setWindowTitle(title);
0519     }
0520 }
0521 
0522 void X11WindowedBackend::handleClientMessage(xcb_client_message_event_t *event)
0523 {
0524     auto it = std::find_if(m_outputs.begin(), m_outputs.end(),
0525                            [event](X11WindowedOutput *output) {
0526                                return output->window() == event->window;
0527                            });
0528     if (it == m_outputs.end()) {
0529         return;
0530     }
0531     if (event->type == m_protocols && m_protocols != XCB_ATOM_NONE) {
0532         if (event->data.data32[0] == m_deleteWindowProtocol && m_deleteWindowProtocol != XCB_ATOM_NONE) {
0533             if (m_outputs.count() == 1) {
0534                 qCDebug(KWIN_X11WINDOWED) << "Backend window is going to be closed, shutting down.";
0535                 QCoreApplication::quit();
0536             } else {
0537                 // remove the window
0538                 qCDebug(KWIN_X11WINDOWED) << "Removing one output window.";
0539 
0540                 auto removedOutput = *it;
0541                 it = m_outputs.erase(it);
0542 
0543                 removedOutput->updateEnabled(false);
0544                 Q_EMIT outputRemoved(removedOutput);
0545                 removedOutput->unref();
0546                 Q_EMIT outputsQueried();
0547             }
0548         }
0549     }
0550 }
0551 
0552 void X11WindowedBackend::handleButtonPress(xcb_button_press_event_t *event)
0553 {
0554     const X11WindowedOutput *output = findOutput(event->event);
0555     if (!output) {
0556         return;
0557     }
0558     bool const pressed = (event->response_type & ~0x80) == XCB_BUTTON_PRESS;
0559     if (event->detail >= XCB_BUTTON_INDEX_4 && event->detail <= 7) {
0560         // wheel
0561         if (!pressed) {
0562             return;
0563         }
0564         const int delta = (event->detail == XCB_BUTTON_INDEX_4 || event->detail == 6) ? -120 : 120;
0565         static const qreal s_defaultAxisStepDistance = 10.0;
0566         InputRedirection::PointerAxis axis;
0567         if (event->detail > 5) {
0568             axis = InputRedirection::PointerAxisHorizontal;
0569         } else {
0570             axis = InputRedirection::PointerAxisVertical;
0571         }
0572         Q_EMIT m_pointerDevice->pointerAxisChanged(axis,
0573                                                    delta * s_defaultAxisStepDistance,
0574                                                    delta,
0575                                                    InputRedirection::PointerAxisSourceUnknown,
0576                                                    std::chrono::milliseconds(event->time),
0577                                                    m_pointerDevice.get());
0578         Q_EMIT m_pointerDevice->pointerFrame(m_pointerDevice.get());
0579         return;
0580     }
0581     uint32_t button = 0;
0582     switch (event->detail) {
0583     case XCB_BUTTON_INDEX_1:
0584         button = BTN_LEFT;
0585         break;
0586     case XCB_BUTTON_INDEX_2:
0587         button = BTN_MIDDLE;
0588         break;
0589     case XCB_BUTTON_INDEX_3:
0590         button = BTN_RIGHT;
0591         break;
0592     default:
0593         button = event->detail + BTN_LEFT - 1;
0594         return;
0595     }
0596 
0597     const QPointF position = output->mapFromGlobal(QPointF(event->root_x, event->root_y));
0598     Q_EMIT m_pointerDevice->pointerMotionAbsolute(position, std::chrono::milliseconds(event->time), m_pointerDevice.get());
0599 
0600     if (pressed) {
0601         Q_EMIT m_pointerDevice->pointerButtonChanged(button, InputRedirection::PointerButtonPressed, std::chrono::milliseconds(event->time), m_pointerDevice.get());
0602     } else {
0603         Q_EMIT m_pointerDevice->pointerButtonChanged(button, InputRedirection::PointerButtonReleased, std::chrono::milliseconds(event->time), m_pointerDevice.get());
0604     }
0605     Q_EMIT m_pointerDevice->pointerFrame(m_pointerDevice.get());
0606 }
0607 
0608 void X11WindowedBackend::handleExpose(xcb_expose_event_t *event)
0609 {
0610     X11WindowedOutput *output = findOutput(event->window);
0611     if (output) {
0612         output->addExposedArea(QRect(event->x, event->y, event->width, event->height));
0613         output->renderLoop()->scheduleRepaint();
0614     }
0615 }
0616 
0617 void X11WindowedBackend::updateSize(xcb_configure_notify_event_t *event)
0618 {
0619     X11WindowedOutput *output = findOutput(event->window);
0620     if (!output) {
0621         return;
0622     }
0623 
0624     output->setHostPosition(QPoint(event->x, event->y));
0625 
0626     const QSize s = QSize(event->width, event->height);
0627     if (s != output->pixelSize()) {
0628         output->resize(s);
0629     }
0630 }
0631 
0632 void X11WindowedBackend::handleXinputEvent(xcb_ge_generic_event_t *ge)
0633 {
0634 #if HAVE_X11_XINPUT
0635     auto te = reinterpret_cast<xXIDeviceEvent *>(ge);
0636     const X11WindowedOutput *output = findOutput(te->event);
0637     if (!output) {
0638         return;
0639     }
0640 
0641     const QPointF position = output->mapFromGlobal(QPointF(fixed1616ToReal(te->root_x), fixed1616ToReal(te->root_y)));
0642 
0643     switch (ge->event_type) {
0644     case XI_TouchBegin: {
0645         Q_EMIT m_touchDevice->touchDown(te->detail, position, std::chrono::milliseconds(te->time), m_touchDevice.get());
0646         Q_EMIT m_touchDevice->touchFrame(m_touchDevice.get());
0647         break;
0648     }
0649     case XI_TouchUpdate: {
0650         Q_EMIT m_touchDevice->touchMotion(te->detail, position, std::chrono::milliseconds(te->time), m_touchDevice.get());
0651         Q_EMIT m_touchDevice->touchFrame(m_touchDevice.get());
0652         break;
0653     }
0654     case XI_TouchEnd: {
0655         Q_EMIT m_touchDevice->touchUp(te->detail, std::chrono::milliseconds(te->time), m_touchDevice.get());
0656         Q_EMIT m_touchDevice->touchFrame(m_touchDevice.get());
0657         break;
0658     }
0659     case XI_TouchOwnership: {
0660         auto te = reinterpret_cast<xXITouchOwnershipEvent *>(ge);
0661         XIAllowTouchEvents(m_display, te->deviceid, te->sourceid, te->touchid, XIAcceptTouch);
0662         break;
0663     }
0664     }
0665 #endif
0666 }
0667 
0668 void X11WindowedBackend::handlePresentEvent(xcb_ge_generic_event_t *ge)
0669 {
0670     switch (ge->event_type) {
0671     case XCB_PRESENT_EVENT_IDLE_NOTIFY: {
0672         xcb_present_idle_notify_event_t *idleNotify = reinterpret_cast<xcb_present_idle_notify_event_t *>(ge);
0673         if (X11WindowedOutput *output = findOutput(idleNotify->window)) {
0674             output->handlePresentIdleNotify(idleNotify);
0675         }
0676         break;
0677     }
0678     case XCB_PRESENT_EVENT_COMPLETE_NOTIFY: {
0679         xcb_present_complete_notify_event_t *completeNotify = reinterpret_cast<xcb_present_complete_notify_event_t *>(ge);
0680         if (X11WindowedOutput *output = findOutput(completeNotify->window)) {
0681             output->handlePresentCompleteNotify(completeNotify);
0682         }
0683         break;
0684     }
0685     }
0686 }
0687 
0688 xcb_window_t X11WindowedBackend::rootWindow() const
0689 {
0690     if (!m_screen) {
0691         return XCB_WINDOW_NONE;
0692     }
0693     return m_screen->root;
0694 }
0695 
0696 gbm_device *X11WindowedBackend::gbmDevice() const
0697 {
0698     return m_gbmDevice;
0699 }
0700 
0701 X11WindowedInputDevice *X11WindowedBackend::pointerDevice() const
0702 {
0703     return m_pointerDevice.get();
0704 }
0705 
0706 X11WindowedInputDevice *X11WindowedBackend::keyboardDevice() const
0707 {
0708     return m_keyboardDevice.get();
0709 }
0710 
0711 X11WindowedInputDevice *X11WindowedBackend::touchDevice() const
0712 {
0713     return m_touchDevice.get();
0714 }
0715 
0716 std::unique_ptr<OpenGLBackend> X11WindowedBackend::createOpenGLBackend()
0717 {
0718     return std::make_unique<X11WindowedEglBackend>(this);
0719 }
0720 
0721 std::unique_ptr<QPainterBackend> X11WindowedBackend::createQPainterBackend()
0722 {
0723     return std::make_unique<X11WindowedQPainterBackend>(this);
0724 }
0725 
0726 std::unique_ptr<InputBackend> X11WindowedBackend::createInputBackend()
0727 {
0728     return std::make_unique<X11WindowedInputBackend>(this);
0729 }
0730 
0731 xcb_connection_t *X11WindowedBackend::connection() const
0732 {
0733     return m_connection;
0734 }
0735 
0736 xcb_screen_t *X11WindowedBackend::screen() const
0737 {
0738     return m_screen;
0739 }
0740 
0741 int X11WindowedBackend::screenNumer() const
0742 {
0743     return m_screenNumber;
0744 }
0745 
0746 ::Display *X11WindowedBackend::display() const
0747 {
0748     return m_display;
0749 }
0750 
0751 bool X11WindowedBackend::hasXInput() const
0752 {
0753     return m_hasXInput;
0754 }
0755 
0756 QHash<uint32_t, QList<uint64_t>> X11WindowedBackend::driFormats() const
0757 {
0758     return m_driFormats;
0759 }
0760 
0761 uint32_t X11WindowedBackend::driFormatForDepth(int depth) const
0762 {
0763     switch (depth) {
0764     case 24:
0765         return DRM_FORMAT_XRGB8888;
0766     case 32:
0767         return DRM_FORMAT_ARGB8888;
0768     default:
0769         return 0;
0770     }
0771 }
0772 
0773 int X11WindowedBackend::driMajorVersion() const
0774 {
0775     return m_driMajorVersion;
0776 }
0777 
0778 int X11WindowedBackend::driMinorVersion() const
0779 {
0780     return m_driMinorVersion;
0781 }
0782 
0783 QList<CompositingType> X11WindowedBackend::supportedCompositors() const
0784 {
0785     QList<CompositingType> ret;
0786     if (m_gbmDevice) {
0787         ret.append(OpenGLCompositing);
0788     }
0789     if (m_hasShm) {
0790         ret.append(QPainterCompositing);
0791     }
0792     return ret;
0793 }
0794 
0795 Outputs X11WindowedBackend::outputs() const
0796 {
0797     return m_outputs;
0798 }
0799 
0800 void X11WindowedBackend::destroyOutputs()
0801 {
0802     while (!m_outputs.isEmpty()) {
0803         auto output = m_outputs.takeLast();
0804         output->updateEnabled(false);
0805         Q_EMIT outputRemoved(output);
0806         delete output;
0807     }
0808 }
0809 
0810 void X11WindowedBackend::setEglDisplay(std::unique_ptr<EglDisplay> &&display)
0811 {
0812     m_eglDisplay = std::move(display);
0813 }
0814 
0815 EglDisplay *X11WindowedBackend::sceneEglDisplayObject() const
0816 {
0817     return m_eglDisplay.get();
0818 }
0819 
0820 } // namespace KWin
0821 
0822 #include "moc_x11_windowed_backend.cpp"