13#include <config-kwin.h>
22#include <KLocalizedString>
23#include <QAbstractEventDispatcher>
24#include <QCoreApplication>
25#include <QSocketNotifier>
28#include <xcb/xcb_keysyms.h>
29#include <xcb/present.h>
32#include <X11/Xlib-xcb.h>
35#include <X11/extensions/XI2proto.h>
36#include <X11/extensions/XInput2.h>
39#include <X11/Xlib-xcb.h>
40#include <X11/keysym.h>
41#include <drm_fourcc.h>
44#include <linux/input.h>
163 m_pointerDevice.reset();
164 m_keyboardDevice.reset();
165 m_touchDevice.reset();
166 m_eglDisplay.reset();
169 gbm_device_destroy(m_gbmDevice);
174 xcb_key_symbols_free(m_keySymbols);
176 xcb_disconnect(m_connection);
177 m_connection =
nullptr;
183 m_display = XOpenDisplay(m_options.
display.toLatin1().constData());
188 m_connection = XGetXCBConnection(m_display);
189 m_screenNumber = XDefaultScreen(m_display);
190 XSetEventQueueOwner(m_display, XCBOwnsEventQueue);
192 int screen = m_screenNumber;
193 for (xcb_screen_iterator_t it = xcb_setup_roots_iterator(xcb_get_setup(m_connection));
195 --
screen, xcb_screen_next(&it)) {
196 if (
screen == m_screenNumber) {
201 const xcb_query_extension_reply_t *presentExtension = xcb_get_extension_data(m_connection, &xcb_present_id);
202 if (presentExtension && presentExtension->present) {
203 m_presentOpcode = presentExtension->major_opcode;
204 xcb_present_query_version_cookie_t cookie = xcb_present_query_version(m_connection, 1, 2);
207 qCWarning(KWIN_X11WINDOWED) <<
"Requested Present extension version is unsupported";
210 m_presentMajorVersion = reply->major_version;
211 m_presentMinorVersion = reply->minor_version;
213 qCWarning(KWIN_X11WINDOWED) <<
"Present X11 extension is unavailable";
217 const xcb_query_extension_reply_t *shmExtension = xcb_get_extension_data(m_connection, &xcb_shm_id);
218 if (shmExtension && shmExtension->present) {
219 xcb_shm_query_version_cookie_t cookie = xcb_shm_query_version(m_connection);
222 qCWarning(KWIN_X11WINDOWED) <<
"Requested SHM extension version is unsupported";
224 if (!reply->shared_pixmaps) {
225 qCWarning(KWIN_X11WINDOWED) <<
"X server supports SHM extension but not shared pixmaps";
232 const xcb_query_extension_reply_t *driExtension = xcb_get_extension_data(m_connection, &xcb_dri3_id);
233 if (driExtension && driExtension->present) {
234 xcb_dri3_query_version_cookie_t cookie = xcb_dri3_query_version(m_connection, 1, 2);
238 m_driMajorVersion = reply->major_version;
239 m_driMinorVersion = reply->minor_version;
241 qCWarning(KWIN_X11WINDOWED) <<
"Requested DRI3 extension version is unsupported";
251 m_pointerDevice = std::make_unique<X11WindowedInputDevice>();
252 m_pointerDevice->setPointer(
true);
253 m_keyboardDevice = std::make_unique<X11WindowedInputDevice>();
254 m_keyboardDevice->setKeyboard(
true);
256 m_touchDevice = std::make_unique<X11WindowedInputDevice>();
257 m_touchDevice->setTouch(
true);
260 m_eventNotifier = std::make_unique<QSocketNotifier>(xcb_get_file_descriptor(m_connection), QSocketNotifier::Read);
261 auto processXcbEvents = [
this] {
262 while (
auto event = xcb_poll_for_event(m_connection)) {
266 xcb_flush(m_connection);
268 connect(m_eventNotifier.get(), &QSocketNotifier::activated,
this, processXcbEvents);
269 connect(QCoreApplication::eventDispatcher(), &QAbstractEventDispatcher::aboutToBlock,
this, processXcbEvents);
270 connect(QCoreApplication::eventDispatcher(), &QAbstractEventDispatcher::awake,
this, processXcbEvents);
276void X11WindowedBackend::initXInput()
279 int xi_opcode, event, error;
281 if (!XQueryExtension(m_display,
"XInputExtension", &xi_opcode, &event, &error)) {
282 qCDebug(KWIN_X11WINDOWED) <<
"XInputExtension not present";
287 int major = 2, minor = 2;
288 int result = XIQueryVersion(m_display, &major, &minor);
289 if (result != Success) {
290 qCDebug(KWIN_X11WINDOWED) <<
"Failed to init XInput 2.2, trying 2.0";
292 if (XIQueryVersion(m_display, &major, &minor) != Success) {
293 qCDebug(KWIN_X11WINDOWED) <<
"Failed to init XInput";
297 m_xiOpcode = xi_opcode;
298 m_majorVersion = major;
299 m_minorVersion = minor;
300 m_hasXInput = m_majorVersion >= 2 && m_minorVersion >= 2;
304void X11WindowedBackend::initDri3()
307 xcb_dri3_open_cookie_t cookie = xcb_dri3_open(m_connection, m_screen->root, 0);
308 UniqueCPtr<xcb_dri3_open_reply_t> reply(xcb_dri3_open_reply(m_connection, cookie,
nullptr));
309 if (reply && reply->nfd == 1) {
310 int fd = xcb_dri3_open_reply_fds(m_connection, reply.get())[0];
311 fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
312 m_drmFileDescriptor = FileDescriptor{fd};
313 m_gbmDevice = gbm_create_device(m_drmFileDescriptor.
get());
317 xcb_depth_iterator_t it = xcb_screen_allowed_depths_iterator(m_screen);
321 QList<uint64_t> &mods = m_driFormats[
format];
323 if (m_driMajorVersion > 1 || m_driMinorVersion >= 2) {
324 xcb_dri3_get_supported_modifiers_cookie_t cookie = xcb_dri3_get_supported_modifiers(m_connection, m_screen->root, it.data->depth, 32);
325 UniqueCPtr<xcb_dri3_get_supported_modifiers_reply_t> reply(xcb_dri3_get_supported_modifiers_reply(m_connection, cookie,
nullptr));
327 const uint64_t *modifiers = xcb_dri3_get_supported_modifiers_screen_modifiers(reply.get());
328 const int modifierCount = xcb_dri3_get_supported_modifiers_screen_modifiers_length(reply.get());
329 for (
int i = 0; i < modifierCount; ++i) {
330 mods.append(modifiers[i]);
335 if (mods.isEmpty()) {
336 mods.append(DRM_FORMAT_MOD_INVALID);
344X11WindowedOutput *X11WindowedBackend::findOutput(xcb_window_t window)
const
346 auto it = std::find_if(m_outputs.constBegin(), m_outputs.constEnd(),
347 [window](X11WindowedOutput *output) {
348 return output->window() == window;
350 if (it != m_outputs.constEnd()) {
356void X11WindowedBackend::createOutputs()
358 Xcb::Atom protocolsAtom(QByteArrayLiteral(
"WM_PROTOCOLS"),
false, m_connection);
359 Xcb::Atom deleteWindowAtom(QByteArrayLiteral(
"WM_DELETE_WINDOW"),
false, m_connection);
365 auto *output =
new X11WindowedOutput(
this);
368 m_protocols = protocolsAtom;
369 m_deleteWindowProtocol = deleteWindowAtom;
371 xcb_change_property(m_connection,
372 XCB_PROP_MODE_REPLACE,
377 &m_deleteWindowProtocol);
381 output->updateEnabled(
true);
386 xcb_flush(m_connection);
391static inline qreal fixed1616ToReal(FP1616 val)
393 return (val)*1.0 / (1 << 16);
397void X11WindowedBackend::handleEvent(xcb_generic_event_t *e)
399 const uint8_t eventType = e->response_type & ~0x80;
401 case XCB_BUTTON_PRESS:
402 case XCB_BUTTON_RELEASE:
403 handleButtonPress(
reinterpret_cast<xcb_button_press_event_t *
>(e));
405 case XCB_MOTION_NOTIFY: {
406 auto event =
reinterpret_cast<xcb_motion_notify_event_t *
>(e);
407 const X11WindowedOutput *output = findOutput(event->event);
411 const QPointF position = output->mapFromGlobal(QPointF(event->root_x, event->root_y));
412 Q_EMIT m_pointerDevice->pointerMotionAbsolute(position, std::chrono::milliseconds(event->time), m_pointerDevice.get());
413 Q_EMIT m_pointerDevice->pointerFrame(m_pointerDevice.get());
416 case XCB_KEY_RELEASE: {
417 auto event =
reinterpret_cast<xcb_key_press_event_t *
>(e);
418 if (eventType == XCB_KEY_PRESS) {
420 m_keySymbols = xcb_key_symbols_alloc(m_connection);
422 const xcb_keysym_t kc = xcb_key_symbols_get_keysym(m_keySymbols, event->detail, 0);
423 if (kc == XK_Control_R) {
424 grabKeyboard(event->time);
426 Q_EMIT m_keyboardDevice->keyChanged(event->detail - 8,
428 std::chrono::milliseconds(event->time),
429 m_keyboardDevice.get());
431 Q_EMIT m_keyboardDevice->keyChanged(event->detail - 8,
433 std::chrono::milliseconds(event->time),
434 m_keyboardDevice.get());
437 case XCB_CONFIGURE_NOTIFY:
438 updateSize(
reinterpret_cast<xcb_configure_notify_event_t *
>(e));
440 case XCB_ENTER_NOTIFY: {
441 auto event =
reinterpret_cast<xcb_enter_notify_event_t *
>(e);
442 const X11WindowedOutput *output = findOutput(event->event);
446 const QPointF position = output->mapFromGlobal(QPointF(event->root_x, event->root_y));
447 Q_EMIT m_pointerDevice->pointerMotionAbsolute(position, std::chrono::milliseconds(event->time), m_pointerDevice.get());
449 case XCB_CLIENT_MESSAGE:
450 handleClientMessage(
reinterpret_cast<xcb_client_message_event_t *
>(e));
453 handleExpose(
reinterpret_cast<xcb_expose_event_t *
>(e));
455 case XCB_MAPPING_NOTIFY:
457 xcb_refresh_keyboard_mapping(m_keySymbols,
reinterpret_cast<xcb_mapping_notify_event_t *
>(e));
463 handlePresentEvent(ev);
464 }
else if (ev->
extension == m_xiOpcode) {
465 handleXinputEvent(ev);
474void X11WindowedBackend::grabKeyboard(xcb_timestamp_t time)
476 const bool oldState = m_keyboardGrabbed;
477 if (m_keyboardGrabbed) {
478 xcb_ungrab_keyboard(m_connection, time);
479 xcb_ungrab_pointer(m_connection, time);
480 m_keyboardGrabbed =
false;
482 const X11WindowedOutput *output =
static_cast<X11WindowedOutput *
>(m_outputs[0]);
483 const auto c = xcb_grab_keyboard_unchecked(m_connection,
false, output->window(), time,
484 XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC);
485 UniqueCPtr<xcb_grab_keyboard_reply_t> grab(xcb_grab_keyboard_reply(m_connection, c,
nullptr));
489 if (grab->status == XCB_GRAB_STATUS_SUCCESS) {
490 const auto c = xcb_grab_pointer_unchecked(m_connection,
false, output->window(),
491 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,
492 XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC,
493 output->window(), XCB_CURSOR_NONE, time);
494 UniqueCPtr<xcb_grab_pointer_reply_t> grab(xcb_grab_pointer_reply(m_connection, c,
nullptr));
495 if (!grab || grab->status != XCB_GRAB_STATUS_SUCCESS) {
496 xcb_ungrab_keyboard(m_connection, time);
499 m_keyboardGrabbed =
true;
502 if (oldState != m_keyboardGrabbed) {
504 xcb_flush(m_connection);
508void X11WindowedBackend::updateWindowTitle()
510 const QString grab = m_keyboardGrabbed ? i18n(
"Press right control to ungrab input") : i18n(
"Press right control key to grab input");
511 const QString title = QStringLiteral(
"%1 - %2").arg(i18n(
"KDE Wayland Compositor"), grab);
512 for (
auto it = m_outputs.constBegin(); it != m_outputs.constEnd(); ++it) {
513 (*it)->setWindowTitle(title);
517void X11WindowedBackend::handleClientMessage(xcb_client_message_event_t *event)
519 auto it = std::find_if(m_outputs.begin(), m_outputs.end(),
520 [event](X11WindowedOutput *output) {
521 return output->window() == event->window;
523 if (it == m_outputs.end()) {
526 if (event->type == m_protocols && m_protocols != XCB_ATOM_NONE) {
527 if (event->data.data32[0] == m_deleteWindowProtocol && m_deleteWindowProtocol != XCB_ATOM_NONE) {
528 if (m_outputs.count() == 1) {
529 qCDebug(KWIN_X11WINDOWED) <<
"Backend window is going to be closed, shutting down.";
530 QCoreApplication::quit();
533 qCDebug(KWIN_X11WINDOWED) <<
"Removing one output window.";
535 auto removedOutput = *it;
536 it = m_outputs.erase(it);
538 removedOutput->updateEnabled(
false);
540 removedOutput->unref();
547void X11WindowedBackend::handleButtonPress(xcb_button_press_event_t *event)
549 const X11WindowedOutput *output = findOutput(event->event);
553 bool const pressed = (
event->response_type & ~0x80) == XCB_BUTTON_PRESS;
554 if (event->detail >= XCB_BUTTON_INDEX_4 && event->detail <= 7) {
559 const int delta = (
event->detail == XCB_BUTTON_INDEX_4 ||
event->detail == 6) ? -120 : 120;
560 static const qreal s_defaultAxisStepDistance = 10.0;
562 if (event->detail > 5) {
567 Q_EMIT m_pointerDevice->pointerAxisChanged(axis,
568 delta * s_defaultAxisStepDistance,
571 std::chrono::milliseconds(event->time),
572 m_pointerDevice.get());
573 Q_EMIT m_pointerDevice->pointerFrame(m_pointerDevice.get());
577 switch (event->detail) {
578 case XCB_BUTTON_INDEX_1:
581 case XCB_BUTTON_INDEX_2:
584 case XCB_BUTTON_INDEX_3:
588 button =
event->detail + BTN_LEFT - 1;
592 const QPointF position = output->mapFromGlobal(QPointF(event->root_x, event->root_y));
593 Q_EMIT m_pointerDevice->pointerMotionAbsolute(position, std::chrono::milliseconds(event->time), m_pointerDevice.get());
600 Q_EMIT m_pointerDevice->pointerFrame(m_pointerDevice.get());
603void X11WindowedBackend::handleExpose(xcb_expose_event_t *event)
605 X11WindowedOutput *output = findOutput(event->window);
607 output->addExposedArea(QRect(event->x, event->y, event->width, event->height));
608 output->renderLoop()->scheduleRepaint();
612void X11WindowedBackend::updateSize(xcb_configure_notify_event_t *event)
614 X11WindowedOutput *output = findOutput(event->window);
619 output->setHostPosition(QPoint(event->x, event->y));
621 const QSize s = QSize(event->width, event->height);
622 if (s != output->pixelSize()) {
630 auto te =
reinterpret_cast<xXIDeviceEvent *
>(ge);
631 const X11WindowedOutput *output = findOutput(te->event);
636 const QPointF position = output->mapFromGlobal(QPointF(fixed1616ToReal(te->root_x), fixed1616ToReal(te->root_y)));
639 case XI_TouchBegin: {
640 Q_EMIT m_touchDevice->touchDown(te->detail, position, std::chrono::milliseconds(te->time), m_touchDevice.get());
641 Q_EMIT m_touchDevice->touchFrame(m_touchDevice.get());
644 case XI_TouchUpdate: {
645 Q_EMIT m_touchDevice->touchMotion(te->detail, position, std::chrono::milliseconds(te->time), m_touchDevice.get());
646 Q_EMIT m_touchDevice->touchFrame(m_touchDevice.get());
650 Q_EMIT m_touchDevice->touchUp(te->detail, std::chrono::milliseconds(te->time), m_touchDevice.get());
651 Q_EMIT m_touchDevice->touchFrame(m_touchDevice.get());
654 case XI_TouchOwnership: {
655 auto te =
reinterpret_cast<xXITouchOwnershipEvent *
>(ge);
656 XIAllowTouchEvents(m_display, te->deviceid, te->sourceid, te->touchid, XIAcceptTouch);
666 case XCB_PRESENT_EVENT_IDLE_NOTIFY: {
667 xcb_present_idle_notify_event_t *idleNotify =
reinterpret_cast<xcb_present_idle_notify_event_t *
>(ge);
668 if (X11WindowedOutput *output = findOutput(idleNotify->window)) {
669 output->handlePresentIdleNotify(idleNotify);
673 case XCB_PRESENT_EVENT_COMPLETE_NOTIFY: {
674 xcb_present_complete_notify_event_t *completeNotify =
reinterpret_cast<xcb_present_complete_notify_event_t *
>(ge);
675 if (X11WindowedOutput *output = findOutput(completeNotify->window)) {
676 output->handlePresentCompleteNotify(completeNotify);
686 return XCB_WINDOW_NONE;
688 return m_screen->root;
698 return m_pointerDevice.get();
703 return m_keyboardDevice.get();
708 return m_touchDevice.get();
713 return std::make_unique<X11WindowedEglBackend>(
this);
718 return std::make_unique<X11WindowedQPainterBackend>(
this);
723 return std::make_unique<X11WindowedInputBackend>(
this);
738 return m_screenNumber;
760 return DRM_FORMAT_XRGB8888;
762 return DRM_FORMAT_ARGB8888;
770 return m_driMajorVersion;
775 return m_driMinorVersion;
780 QList<CompositingType> ret;
795void X11WindowedBackend::destroyOutputs()
797 while (!m_outputs.isEmpty()) {
798 auto output = m_outputs.takeLast();
799 output->updateEnabled(
false);
807 m_eglDisplay = std::move(
display);
812 return m_eglDisplay.get();
817#include "moc_x11_windowed_backend.cpp"
void outputAdded(Output *output)
void outputRemoved(Output *output)
X11WindowedInputDevice * pointerDevice() const
void setEglDisplay(std::unique_ptr< EglDisplay > &&display)
std::unique_ptr< OpenGLBackend > createOpenGLBackend() override
X11WindowedInputDevice * touchDevice() const
gbm_device * gbmDevice() const
int driMajorVersion() const
~X11WindowedBackend() override
xcb_connection_t * connection() const
std::unique_ptr< InputBackend > createInputBackend() override
X11WindowedBackend(const X11WindowedBackendOptions &options)
EglDisplay * sceneEglDisplayObject() const override
uint32_t driFormatForDepth(int depth) const
int driMinorVersion() const
bool initialize() override
QHash< uint32_t, QList< uint64_t > > driFormats() const
X11WindowedInputDevice * keyboardDevice() const
Outputs outputs() const override
std::unique_ptr< QPainterBackend > createQPainterBackend() override
::Display * display() const
xcb_window_t rootWindow() const
QList< CompositingType > supportedCompositors() const override
xcb_screen_t * screen() const
void init(xcb_connection_t *connection, xcb_window_t rootWindow)
std::unique_ptr< T, CDeleter > UniqueCPtr