From 7939352fb7629da4e51d9d6e76ca3c8149002431 Mon Sep 17 00:00:00 2001 From: Vlad Zahorodnii Date: Fri, 2 Dec 2022 15:19:48 +0200 Subject: [PATCH] backends/x11: Provide a cursor per output The main motivation behind this change is to allow setting the cursor per output, which eases up implementing things such as cursor output layers. It also has another advantage - output related code is more encapsulated. Furthermore, we could decouple output backends from Cursor. --- .../x11/windowed/x11_windowed_backend.cpp | 50 -------------- .../x11/windowed/x11_windowed_backend.h | 2 - .../x11/windowed/x11_windowed_output.cpp | 65 +++++++++++++++++++ .../x11/windowed/x11_windowed_output.h | 19 ++++++ 4 files changed, 84 insertions(+), 52 deletions(-) diff --git a/src/backends/x11/windowed/x11_windowed_backend.cpp b/src/backends/x11/windowed/x11_windowed_backend.cpp index 410562fa0b..90c1987fe7 100644 --- a/src/backends/x11/windowed/x11_windowed_backend.cpp +++ b/src/backends/x11/windowed/x11_windowed_backend.cpp @@ -17,7 +17,6 @@ #include "x11_windowed_logging.h" #include "x11_windowed_output.h" #include "x11_windowed_qpainter_backend.h" -#include #include // KDE #include @@ -171,9 +170,6 @@ X11WindowedBackend::~X11WindowedBackend() if (m_keySymbols) { xcb_key_symbols_free(m_keySymbols); } - if (m_cursor) { - xcb_free_cursor(m_connection, m_cursor); - } xcb_disconnect(m_connection); } } @@ -203,10 +199,6 @@ bool X11WindowedBackend::initialize() XRenderUtils::init(m_connection, m_screen->root); createOutputs(); connect(kwinApp(), &Application::workspaceCreated, this, &X11WindowedBackend::startEventReading); - connect(Cursors::self(), &Cursors::currentCursorChanged, this, [this]() { - KWin::Cursor *c = KWin::Cursors::self()->currentCursor(); - createCursor(c->image(), c->hotspot()); - }); m_pointerDevice = std::make_unique(); m_pointerDevice->setPointer(true); m_keyboardDevice = std::make_unique(); @@ -573,48 +565,6 @@ void X11WindowedBackend::updateSize(xcb_configure_notify_event_t *event) Q_EMIT sizeChanged(); } -void X11WindowedBackend::createCursor(const QImage &srcImage, const QPoint &hotspot) -{ - xcb_pixmap_t pix = XCB_PIXMAP_NONE; - xcb_gcontext_t gc = XCB_NONE; - xcb_cursor_t cid = XCB_CURSOR_NONE; - - if (!srcImage.isNull()) { - pix = xcb_generate_id(m_connection); - gc = xcb_generate_id(m_connection); - 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 = 1; - 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.sizeInBytes(), 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_outputs.constBegin(); it != m_outputs.constEnd(); ++it) { - xcb_change_window_attributes(m_connection, (*it)->window(), XCB_CW_CURSOR, &cid); - } - - if (pix) { - xcb_free_pixmap(m_connection, pix); - } - if (gc) { - xcb_free_gc(m_connection, gc); - } - if (m_cursor) { - xcb_free_cursor(m_connection, m_cursor); - } - m_cursor = cid; - xcb_flush(m_connection); -} - xcb_window_t X11WindowedBackend::rootWindow() const { if (!m_screen) { diff --git a/src/backends/x11/windowed/x11_windowed_backend.h b/src/backends/x11/windowed/x11_windowed_backend.h index ed1898b751..b4116f63ea 100644 --- a/src/backends/x11/windowed/x11_windowed_backend.h +++ b/src/backends/x11/windowed/x11_windowed_backend.h @@ -150,7 +150,6 @@ private: 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 initXInput(); X11WindowedOutput *findOutput(xcb_window_t window) const; @@ -166,7 +165,6 @@ private: 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; std::unique_ptr m_eventNotifier; diff --git a/src/backends/x11/windowed/x11_windowed_output.cpp b/src/backends/x11/windowed/x11_windowed_output.cpp index 18253e1425..1bf3a34536 100644 --- a/src/backends/x11/windowed/x11_windowed_output.cpp +++ b/src/backends/x11/windowed/x11_windowed_output.cpp @@ -7,10 +7,12 @@ SPDX-License-Identifier: GPL-2.0-or-later */ #include "x11_windowed_output.h" +#include "../common/kwinxrenderutils.h" #include #include "core/renderloop_p.h" +#include "cursor.h" #include "softwarevsyncmonitor.h" #include "x11_windowed_backend.h" @@ -25,6 +27,63 @@ namespace KWin { +X11WindowedCursor::X11WindowedCursor(X11WindowedOutput *output) + : m_output(output) +{ +} + +X11WindowedCursor::~X11WindowedCursor() +{ + if (m_handle != XCB_CURSOR_NONE) { + xcb_free_cursor(m_output->backend()->connection(), m_handle); + m_handle = XCB_CURSOR_NONE; + } +} + +void X11WindowedCursor::update(const QImage &image, const QPoint &hotspot) +{ + X11WindowedBackend *backend = m_output->backend(); + + xcb_connection_t *connection = backend->connection(); + xcb_pixmap_t pix = XCB_PIXMAP_NONE; + xcb_gcontext_t gc = XCB_NONE; + xcb_cursor_t cid = XCB_CURSOR_NONE; + + if (!image.isNull()) { + pix = xcb_generate_id(connection); + gc = xcb_generate_id(connection); + cid = xcb_generate_id(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 = 1; + const QSize targetSize = image.size() * outputScale / image.devicePixelRatio(); + const QImage img = image.scaled(targetSize, Qt::KeepAspectRatio); + + xcb_create_pixmap(connection, 32, pix, backend->screen()->root, img.width(), img.height()); + xcb_create_gc(connection, gc, pix, 0, nullptr); + + xcb_put_image(connection, XCB_IMAGE_FORMAT_Z_PIXMAP, pix, gc, img.width(), img.height(), 0, 0, 0, 32, img.sizeInBytes(), img.constBits()); + + XRenderPicture pic(pix, 32); + xcb_render_create_cursor(connection, cid, pic, qRound(hotspot.x() * outputScale), qRound(hotspot.y() * outputScale)); + } + + xcb_change_window_attributes(connection, m_output->window(), XCB_CW_CURSOR, &cid); + + if (pix) { + xcb_free_pixmap(connection, pix); + } + if (gc) { + xcb_free_gc(connection, gc); + } + + if (m_handle) { + xcb_free_cursor(connection, m_handle); + } + m_handle = cid; + xcb_flush(connection); +} + X11WindowedOutput::X11WindowedOutput(X11WindowedBackend *backend) : Output(backend) , m_renderLoop(std::make_unique()) @@ -138,6 +197,12 @@ void X11WindowedOutput::init(const QSize &pixelSize, qreal scale) addIcon(QSize(32, 32)); addIcon(QSize(48, 48)); + m_cursor = std::make_unique(this); + connect(Cursors::self(), &Cursors::currentCursorChanged, this, [this]() { + KWin::Cursor *c = KWin::Cursors::self()->currentCursor(); + m_cursor->update(c->image(), c->hotspot()); + }); + xcb_map_window(m_backend->connection(), m_window); } diff --git a/src/backends/x11/windowed/x11_windowed_output.h b/src/backends/x11/windowed/x11_windowed_output.h index 1ad22b8927..72d7c085fb 100644 --- a/src/backends/x11/windowed/x11_windowed_output.h +++ b/src/backends/x11/windowed/x11_windowed_output.h @@ -24,6 +24,20 @@ namespace KWin class SoftwareVsyncMonitor; class X11WindowedBackend; +class X11WindowedOutput; + +class X11WindowedCursor +{ +public: + explicit X11WindowedCursor(X11WindowedOutput *output); + ~X11WindowedCursor(); + + void update(const QImage &image, const QPoint &hotspot); + +private: + X11WindowedOutput *m_output; + xcb_cursor_t m_handle = XCB_CURSOR_NONE; +}; /** * Wayland outputs in a nested X11 setup @@ -41,6 +55,10 @@ public: void init(const QSize &pixelSize, qreal scale); void resize(const QSize &pixelSize); + X11WindowedBackend *backend() const + { + return m_backend; + } xcb_window_t window() const { return m_window; @@ -76,6 +94,7 @@ private: std::unique_ptr m_winInfo; std::unique_ptr m_renderLoop; std::unique_ptr m_vsyncMonitor; + std::unique_ptr m_cursor; QPoint m_hostPosition; QRegion m_exposedArea;