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.
This commit is contained in:
Vlad Zahorodnii 2022-12-02 15:19:48 +02:00 committed by Xaver Hugl
parent 6e6caa1021
commit 7939352fb7
4 changed files with 84 additions and 52 deletions

View file

@ -17,7 +17,6 @@
#include "x11_windowed_logging.h"
#include "x11_windowed_output.h"
#include "x11_windowed_qpainter_backend.h"
#include <cursor.h>
#include <pointer_input.h>
// KDE
#include <KLocalizedString>
@ -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<X11WindowedInputDevice>();
m_pointerDevice->setPointer(true);
m_keyboardDevice = std::make_unique<X11WindowedInputDevice>();
@ -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) {

View file

@ -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<QSocketNotifier> m_eventNotifier;

View file

@ -7,10 +7,12 @@
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "x11_windowed_output.h"
#include "../common/kwinxrenderutils.h"
#include <config-kwin.h>
#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<RenderLoop>())
@ -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<X11WindowedCursor>(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);
}

View file

@ -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<NETWinInfo> m_winInfo;
std::unique_ptr<RenderLoop> m_renderLoop;
std::unique_ptr<SoftwareVsyncMonitor> m_vsyncMonitor;
std::unique_ptr<X11WindowedCursor> m_cursor;
QPoint m_hostPosition;
QRegion m_exposedArea;