diff --git a/plugins/platforms/x11/windowed/CMakeLists.txt b/plugins/platforms/x11/windowed/CMakeLists.txt index 51ebf364e..0c86345b6 100644 --- a/plugins/platforms/x11/windowed/CMakeLists.txt +++ b/plugins/platforms/x11/windowed/CMakeLists.txt @@ -1,21 +1,22 @@ set(X11BACKEND_SOURCES logging.cpp egl_x11_backend.cpp scene_qpainter_x11_backend.cpp x11windowed_backend.cpp + x11windowed_output.cpp ) include_directories(${CMAKE_SOURCE_DIR}/platformsupport/scenes/opengl) add_library(KWinWaylandX11Backend MODULE ${X11BACKEND_SOURCES}) set_target_properties(KWinWaylandX11Backend PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin/org.kde.kwin.waylandbackends/") target_link_libraries(KWinWaylandX11Backend eglx11common kwin kwinxrenderutils X11::XCB SceneQPainterBackend SceneOpenGLBackend) if(X11_Xinput_FOUND) target_link_libraries(KWinWaylandX11Backend ${X11_Xinput_LIB}) endif() install( TARGETS KWinWaylandX11Backend DESTINATION ${PLUGIN_INSTALL_DIR}/org.kde.kwin.waylandbackends/ ) diff --git a/plugins/platforms/x11/windowed/x11windowed_backend.cpp b/plugins/platforms/x11/windowed/x11windowed_backend.cpp index 9282c9482..12c8ca2ad 100644 --- a/plugins/platforms/x11/windowed/x11windowed_backend.cpp +++ b/plugins/platforms/x11/windowed/x11windowed_backend.cpp @@ -1,607 +1,549 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2015 Martin Gräßlin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "x11windowed_backend.h" +#include "x11windowed_output.h" #include "scene_qpainter_x11_backend.h" #include "logging.h" #include "wayland_server.h" #include "xcbutils.h" #include "egl_x11_backend.h" -#include "screens.h" +#include "outputscreens.h" #include // KDE #include -#include #include #include -#include #include // kwayland -#include #include #include -#include // xcb #include // X11 #if HAVE_X11_XINPUT #include "ge_event_mem_mover.h" #include #include #endif // system #include #include namespace KWin { X11WindowedBackend::X11WindowedBackend(QObject *parent) : Platform(parent) { setSupportsPointerWarping(true); connect(this, &X11WindowedBackend::sizeChanged, this, &X11WindowedBackend::screenSizeChanged); } X11WindowedBackend::~X11WindowedBackend() { if (m_connection) { if (m_keySymbols) { xcb_key_symbols_free(m_keySymbols); } - for (auto it = m_windows.begin(); it != m_windows.end(); ++it) { - xcb_unmap_window(m_connection, (*it).window); - xcb_destroy_window(m_connection, (*it).window); - delete (*it).winInfo; - } if (m_cursor) { xcb_free_cursor(m_connection, m_cursor); } xcb_disconnect(m_connection); } } void X11WindowedBackend::init() { int screen = 0; xcb_connection_t *c = nullptr; Display *xDisplay = XOpenDisplay(deviceIdentifier().constData()); if (xDisplay) { c = XGetXCBConnection(xDisplay); XSetEventQueueOwner(xDisplay, XCBOwnsEventQueue); screen = XDefaultScreen(xDisplay); } if (c && !xcb_connection_has_error(c)) { m_connection = c; m_screenNumber = screen; m_display = xDisplay; for (xcb_screen_iterator_t it = xcb_setup_roots_iterator(xcb_get_setup(m_connection)); it.rem; --screen, xcb_screen_next(&it)) { if (screen == m_screenNumber) { m_screen = it.data; } } initXInput(); XRenderUtils::init(m_connection, m_screen->root); - createWindow(); + createOutputs(); connect(kwinApp(), &Application::workspaceCreated, this, &X11WindowedBackend::startEventReading); connect(this, &X11WindowedBackend::cursorChanged, this, [this] { createCursor(softwareCursor(), softwareCursorHotspot()); } ); setReady(true); waylandServer()->seat()->setHasPointer(true); waylandServer()->seat()->setHasKeyboard(true); if (m_hasXInput) { waylandServer()->seat()->setHasTouch(true); } emit screensQueried(); } else { emit initFailed(); } } void X11WindowedBackend::initXInput() { #if HAVE_X11_XINPUT int xi_opcode, event, error; // init XInput extension if (!XQueryExtension(m_display, "XInputExtension", &xi_opcode, &event, &error)) { qCDebug(KWIN_X11WINDOWED) << "XInputExtension not present"; return; } // verify that the XInput extension is at at least version 2.0 int major = 2, minor = 2; int result = XIQueryVersion(m_display, &major, &minor); if (result != Success) { qCDebug(KWIN_X11WINDOWED) << "Failed to init XInput 2.2, trying 2.0"; minor = 0; if (XIQueryVersion(m_display, &major, &minor) != Success) { qCDebug(KWIN_X11WINDOWED) << "Failed to init XInput"; return; } } m_xiOpcode = xi_opcode; m_majorVersion = major; m_minorVersion = minor; m_hasXInput = m_majorVersion >=2 && m_minorVersion >= 2; #endif } -void X11WindowedBackend::createWindow() +void X11WindowedBackend::createOutputs() { Xcb::Atom protocolsAtom(QByteArrayLiteral("WM_PROTOCOLS"), false, m_connection); Xcb::Atom deleteWindowAtom(QByteArrayLiteral("WM_DELETE_WINDOW"), false, m_connection); - for (int i = 0; i < initialOutputCount(); ++i) { - Output o; - o.window = xcb_generate_id(m_connection); - uint32_t mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK; - const uint32_t values[] = { - m_screen->black_pixel, - XCB_EVENT_MASK_KEY_PRESS | - XCB_EVENT_MASK_KEY_RELEASE | - 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 | - XCB_EVENT_MASK_STRUCTURE_NOTIFY | - XCB_EVENT_MASK_EXPOSURE - }; - o.scale = initialOutputScale(); - o.size = initialWindowSize() * o.scale; - if (!m_windows.isEmpty()) { - const auto &p = m_windows.last(); - o.internalPosition = QPoint(p.internalPosition.x() + p.size.width() / p.scale, 0); - } - xcb_create_window(m_connection, XCB_COPY_FROM_PARENT, o.window, m_screen->root, - 0, 0, o.size.width(), o.size.height(), - 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, mask, values); - - // select xinput 2 events - initXInputForWindow(o.window); - - o.winInfo = new NETWinInfo(m_connection, o.window, m_screen->root, NET::WMWindowType, NET::Properties2()); - o.winInfo->setWindowType(NET::Normal); - o.winInfo->setPid(QCoreApplication::applicationPid()); - QIcon windowIcon = QIcon::fromTheme(QStringLiteral("kwin")); - auto addIcon = [&o, &windowIcon] (const QSize &size) { - if (windowIcon.actualSize(size) != size) { - return; - } - NETIcon icon; - icon.data = windowIcon.pixmap(size).toImage().bits(); - icon.size.width = size.width(); - icon.size.height = size.height(); - o.winInfo->setIcon(icon, false); - }; - addIcon(QSize(16, 16)); - addIcon(QSize(32, 32)); - addIcon(QSize(48, 48)); - xcb_map_window(m_connection, o.window); + // we need to multiply the initial window size with the scale in order to + // create an output window of this size in the end + const int pixelWidth = initialWindowSize().width() * initialOutputScale() + 0.5; + const int pixelHeight = initialWindowSize().height() * initialOutputScale() + 0.5; + const int logicalWidth = initialWindowSize().width(); + + int logicalWidthSum = 0; + for (int i = 0; i < initialOutputCount(); ++i) { + auto *output = new X11WindowedOutput(this); + output->setScale(initialOutputScale()); + output->setGeometry(QPoint(logicalWidthSum, 0), QSize(pixelWidth, pixelHeight)); + output->init(); m_protocols = protocolsAtom; m_deleteWindowProtocol = deleteWindowAtom; - xcb_change_property(m_connection, XCB_PROP_MODE_REPLACE, o.window, m_protocols, XCB_ATOM_ATOM, 32, 1, &m_deleteWindowProtocol); - m_windows << o; + xcb_change_property(m_connection, + XCB_PROP_MODE_REPLACE, + output->window(), + m_protocols, + XCB_ATOM_ATOM, + 32, 1, + &m_deleteWindowProtocol); + + logicalWidthSum += logicalWidth; + m_outputs << output; } updateWindowTitle(); xcb_flush(m_connection); } -void X11WindowedBackend::initXInputForWindow(xcb_window_t window) -{ - if (!m_hasXInput) { - return; - } -#if HAVE_X11_XINPUT - XIEventMask evmasks[1]; - unsigned char mask1[XIMaskLen(XI_LASTEVENT)]; - - memset(mask1, 0, sizeof(mask1)); - XISetMask(mask1, XI_TouchBegin); - XISetMask(mask1, XI_TouchUpdate); - XISetMask(mask1, XI_TouchOwnership); - XISetMask(mask1, XI_TouchEnd); - evmasks[0].deviceid = XIAllMasterDevices; - evmasks[0].mask_len = sizeof(mask1); - evmasks[0].mask = mask1; - XISelectEvents(m_display, window, evmasks, 1); -#else - Q_UNUSED(window) -#endif -} - void X11WindowedBackend::startEventReading() { QSocketNotifier *notifier = new QSocketNotifier(xcb_get_file_descriptor(m_connection), QSocketNotifier::Read, this); auto processXcbEvents = [this] { while (auto event = xcb_poll_for_event(m_connection)) { handleEvent(event); free(event); } xcb_flush(m_connection); }; connect(notifier, &QSocketNotifier::activated, this, processXcbEvents); connect(QCoreApplication::eventDispatcher(), &QAbstractEventDispatcher::aboutToBlock, this, processXcbEvents); connect(QCoreApplication::eventDispatcher(), &QAbstractEventDispatcher::awake, this, processXcbEvents); } #if HAVE_X11_XINPUT static inline qreal fixed1616ToReal(FP1616 val) { return (val) * 1.0 / (1 << 16); } #endif void X11WindowedBackend::handleEvent(xcb_generic_event_t *e) { const uint8_t eventType = e->response_type & ~0x80; switch (eventType) { case XCB_BUTTON_PRESS: case XCB_BUTTON_RELEASE: handleButtonPress(reinterpret_cast(e)); break; case XCB_MOTION_NOTIFY: { auto event = reinterpret_cast(e); - auto it = std::find_if(m_windows.constBegin(), m_windows.constEnd(), [event] (const Output &o) { return o.window == event->event; }); - if (it == m_windows.constEnd()) { + auto it = std::find_if(m_outputs.constBegin(), m_outputs.constEnd(), + [event] (X11WindowedOutput *output) { return output->window() == event->event; } + ); + if (it == m_outputs.constEnd()) { break; } //generally we don't need to normalise input to the output scale; however because we're getting input //from a host window that doesn't understand scaling, we need to apply it ourselves so the cursor matches - pointerMotion(QPointF(event->root_x - (*it).xPosition.x() + (*it).internalPosition.x(), - event->root_y - (*it).xPosition.y() + (*it).internalPosition.y()) / it->scale, - event->time); + pointerMotion(QPointF(event->root_x - (*it)->hostPosition().x() + (*it)->internalPosition().x(), + event->root_y - (*it)->hostPosition().y() + (*it)->internalPosition().y()) / (*it)->scale(), + event->time); } break; case XCB_KEY_PRESS: case XCB_KEY_RELEASE: { auto event = reinterpret_cast(e); if (eventType == XCB_KEY_PRESS) { if (!m_keySymbols) { m_keySymbols = xcb_key_symbols_alloc(m_connection); } const xcb_keysym_t kc = xcb_key_symbols_get_keysym(m_keySymbols, event->detail, 0); if (kc == XK_Control_R) { grabKeyboard(event->time); } keyboardKeyPressed(event->detail - 8, event->time); } else { keyboardKeyReleased(event->detail - 8, event->time); } } break; case XCB_CONFIGURE_NOTIFY: updateSize(reinterpret_cast(e)); break; case XCB_ENTER_NOTIFY: { auto event = reinterpret_cast(e); - auto it = std::find_if(m_windows.constBegin(), m_windows.constEnd(), [event] (const Output &o) { return o.window == event->event; }); - if (it == m_windows.constEnd()) { + auto it = std::find_if(m_outputs.constBegin(), m_outputs.constEnd(), + [event] (X11WindowedOutput *output) { return output->window() == event->event; } + ); + if (it == m_outputs.constEnd()) { break; } - pointerMotion(QPointF(event->root_x - (*it).xPosition.x() + (*it).internalPosition.x(), - event->root_y - (*it).xPosition.y() + (*it).internalPosition.y()) / it->scale, - event->time); + pointerMotion(QPointF(event->root_x - (*it)->hostPosition().x() + (*it)->internalPosition().x(), + event->root_y - (*it)->hostPosition().y() + (*it)->internalPosition().y()) / (*it)->scale(), + event->time); } break; case XCB_CLIENT_MESSAGE: handleClientMessage(reinterpret_cast(e)); break; case XCB_EXPOSE: handleExpose(reinterpret_cast(e)); break; case XCB_MAPPING_NOTIFY: if (m_keySymbols) { xcb_refresh_keyboard_mapping(m_keySymbols, reinterpret_cast(e)); } break; #if HAVE_X11_XINPUT case XCB_GE_GENERIC: { GeEventMemMover ge(e); auto te = reinterpret_cast(e); - auto it = std::find_if(m_windows.constBegin(), m_windows.constEnd(), [te] (const Output &o) { return o.window == te->event; }); - if (it == m_windows.constEnd()) { + auto it = std::find_if(m_outputs.constBegin(), m_outputs.constEnd(), + [te] (X11WindowedOutput *output) { return output->window() == te->event; } + ); + if (it == m_outputs.constEnd()) { break; } QPointF position{ - fixed1616ToReal(te->root_x) - (*it).xPosition.x() + (*it).internalPosition.x(), - fixed1616ToReal(te->root_y) - (*it).xPosition.y() + (*it).internalPosition.y() + fixed1616ToReal(te->root_x) - (*it)->hostPosition().x() + (*it)->internalPosition().x(), + fixed1616ToReal(te->root_y) - (*it)->hostPosition().y() + (*it)->internalPosition().y() }; - position /= it->scale; + position /= (*it)->scale(); switch (ge->event_type) { case XI_TouchBegin: { touchDown(te->detail, position, te->time); touchFrame(); break; } case XI_TouchUpdate: { touchMotion(te->detail, position, te->time); touchFrame(); break; } case XI_TouchEnd: { touchUp(te->detail, te->time); touchFrame(); break; } case XI_TouchOwnership: { auto te = reinterpret_cast(e); XIAllowTouchEvents(m_display, te->deviceid, te->sourceid, te->touchid, XIAcceptTouch); break; } } break; } #endif default: break; } } void X11WindowedBackend::grabKeyboard(xcb_timestamp_t time) { const bool oldState = m_keyboardGrabbed; if (m_keyboardGrabbed) { xcb_ungrab_keyboard(m_connection, time); xcb_ungrab_pointer(m_connection, time); m_keyboardGrabbed = false; } else { const auto c = xcb_grab_keyboard_unchecked(m_connection, false, window(), time, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC); ScopedCPointer grab(xcb_grab_keyboard_reply(m_connection, c, nullptr)); if (grab.isNull()) { return; } if (grab->status == XCB_GRAB_STATUS_SUCCESS) { const auto c = xcb_grab_pointer_unchecked(m_connection, false, window(), 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, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, window(), XCB_CURSOR_NONE, time); ScopedCPointer grab(xcb_grab_pointer_reply(m_connection, c, nullptr)); if (grab.isNull() || grab->status != XCB_GRAB_STATUS_SUCCESS) { xcb_ungrab_keyboard(m_connection, time); return; } m_keyboardGrabbed = true; } } if (oldState != m_keyboardGrabbed) { updateWindowTitle(); xcb_flush(m_connection); } } void X11WindowedBackend::updateWindowTitle() { const QString grab = m_keyboardGrabbed ? i18n("Press right control to ungrab input") : i18n("Press right control key to grab input"); const QString title = QStringLiteral("%1 (%2) - %3").arg(i18n("KDE Wayland Compositor")) .arg(waylandServer()->display()->socketName()) .arg(grab); - for (auto it = m_windows.constBegin(); it != m_windows.constEnd(); ++it) { - (*it).winInfo->setName(title.toUtf8().constData()); + for (auto it = m_outputs.constBegin(); it != m_outputs.constEnd(); ++it) { + (*it)->setWindowTitle(title); } } void X11WindowedBackend::handleClientMessage(xcb_client_message_event_t *event) { - auto it = std::find_if(m_windows.begin(), m_windows.end(), [event] (const Output &o) { return o.window == event->window; }); - if (it == m_windows.end()) { + auto it = std::find_if(m_outputs.begin(), m_outputs.end(), + [event] (X11WindowedOutput *output) { return output->window() == event->window; } + ); + if (it == m_outputs.end()) { return; } if (event->type == m_protocols && m_protocols != XCB_ATOM_NONE) { if (event->data.data32[0] == m_deleteWindowProtocol && m_deleteWindowProtocol != XCB_ATOM_NONE) { - if (m_windows.count() == 1) { + if (m_outputs.count() == 1) { qCDebug(KWIN_X11WINDOWED) << "Backend window is going to be closed, shutting down."; QCoreApplication::quit(); } else { // remove the window qCDebug(KWIN_X11WINDOWED) << "Removing one output window."; - auto o = *it; - it = m_windows.erase(it); - xcb_unmap_window(m_connection, o.window); - xcb_destroy_window(m_connection, o.window); - delete o.winInfo; + + auto removedOutput = *it; + it = m_outputs.erase(it); // update the sizes - int x = o.internalPosition.x(); - for (; it != m_windows.end(); ++it) { - (*it).internalPosition.setX(x); - x += (*it).size.width(); + int x = removedOutput->internalPosition().x(); + for (; it != m_outputs.end(); ++it) { + (*it)->setGeometry(QPoint(x, 0), (*it)->pixelSize()); + x += (*it)->geometry().width(); } + + delete removedOutput; QMetaObject::invokeMethod(screens(), "updateCount"); } } } } void X11WindowedBackend::handleButtonPress(xcb_button_press_event_t *event) { - auto it = std::find_if(m_windows.constBegin(), m_windows.constEnd(), [event] (const Output &o) { return o.window == event->event; }); - if (it == m_windows.constEnd()) { + auto it = std::find_if(m_outputs.constBegin(), m_outputs.constEnd(), + [event] (X11WindowedOutput *output) { return output->window() == event->event; } + ); + if (it == m_outputs.constEnd()) { return; } bool const pressed = (event->response_type & ~0x80) == XCB_BUTTON_PRESS; if (event->detail >= XCB_BUTTON_INDEX_4 && event->detail <= 7) { // wheel if (!pressed) { return; } const int delta = (event->detail == XCB_BUTTON_INDEX_4 || event->detail == 6) ? -1 : 1; static const qreal s_defaultAxisStepDistance = 10.0; if (event->detail > 5) { pointerAxisHorizontal(delta * s_defaultAxisStepDistance, event->time, delta); } else { pointerAxisVertical(delta * s_defaultAxisStepDistance, event->time, delta); } return; } uint32_t button = 0; switch (event->detail) { case XCB_BUTTON_INDEX_1: button = BTN_LEFT; break; case XCB_BUTTON_INDEX_2: button = BTN_MIDDLE; break; case XCB_BUTTON_INDEX_3: button = BTN_RIGHT; break; default: button = event->detail + BTN_LEFT - 1; return; } - pointerMotion(QPointF(event->root_x - (*it).xPosition.x() + (*it).internalPosition.x(), - event->root_y - (*it).xPosition.y() + (*it).internalPosition.y()) / it->scale, - event->time); + pointerMotion(QPointF(event->root_x - (*it)->hostPosition().x() + (*it)->internalPosition().x(), + event->root_y - (*it)->hostPosition().y() + (*it)->internalPosition().y()) / (*it)->scale(), + event->time); if (pressed) { pointerButtonPressed(button, event->time); } else { pointerButtonReleased(button, event->time); } } void X11WindowedBackend::handleExpose(xcb_expose_event_t *event) { repaint(QRect(event->x, event->y, event->width, event->height)); } void X11WindowedBackend::updateSize(xcb_configure_notify_event_t *event) { - auto it = std::find_if(m_windows.begin(), m_windows.end(), [event] (const Output &o) { return o.window == event->window; }); - if (it == m_windows.end()) { + auto it = std::find_if(m_outputs.begin(), m_outputs.end(), + [event] (X11WindowedOutput *output) { return output->window() == event->window; } + ); + if (it == m_outputs.end()) { return; } - (*it).xPosition = QPoint(event->x, event->y); - QSize s = QSize(event->width, event->height); - if (s != (*it).size) { - (*it).size = s; - int x = (*it).internalPosition.x() + (*it).size.width() / (*it).scale; - it++; - for (; it != m_windows.end(); ++it) { - (*it).internalPosition.setX(x); - x += (*it).size.width() / (*it).scale; - } - emit sizeChanged(); + + (*it)->setHostPosition(QPoint(event->x, event->y)); + + const QSize s = QSize(event->width, event->height); + if (s != (*it)->pixelSize()) { + (*it)->setGeometry((*it)->internalPosition(), s); } + emit sizeChanged(); } void X11WindowedBackend::createCursor(const QImage &srcImage, const QPoint &hotspot) { const xcb_pixmap_t pix = xcb_generate_id(m_connection); const xcb_gcontext_t gc = xcb_generate_id(m_connection); const xcb_cursor_t cid = xcb_generate_id(m_connection); //right now on X we only have one scale between all screens, and we know we will have at least one screen const qreal outputScale = screenScales().first(); const QSize targetSize = srcImage.size() * outputScale / srcImage.devicePixelRatio(); const QImage img = srcImage.scaled(targetSize, Qt::KeepAspectRatio); xcb_create_pixmap(m_connection, 32, pix, m_screen->root, img.width(), img.height()); xcb_create_gc(m_connection, gc, pix, 0, nullptr); xcb_put_image(m_connection, XCB_IMAGE_FORMAT_Z_PIXMAP, pix, gc, img.width(), img.height(), 0, 0, 0, 32, img.byteCount(), img.constBits()); XRenderPicture pic(pix, 32); xcb_render_create_cursor(m_connection, cid, pic, qRound(hotspot.x() * outputScale), qRound(hotspot.y() * outputScale)); - for (auto it = m_windows.constBegin(); it != m_windows.constEnd(); ++it) { - xcb_change_window_attributes(m_connection, (*it).window, XCB_CW_CURSOR, &cid); + for (auto it = m_outputs.constBegin(); it != m_outputs.constEnd(); ++it) { + xcb_change_window_attributes(m_connection, (*it)->window(), XCB_CW_CURSOR, &cid); } xcb_free_pixmap(m_connection, pix); xcb_free_gc(m_connection, gc); if (m_cursor) { xcb_free_cursor(m_connection, m_cursor); } m_cursor = cid; xcb_flush(m_connection); markCursorAsRendered(); } xcb_window_t X11WindowedBackend::rootWindow() const { if (!m_screen) { return XCB_WINDOW_NONE; } return m_screen->root; } Screens *X11WindowedBackend::createScreens(QObject *parent) { - return new BasicScreens(this, parent); + return new OutputScreens(this, parent); } OpenGLBackend *X11WindowedBackend::createOpenGLBackend() { return new EglX11Backend(this); } QPainterBackend *X11WindowedBackend::createQPainterBackend() { return new X11WindowedQPainterBackend(this); } void X11WindowedBackend::warpPointer(const QPointF &globalPos) { - const xcb_window_t w = m_windows.at(0).window; + const xcb_window_t w = m_outputs.at(0)->window(); xcb_warp_pointer(m_connection, w, w, 0, 0, 0, 0, globalPos.x(), globalPos.y()); xcb_flush(m_connection); } xcb_window_t X11WindowedBackend::windowForScreen(int screen) const { - if (screen > m_windows.count()) { + if (screen > m_outputs.count()) { return XCB_WINDOW_NONE; } - return m_windows.at(screen).window; + return m_outputs.at(screen)->window(); } -QVector X11WindowedBackend::screenGeometries() const +Outputs X11WindowedBackend::outputs() const { - QVector ret; - for (auto it = m_windows.constBegin(); it != m_windows.constEnd(); ++it) { - ret << QRect((*it).internalPosition, (*it).size / (*it).scale); - } - return ret; + return m_outputs; } -QVector X11WindowedBackend::screenScales() const +Outputs X11WindowedBackend::enabledOutputs() const { - QVector ret; - for (auto it = m_windows.constBegin(); it != m_windows.constEnd(); ++it) { - ret << (*it).scale; - } - return ret; + return m_outputs; } } diff --git a/plugins/platforms/x11/windowed/x11windowed_backend.h b/plugins/platforms/x11/windowed/x11windowed_backend.h index b1df54573..9694b9b72 100644 --- a/plugins/platforms/x11/windowed/x11windowed_backend.h +++ b/plugins/platforms/x11/windowed/x11windowed_backend.h @@ -1,123 +1,124 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2015 Martin Gräßlin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #ifndef KWIN_X11WINDOWED_BACKEND_H #define KWIN_X11WINDOWED_BACKEND_H #include "platform.h" #include #include #include #include struct _XDisplay; typedef struct _XDisplay Display; typedef struct _XCBKeySymbols xcb_key_symbols_t; class NETWinInfo; namespace KWin { +class X11WindowedOutput; class KWIN_EXPORT X11WindowedBackend : public Platform { Q_OBJECT Q_INTERFACES(KWin::Platform) Q_PLUGIN_METADATA(IID "org.kde.kwin.Platform" FILE "x11.json") Q_PROPERTY(QSize size READ screenSize NOTIFY sizeChanged) public: X11WindowedBackend(QObject *parent = nullptr); virtual ~X11WindowedBackend(); void init() override; - QVector screenGeometries() const override; - QVector screenScales() const override; xcb_connection_t *connection() const { return m_connection; } + xcb_screen_t *screen() const { + return m_screen; + } int screenNumer() const { return m_screenNumber; } xcb_window_t window() const { return windowForScreen(0); } xcb_window_t windowForScreen(int screen) const; Display *display() const { return m_display; } xcb_window_t rootWindow() const; + bool hasXInput() const { + return m_hasXInput; + } Screens *createScreens(QObject *parent = nullptr) override; OpenGLBackend *createOpenGLBackend() override; QPainterBackend* createQPainterBackend() override; void warpPointer(const QPointF &globalPos) override; QVector supportedCompositors() const override { if (selectedCompositor() != NoCompositing) { return {selectedCompositor()}; } return QVector{OpenGLCompositing, QPainterCompositing}; } + Outputs outputs() const override; + Outputs enabledOutputs() const override; + Q_SIGNALS: void sizeChanged(); private: - void createWindow(); + void createOutputs(); void startEventReading(); void grabKeyboard(xcb_timestamp_t time); void updateWindowTitle(); void handleEvent(xcb_generic_event_t *event); void handleClientMessage(xcb_client_message_event_t *event); void handleButtonPress(xcb_button_press_event_t *event); void handleExpose(xcb_expose_event_t *event); void updateSize(xcb_configure_notify_event_t *event); void createCursor(const QImage &img, const QPoint &hotspot); - void initXInputForWindow(xcb_window_t window); void initXInput(); xcb_connection_t *m_connection = nullptr; xcb_screen_t *m_screen = nullptr; xcb_key_symbols_t *m_keySymbols = nullptr; int m_screenNumber = 0; - struct Output { - xcb_window_t window = XCB_WINDOW_NONE; - QSize size; - qreal scale = 1; - QPoint xPosition; - QPoint internalPosition; - NETWinInfo *winInfo = nullptr; - }; - QVector m_windows; + xcb_atom_t m_protocols = XCB_ATOM_NONE; xcb_atom_t m_deleteWindowProtocol = XCB_ATOM_NONE; xcb_cursor_t m_cursor = XCB_CURSOR_NONE; Display *m_display = nullptr; bool m_keyboardGrabbed = false; bool m_hasXInput = false; int m_xiOpcode = 0; int m_majorVersion = 0; int m_minorVersion = 0; + + QVector m_outputs; }; } #endif diff --git a/plugins/platforms/x11/windowed/x11windowed_output.cpp b/plugins/platforms/x11/windowed/x11windowed_output.cpp new file mode 100644 index 000000000..cbcbd65a5 --- /dev/null +++ b/plugins/platforms/x11/windowed/x11windowed_output.cpp @@ -0,0 +1,156 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright 2019 Roman Gilg + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*********************************************************************/ +#include "x11windowed_output.h" + +#include "x11windowed_backend.h" + +#include + +#if HAVE_X11_XINPUT +#include +#endif + +#include + +namespace KWin +{ + +X11WindowedOutput::X11WindowedOutput(X11WindowedBackend *backend) + : AbstractOutput(backend) + , m_backend(backend) +{ + m_window = xcb_generate_id(m_backend->connection()); +} + +X11WindowedOutput::~X11WindowedOutput() +{ + xcb_unmap_window(m_backend->connection(), m_window); + xcb_destroy_window(m_backend->connection(), m_window); + delete m_winInfo; + xcb_flush(m_backend->connection()); +} + +void X11WindowedOutput::init() +{ + uint32_t mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK; + const uint32_t values[] = { + m_backend->screen()->black_pixel, + XCB_EVENT_MASK_KEY_PRESS | + XCB_EVENT_MASK_KEY_RELEASE | + 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 | + XCB_EVENT_MASK_STRUCTURE_NOTIFY | + XCB_EVENT_MASK_EXPOSURE + }; + xcb_create_window(m_backend->connection(), + XCB_COPY_FROM_PARENT, + m_window, + m_backend->screen()->root, + 0, 0, + pixelSize().width(), pixelSize().height(), + 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, + mask, values); + + // select xinput 2 events + initXInputForWindow(); + + m_winInfo = new NETWinInfo(m_backend->connection(), + m_window, + m_backend->screen()->root, + NET::WMWindowType, NET::Properties2()); + + m_winInfo->setWindowType(NET::Normal); + m_winInfo->setPid(QCoreApplication::applicationPid()); + QIcon windowIcon = QIcon::fromTheme(QStringLiteral("kwin")); + auto addIcon = [&windowIcon, this] (const QSize &size) { + if (windowIcon.actualSize(size) != size) { + return; + } + NETIcon icon; + icon.data = windowIcon.pixmap(size).toImage().bits(); + icon.size.width = size.width(); + icon.size.height = size.height(); + m_winInfo->setIcon(icon, false); + }; + addIcon(QSize(16, 16)); + addIcon(QSize(32, 32)); + addIcon(QSize(48, 48)); + + xcb_map_window(m_backend->connection(), m_window); +} + +void X11WindowedOutput::initXInputForWindow() +{ + if (!m_backend->hasXInput()) { + return; + } +#if HAVE_X11_XINPUT + XIEventMask evmasks[1]; + unsigned char mask1[XIMaskLen(XI_LASTEVENT)]; + + memset(mask1, 0, sizeof(mask1)); + XISetMask(mask1, XI_TouchBegin); + XISetMask(mask1, XI_TouchUpdate); + XISetMask(mask1, XI_TouchOwnership); + XISetMask(mask1, XI_TouchEnd); + evmasks[0].deviceid = XIAllMasterDevices; + evmasks[0].mask_len = sizeof(mask1); + evmasks[0].mask = mask1; + XISelectEvents(m_backend->display(), m_window, evmasks, 1); +#else + Q_UNUSED(window) +#endif +} + +QSize X11WindowedOutput::pixelSize() const +{ + return m_pixelSize; +} + +void X11WindowedOutput::setGeometry(const QPoint &logicalPosition, const QSize &pixelSize) +{ + m_pixelSize = pixelSize; + + // Physicial size must be adjusted, such that QPA calculates correct sizes of + // internal elements. + setRawPhysicalSize(pixelSize / 96.0 * 25.4 / scale()); + + setGlobalPos(logicalPosition); +} + +void X11WindowedOutput::setWindowTitle(const QString &title) +{ + m_winInfo->setName(title.toUtf8().constData()); +} + +QPoint X11WindowedOutput::internalPosition() const +{ + return geometry().topLeft(); +} + +void X11WindowedOutput::setHostPosition(const QPoint &pos) +{ + m_hostPosition = pos; +} + +} diff --git a/plugins/platforms/x11/windowed/x11windowed_output.h b/plugins/platforms/x11/windowed/x11windowed_output.h new file mode 100644 index 000000000..06390c2b5 --- /dev/null +++ b/plugins/platforms/x11/windowed/x11windowed_output.h @@ -0,0 +1,85 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright 2019 Roman Gilg + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*********************************************************************/ +#ifndef KWIN_X11WINDOWED_OUTPUT_H +#define KWIN_X11WINDOWED_OUTPUT_H + +#include "abstract_output.h" +#include + +#include +#include +#include + +#include + +class NETWinInfo; + +namespace KWin +{ +class X11WindowedBackend; + +/** + * Wayland outputs in a nested X11 setup + **/ +class KWIN_EXPORT X11WindowedOutput : public AbstractOutput +{ + Q_OBJECT +public: + explicit X11WindowedOutput(X11WindowedBackend *backend); + ~X11WindowedOutput() override; + + void init(); + + xcb_window_t window() const { + return m_window; + } + + QPoint internalPosition() const; + QPoint hostPosition() const { + return m_hostPosition; + } + void setHostPosition(const QPoint &pos); + + void setWindowTitle(const QString &title); + + QSize pixelSize() const override; + + /** + * @brief defines the geometry of the output + * @param logicalPosition top left position of the output in compositor space + * @param pixelSize output size as seen from the outside + */ + void setGeometry(const QPoint &logicalPosition, const QSize &pixelSize); + +private: + void initXInputForWindow(); + + xcb_window_t m_window = XCB_WINDOW_NONE; + NETWinInfo *m_winInfo = nullptr; + + QPoint m_hostPosition; + QSize m_pixelSize; + + X11WindowedBackend *m_backend; +}; + +} + +#endif