diff --git a/CMakeLists.txt b/CMakeLists.txt index ac0f92e988..f64e9a2ebc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -415,8 +415,10 @@ if(HAVE_WAYLAND) set(kwin_KDEINIT_SRCS ${kwin_KDEINIT_SRCS} screens_wayland.cpp + screens_x11windowed.cpp wayland_backend.cpp wayland_server.cpp + x11windowed_backend.cpp ) if(KWIN_HAVE_EGL AND Wayland_Egl_FOUND) set(kwin_KDEINIT_SRCS ${kwin_KDEINIT_SRCS} egl_wayland_backend.cpp) diff --git a/composite.cpp b/composite.cpp index f0d41aed35..b3d7439718 100644 --- a/composite.cpp +++ b/composite.cpp @@ -118,8 +118,12 @@ Compositor::Compositor(QObject* workspace) connect(&m_unusedSupportPropertyTimer, SIGNAL(timeout()), SLOT(deleteUnusedSupportProperties())); #if HAVE_WAYLAND if (kwinApp()->operationMode() != Application::OperationModeX11) { - connect(Wayland::WaylandBackend::self(), &Wayland::WaylandBackend::systemCompositorDied, this, &Compositor::finish); - connect(Wayland::WaylandBackend::self(), &Wayland::WaylandBackend::backendReady, this, &Compositor::setup); + if (Wayland::WaylandBackend::self()) { + connect(Wayland::WaylandBackend::self(), &Wayland::WaylandBackend::systemCompositorDied, this, &Compositor::finish); + connect(Wayland::WaylandBackend::self(), &Wayland::WaylandBackend::backendReady, this, &Compositor::setup); + } else { + QMetaObject::invokeMethod(this, "setup", Qt::QueuedConnection); + } } else #endif diff --git a/input.cpp b/input.cpp index 8aa4b99996..1339405ed7 100644 --- a/input.cpp +++ b/input.cpp @@ -35,6 +35,7 @@ along with this program. If not, see . #if HAVE_WAYLAND #include "wayland_backend.h" #include "wayland_server.h" +#include "x11windowed_backend.h" #include #endif // Qt @@ -255,6 +256,25 @@ static KWayland::Server::SeatInterface *findSeat() } #endif +template +static +void disconnectSeat(KWayland::Server::SeatInterface *seat) +{ + if (T *w = T::self()) { + QObject::disconnect(seat->focusedPointer()->cursor(), &KWayland::Server::Cursor::changed, w, &T::installCursorFromServer); + } +} + +template +static +void connectSeat(KWayland::Server::SeatInterface *seat) +{ + if (T *w = T::self()) { + w->installCursorFromServer(); + QObject::connect(seat->focusedPointer()->cursor(), &KWayland::Server::Cursor::changed, w, &T::installCursorFromServer); + } +} + void InputRedirection::updatePointerWindow() { // TODO: handle pointer grab aka popups @@ -268,17 +288,14 @@ void InputRedirection::updatePointerWindow() // disconnect old surface if (oldWindow) { disconnect(oldWindow.data(), &Toplevel::geometryChanged, this, &InputRedirection::updateFocusedPointerPosition); - if (Wayland::WaylandBackend *w = Wayland::WaylandBackend::self()) { - disconnect(seat->focusedPointer()->cursor(), &KWayland::Server::Cursor::changed, w, &Wayland::WaylandBackend::installCursorFromServer); - } + disconnectSeat(seat); + disconnectSeat(seat); } if (t && t->surface()) { seat->setFocusedPointerSurface(t->surface(), t->pos()); connect(t, &Toplevel::geometryChanged, this, &InputRedirection::updateFocusedPointerPosition); - if (Wayland::WaylandBackend *w = Wayland::WaylandBackend::self()) { - w->installCursorFromServer(); - connect(seat->focusedPointer()->cursor(), &KWayland::Server::Cursor::changed, w, &Wayland::WaylandBackend::installCursorFromServer); - } + connectSeat(seat); + connectSeat(seat); } else { seat->setFocusedPointerSurface(nullptr); t = nullptr; diff --git a/main_wayland.cpp b/main_wayland.cpp index 0e3ee99043..786d022e34 100644 --- a/main_wayland.cpp +++ b/main_wayland.cpp @@ -24,9 +24,11 @@ along with this program. If not, see . #include "wayland_backend.h" #include "wayland_server.h" #include "xcbutils.h" +#include "x11windowed_backend.h" // KWayland #include +#include // KDE #include // Qt @@ -86,19 +88,26 @@ void ApplicationWayland::performStartup() // try creating the Wayland Backend createInput(); - Wayland::WaylandBackend *backend = Wayland::WaylandBackend::create(); - connect(backend, &Wayland::WaylandBackend::connectionFailed, this, - [] () { - fputs(i18n("kwin_wayland: could not connect to Wayland Server, ensure WAYLAND_DISPLAY is set.\n").toLocal8Bit().constData(), stderr); - ::exit(1); - } - ); - connect(backend, &Wayland::WaylandBackend::outputsChanged, this, &ApplicationWayland::continueStartupWithScreens); + if (!X11WindowedBackend::self()) { + // only create WaylandBackend if we do not use X11WindowedBackend + Wayland::WaylandBackend *backend = Wayland::WaylandBackend::create(); + connect(backend, &Wayland::WaylandBackend::connectionFailed, this, + [] () { + fputs(i18n("kwin_wayland: could not connect to Wayland Server, ensure WAYLAND_DISPLAY is set.\n").toLocal8Bit().constData(), stderr); + ::exit(1); + } + ); + connect(backend, &Wayland::WaylandBackend::outputsChanged, this, &ApplicationWayland::continueStartupWithScreens); + } else { + continueStartupWithScreens(); + } } void ApplicationWayland::continueStartupWithScreens() { - disconnect(Wayland::WaylandBackend::self(), &Wayland::WaylandBackend::outputsChanged, this, &ApplicationWayland::continueStartupWithScreens); + if (Wayland::WaylandBackend::self()) { + disconnect(Wayland::WaylandBackend::self(), &Wayland::WaylandBackend::outputsChanged, this, &ApplicationWayland::continueStartupWithScreens); + } createScreens(); waylandServer()->initOutputs(); @@ -338,11 +347,28 @@ KWIN_EXPORT int kdemain(int argc, char * argv[]) QCommandLineOption waylandSocketOption(QStringList{QStringLiteral("s"), QStringLiteral("socket")}, i18n("Name of the Wayland socket to listen on. If not set \"wayland-0\" is used."), QStringLiteral("socket")); + QCommandLineOption windowedOption(QStringLiteral("windowed"), + i18n("Use a nested compositor in windowed mode.")); + QCommandLineOption x11DisplayOption(QStringLiteral("x11-display"), + i18n("The X11 Display to use in windowed mode on platform X11."), + QStringLiteral("display")); + QCommandLineOption widthOption(QStringLiteral("width"), + i18n("The width for windowed mode. Default width is 1024."), + QStringLiteral("width")); + widthOption.setDefaultValue(QString::number(1024)); + QCommandLineOption heightOption(QStringLiteral("height"), + i18n("The height for windowed mode. Default height is 768."), + QStringLiteral("height")); + heightOption.setDefaultValue(QString::number(768)); QCommandLineParser parser; a.setupCommandLine(&parser); parser.addOption(xwaylandOption); parser.addOption(waylandSocketOption); + parser.addOption(windowedOption); + parser.addOption(x11DisplayOption); + parser.addOption(widthOption); + parser.addOption(heightOption); #if HAVE_INPUT QCommandLineOption libinputOption(QStringLiteral("libinput"), i18n("Enable libinput support for input events processing. Note: never use in a nested session.")); @@ -356,6 +382,27 @@ KWIN_EXPORT int kdemain(int argc, char * argv[]) KWin::Application::setUseLibinput(parser.isSet(libinputOption)); #endif + if (parser.isSet(windowedOption)) { + bool ok = false; + const int width = parser.value(widthOption).toInt(&ok); + if (!ok) { + std::cerr << "FATAL ERROR incorrect value for width" << std::endl; + return 1; + } + const int height = parser.value(heightOption).toInt(&ok); + if (!ok) { + std::cerr << "FATAL ERROR incorrect value for height" << std::endl; + return 1; + } + KWin::X11WindowedBackend *x11Backend = KWin::X11WindowedBackend::create(parser.value(x11DisplayOption), QSize(width, height), &a); + if (!x11Backend->isValid()) { + std::cerr << "FATAL ERROR failed to connet to X Server" << std::endl; + return 1; + } + server->seat()->setHasPointer(true); + server->seat()->setHasKeyboard(true); + } + a.setStartXwayland(parser.isSet(xwaylandOption)); a.start(); diff --git a/scene_qpainter.cpp b/scene_qpainter.cpp index e6001dcbf0..3aea1deeae 100644 --- a/scene_qpainter.cpp +++ b/scene_qpainter.cpp @@ -27,6 +27,7 @@ along with this program. If not, see . #include "toplevel.h" #if HAVE_WAYLAND #include "wayland_backend.h" +#include "x11windowed_backend.h" #include #include #include @@ -182,6 +183,66 @@ bool WaylandQPainterBackend::needsFullRepaint() const return m_needsFullRepaint; } +//**************************************** +// X11WindowedBackend +//**************************************** +X11WindowedQPainterBackend::X11WindowedQPainterBackend() + : QPainterBackend() + , m_backBuffer(X11WindowedBackend::self()->size(), QImage::Format_RGB32) +{ +} + +X11WindowedQPainterBackend::~X11WindowedQPainterBackend() +{ + if (m_gc) { + xcb_free_gc(X11WindowedBackend::self()->connection(), m_gc); + } +} + +QImage *X11WindowedQPainterBackend::buffer() +{ + return &m_backBuffer; +} + +bool X11WindowedQPainterBackend::needsFullRepaint() const +{ + return m_needsFullRepaint; +} + +void X11WindowedQPainterBackend::prepareRenderingFrame() +{ +} + +void X11WindowedQPainterBackend::screenGeometryChanged(const QSize &size) +{ + if (m_backBuffer.size() != size) { + m_backBuffer = QImage(size, QImage::Format_RGB32); + m_backBuffer.fill(Qt::black); + m_needsFullRepaint = true; + } +} + +void X11WindowedQPainterBackend::present(int mask, const QRegion &damage) +{ + Q_UNUSED(mask) + Q_UNUSED(damage) + xcb_connection_t *c = X11WindowedBackend::self()->connection(); + const xcb_window_t window = X11WindowedBackend::self()->window(); + if (m_gc == XCB_NONE) { + m_gc = xcb_generate_id(c); + xcb_create_gc(c, m_gc, window, 0, nullptr); + } + // TODO: only update changes? + xcb_put_image(c, XCB_IMAGE_FORMAT_Z_PIXMAP, window, m_gc, + m_backBuffer.width(), m_backBuffer.height(), 0, 0, 0, 24, + m_backBuffer.byteCount(), m_backBuffer.constBits()); +} + +bool X11WindowedQPainterBackend::usesOverlayWindow() const +{ + return false; +} + #endif //**************************************** @@ -192,7 +253,11 @@ SceneQPainter *SceneQPainter::createScene(QObject *parent) QScopedPointer backend; #if HAVE_WAYLAND if (kwinApp()->shouldUseWaylandForCompositing()) { - backend.reset(new WaylandQPainterBackend); + if (X11WindowedBackend::self()) { + backend.reset(new X11WindowedQPainterBackend); + } else { + backend.reset(new WaylandQPainterBackend); + } if (backend->isFailed()) { return NULL; } @@ -280,6 +345,12 @@ Shadow *SceneQPainter::createShadow(Toplevel *toplevel) return new SceneQPainterShadow(toplevel); } +void SceneQPainter::screenGeometryChanged(const QSize &size) +{ + Scene::screenGeometryChanged(size); + m_backend->screenGeometryChanged(size); +} + //**************************************** // SceneQPainter::Window //**************************************** diff --git a/scene_qpainter.h b/scene_qpainter.h index ff8f8c839f..e80d599d1d 100644 --- a/scene_qpainter.h +++ b/scene_qpainter.h @@ -124,6 +124,26 @@ private: QImage m_backBuffer; QWeakPointer m_buffer; }; + +class X11WindowedQPainterBackend : public QObject, public QPainterBackend +{ + Q_OBJECT +public: + X11WindowedQPainterBackend(); + virtual ~X11WindowedQPainterBackend(); + + QImage *buffer() override; + bool needsFullRepaint() const override; + bool usesOverlayWindow() const override; + void prepareRenderingFrame() override; + void present(int mask, const QRegion &damage) override; + void screenGeometryChanged(const QSize &size); + +private: + bool m_needsFullRepaint = true; + xcb_gcontext_t m_gc = XCB_NONE; + QImage m_backBuffer; +}; #endif class SceneQPainter : public Scene @@ -141,6 +161,7 @@ public: virtual EffectFrame *createEffectFrame(EffectFrameImpl *frame) override; virtual Shadow *createShadow(Toplevel *toplevel) override; Decoration::Renderer *createDecorationRenderer(Decoration::DecoratedClientImpl *impl) override; + void screenGeometryChanged(const QSize &size) override; QPainter *painter(); diff --git a/screens.cpp b/screens.cpp index 9cc7ffd29b..2890832dbe 100644 --- a/screens.cpp +++ b/screens.cpp @@ -26,6 +26,9 @@ along with this program. If not, see . #include "screens_xrandr.h" #if HAVE_WAYLAND #include "screens_wayland.h" +#include "wayland_backend.h" +#include "x11windowed_backend.h" +#include "screens_x11windowed.h" #endif #ifdef KWIN_UNIT_TEST #include @@ -43,13 +46,18 @@ Screens *Screens::create(QObject *parent) #else #if HAVE_WAYLAND if (kwinApp()->shouldUseWaylandForCompositing()) { - s_self = new WaylandScreens(parent); + if (X11WindowedBackend::self()) { + s_self = new X11WindowedScreens(parent); + } else if (Wayland::WaylandBackend::self()) { + s_self = new WaylandScreens(parent); + } } #endif if (kwinApp()->operationMode() == Application::OperationModeX11) { s_self = new XRandRScreens(parent); } #endif + Q_ASSERT(s_self); s_self->init(); return s_self; } diff --git a/screens_x11windowed.cpp b/screens_x11windowed.cpp new file mode 100644 index 0000000000..8e8039c0bc --- /dev/null +++ b/screens_x11windowed.cpp @@ -0,0 +1,69 @@ +/******************************************************************** + 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 "screens_x11windowed.h" +#include "x11windowed_backend.h" + +namespace KWin +{ + +X11WindowedScreens::X11WindowedScreens(QObject *parent) + : Screens(parent) +{ +} + +X11WindowedScreens::~X11WindowedScreens() = default; + +void X11WindowedScreens::init() +{ + KWin::Screens::init(); + connect(X11WindowedBackend::self(), &X11WindowedBackend::sizeChanged, + this, &X11WindowedScreens::startChangedTimer); + updateCount(); + emit changed(); +} + +QRect X11WindowedScreens::geometry(int screen) const +{ + if (screen == 0) { + return QRect(QPoint(0, 0), size(screen)); + } + return QRect(); +} + +QSize X11WindowedScreens::size(int screen) const +{ + if (screen == 0) { + return X11WindowedBackend::self()->size(); + } + return QSize(); +} + +void X11WindowedScreens::updateCount() +{ + setCount(1); +} + +int X11WindowedScreens::number(const QPoint &pos) const +{ + Q_UNUSED(pos) + return 0; +} + +} diff --git a/screens_x11windowed.h b/screens_x11windowed.h new file mode 100644 index 0000000000..5b47d86771 --- /dev/null +++ b/screens_x11windowed.h @@ -0,0 +1,42 @@ +/******************************************************************** + 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_SCREENS_X11WINDOWED_H +#define KWIN_SCREENS_X11WINDOWED_H +#include "screens.h" + +namespace KWin +{ + +class X11WindowedScreens : public Screens +{ + Q_OBJECT +public: + X11WindowedScreens(QObject *parent = nullptr); + virtual ~X11WindowedScreens(); + void init() override; + QRect geometry(int screen) const override; + int number(const QPoint &pos) const override; + QSize size(int screen) const override; + void updateCount() override; +}; + +} + +#endif diff --git a/x11windowed_backend.cpp b/x11windowed_backend.cpp new file mode 100644 index 0000000000..3616b5992a --- /dev/null +++ b/x11windowed_backend.cpp @@ -0,0 +1,281 @@ +/******************************************************************** + 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 "composite.h" +#include "input.h" +#include "utils.h" +#include "wayland_server.h" +#include "xcbutils.h" +#include +#include +#include +#include +#include +// kwayland +#include +#include +#include +// system +#include + +namespace KWin +{ + +X11WindowedBackend *X11WindowedBackend::s_self = nullptr; + +X11WindowedBackend *X11WindowedBackend::create(const QString &display, const QSize &size, QObject *parent) +{ + Q_ASSERT(!s_self); + s_self = new X11WindowedBackend(display, size, parent); + return s_self; +} + +X11WindowedBackend::X11WindowedBackend(const QString &display, const QSize &size, QObject *parent) + : QObject(parent) + , m_size(size) +{ + int screen = 0; + auto c = xcb_connect(display.toUtf8().constData(), &screen); + if (!xcb_connection_has_error(c)) { + m_connection = c; + m_screenNumber = screen; + 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; + } + } + XRenderUtils::init(m_connection, m_screen->root); + createWindow(); + startEventReading(); + } +} + +X11WindowedBackend::~X11WindowedBackend() +{ + if (m_connection) { + if (m_window) { + xcb_unmap_window(m_connection, m_window); + xcb_destroy_window(m_connection, m_window); + } + if (m_cursor) { + xcb_free_cursor(m_connection, m_cursor); + } + xcb_disconnect(m_connection); + } +} + +void X11WindowedBackend::createWindow() +{ + Q_ASSERT(m_window == XCB_WINDOW_NONE); + Xcb::Atom protocolsAtom(QByteArrayLiteral("WM_PROTOCOLS"), false, m_connection); + Xcb::Atom deleteWindowAtom(QByteArrayLiteral("WM_DELETE_WINDOW"), false, m_connection); + m_window = xcb_generate_id(m_connection); + uint32_t mask = XCB_CW_EVENT_MASK; + const uint32_t values[] = { + 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_connection, XCB_COPY_FROM_PARENT, m_window, m_screen->root, + 0, 0, m_size.width(), m_size.height(), + 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, mask, values); + xcb_map_window(m_connection, m_window); + + m_protocols = protocolsAtom; + m_deleteWindowProtocol = deleteWindowAtom; + xcb_change_property(m_connection, XCB_PROP_MODE_REPLACE, m_window, m_protocols, XCB_ATOM_ATOM, 32, 1, &m_deleteWindowProtocol); + + xcb_flush(m_connection); +} + +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); +} + +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: + if (input()) { + auto event = reinterpret_cast(e); + input()->processPointerMotion(QPointF(event->event_x, event->event_y), event->time); + } + break; + case XCB_KEY_PRESS: + case XCB_KEY_RELEASE: + if (input()) { + auto event = reinterpret_cast(e); + input()->processKeyboardKey(event->detail - 8, eventType == XCB_KEY_PRESS ? InputRedirection::KeyboardKeyPressed : InputRedirection::KeyboardKeyReleased, event->time); + } + break; + case XCB_CONFIGURE_NOTIFY: + updateSize(reinterpret_cast(e)); + break; + case XCB_ENTER_NOTIFY: + if (input()) { + auto event = reinterpret_cast(e); + input()->processPointerMotion(QPointF(event->event_x, event->event_y), event->time); + } + break; + case XCB_CLIENT_MESSAGE: + handleClientMessage(reinterpret_cast(e)); + break; + case XCB_EXPOSE: + handleExpose(reinterpret_cast(e)); + break; + default: + break; + } +} + +void X11WindowedBackend::handleClientMessage(xcb_client_message_event_t *event) +{ + if (event->window != m_window) { + return; + } + if (event->type == m_protocols && m_protocols != XCB_ATOM_NONE) { + if (event->data.data32[0] == m_deleteWindowProtocol && m_deleteWindowProtocol != XCB_ATOM_NONE) { + qCDebug(KWIN_CORE) << "Backend window is going to be closed, shutting down."; + QCoreApplication::quit(); + } + } +} + +void X11WindowedBackend::handleButtonPress(xcb_button_press_event_t *event) +{ + if (!input()) { + 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; + InputRedirection::PointerAxis axis = (event->detail > 5) ? InputRedirection::PointerAxisHorizontal : InputRedirection::PointerAxisVertical; + static const qreal s_defaultAxisStepDistance = 10.0; + input()->processPointerAxis(axis, delta * s_defaultAxisStepDistance, event->time); + 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; + } + input()->processPointerMotion(QPointF(event->event_x, event->event_y), event->time); + input()->processPointerButton(button, pressed ? InputRedirection::PointerButtonPressed : InputRedirection::PointerButtonReleased, event->time); +} + +void X11WindowedBackend::handleExpose(xcb_expose_event_t *event) +{ + if (!Compositor::self()) { + return; + } + Compositor::self()->addRepaint(QRect(event->x, event->y, event->width, event->height)); +} + +void X11WindowedBackend::updateSize(xcb_configure_notify_event_t *event) +{ + if (event->window != m_window) { + return; + } + QSize s = QSize(event->width, event->height); + if (s != m_size) { + m_size = s; + emit sizeChanged(); + } +} + +void X11WindowedBackend::installCursorFromServer() +{ + if (!waylandServer() || !waylandServer()->seat()->focusedPointer()) { + return; + } + auto c = waylandServer()->seat()->focusedPointer()->cursor(); + if (c) { + auto cursorSurface = c->surface(); + if (!cursorSurface.isNull()) { + auto buffer = cursorSurface.data()->buffer(); + if (buffer) { + // TODO: cache generated cursors? + 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); + + xcb_create_pixmap(m_connection, 32, pix, m_screen->root, buffer->size().width(), buffer->size().height()); + xcb_create_gc(m_connection, gc, pix, 0, nullptr); + + const QImage img = buffer->data(); + 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, c->hotspot().x(), c->hotspot().y()); + xcb_change_window_attributes(m_connection, m_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); + return; + } + } + } + // TODO: unset cursor +} + +} diff --git a/x11windowed_backend.h b/x11windowed_backend.h new file mode 100644 index 0000000000..fc005b8ebf --- /dev/null +++ b/x11windowed_backend.h @@ -0,0 +1,94 @@ +/******************************************************************** + 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 + +#include +#include + +#include + +namespace KWin +{ + +class KWIN_EXPORT X11WindowedBackend : public QObject +{ + Q_OBJECT + Q_PROPERTY(QSize size READ size NOTIFY sizeChanged) +public: + virtual ~X11WindowedBackend(); + + xcb_connection_t *connection() const { + return m_connection; + } + int screenNumer() const { + return m_screenNumber; + } + xcb_window_t window() const { + return m_window; + } + + bool isValid() const { + return m_connection != nullptr && m_window != XCB_WINDOW_NONE; + } + + QSize size() const { + return m_size; + } + + void installCursorFromServer(); + + static X11WindowedBackend *self(); + static X11WindowedBackend *create(const QString &display, const QSize &size, QObject *parent); + +Q_SIGNALS: + void sizeChanged(); + +private: + X11WindowedBackend(const QString &display, const QSize &size, QObject *parent); + void createWindow(); + void startEventReading(); + 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); + + xcb_connection_t *m_connection = nullptr; + xcb_screen_t *m_screen = nullptr; + int m_screenNumber = 0; + xcb_window_t m_window = XCB_WINDOW_NONE; + QSize m_size; + xcb_atom_t m_protocols = XCB_ATOM_NONE; + xcb_atom_t m_deleteWindowProtocol = XCB_ATOM_NONE; + xcb_cursor_t m_cursor = XCB_CURSOR_NONE; + static X11WindowedBackend *s_self; +}; + +inline X11WindowedBackend *X11WindowedBackend::self() +{ + return s_self; +} + +} + +#endif