1/********************************************************************
2 KWin - the KDE window manager
3 This file is part of the KDE project.
4
5Copyright (C) 2013 Martin Gräßlin <mgraesslin@kde.org>
6
7This program is free software; you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published by
9the Free Software Foundation; either version 2 of the License, or
10(at your option) any later version.
11
12This program is distributed in the hope that it will be useful,
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15GNU General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along with this program. If not, see <http://www.gnu.org/licenses/>.
19*********************************************************************/
20#define WL_EGL_PLATFORM 1
21#include "egl_wayland_backend.h"
22// kwin
23#include "cursor.h"
24#include "options.h"
25// kwin libs
26#include <kwinglplatform.h>
27// KDE
28#include <KDE/KDebug>
29#include <KDE/KTemporaryFile>
30// Qt
31#include <QSocketNotifier>
32// xcb
33#include <xcb/xtest.h>
34// Wayland
35#include <wayland-client-protocol.h>
36// system
37#include <linux/input.h>
38#include <unistd.h>
39#include <sys/mman.h>
40#include <sys/shm.h>
41#include <sys/types.h>
42
43namespace KWin
44{
45
46namespace Wayland
47{
48
49/**
50 * Callback for announcing global objects in the registry
51 **/
52static void registryHandleGlobal(void *data, struct wl_registry *registry,
53 uint32_t name, const char *interface, uint32_t version)
54{
55 Q_UNUSED(version)
56 WaylandBackend *d = reinterpret_cast<WaylandBackend*>(data);
57
58 if (strcmp(interface, "wl_compositor") == 0) {
59 d->setCompositor(reinterpret_cast<wl_compositor*>(wl_registry_bind(registry, name, &wl_compositor_interface, 1)));
60 } else if (strcmp(interface, "wl_shell") == 0) {
61 d->setShell(reinterpret_cast<wl_shell *>(wl_registry_bind(registry, name, &wl_shell_interface, 1)));
62 } else if (strcmp(interface, "wl_seat") == 0) {
63 d->createSeat(name);
64 } else if (strcmp(interface, "wl_shm") == 0) {
65 d->createShm(name);
66 }
67 kDebug(1212) << "Wayland Interface: " << interface;
68}
69
70/**
71 * Callback for removal of global objects in the registry
72 **/
73static void registryHandleGlobalRemove(void *data, struct wl_registry *registry, uint32_t name)
74{
75 Q_UNUSED(data)
76 Q_UNUSED(registry)
77 Q_UNUSED(name)
78 // TODO: implement me
79}
80
81/**
82 * Call back for ping from Wayland Shell.
83 **/
84static void handlePing(void *data, struct wl_shell_surface *shellSurface, uint32_t serial)
85{
86 Q_UNUSED(shellSurface);
87 reinterpret_cast<WaylandBackend*>(data)->ping(serial);
88}
89
90/**
91 * Callback for a configure request for a shell surface
92 **/
93static void handleConfigure(void *data, struct wl_shell_surface *shellSurface, uint32_t edges, int32_t width, int32_t height)
94{
95 Q_UNUSED(shellSurface)
96 Q_UNUSED(edges)
97 WaylandBackend *display = reinterpret_cast<WaylandBackend*>(data);
98 wl_egl_window_resize(display->overlay(), width, height, 0, 0);
99 // TODO: this information should probably go into Screens
100}
101
102/**
103 * Callback for popups - not needed, we don't have popups
104 **/
105static void handlePopupDone(void *data, struct wl_shell_surface *shellSurface)
106{
107 Q_UNUSED(data)
108 Q_UNUSED(shellSurface)
109}
110
111static void seatHandleCapabilities(void *data, wl_seat *seat, uint32_t capabilities)
112{
113 WaylandSeat *s = reinterpret_cast<WaylandSeat*>(data);
114 if (seat != s->seat()) {
115 return;
116 }
117 s->changed(capabilities);
118}
119
120static void pointerHandleEnter(void *data, wl_pointer *pointer, uint32_t serial, wl_surface *surface,
121 wl_fixed_t sx, wl_fixed_t sy)
122{
123 Q_UNUSED(data)
124 Q_UNUSED(pointer)
125 Q_UNUSED(surface)
126 Q_UNUSED(sx)
127 Q_UNUSED(sy)
128 WaylandSeat *seat = reinterpret_cast<WaylandSeat*>(data);
129 seat->pointerEntered(serial);
130}
131
132static void pointerHandleLeave(void *data, wl_pointer *pointer, uint32_t serial, wl_surface *surface)
133{
134 Q_UNUSED(data)
135 Q_UNUSED(pointer)
136 Q_UNUSED(serial)
137 Q_UNUSED(surface)
138}
139
140static void pointerHandleMotion(void *data, wl_pointer *pointer, uint32_t time, wl_fixed_t sx, wl_fixed_t sy)
141{
142 Q_UNUSED(data)
143 Q_UNUSED(pointer)
144 Q_UNUSED(time)
145 xcb_test_fake_input(connection(), XCB_MOTION_NOTIFY, 0, XCB_TIME_CURRENT_TIME, XCB_WINDOW_NONE,
146 wl_fixed_to_int(sx), wl_fixed_to_int(sy), 0);
147}
148
149static void pointerHandleButton(void *data, wl_pointer *pointer, uint32_t serial, uint32_t time,
150 uint32_t button, uint32_t state)
151{
152 Q_UNUSED(data)
153 Q_UNUSED(pointer)
154 Q_UNUSED(serial)
155 Q_UNUSED(time)
156 uint8_t type = XCB_BUTTON_PRESS;
157 if (state == WL_POINTER_BUTTON_STATE_RELEASED) {
158 type = XCB_BUTTON_RELEASE;
159 }
160 // TODO: there must be a better way for mapping
161 uint8_t xButton = 0;
162 switch (button) {
163 case BTN_LEFT:
164 xButton = XCB_BUTTON_INDEX_1;
165 break;
166 case BTN_RIGHT:
167 xButton = XCB_BUTTON_INDEX_3;
168 break;
169 case BTN_MIDDLE:
170 xButton = XCB_BUTTON_INDEX_2;
171 break;
172 default:
173 // TODO: add more buttons
174 return;
175 }
176 xcb_test_fake_input(connection(), type, xButton, XCB_TIME_CURRENT_TIME, XCB_WINDOW_NONE, 0, 0, 0);
177}
178
179static void pointerHandleAxis(void *data, wl_pointer *pointer, uint32_t time, uint32_t axis, wl_fixed_t value)
180{
181 Q_UNUSED(data)
182 Q_UNUSED(pointer)
183 Q_UNUSED(time)
184 uint8_t xButton = 0;
185 const int delta = wl_fixed_to_int(value);
186 if (delta == 0) {
187 return;
188 }
189 switch (axis) {
190 case WL_POINTER_AXIS_VERTICAL_SCROLL:
191 xButton = delta > 0 ? XCB_BUTTON_INDEX_5 : XCB_BUTTON_INDEX_4;
192 break;
193 case WL_POINTER_AXIS_HORIZONTAL_SCROLL:
194 // no enum values defined for buttons larger than 5
195 xButton = delta > 0 ? 7 : 6;
196 break;
197 default:
198 // doesn't exist
199 return;
200 }
201 for (int i = 0; i < qAbs(delta); ++i) {
202 xcb_test_fake_input(connection(), XCB_BUTTON_PRESS, xButton, XCB_TIME_CURRENT_TIME, XCB_WINDOW_NONE, 0, 0, 0);
203 xcb_test_fake_input(connection(), XCB_BUTTON_RELEASE, xButton, XCB_TIME_CURRENT_TIME, XCB_WINDOW_NONE, 0, 0, 0);
204 }
205}
206
207static void keyboardHandleKeymap(void *data, wl_keyboard *keyboard,
208 uint32_t format, int fd, uint32_t size)
209{
210 Q_UNUSED(data)
211 Q_UNUSED(keyboard)
212 Q_UNUSED(format)
213 Q_UNUSED(fd)
214 Q_UNUSED(size)
215}
216
217static void keyboardHandleEnter(void *data, wl_keyboard *keyboard,
218 uint32_t serial, wl_surface *surface,
219 wl_array *keys)
220{
221 Q_UNUSED(data)
222 Q_UNUSED(keyboard)
223 Q_UNUSED(serial)
224 Q_UNUSED(surface)
225 Q_UNUSED(keys)
226}
227
228static void keyboardHandleLeave(void *data, wl_keyboard *keyboard, uint32_t serial, wl_surface *surface)
229{
230 Q_UNUSED(data)
231 Q_UNUSED(keyboard)
232 Q_UNUSED(serial)
233 Q_UNUSED(surface)
234}
235
236static void keyboardHandleKey(void *data, wl_keyboard *keyboard, uint32_t serial, uint32_t time,
237 uint32_t key, uint32_t state)
238{
239 Q_UNUSED(data)
240 Q_UNUSED(keyboard)
241 Q_UNUSED(serial)
242 Q_UNUSED(time)
243 uint8_t type = XCB_KEY_PRESS;
244 if (state == WL_KEYBOARD_KEY_STATE_RELEASED) {
245 type = XCB_KEY_RELEASE;
246 }
247 xcb_test_fake_input(connection(), type, key + 8 /*magic*/, XCB_TIME_CURRENT_TIME, XCB_WINDOW_NONE, 0, 0, 0);
248}
249
250static void keyboardHandleModifiers(void *data, wl_keyboard *keyboard, uint32_t serial, uint32_t modsDepressed,
251 uint32_t modsLatched, uint32_t modsLocked, uint32_t group)
252{
253 Q_UNUSED(data)
254 Q_UNUSED(keyboard)
255 Q_UNUSED(serial)
256 Q_UNUSED(modsDepressed)
257 Q_UNUSED(modsLatched)
258 Q_UNUSED(modsLocked)
259 Q_UNUSED(group)
260}
261
262// handlers
263static const struct wl_registry_listener s_registryListener = {
264 registryHandleGlobal,
265 registryHandleGlobalRemove
266};
267
268static const struct wl_shell_surface_listener s_shellSurfaceListener = {
269 handlePing,
270 handleConfigure,
271 handlePopupDone
272};
273
274static const struct wl_pointer_listener s_pointerListener = {
275 pointerHandleEnter,
276 pointerHandleLeave,
277 pointerHandleMotion,
278 pointerHandleButton,
279 pointerHandleAxis
280};
281
282static const struct wl_keyboard_listener s_keyboardListener = {
283 keyboardHandleKeymap,
284 keyboardHandleEnter,
285 keyboardHandleLeave,
286 keyboardHandleKey,
287 keyboardHandleModifiers,
288};
289
290static const struct wl_seat_listener s_seatListener = {
291 seatHandleCapabilities
292};
293
294CursorData::CursorData(ShmPool *pool)
295 : m_cursor(NULL)
296 , m_valid(init(pool))
297{
298}
299
300CursorData::~CursorData()
301{
302}
303
304bool CursorData::init(ShmPool *pool)
305{
306 QScopedPointer<xcb_xfixes_get_cursor_image_reply_t, QScopedPointerPodDeleter> cursor(
307 xcb_xfixes_get_cursor_image_reply(connection(),
308 xcb_xfixes_get_cursor_image_unchecked(connection()),
309 NULL));
310 if (cursor.isNull()) {
311 return false;
312 }
313
314 QImage cursorImage((uchar *) xcb_xfixes_get_cursor_image_cursor_image(cursor.data()), cursor->width, cursor->height,
315 QImage::Format_ARGB32_Premultiplied);
316 if (cursorImage.isNull()) {
317 return false;
318 }
319 m_size = QSize(cursor->width, cursor->height);
320
321 m_cursor = pool->createBuffer(cursorImage);
322 if (!m_cursor) {
323 kDebug(1212) << "Creating cursor buffer failed";
324 return false;
325 }
326
327 m_hotSpot = QPoint(cursor->xhot, cursor->yhot);
328 return true;
329}
330
331X11CursorTracker::X11CursorTracker(wl_pointer *pointer, WaylandBackend *backend, QObject* parent)
332 : QObject(parent)
333 , m_pointer(pointer)
334 , m_backend(backend)
335 , m_cursor(wl_compositor_create_surface(backend->compositor()))
336 , m_enteredSerial(0)
337 , m_lastX11Cursor(0)
338{
339 Cursor::self()->startCursorTracking();
340 connect(Cursor::self(), SIGNAL(cursorChanged(uint32_t)), SLOT(cursorChanged(uint32_t)));
341}
342
343X11CursorTracker::~X11CursorTracker()
344{
345 Cursor::self()->stopCursorTracking();
346 if (m_cursor) {
347 wl_surface_destroy(m_cursor);
348 }
349}
350
351void X11CursorTracker::cursorChanged(uint32_t serial)
352{
353 if (m_lastX11Cursor == serial) {
354 // not changed;
355 return;
356 }
357 m_lastX11Cursor = serial;
358 QHash<uint32_t, CursorData>::iterator it = m_cursors.find(serial);
359 if (it != m_cursors.end()) {
360 installCursor(it.value());
361 return;
362 }
363 ShmPool *pool = m_backend->shmPool();
364 if (!pool) {
365 return;
366 }
367 CursorData cursor(pool);
368 if (cursor.isValid()) {
369 // TODO: discard unused cursors after some time?
370 m_cursors.insert(serial, cursor);
371 }
372 installCursor(cursor);
373}
374
375void X11CursorTracker::installCursor(const CursorData& cursor)
376{
377 wl_pointer_set_cursor(m_pointer, m_enteredSerial, m_cursor, cursor.hotSpot().x(), cursor.hotSpot().y());
378 wl_surface_attach(m_cursor, cursor.cursor(), 0, 0);
379 wl_surface_damage(m_cursor, 0, 0, cursor.size().width(), cursor.size().height());
380 wl_surface_commit(m_cursor);
381}
382
383void X11CursorTracker::setEnteredSerial(uint32_t serial)
384{
385 m_enteredSerial = serial;
386}
387
388void X11CursorTracker::resetCursor()
389{
390 QHash<uint32_t, CursorData>::iterator it = m_cursors.find(m_lastX11Cursor);
391 if (it != m_cursors.end()) {
392 installCursor(it.value());
393 }
394}
395
396ShmPool::ShmPool(wl_shm *shm)
397 : m_shm(shm)
398 , m_pool(NULL)
399 , m_poolData(NULL)
400 , m_size(1024 * 1024) // TODO: useful size?
401 , m_tmpFile(new KTemporaryFile())
402 , m_valid(createPool())
403 , m_offset(0)
404{
405}
406
407ShmPool::~ShmPool()
408{
409 if (m_poolData) {
410 munmap(m_poolData, m_size);
411 }
412 if (m_pool) {
413 wl_shm_pool_destroy(m_pool);
414 }
415 if (m_shm) {
416 wl_shm_destroy(m_shm);
417 }
418}
419
420bool ShmPool::createPool()
421{
422 if (!m_tmpFile->open()) {
423 kDebug(1212) << "Could not open temporary file for Shm pool";
424 return false;
425 }
426 if (ftruncate(m_tmpFile->handle(), m_size) < 0) {
427 kDebug(1212) << "Could not set size for Shm pool file";
428 return false;
429 }
430 m_poolData = mmap(NULL, m_size, PROT_READ | PROT_WRITE, MAP_SHARED, m_tmpFile->handle(), 0);
431 m_pool = wl_shm_create_pool(m_shm, m_tmpFile->handle(), m_size);
432
433 if (!m_poolData || !m_pool) {
434 kDebug(1212) << "Creating Shm pool failed";
435 return false;
436 }
437 m_tmpFile->close();
438 return true;
439}
440
441wl_buffer *ShmPool::createBuffer(const QImage& image)
442{
443 if (image.isNull() || !m_valid) {
444 return NULL;
445 }
446 // TODO: test whether buffer needs resizing
447 wl_buffer *buffer = wl_shm_pool_create_buffer(m_pool, m_offset, image.width(), image.height(),
448 image.bytesPerLine(), WL_SHM_FORMAT_ARGB8888);
449 if (buffer) {
450 memcpy((char *)m_poolData + m_offset, image.bits(), image.byteCount());
451 m_offset += image.byteCount();
452 }
453 return buffer;
454}
455
456WaylandSeat::WaylandSeat(wl_seat *seat, WaylandBackend *backend)
457 : m_seat(seat)
458 , m_pointer(NULL)
459 , m_keyboard(NULL)
460 , m_cursorTracker()
461 , m_backend(backend)
462{
463 if (m_seat) {
464 wl_seat_add_listener(m_seat, &s_seatListener, this);
465 }
466}
467
468WaylandSeat::~WaylandSeat()
469{
470 destroyPointer();
471 destroyKeyboard();
472 if (m_seat) {
473 wl_seat_destroy(m_seat);
474 }
475}
476
477void WaylandSeat::destroyPointer()
478{
479 if (m_pointer) {
480 wl_pointer_destroy(m_pointer);
481 m_pointer = NULL;
482 m_cursorTracker.reset();
483 }
484}
485
486void WaylandSeat::destroyKeyboard()
487{
488 if (m_keyboard) {
489 wl_keyboard_destroy(m_keyboard);
490 m_keyboard = NULL;
491 }
492}
493
494void WaylandSeat::changed(uint32_t capabilities)
495{
496 if ((capabilities & WL_SEAT_CAPABILITY_POINTER) && !m_pointer) {
497 m_pointer = wl_seat_get_pointer(m_seat);
498 wl_pointer_add_listener(m_pointer, &s_pointerListener, this);
499 m_cursorTracker.reset(new X11CursorTracker(m_pointer, m_backend));
500 } else if (!(capabilities & WL_SEAT_CAPABILITY_POINTER)) {
501 destroyPointer();
502 }
503 if ((capabilities & WL_SEAT_CAPABILITY_KEYBOARD)) {
504 m_keyboard = wl_seat_get_keyboard(m_seat);
505 wl_keyboard_add_listener(m_keyboard, &s_keyboardListener, this);
506 } else if (!(capabilities & WL_SEAT_CAPABILITY_KEYBOARD)) {
507 destroyKeyboard();
508 }
509}
510
511void WaylandSeat::pointerEntered(uint32_t serial)
512{
513 if (m_cursorTracker.isNull()) {
514 return;
515 }
516 m_cursorTracker->setEnteredSerial(serial);
517}
518
519void WaylandSeat::resetCursor()
520{
521 if (!m_cursorTracker.isNull()) {
522 m_cursorTracker->resetCursor();
523 }
524}
525
526WaylandBackend::WaylandBackend()
527 : QObject(NULL)
528 , m_display(wl_display_connect(NULL))
529 , m_registry(wl_display_get_registry(m_display))
530 , m_compositor(NULL)
531 , m_shell(NULL)
532 , m_surface(NULL)
533 , m_overlay(NULL)
534 , m_shellSurface(NULL)
535 , m_seat()
536 , m_shm()
537{
538 kDebug(1212) << "Created Wayland display";
539 // setup the registry
540 wl_registry_add_listener(m_registry, &s_registryListener, this);
541 wl_display_dispatch(m_display);
542 int fd = wl_display_get_fd(m_display);
543 QSocketNotifier *notifier = new QSocketNotifier(fd, QSocketNotifier::Read, this);
544 connect(notifier, SIGNAL(activated(int)), SLOT(readEvents()));
545}
546
547WaylandBackend::~WaylandBackend()
548{
549 if (m_overlay) {
550 wl_egl_window_destroy(m_overlay);
551 }
552 if (m_shellSurface) {
553 wl_shell_surface_destroy(m_shellSurface);
554 }
555 if (m_surface) {
556 wl_surface_destroy(m_surface);
557 }
558 if (m_shell) {
559 wl_shell_destroy(m_shell);
560 }
561 if (m_compositor) {
562 wl_compositor_destroy(m_compositor);
563 }
564 if (m_registry) {
565 wl_registry_destroy(m_registry);
566 }
567 if (m_display) {
568 wl_display_flush(m_display);
569 wl_display_disconnect(m_display);
570 }
571 kDebug(1212) << "Destroyed Wayland display";
572}
573
574void WaylandBackend::readEvents()
575{
576 // TODO: this still seems to block
577 wl_display_flush(m_display);
578 wl_display_dispatch(m_display);
579}
580
581void WaylandBackend::createSeat(uint32_t name)
582{
583 wl_seat *seat = reinterpret_cast<wl_seat*>(wl_registry_bind(m_registry, name, &wl_seat_interface, 1));
584 m_seat.reset(new WaylandSeat(seat, this));
585}
586
587bool WaylandBackend::createSurface()
588{
589 m_surface = wl_compositor_create_surface(m_compositor);
590 if (!m_surface) {
591 kError(1212) << "Creating Wayland Surface failed";
592 return false;
593 }
594 // map the surface as fullscreen
595 m_shellSurface = wl_shell_get_shell_surface(m_shell, m_surface);
596 wl_shell_surface_add_listener(m_shellSurface, &s_shellSurfaceListener, this);
597
598 // TODO: do something better than displayWidth/displayHeight
599 m_overlay = wl_egl_window_create(m_surface, displayWidth(), displayHeight());
600 if (!m_overlay) {
601 kError(1212) << "Creating Wayland Egl window failed";
602 return false;
603 }
604 wl_shell_surface_set_fullscreen(m_shellSurface, WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT, 0, NULL);
605
606 return true;
607}
608
609void WaylandBackend::createShm(uint32_t name)
610{
611 m_shm.reset(new ShmPool(reinterpret_cast<wl_shm *>(wl_registry_bind(m_registry, name, &wl_shm_interface, 1))));
612 if (!m_shm->isValid()) {
613 m_shm.reset();
614 }
615}
616
617void WaylandBackend::ping(uint32_t serial)
618{
619 wl_shell_surface_pong(m_shellSurface, serial);
620 if (!m_seat.isNull()) {
621 m_seat->resetCursor();
622 }
623}
624
625}
626
627EglWaylandBackend::EglWaylandBackend()
628 : OpenGLBackend()
629 , m_context(EGL_NO_CONTEXT)
630 , m_wayland(new Wayland::WaylandBackend)
631{
632 kDebug(1212) << "Connected to Wayland display?" << (m_wayland->display() ? "yes" : "no" );
633 if (!m_wayland->display()) {
634 setFailed("Could not connect to Wayland compositor");
635 return;
636 }
637 initializeEgl();
638 init();
639 // Egl is always direct rendering
640 setIsDirectRendering(true);
641
642 kWarning(1212) << "Using Wayland rendering backend";
643 kWarning(1212) << "This is a highly experimental backend, do not use for productive usage!";
644 kWarning(1212) << "Please do not report any issues you might encounter when using this backend!";
645}
646
647EglWaylandBackend::~EglWaylandBackend()
648{
649 cleanupGL();
650 checkGLError("Cleanup");
651 eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
652 eglDestroyContext(m_display, m_context);
653 eglDestroySurface(m_display, m_surface);
654 eglTerminate(m_display);
655 eglReleaseThread();
656}
657
658bool EglWaylandBackend::initializeEgl()
659{
660 m_display = eglGetDisplay(m_wayland->display());
661 if (m_display == EGL_NO_DISPLAY)
662 return false;
663
664 EGLint major, minor;
665 if (eglInitialize(m_display, &major, &minor) == EGL_FALSE)
666 return false;
667 EGLint error = eglGetError();
668 if (error != EGL_SUCCESS) {
669 kWarning(1212) << "Error during eglInitialize " << error;
670 return false;
671 }
672 kDebug(1212) << "Egl Initialize succeeded";
673
674#ifdef KWIN_HAVE_OPENGLES
675 eglBindAPI(EGL_OPENGL_ES_API);
676#else
677 if (eglBindAPI(EGL_OPENGL_API) == EGL_FALSE) {
678 kError(1212) << "bind OpenGL API failed";
679 return false;
680 }
681#endif
682 kDebug(1212) << "EGL version: " << major << "." << minor;
683 return true;
684}
685
686void EglWaylandBackend::init()
687{
688 if (!initRenderingContext()) {
689 setFailed("Could not initialize rendering context");
690 return;
691 }
692
693 initEGL();
694 GLPlatform *glPlatform = GLPlatform::instance();
695 glPlatform->detect(EglPlatformInterface);
696 glPlatform->printResults();
697 initGL(EglPlatformInterface);
698}
699
700bool EglWaylandBackend::initRenderingContext()
701{
702 initBufferConfigs();
703
704#ifdef KWIN_HAVE_OPENGLES
705 const EGLint context_attribs[] = {
706 EGL_CONTEXT_CLIENT_VERSION, 2,
707 EGL_NONE
708 };
709
710 m_context = eglCreateContext(m_display, m_config, EGL_NO_CONTEXT, context_attribs);
711#else
712 const EGLint context_attribs_31_core[] = {
713 EGL_CONTEXT_MAJOR_VERSION_KHR, 3,
714 EGL_CONTEXT_MINOR_VERSION_KHR, 1,
715 EGL_CONTEXT_FLAGS_KHR, EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR,
716 EGL_NONE
717 };
718
719 const EGLint context_attribs_legacy[] = {
720 EGL_NONE
721 };
722
723 const QByteArray eglExtensions = eglQueryString(m_display, EGL_EXTENSIONS);
724 const QList<QByteArray> extensions = eglExtensions.split(' ');
725
726 // Try to create a 3.1 core context
727 if (options->glCoreProfile() && extensions.contains("EGL_KHR_create_context"))
728 m_context = eglCreateContext(m_display, m_config, EGL_NO_CONTEXT, context_attribs_31_core);
729
730 if (m_context == EGL_NO_CONTEXT)
731 m_context = eglCreateContext(m_display, m_config, EGL_NO_CONTEXT, context_attribs_legacy);
732#endif
733
734 if (m_context == EGL_NO_CONTEXT) {
735 kError(1212) << "Create Context failed";
736 return false;
737 }
738
739 if (!m_wayland->createSurface()) {
740 return false;
741 }
742
743 m_surface = eglCreateWindowSurface(m_display, m_config, m_wayland->overlay(), NULL);
744 if (m_surface == EGL_NO_SURFACE) {
745 kError(1212) << "Create Window Surface failed";
746 return false;
747 }
748
749 return makeContextCurrent();
750}
751
752bool EglWaylandBackend::makeContextCurrent()
753{
754 if (eglMakeCurrent(m_display, m_surface, m_surface, m_context) == EGL_FALSE) {
755 kError(1212) << "Make Context Current failed";
756 return false;
757 }
758
759 EGLint error = eglGetError();
760 if (error != EGL_SUCCESS) {
761 kWarning(1212) << "Error occurred while creating context " << error;
762 return false;
763 }
764 return true;
765}
766
767bool EglWaylandBackend::initBufferConfigs()
768{
769 const EGLint config_attribs[] = {
770 EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
771 EGL_RED_SIZE, 1,
772 EGL_GREEN_SIZE, 1,
773 EGL_BLUE_SIZE, 1,
774 EGL_ALPHA_SIZE, 0,
775#ifdef KWIN_HAVE_OPENGLES
776 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
777#else
778 EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
779#endif
780 EGL_CONFIG_CAVEAT, EGL_NONE,
781 EGL_NONE,
782 };
783
784 EGLint count;
785 EGLConfig configs[1024];
786 if (eglChooseConfig(m_display, config_attribs, configs, 1, &count) == EGL_FALSE) {
787 kError(1212) << "choose config failed";
788 return false;
789 }
790 if (count != 1) {
791 kError(1212) << "choose config did not return a config" << count;
792 return false;
793 }
794 m_config = configs[0];
795
796 return true;
797}
798
799void EglWaylandBackend::present()
800{
801 setLastDamage(QRegion());
802 // need to dispatch pending events as eglSwapBuffers can block
803 wl_display_dispatch_pending(m_wayland->display());
804 wl_display_flush(m_wayland->display());
805 eglSwapBuffers(m_display, m_surface);
806}
807
808void EglWaylandBackend::screenGeometryChanged(const QSize &size)
809{
810 Q_UNUSED(size)
811 // no backend specific code needed
812 // TODO: base implementation in OpenGLBackend
813}
814
815SceneOpenGL::TexturePrivate *EglWaylandBackend::createBackendTexture(SceneOpenGL::Texture *texture)
816{
817 return new EglWaylandTexture(texture, this);
818}
819
820QRegion EglWaylandBackend::prepareRenderingFrame()
821{
822 if (!lastDamage().isEmpty())
823 present();
824
825 eglWaitNative(EGL_CORE_NATIVE_ENGINE);
826 startRenderTimer();
827
828 return QRegion();
829}
830
831void EglWaylandBackend::endRenderingFrame(const QRegion &renderedRegion, const QRegion &damagedRegion)
832{
833 setLastDamage(renderedRegion);
834 glFlush();
835}
836
837Shm *EglWaylandBackend::shm()
838{
839 if (m_shm.isNull()) {
840 m_shm.reset(new Shm);
841 }
842 return m_shm.data();
843}
844
845/************************************************
846 * EglTexture
847 ************************************************/
848
849EglWaylandTexture::EglWaylandTexture(KWin::SceneOpenGL::Texture *texture, KWin::EglWaylandBackend *backend)
850 : SceneOpenGL::TexturePrivate()
851 , q(texture)
852 , m_backend(backend)
853 , m_referencedPixmap(XCB_PIXMAP_NONE)
854{
855 m_target = GL_TEXTURE_2D;
856}
857
858EglWaylandTexture::~EglWaylandTexture()
859{
860}
861
862OpenGLBackend *EglWaylandTexture::backend()
863{
864 return m_backend;
865}
866
867void EglWaylandTexture::findTarget()
868{
869 if (m_target != GL_TEXTURE_2D) {
870 m_target = GL_TEXTURE_2D;
871 }
872}
873
874bool EglWaylandTexture::loadTexture(const Pixmap &pix, const QSize &size, int depth)
875{
876 // HACK: egl wayland platform doesn't support texture from X11 pixmap through the KHR_image_pixmap
877 // extension. To circumvent this problem we copy the pixmap content into a SHM image and from there
878 // to the OpenGL texture. This is a temporary solution. In future we won't need to get the content
879 // from X11 pixmaps. That's what we have XWayland for to get the content into a nice Wayland buffer.
880 Q_UNUSED(depth)
881 if (pix == XCB_PIXMAP_NONE)
882 return false;
883 m_referencedPixmap = pix;
884
885 Shm *shm = m_backend->shm();
886 if (!shm->isValid()) {
887 return false;
888 }
889
890 xcb_shm_get_image_cookie_t cookie = xcb_shm_get_image_unchecked(connection(), pix, 0, 0, size.width(),
891 size.height(), ~0, XCB_IMAGE_FORMAT_Z_PIXMAP, shm->segment(), 0);
892
893 glGenTextures(1, &m_texture);
894 q->setWrapMode(GL_CLAMP_TO_EDGE);
895 q->setFilter(GL_LINEAR);
896 q->bind();
897
898 ScopedCPointer<xcb_shm_get_image_reply_t> image(xcb_shm_get_image_reply(connection(), cookie, NULL));
899 if (image.isNull()) {
900 return false;
901 }
902
903 // TODO: other formats
904#ifndef KWIN_HAVE_OPENGLES
905 glTexImage2D(m_target, 0, GL_RGBA8, size.width(), size.height(), 0,
906 GL_BGRA, GL_UNSIGNED_BYTE, shm->buffer());
907#endif
908
909 q->unbind();
910 checkGLError("load texture");
911 q->setYInverted(true);
912 m_size = size;
913 updateMatrix();
914 return true;
915}
916
917bool EglWaylandTexture::update(const QRegion &damage)
918{
919 if (m_referencedPixmap == XCB_PIXMAP_NONE) {
920 return false;
921 }
922
923 Shm *shm = m_backend->shm();
924 if (!shm->isValid()) {
925 return false;
926 }
927
928 // TODO: optimize by only updating the damaged areas
929 const QRect &damagedRect = damage.boundingRect();
930 xcb_shm_get_image_cookie_t cookie = xcb_shm_get_image_unchecked(connection(), m_referencedPixmap,
931 damagedRect.x(), damagedRect.y(), damagedRect.width(), damagedRect.height(),
932 ~0, XCB_IMAGE_FORMAT_Z_PIXMAP, shm->segment(), 0);
933
934 q->bind();
935
936 ScopedCPointer<xcb_shm_get_image_reply_t> image(xcb_shm_get_image_reply(connection(), cookie, NULL));
937 if (image.isNull()) {
938 return false;
939 }
940
941 // TODO: other formats
942#ifndef KWIN_HAVE_OPENGLES
943 glTexSubImage2D(m_target, 0, damagedRect.x(), damagedRect.y(), damagedRect.width(), damagedRect.height(), GL_BGRA, GL_UNSIGNED_BYTE, shm->buffer());
944#endif
945
946 q->unbind();
947 checkGLError("update texture");
948 return true;
949}
950
951Shm::Shm()
952 : m_shmId(-1)
953 , m_buffer(NULL)
954 , m_segment(XCB_NONE)
955 , m_valid(false)
956{
957 m_valid = init();
958}
959
960Shm::~Shm()
961{
962 if (m_valid) {
963 xcb_shm_detach(connection(), m_segment);
964 shmdt(m_buffer);
965 }
966}
967
968bool Shm::init()
969{
970 const xcb_query_extension_reply_t *ext = xcb_get_extension_data(connection(), &xcb_shm_id);
971 if (!ext || !ext->present) {
972 kDebug(1212) << "SHM extension not available";
973 return false;
974 }
975 ScopedCPointer<xcb_shm_query_version_reply_t> version(xcb_shm_query_version_reply(connection(),
976 xcb_shm_query_version_unchecked(connection()), NULL));
977 if (version.isNull()) {
978 kDebug(1212) << "Failed to get SHM extension version information";
979 return false;
980 }
981 const int MAXSIZE = 4096 * 2048 * 4; // TODO check there are not larger windows
982 m_shmId = shmget(IPC_PRIVATE, MAXSIZE, IPC_CREAT | 0600);
983 if (m_shmId < 0) {
984 kDebug(1212) << "Failed to allocate SHM segment";
985 return false;
986 }
987 m_buffer = shmat(m_shmId, NULL, 0 /*read/write*/);
988 if (-1 == reinterpret_cast<long>(m_buffer)) {
989 kDebug(1212) << "Failed to attach SHM segment";
990 shmctl(m_shmId, IPC_RMID, NULL);
991 return false;
992 }
993 shmctl(m_shmId, IPC_RMID, NULL);
994
995 m_segment = xcb_generate_id(connection());
996 const xcb_void_cookie_t cookie = xcb_shm_attach_checked(connection(), m_segment, m_shmId, false);
997 ScopedCPointer<xcb_generic_error_t> error(xcb_request_check(connection(), cookie));
998 if (!error.isNull()) {
999 kDebug(1212) << "xcb_shm_attach error: " << error->error_code;
1000 shmdt(m_buffer);
1001 return false;
1002 }
1003
1004 return true;
1005}
1006
1007} // namespace
1008