KWin
Loading...
Searching...
No Matches
x11_windowed_output.cpp
Go to the documentation of this file.
1/*
2 KWin - the KDE window manager
3 This file is part of the KDE project.
4
5 SPDX-FileCopyrightText: 2019 Roman Gilg <subdiff@gmail.com>
6
7 SPDX-License-Identifier: GPL-2.0-or-later
8*/
13
14#include <config-kwin.h>
15
16#include "compositor.h"
17#include "core/graphicsbuffer.h"
18#include "core/outputlayer.h"
19#include "core/renderbackend.h"
20#include "core/renderloop_p.h"
21
22#include <NETWM>
23
24#if HAVE_X11_XINPUT
25#include <X11/extensions/XInput2.h>
26#endif
27
28#include <QIcon>
29#include <QPainter>
30
31#include <drm_fourcc.h>
32#include <xcb/dri3.h>
33#include <xcb/shm.h>
34
35namespace KWin
36{
37
38X11WindowedBuffer::X11WindowedBuffer(X11WindowedOutput *output, xcb_pixmap_t pixmap, GraphicsBuffer *graphicsBuffer)
39 : m_output(output)
40 , m_buffer(graphicsBuffer)
41 , m_pixmap(pixmap)
42{
43 connect(graphicsBuffer, &GraphicsBuffer::destroyed, this, &X11WindowedBuffer::defunct);
44}
45
47{
48 m_buffer->disconnect(this);
49 xcb_free_pixmap(m_output->backend()->connection(), m_pixmap);
50 unlock();
51}
52
54{
55 return m_buffer;
56}
57
58xcb_pixmap_t X11WindowedBuffer::pixmap() const
59{
60 return m_pixmap;
61}
62
64{
65 if (!m_locked) {
66 m_locked = true;
67 m_buffer->ref();
68 }
69}
70
72{
73 if (m_locked) {
74 m_locked = false;
75 m_buffer->unref();
76 }
77}
78
80 : m_output(output)
81{
82}
83
85{
86 if (m_handle != XCB_CURSOR_NONE) {
87 xcb_free_cursor(m_output->backend()->connection(), m_handle);
88 m_handle = XCB_CURSOR_NONE;
89 }
90}
91
92void X11WindowedCursor::update(const QImage &image, const QPointF &hotspot)
93{
94 X11WindowedBackend *backend = m_output->backend();
95
96 xcb_connection_t *connection = backend->connection();
97 xcb_pixmap_t pix = XCB_PIXMAP_NONE;
98 xcb_gcontext_t gc = XCB_NONE;
99 xcb_cursor_t cid = XCB_CURSOR_NONE;
100
101 if (!image.isNull()) {
102 pix = xcb_generate_id(connection);
103 gc = xcb_generate_id(connection);
104 cid = xcb_generate_id(connection);
105
106 // right now on X we only have one scale between all screens, and we know we will have at least one screen
107 const qreal outputScale = 1;
108 const QSize targetSize = image.size() * outputScale / image.devicePixelRatio();
109 const QImage img = image.scaled(targetSize, Qt::KeepAspectRatio).convertedTo(QImage::Format_ARGB32_Premultiplied);
110
111 xcb_create_pixmap(connection, 32, pix, backend->screen()->root, img.width(), img.height());
112 xcb_create_gc(connection, gc, pix, 0, nullptr);
113
114 xcb_put_image(connection, XCB_IMAGE_FORMAT_Z_PIXMAP, pix, gc, img.width(), img.height(), 0, 0, 0, 32, img.sizeInBytes(), img.constBits());
115
116 XRenderPicture pic(pix, 32);
117 xcb_render_create_cursor(connection, cid, pic, qRound(hotspot.x() * outputScale), qRound(hotspot.y() * outputScale));
118 }
119
120 xcb_change_window_attributes(connection, m_output->window(), XCB_CW_CURSOR, &cid);
121
122 if (pix) {
123 xcb_free_pixmap(connection, pix);
124 }
125 if (gc) {
126 xcb_free_gc(connection, gc);
127 }
128
129 if (m_handle) {
130 xcb_free_cursor(connection, m_handle);
131 }
132 m_handle = cid;
133 xcb_flush(connection);
134}
135
137 : Output(backend)
138 , m_renderLoop(std::make_unique<RenderLoop>(this))
139 , m_backend(backend)
140{
141 m_window = xcb_generate_id(m_backend->connection());
142
143 static int identifier = -1;
144 identifier++;
146 .name = QStringLiteral("X11-%1").arg(identifier),
147 });
148}
149
151{
152 m_buffers.clear();
153
154 xcb_present_select_input(m_backend->connection(), m_presentEvent, m_window, 0);
155 xcb_unmap_window(m_backend->connection(), m_window);
156 xcb_destroy_window(m_backend->connection(), m_window);
157 xcb_flush(m_backend->connection());
158}
159
161{
162 return m_exposedArea;
163}
164
166{
167 m_exposedArea += rect;
168}
169
171{
172 m_exposedArea = QRegion();
173}
174
176{
177 return m_renderLoop.get();
178}
179
181{
182 return m_backend;
183}
184
186{
187 return m_cursor.get();
188}
189
190xcb_window_t X11WindowedOutput::window() const
191{
192 return m_window;
193}
194
196{
197 return m_backend->screen()->root_depth;
198}
199
201{
202 return m_hostPosition;
203}
204
205void X11WindowedOutput::init(const QSize &pixelSize, qreal scale)
206{
207 const int refreshRate = 60000; // TODO: get refresh rate via randr
208 m_renderLoop->setRefreshRate(refreshRate);
209
210 auto mode = std::make_shared<OutputMode>(pixelSize, m_renderLoop->refreshRate());
211
212 State initialState;
213 initialState.modes = {mode};
214 initialState.currentMode = mode;
215 initialState.scale = scale;
216 setState(initialState);
217
218 const uint32_t eventMask = XCB_EVENT_MASK_KEY_PRESS
219 | XCB_EVENT_MASK_KEY_RELEASE
220 | XCB_EVENT_MASK_BUTTON_PRESS
221 | XCB_EVENT_MASK_BUTTON_RELEASE
222 | XCB_EVENT_MASK_POINTER_MOTION
223 | XCB_EVENT_MASK_ENTER_WINDOW
224 | XCB_EVENT_MASK_LEAVE_WINDOW
225 | XCB_EVENT_MASK_STRUCTURE_NOTIFY
226 | XCB_EVENT_MASK_EXPOSURE;
227
228 const uint32_t values[] = {
229 m_backend->screen()->black_pixel,
230 eventMask,
231 };
232
233 uint32_t valueMask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
234
235 xcb_create_window(m_backend->connection(),
236 XCB_COPY_FROM_PARENT,
237 m_window,
238 m_backend->screen()->root,
239 0, 0,
240 pixelSize.width(), pixelSize.height(),
241 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT,
242 valueMask, values);
243
244 // select xinput 2 events
245 initXInputForWindow();
246
247 const uint32_t presentEventMask = XCB_PRESENT_EVENT_MASK_IDLE_NOTIFY | XCB_PRESENT_EVENT_MASK_COMPLETE_NOTIFY;
248 m_presentEvent = xcb_generate_id(m_backend->connection());
249 xcb_present_select_input(m_backend->connection(), m_presentEvent, m_window, presentEventMask);
250
251 m_winInfo = std::make_unique<NETWinInfo>(m_backend->connection(), m_window, m_backend->screen()->root,
252 NET::WMWindowType, NET::Properties2());
253
254 m_winInfo->setWindowType(NET::Normal);
255 m_winInfo->setPid(QCoreApplication::applicationPid());
256 QIcon windowIcon = QIcon::fromTheme(QStringLiteral("kwin"));
257 auto addIcon = [&windowIcon, this](const QSize &size) {
258 if (windowIcon.actualSize(size) != size) {
259 return;
260 }
261 NETIcon icon;
262 QImage windowImage = windowIcon.pixmap(size).toImage();
263 icon.data = windowImage.bits();
264 icon.size.width = size.width();
265 icon.size.height = size.height();
266 m_winInfo->setIcon(icon, false);
267 };
268 addIcon(QSize(16, 16));
269 addIcon(QSize(32, 32));
270 addIcon(QSize(48, 48));
271
272 m_cursor = std::make_unique<X11WindowedCursor>(this);
273
274 xcb_map_window(m_backend->connection(), m_window);
275}
276
277void X11WindowedOutput::initXInputForWindow()
278{
279 if (!m_backend->hasXInput()) {
280 return;
281 }
282#if HAVE_X11_XINPUT
283 XIEventMask evmasks[1];
284 unsigned char mask1[XIMaskLen(XI_LASTEVENT)];
285
286 memset(mask1, 0, sizeof(mask1));
287 XISetMask(mask1, XI_TouchBegin);
288 XISetMask(mask1, XI_TouchUpdate);
289 XISetMask(mask1, XI_TouchOwnership);
290 XISetMask(mask1, XI_TouchEnd);
291 evmasks[0].deviceid = XIAllMasterDevices;
292 evmasks[0].mask_len = sizeof(mask1);
293 evmasks[0].mask = mask1;
294 XISelectEvents(m_backend->display(), m_window, evmasks, 1);
295#endif
296}
297
298void X11WindowedOutput::resize(const QSize &pixelSize)
299{
300 auto mode = std::make_shared<OutputMode>(pixelSize, m_renderLoop->refreshRate());
301
302 State next = m_state;
303 next.modes = {mode};
304 next.currentMode = mode;
305 setState(next);
306}
307
308void X11WindowedOutput::handlePresentCompleteNotify(xcb_present_complete_notify_event_t *event)
309{
310 std::chrono::microseconds timestamp(event->ust);
311 m_frame->presented(std::chrono::nanoseconds(1'000'000'000'000 / refreshRate()), timestamp, Compositor::self()->backend()->primaryLayer(this)->queryRenderTime(), PresentationMode::VSync);
312 m_frame.reset();
313}
314
315void X11WindowedOutput::handlePresentIdleNotify(xcb_present_idle_notify_event_t *event)
316{
317 for (auto &[graphicsBuffer, x11Buffer] : m_buffers) {
318 if (x11Buffer->pixmap() == event->pixmap) {
319 x11Buffer->unlock();
320 return;
321 }
322 }
323}
324
325void X11WindowedOutput::setWindowTitle(const QString &title)
326{
327 m_winInfo->setName(title.toUtf8().constData());
328}
329
331{
332 return geometry().topLeft();
333}
334
336{
337 m_hostPosition = pos;
338}
339
340QPointF X11WindowedOutput::mapFromGlobal(const QPointF &pos) const
341{
342 return (pos - hostPosition() + internalPosition()) / scale();
343}
344
346{
347 const auto layer = Compositor::self()->backend()->cursorLayer(this);
348 if (layer->isEnabled()) {
349 xcb_xfixes_show_cursor(m_backend->connection(), m_window);
350 // the cursor layers update the image on their own already
351 } else {
352 xcb_xfixes_hide_cursor(m_backend->connection(), m_window);
353 }
354 return true;
355}
356
358{
359 State next = m_state;
360 next.enabled = enabled;
361 setState(next);
362}
363
364xcb_pixmap_t X11WindowedOutput::importDmaBufBuffer(const DmaBufAttributes *attributes)
365{
366 uint8_t depth;
367 uint8_t bpp;
368 switch (attributes->format) {
369 case DRM_FORMAT_ARGB8888:
370 depth = 32;
371 bpp = 32;
372 break;
373 case DRM_FORMAT_XRGB8888:
374 depth = 24;
375 bpp = 32;
376 break;
377 default:
378 qCWarning(KWIN_X11WINDOWED) << "Cannot import a buffer with unsupported format";
379 return XCB_PIXMAP_NONE;
380 }
381
382 xcb_pixmap_t pixmap = xcb_generate_id(m_backend->connection());
383 if (m_backend->driMajorVersion() >= 1 || m_backend->driMinorVersion() >= 2) {
384 // xcb_dri3_pixmap_from_buffers() takes the ownership of the file descriptors.
385 int fds[4] = {
386 attributes->fd[0].duplicate().take(),
387 attributes->fd[1].duplicate().take(),
388 attributes->fd[2].duplicate().take(),
389 attributes->fd[3].duplicate().take(),
390 };
391 xcb_dri3_pixmap_from_buffers(m_backend->connection(), pixmap, m_window, attributes->planeCount,
392 attributes->width, attributes->height,
393 attributes->pitch[0], attributes->offset[0],
394 attributes->pitch[1], attributes->offset[1],
395 attributes->pitch[2], attributes->offset[2],
396 attributes->pitch[3], attributes->offset[3],
397 depth, bpp, attributes->modifier, fds);
398 } else {
399 // xcb_dri3_pixmap_from_buffer() takes the ownership of the file descriptor.
400 xcb_dri3_pixmap_from_buffer(m_backend->connection(), pixmap, m_window,
401 attributes->height * attributes->pitch[0], attributes->width, attributes->height,
402 attributes->pitch[0], depth, bpp, attributes->fd[0].duplicate().take());
403 }
404
405 return pixmap;
406}
407
408xcb_pixmap_t X11WindowedOutput::importShmBuffer(const ShmAttributes *attributes)
409{
410 // xcb_shm_attach_fd() takes the ownership of the passed shm file descriptor.
411 FileDescriptor poolFileDescriptor = attributes->fd.duplicate();
412 if (!poolFileDescriptor.isValid()) {
413 qCWarning(KWIN_X11WINDOWED) << "Failed to duplicate shm file descriptor";
414 return XCB_PIXMAP_NONE;
415 }
416
417 xcb_shm_seg_t segment = xcb_generate_id(m_backend->connection());
418 xcb_shm_attach_fd(m_backend->connection(), segment, poolFileDescriptor.take(), 0);
419
420 xcb_pixmap_t pixmap = xcb_generate_id(m_backend->connection());
421 xcb_shm_create_pixmap(m_backend->connection(), pixmap, m_window, attributes->size.width(), attributes->size.height(), depth(), segment, 0);
422 xcb_shm_detach(m_backend->connection(), segment);
423
424 return pixmap;
425}
426
428{
429 std::unique_ptr<X11WindowedBuffer> &x11Buffer = m_buffers[graphicsBuffer];
430 if (!x11Buffer) {
431 xcb_pixmap_t pixmap = XCB_PIXMAP_NONE;
432 if (const DmaBufAttributes *attributes = graphicsBuffer->dmabufAttributes()) {
433 pixmap = importDmaBufBuffer(attributes);
434 } else if (const ShmAttributes *attributes = graphicsBuffer->shmAttributes()) {
435 pixmap = importShmBuffer(attributes);
436 }
437 if (pixmap == XCB_PIXMAP_NONE) {
438 return XCB_PIXMAP_NONE;
439 }
440
441 x11Buffer = std::make_unique<X11WindowedBuffer>(this, pixmap, graphicsBuffer);
442 connect(x11Buffer.get(), &X11WindowedBuffer::defunct, this, [this, graphicsBuffer]() {
443 m_buffers.erase(graphicsBuffer);
444 });
445 }
446
447 x11Buffer->lock();
448 return x11Buffer->pixmap();
449}
450
451void X11WindowedOutput::framePending(const std::shared_ptr<OutputFrame> &frame)
452{
453 m_frame = frame;
454}
455
456} // namespace KWin
457
458#include "moc_x11_windowed_output.cpp"
RenderBackend * backend() const
Definition compositor.h:68
static Compositor * self()
virtual const DmaBufAttributes * dmabufAttributes() const
virtual const ShmAttributes * shmAttributes() const
QRect rect() const
Definition output.h:484
qreal scale() const
Definition output.cpp:455
uint32_t refreshRate() const
Definition output.cpp:475
void setInformation(const Information &information)
Definition output.cpp:556
void setState(const State &state)
Definition output.cpp:562
State m_state
Definition output.h:477
QRect geometry
Definition output.h:134
QSize pixelSize() const
Definition output.cpp:485
virtual OutputLayer * cursorLayer(Output *output)
xcb_connection_t * connection() const
xcb_screen_t * screen() const
GraphicsBuffer * buffer() const
X11WindowedBuffer(X11WindowedOutput *output, xcb_pixmap_t pixmap, GraphicsBuffer *buffer)
xcb_pixmap_t pixmap() const
X11WindowedCursor(X11WindowedOutput *output)
void update(const QImage &image, const QPointF &hotspot)
void resize(const QSize &pixelSize)
xcb_pixmap_t importBuffer(GraphicsBuffer *buffer)
QPointF mapFromGlobal(const QPointF &pos) const
void handlePresentCompleteNotify(xcb_present_complete_notify_event_t *event)
void framePending(const std::shared_ptr< OutputFrame > &frame)
X11WindowedCursor * cursor() const
X11WindowedBackend * backend() const
void setHostPosition(const QPoint &pos)
RenderLoop * renderLoop() const override
X11WindowedOutput(X11WindowedBackend *backend)
void handlePresentIdleNotify(xcb_present_idle_notify_event_t *event)
void addExposedArea(const QRect &rect)
void init(const QSize &pixelSize, qreal scale)
void setWindowTitle(const QString &title)
Wrapper around XRender Picture.
KWIN_EXPORT xcb_connection_t * connection()
Definition xcb.h:19
std::array< uint32_t, 4 > offset
std::array< uint32_t, 4 > pitch
std::array< FileDescriptor, 4 > fd
std::shared_ptr< OutputMode > currentMode
Definition output.h:454
QList< std::shared_ptr< OutputMode > > modes
Definition output.h:453