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;