[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-commits
Subject:    [kde-workspace] kwin: Egl Backend using a Wayland surface for rendering
From:       Martin_Gräßlin <mgraesslin () kde ! org>
Date:       2013-06-05 11:47:37
Message-ID: 20130605114737.5CAB3A605A () git ! kde ! org
[Download RAW message or body]

Git commit 9cc9fa844915db2773486f55c54fbc36cb891bd4 by Martin Gräßlin.
Committed on 15/05/2013 at 13:47.
Pushed by graesslin into branch 'master'.

Egl Backend using a Wayland surface for rendering

This backend is able to composite on a Wayland surface instead of an X11
overlay window. It can be considered as a prototype for a Wayland session
compositor.

For texture from X11 pixmap the backend uses XShm. This is far from
optimal, but the KHR_image_pixmap extension is not available in Mesa's
Wayland backend. It's a temporary solution till we have XWayland and
texture from Wayland buffer.

To use this backend one needs to specify the environment variable
KWIN_OPENGL_INTERFACE with "egl_wayland". In future KWin should probably
use this backend if the Wayland display env variable is defined.

To use this setup:
1. Have a normal X-Server running on e.g. VT7
2. Start Weston on VT1
3. Start a terminal on Weston
4. start KWin with:

DISPLAY=:0 KWIN_OPENGL_INTERFACE=egl_wayland kwin --replace &

This should map a Wayland surface to Weston showing the content of the X
setup. At the moment it's not yet possible to interact with the surface
as input events are not yet recieved in the backend.

There are still a lot of limitations as documented in the code.

M  +18   -0    kwin/CMakeLists.txt
M  +2    -1    kwin/compositingprefs.cpp
A  +547  -0    kwin/egl_wayland_backend.cpp     [License: GPL (v2)]
A  +225  -0    kwin/egl_wayland_backend.h     [License: GPL (v2)]
M  +30   -1    kwin/scene_opengl.cpp
M  +2    -0    kwin/scene_opengl.h

http://commits.kde.org/kde-workspace/9cc9fa844915db2773486f55c54fbc36cb891bd4

diff --git a/kwin/CMakeLists.txt b/kwin/CMakeLists.txt
index 6570b1a..f9d9c21 100644
--- a/kwin/CMakeLists.txt
+++ b/kwin/CMakeLists.txt
@@ -54,6 +54,10 @@ endif()
 
 include_directories(${XCB_INCLUDE_DIR})
 
+if(WAYLAND_FOUND)
+    include_directories(${WAYLAND_INCLUDE_DIR})
+endif()
+
 # for things that are also used by kwin libraries
 configure_file(libkwineffects/kwinconfig.h.cmake \
${CMAKE_CURRENT_BINARY_DIR}/libkwineffects/kwinconfig.h )  # for kwin internal things
@@ -192,6 +196,10 @@ if(KWIN_HAVE_EGL)
     set(kwin_KDEINIT_SRCS ${kwin_KDEINIT_SRCS} eglonxbackend.cpp)
 endif()
 
+if(WAYLAND_FOUND)
+    set(kwin_KDEINIT_SRCS ${kwin_KDEINIT_SRCS} egl_wayland_backend.cpp)
+endif()
+
 kde4_add_kcfg_files(kwin_KDEINIT_SRCS settings.kcfgc)
 
 qt4_add_dbus_adaptor( kwin_KDEINIT_SRCS org.kde.KWin.xml dbusinterface.h \
KWin::DBusInterface ) @@ -248,6 +256,12 @@ set(kwin_XCB_LIBS
     ${XCB_ICCCM_LIBRARIES}
 )
 
+set(kwin_WAYLAND_LIBS
+    ${WAYLAND_CLIENT_LIBRARIES}
+    ${WAYLAND_EGL_LIBRARIES}
+    ${XCB_SHM_LIBRARIES}
+)
+
 set(kwin_OPENGL_LIBS )
 
 find_library(XF86VM_LIBRARY Xxf86vm)
@@ -278,6 +292,10 @@ set(kwinLibs
     ${kwin_OPENGL_LIBS}
 )
 
+if(WAYLAND_FOUND)
+    set(kwinLibs ${kwinLibs} ${kwin_WAYLAND_LIBS})
+endif()
+
 kde4_add_kdeinit_executable( kwin ${kwin_KDEINIT_SRCS})
 
 target_link_libraries(kdeinit_kwin ${kwinLibs})
diff --git a/kwin/compositingprefs.cpp b/kwin/compositingprefs.cpp
index 87a6e91..f46dac4 100644
--- a/kwin/compositingprefs.cpp
+++ b/kwin/compositingprefs.cpp
@@ -139,7 +139,8 @@ void CompositingPrefs::detect()
 #ifndef KWIN_HAVE_OPENGLES
     // HACK: This is needed for AIGLX
     const bool forceIndirect = qstrcmp(qgetenv("LIBGL_ALWAYS_INDIRECT"), "1") == 0;
-    const bool forceEgl = qstrcmp(qgetenv("KWIN_OPENGL_INTERFACE"), "egl") == 0;
+    const bool forceEgl = qstrcmp(qgetenv("KWIN_OPENGL_INTERFACE"), "egl") == 0 ||
+            qstrcmp(qgetenv("KWIN_OPENGL_INTERFACE"), "egl_wayland") == 0;
     if (!forceIndirect && !forceEgl && qstrcmp(qgetenv("KWIN_DIRECT_GL"), "1") != 0) \
                {
         // Start an external helper program that initializes GLX and returns
         // 0 if we can use direct rendering, and 1 otherwise.
diff --git a/kwin/egl_wayland_backend.cpp b/kwin/egl_wayland_backend.cpp
new file mode 100644
index 0000000..1d49f89
--- /dev/null
+++ b/kwin/egl_wayland_backend.cpp
@@ -0,0 +1,547 @@
+/********************************************************************
+ KWin - the KDE window manager
+ This file is part of the KDE project.
+
+Copyright (C) 2013 Martin Gräßlin <mgraesslin@kde.org>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*********************************************************************/
+#define WL_EGL_PLATFORM 1
+#include "egl_wayland_backend.h"
+// kwin
+#include "options.h"
+// kwin libs
+#include <kwinglplatform.h>
+// KDE
+#include <KDE/KDebug>
+// system
+#include <sys/shm.h>
+
+namespace KWin
+{
+
+namespace Wayland
+{
+
+/**
+ * Callback for announcing global objects in the registry
+ **/
+static void registryHandleGlobal(void *data, struct wl_registry *registry,
+                                 uint32_t name, const char *interface, uint32_t \
version) +{
+    Q_UNUSED(version)
+    WaylandBackend *d = reinterpret_cast<WaylandBackend*>(data);
+
+    if (strcmp(interface, "wl_compositor") == 0) {
+        d->setCompositor(reinterpret_cast<wl_compositor*>(wl_registry_bind(registry, \
name, &wl_compositor_interface, 1))); +    } else if (strcmp(interface, "wl_shell") \
== 0) { +        d->setShell(reinterpret_cast<wl_shell *>(wl_registry_bind(registry, \
name, &wl_shell_interface, 1))); +    }
+    kDebug(1212) << "Wayland Interface: " << interface;
+}
+
+/**
+ * Callback for removal of global objects in the registry
+ **/
+static void registryHandleGlobalRemove(void *data, struct wl_registry *registry, \
uint32_t name) +{
+    Q_UNUSED(data)
+    Q_UNUSED(registry)
+    Q_UNUSED(name)
+    // TODO: implement me
+}
+
+/**
+ * Call back for ping from Wayland Shell.
+ **/
+static void handlePing(void *data, struct wl_shell_surface *shellSurface, uint32_t \
serial) +{
+    Q_UNUSED(data)
+    wl_shell_surface_pong(shellSurface, serial);
+}
+
+/**
+ * Callback for a configure request for a shell surface
+ **/
+static void handleConfigure(void *data, struct wl_shell_surface *shellSurface, \
uint32_t edges, int32_t width, int32_t height) +{
+    Q_UNUSED(shellSurface)
+    Q_UNUSED(edges)
+    WaylandBackend *display = reinterpret_cast<WaylandBackend*>(data);
+    wl_egl_window_resize(display->overlay(), width, height, 0, 0);
+    // TODO: this information should probably go into Screens
+}
+
+/**
+ * Callback for popups - not needed, we don't have popups
+ **/
+static void handlePopupDone(void *data, struct wl_shell_surface *shellSurface)
+{
+    Q_UNUSED(data)
+    Q_UNUSED(shellSurface)
+}
+
+// handlers
+static const struct wl_registry_listener s_registryListener = {
+    registryHandleGlobal,
+    registryHandleGlobalRemove
+};
+
+static const struct wl_shell_surface_listener s_shellSurfaceListener = {
+    handlePing,
+    handleConfigure,
+    handlePopupDone
+};
+
+WaylandBackend::WaylandBackend()
+    : m_display(wl_display_connect(NULL))
+    , m_registry(wl_display_get_registry(m_display))
+    , m_compositor(NULL)
+    , m_shell(NULL)
+    , m_surface(NULL)
+    , m_overlay(NULL)
+    , m_shellSurface(NULL)
+{
+    kDebug(1212) << "Created Wayland display";
+    // setup the registry
+    wl_registry_add_listener(m_registry, &s_registryListener, this);
+    wl_display_dispatch(m_display);
+}
+
+WaylandBackend::~WaylandBackend()
+{
+    if (m_overlay) {
+        wl_egl_window_destroy(m_overlay);
+    }
+    if (m_shellSurface) {
+        wl_shell_surface_destroy(m_shellSurface);
+    }
+    if (m_surface) {
+        wl_surface_destroy(m_surface);
+    }
+    if (m_shell) {
+        wl_shell_destroy(m_shell);
+    }
+    if (m_compositor) {
+        wl_compositor_destroy(m_compositor);
+    }
+    if (m_registry) {
+        wl_registry_destroy(m_registry);
+    }
+    if (m_display) {
+        wl_display_flush(m_display);
+        wl_display_disconnect(m_display);
+    }
+    kDebug(1212) << "Destroyed Wayland display";
+}
+
+bool WaylandBackend::createSurface()
+{
+    m_surface = wl_compositor_create_surface(m_compositor);
+    if (!m_surface) {
+        kError(1212) << "Creating Wayland Surface failed";
+        return false;
+    }
+    // map the surface as fullscreen
+    m_shellSurface = wl_shell_get_shell_surface(m_shell, m_surface);
+    wl_shell_surface_add_listener(m_shellSurface, &s_shellSurfaceListener, this);
+
+    // TODO: do something better than displayWidth/displayHeight
+    m_overlay = wl_egl_window_create(m_surface, displayWidth(), displayHeight());
+    if (!m_overlay) {
+        kError(1212) << "Creating Wayland Egl window failed";
+        return false;
+    }
+//     wl_shell_surface_set_fullscreen(m_shellSurface, \
WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT, 0, NULL); +    \
wl_shell_surface_set_toplevel(m_shellSurface); +    handleConfigure(this, \
m_shellSurface, 0, displayWidth(), displayHeight()); +
+    return true;
+}
+
+}
+
+EglWaylandBackend::EglWaylandBackend()
+    : OpenGLBackend()
+    , m_context(EGL_NO_CONTEXT)
+    , m_wayland(new Wayland::WaylandBackend)
+{
+    kDebug(1212) << "Connected to Wayland display?" << (m_wayland->display() ? "yes" \
: "no" ); +    if (!m_wayland->display()) {
+        setFailed("Could not connect to Wayland compositor");
+        return;
+    }
+    initializeEgl();
+    init();
+    // Egl is always direct rendering
+    setIsDirectRendering(true);
+}
+
+EglWaylandBackend::~EglWaylandBackend()
+{
+    cleanupGL();
+    eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+    eglDestroyContext(m_display, m_context);
+    eglDestroySurface(m_display, m_surface);
+    eglTerminate(m_display);
+    eglReleaseThread();
+}
+
+bool EglWaylandBackend::initializeEgl()
+{
+    m_display = eglGetDisplay(m_wayland->display());
+    if (m_display == EGL_NO_DISPLAY)
+        return false;
+
+    EGLint major, minor;
+    if (eglInitialize(m_display, &major, &minor) == EGL_FALSE)
+        return false;
+    EGLint error = eglGetError();
+    if (error != EGL_SUCCESS) {
+        kWarning(1212) << "Error during eglInitialize " << error;
+        return false;
+    }
+    kDebug(1212) << "Egl Initialize succeeded";
+
+#ifdef KWIN_HAVE_OPENGLES
+    eglBindAPI(EGL_OPENGL_ES_API);
+#else
+    if (eglBindAPI(EGL_OPENGL_API) == EGL_FALSE) {
+        kError(1212) << "bind OpenGL API failed";
+        return false;
+    }
+#endif
+    kDebug(1212) << "EGL version: " << major << "." << minor;
+    return true;
+}
+
+void EglWaylandBackend::init()
+{
+    if (!initRenderingContext()) {
+        setFailed("Could not initialize rendering context");
+        return;
+    }
+
+    initEGL();
+    GLPlatform *glPlatform = GLPlatform::instance();
+    glPlatform->detect(EglPlatformInterface);
+    glPlatform->printResults();
+    initGL(EglPlatformInterface);
+}
+
+bool EglWaylandBackend::initRenderingContext()
+{
+    initBufferConfigs();
+
+#ifdef KWIN_HAVE_OPENGLES
+    const EGLint context_attribs[] = {
+        EGL_CONTEXT_CLIENT_VERSION, 2,
+        EGL_NONE
+    };
+
+    m_context = eglCreateContext(m_display, m_config, EGL_NO_CONTEXT, \
context_attribs); +#else
+    const EGLint context_attribs_31_core[] = {
+        EGL_CONTEXT_MAJOR_VERSION_KHR, 3,
+        EGL_CONTEXT_MINOR_VERSION_KHR, 1,
+        EGL_CONTEXT_FLAGS_KHR,         \
EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR, +        EGL_NONE
+    };
+
+    const EGLint context_attribs_legacy[] = {
+        EGL_NONE
+    };
+
+    const QByteArray eglExtensions = eglQueryString(m_display, EGL_EXTENSIONS);
+    const QList<QByteArray> extensions = eglExtensions.split(' ');
+
+    // Try to create a 3.1 core context
+    if (options->glCoreProfile() && extensions.contains("EGL_KHR_create_context"))
+        m_context = eglCreateContext(m_display, m_config, EGL_NO_CONTEXT, \
context_attribs_31_core); +
+    if (m_context == EGL_NO_CONTEXT)
+        m_context = eglCreateContext(m_display, m_config, EGL_NO_CONTEXT, \
context_attribs_legacy); +#endif
+
+    if (m_context == EGL_NO_CONTEXT) {
+        kError(1212) << "Create Context failed";
+        return false;
+    }
+
+    if (!m_wayland->createSurface()) {
+        return false;
+    }
+
+    m_surface = eglCreateWindowSurface(m_display, m_config, m_wayland->overlay(), \
NULL); +    if (m_surface == EGL_NO_SURFACE) {
+        kError(1212) << "Create Window Surface failed";
+        return false;
+    }
+
+    return makeContextCurrent();
+}
+
+bool EglWaylandBackend::makeContextCurrent()
+{
+    if (eglMakeCurrent(m_display, m_surface, m_surface, m_context) == EGL_FALSE) {
+        kError(1212) << "Make Context Current failed";
+        return false;
+    }
+
+    EGLint error = eglGetError();
+    if (error != EGL_SUCCESS) {
+        kWarning(1212) << "Error occurred while creating context " << error;
+        return false;
+    }
+    return true;
+}
+
+bool EglWaylandBackend::initBufferConfigs()
+{
+    const EGLint config_attribs[] = {
+        EGL_SURFACE_TYPE,         EGL_WINDOW_BIT,
+        EGL_RED_SIZE,             1,
+        EGL_GREEN_SIZE,           1,
+        EGL_BLUE_SIZE,            1,
+        EGL_ALPHA_SIZE,           0,
+#ifdef KWIN_HAVE_OPENGLES
+        EGL_RENDERABLE_TYPE,      EGL_OPENGL_ES2_BIT,
+#else
+        EGL_RENDERABLE_TYPE,      EGL_OPENGL_BIT,
+#endif
+        EGL_CONFIG_CAVEAT,        EGL_NONE,
+        EGL_NONE,
+    };
+
+    EGLint count;
+    EGLConfig configs[1024];
+    if (eglChooseConfig(m_display, config_attribs, configs, 1, &count) == EGL_FALSE) \
{ +        kError(1212) << "choose config failed";
+        return false;
+    }
+    if (count != 1) {
+        kError(1212) << "choose config did not return a config" << count;
+        return false;
+    }
+    m_config = configs[0];
+
+    return true;
+}
+
+void EglWaylandBackend::present()
+{
+    setLastDamage(QRegion());
+    wl_display_dispatch_pending(m_wayland->display());
+    wl_display_flush(m_wayland->display());
+    eglSwapBuffers(m_display, m_surface);
+    eglWaitGL();
+}
+
+void EglWaylandBackend::screenGeometryChanged(const QSize &size)
+{
+    Q_UNUSED(size)
+    // no backend specific code needed
+    // TODO: base implementation in OpenGLBackend
+}
+
+SceneOpenGL::TexturePrivate \
*EglWaylandBackend::createBackendTexture(SceneOpenGL::Texture *texture) +{
+    return new EglWaylandTexture(texture, this);
+}
+
+void EglWaylandBackend::prepareRenderingFrame()
+{
+    if (!lastDamage().isEmpty())
+        present();
+    eglWaitNative(EGL_CORE_NATIVE_ENGINE);
+    startRenderTimer();
+}
+
+void EglWaylandBackend::endRenderingFrame(const QRegion &damage)
+{
+    setLastDamage(damage);
+    glFlush();
+}
+
+Shm *EglWaylandBackend::shm()
+{
+    if (m_shm.isNull()) {
+        m_shm.reset(new Shm);
+    }
+    return m_shm.data();
+}
+
+/************************************************
+ * EglTexture
+ ************************************************/
+
+EglWaylandTexture::EglWaylandTexture(KWin::SceneOpenGL::Texture *texture, \
KWin::EglWaylandBackend *backend) +    : SceneOpenGL::TexturePrivate()
+    , q(texture)
+    , m_backend(backend)
+    , m_referencedPixmap(XCB_PIXMAP_NONE)
+{
+    m_target = GL_TEXTURE_2D;
+}
+
+EglWaylandTexture::~EglWaylandTexture()
+{
+}
+
+OpenGLBackend *EglWaylandTexture::backend()
+{
+    return m_backend;
+}
+
+void EglWaylandTexture::findTarget()
+{
+    if (m_target != GL_TEXTURE_2D) {
+        m_target = GL_TEXTURE_2D;
+    }
+}
+
+bool EglWaylandTexture::loadTexture(const Pixmap &pix, const QSize &size, int depth)
+{
+    // HACK: egl wayland platform doesn't support texture from X11 pixmap through \
the KHR_image_pixmap +    // extension. To circumvent this problem we copy the pixmap \
content into a SHM image and from there +    // to the OpenGL texture. This is a \
temporary solution. In future we won't need to get the content +    // from X11 \
pixmaps. That's what we have XWayland for to get the content into a nice Wayland \
buffer. +    Q_UNUSED(depth)
+    if (pix == XCB_PIXMAP_NONE)
+        return false;
+    m_referencedPixmap = pix;
+
+    Shm *shm = m_backend->shm();
+    if (!shm->isValid()) {
+        return false;
+    }
+
+    xcb_shm_get_image_cookie_t cookie = xcb_shm_get_image_unchecked(connection(), \
pix, 0, 0, size.width(), +        size.height(), ~0, XCB_IMAGE_FORMAT_Z_PIXMAP, \
shm->segment(), 0); +
+    glGenTextures(1, &m_texture);
+    q->setWrapMode(GL_CLAMP_TO_EDGE);
+    q->setFilter(GL_LINEAR);
+    q->bind();
+
+    ScopedCPointer<xcb_shm_get_image_reply_t> \
image(xcb_shm_get_image_reply(connection(), cookie, NULL)); +    if (image.isNull()) \
{ +        return false;
+    }
+
+    // TODO: other formats
+#ifndef KWIN_HAVE_OPENGLES
+    glTexImage2D(m_target, 0, GL_RGBA8, size.width(), size.height(), 0,
+                 GL_BGRA, GL_UNSIGNED_BYTE, shm->buffer());
+#endif
+
+    q->unbind();
+    checkGLError("load texture");
+    q->setYInverted(true);
+    m_size = size;
+    updateMatrix();
+    return true;
+}
+
+bool EglWaylandTexture::update(const QRegion &damage)
+{
+    if (m_referencedPixmap == XCB_PIXMAP_NONE) {
+        return false;
+    }
+
+    Shm *shm = m_backend->shm();
+    if (!shm->isValid()) {
+        return false;
+    }
+
+    // TODO: optimize by only updating the damaged areas
+    const QRect &damagedRect = damage.boundingRect();
+    xcb_shm_get_image_cookie_t cookie = xcb_shm_get_image_unchecked(connection(), \
m_referencedPixmap, +        damagedRect.x(), damagedRect.y(), damagedRect.width(), \
damagedRect.height(), +        ~0, XCB_IMAGE_FORMAT_Z_PIXMAP, shm->segment(), 0);
+
+    q->bind();
+
+    ScopedCPointer<xcb_shm_get_image_reply_t> \
image(xcb_shm_get_image_reply(connection(), cookie, NULL)); +    if (image.isNull()) \
{ +        return false;
+    }
+
+    // TODO: other formats
+#ifndef KWIN_HAVE_OPENGLES
+    glTexSubImage2D(m_target, 0, damagedRect.x(), damagedRect.y(), \
damagedRect.width(), damagedRect.height(), GL_BGRA, GL_UNSIGNED_BYTE, shm->buffer()); \
+#endif +
+    q->unbind();
+    checkGLError("update texture");
+    return true;
+}
+
+Shm::Shm()
+    : m_shmId(-1)
+    , m_buffer(NULL)
+    , m_segment(XCB_NONE)
+    , m_valid(false)
+{
+    m_valid = init();
+}
+
+Shm::~Shm()
+{
+    if (m_valid) {
+        xcb_shm_detach(connection(), m_segment);
+        shmdt(m_buffer);
+    }
+}
+
+bool Shm::init()
+{
+    const xcb_query_extension_reply_t *ext = xcb_get_extension_data(connection(), \
&xcb_shm_id); +    if (!ext || !ext->present) {
+        kDebug(1212) << "SHM extension not available";
+        return false;
+    }
+    ScopedCPointer<xcb_shm_query_version_reply_t> \
version(xcb_shm_query_version_reply(connection(), +        \
xcb_shm_query_version_unchecked(connection()), NULL)); +    if (version.isNull()) {
+        kDebug(1212) << "Failed to get SHM extension version information";
+        return false;
+    }
+    const int MAXSIZE = 4096 * 2048 * 4; // TODO check there are not larger windows
+    m_shmId = shmget(IPC_PRIVATE, MAXSIZE, IPC_CREAT | 0600);
+    if (m_shmId < 0) {
+        kDebug(1212) << "Failed to allocate SHM segment";
+        return false;
+    }
+    m_buffer = shmat(m_shmId, NULL, 0 /*read/write*/);
+    if (-1 == reinterpret_cast<long>(m_buffer)) {
+        kDebug(1212) << "Failed to attach SHM segment";
+        shmctl(m_shmId, IPC_RMID, NULL);
+        return false;
+    }
+    shmctl(m_shmId, IPC_RMID, NULL);
+
+    m_segment = xcb_generate_id(connection());
+    const xcb_void_cookie_t cookie = xcb_shm_attach_checked(connection(), m_segment, \
m_shmId, false); +    ScopedCPointer<xcb_generic_error_t> \
error(xcb_request_check(connection(), cookie)); +    if (!error.isNull()) {
+        kDebug(1212) << "xcb_shm_attach error: " << error->error_code;
+        shmdt(m_buffer);
+        return false;
+    }
+
+    return true;
+}
+
+} // namespace
diff --git a/kwin/egl_wayland_backend.h b/kwin/egl_wayland_backend.h
new file mode 100644
index 0000000..711ad1d
--- /dev/null
+++ b/kwin/egl_wayland_backend.h
@@ -0,0 +1,225 @@
+/********************************************************************
+ KWin - the KDE window manager
+ This file is part of the KDE project.
+
+Copyright (C) 2013 Martin Gräßlin <mgraesslin@kde.org>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*********************************************************************/
+#ifndef KWIN_EGL_WAYLAND_BACKEND_H
+#define KWIN_EGL_WAYLAND_BACKEND_H
+#include "scene_opengl.h"
+// wayland
+#include <wayland-client.h>
+#include <wayland-egl.h>
+// xcb
+#include <xcb/shm.h>
+
+namespace KWin
+{
+
+namespace Wayland
+{
+/**
+* @brief Class encapsulating all Wayland data structures needed by the Egl backend.
+*
+* It creates the connection to the Wayland Compositor, set's up the registry and \
creates +* the Wayland surface and it's shell and egl mapping.
+*/
+class WaylandBackend
+{
+public:
+    WaylandBackend();
+    virtual ~WaylandBackend();
+    wl_display *display();
+    wl_registry *registry();
+    void setCompositor(wl_compositor *c);
+    wl_compositor *compositor();
+    void setShell(wl_shell *s);
+    wl_shell *shell();
+    wl_egl_window *overlay();
+
+    bool createSurface();
+private:
+    wl_display *m_display;
+    wl_registry *m_registry;
+    wl_compositor *m_compositor;
+    wl_shell *m_shell;
+    wl_surface *m_surface;
+    wl_egl_window *m_overlay;
+    wl_shell_surface *m_shellSurface;
+};
+
+inline
+wl_display *WaylandBackend::display()
+{
+    return m_display;
+}
+
+inline
+wl_registry *WaylandBackend::registry()
+{
+    return m_registry;
+}
+
+inline
+void WaylandBackend::setCompositor(wl_compositor *c)
+{
+    m_compositor = c;
+}
+
+inline
+wl_compositor *WaylandBackend::compositor()
+{
+    return m_compositor;
+}
+
+inline
+wl_egl_window *WaylandBackend::overlay()
+{
+    return m_overlay;
+}
+
+inline
+void WaylandBackend::setShell(wl_shell *s)
+{
+    m_shell = s;
+}
+
+inline
+wl_shell *WaylandBackend::shell()
+{
+    return m_shell;
+}
+
+} // namespace Wayland
+
+class Shm;
+
+/**
+ * @brief OpenGL Backend using Egl on a Wayland surface.
+ *
+ * This Backend is the basis for a session compositor running on top of a Wayland \
system compositor. + * It creates a Surface as large as the screen and maps it as a \
fullscreen shell surface on the + * system compositor. The OpenGL context is created \
on the Wayland surface, so for rendering X11 is + * not involved.
+ *
+ * At the moment the backend is still rather limited. For getting textures from \
pixmap it uses the + * XShm library. This is currently a hack and only as proof of \
concept till we support texture from + * Wayland buffers. From then on we should use \
XWayland for texture mapping. + *
+ * Also in repainting the backend is currently still rather limited. Only supported \
mode is fullscreen + * repaints, which is obviously not optimal. Best solution is \
probably to go for buffer_age extension + * and make it the only available solution \
next to fullscreen repaints. + **/
+class EglWaylandBackend : public OpenGLBackend
+{
+public:
+    EglWaylandBackend();
+    virtual ~EglWaylandBackend();
+    virtual void screenGeometryChanged(const QSize &size);
+    virtual SceneOpenGL::TexturePrivate *createBackendTexture(SceneOpenGL::Texture \
*texture); +    virtual void prepareRenderingFrame();
+    virtual void endRenderingFrame(const QRegion &damage);
+    Shm *shm();
+
+protected:
+    virtual void present();
+
+private:
+    void init();
+    bool initializeEgl();
+    bool initBufferConfigs();
+    bool initRenderingContext();
+    bool makeContextCurrent();
+    EGLDisplay m_display;
+    EGLConfig m_config;
+    EGLSurface m_surface;
+    EGLContext m_context;
+    QScopedPointer<Wayland::WaylandBackend> m_wayland;
+    QScopedPointer<Shm> m_shm;
+    friend class EglWaylandTexture;
+};
+
+/**
+ * @brief Texture using an EGLImageKHR.
+ **/
+class EglWaylandTexture : public SceneOpenGL::TexturePrivate
+{
+public:
+    virtual ~EglWaylandTexture();
+    virtual void findTarget();
+    virtual bool loadTexture(const Pixmap& pix, const QSize& size, int depth);
+    virtual OpenGLBackend *backend();
+    virtual bool update(const QRegion &damage);
+
+private:
+    friend class EglWaylandBackend;
+    EglWaylandTexture(SceneOpenGL::Texture *texture, EglWaylandBackend *backend);
+    SceneOpenGL::Texture *q;
+    EglWaylandBackend *m_backend;
+    /**
+     * The Pixmap of the window content. Get's updated in loadTexture.
+     */
+    xcb_pixmap_t m_referencedPixmap;
+};
+
+/**
+ * @brief Small helper class to encapsulate SHM related functionality.
+ *
+ */
+class Shm
+{
+public:
+    Shm();
+    ~Shm();
+    int shmId() const;
+    void *buffer() const;
+    xcb_shm_seg_t segment() const;
+    bool isValid() const;
+private:
+    bool init();
+    int m_shmId;
+    void *m_buffer;
+    xcb_shm_seg_t m_segment;
+    bool m_valid;
+};
+
+inline
+void *Shm::buffer() const
+{
+    return m_buffer;
+}
+
+inline
+bool Shm::isValid() const
+{
+    return m_valid;
+}
+
+inline
+xcb_shm_seg_t Shm::segment() const
+{
+    return m_segment;
+}
+
+inline
+int Shm::shmId() const
+{
+    return m_shmId;
+}
+
+} // namespace
+
+#endif //  KWIN_EGL_ON_X_BACKEND_H
diff --git a/kwin/scene_opengl.cpp b/kwin/scene_opengl.cpp
index 4201136..2f54321 100644
--- a/kwin/scene_opengl.cpp
+++ b/kwin/scene_opengl.cpp
@@ -24,6 +24,11 @@ along with this program.  If not, see \
<http://www.gnu.org/licenses/>.  #include "scene_opengl.h"
 #ifdef KWIN_HAVE_EGL
 #include "eglonxbackend.h"
+// for Wayland
+#include "config-workspace.h"
+#ifdef WAYLAND_FOUND
+#include "egl_wayland_backend.h"
+#endif
 #endif
 #ifndef KWIN_HAVE_OPENGLES
 #include "glxbackend.h"
@@ -173,13 +178,15 @@ SceneOpenGL *SceneOpenGL::createScene()
     platformInterface = GlxPlatformInterface;
 #endif
 
+    const QByteArray envOpenGLInterface(qgetenv("KWIN_OPENGL_INTERFACE"));
 #ifdef KWIN_HAVE_EGL
 #ifdef KWIN_HAVE_OPENGLES
     // for OpenGL ES we need to use the Egl Backend
     platformInterface = EglPlatformInterface;
 #else
     // check environment variable
-    if (qstrcmp(qgetenv("KWIN_OPENGL_INTERFACE"), "egl") == 0) {
+    if (qstrcmp(envOpenGLInterface, "egl") == 0 ||
+            qstrcmp(envOpenGLInterface, "egl_wayland") == 0) {
         kDebug(1212) << "Forcing EGL native interface through environment variable";
         platformInterface = EglPlatformInterface;
     }
@@ -194,8 +201,16 @@ SceneOpenGL *SceneOpenGL::createScene()
         break;
     case EglPlatformInterface:
 #ifdef KWIN_HAVE_EGL
+#ifdef WAYLAND_FOUND
+        if (qstrcmp(envOpenGLInterface, "egl_wayland") == 0) {
+            backend = new EglWaylandBackend();
+        } else {
+            backend = new EglOnXBackend();
+        }
+#else
         backend = new EglOnXBackend();
 #endif
+#endif
         break;
     default:
         // no backend available
@@ -941,6 +956,12 @@ bool SceneOpenGL::Texture::load(const Pixmap& pix, const QSize& \
size,  return d->loadTexture(pix, size, depth);
 }
 
+bool SceneOpenGL::Texture::update(const QRegion &damage)
+{
+    Q_D(Texture);
+    return d->update(damage);
+}
+
 //****************************************
 // SceneOpenGL::Texture
 //****************************************
@@ -952,6 +973,12 @@ SceneOpenGL::TexturePrivate::~TexturePrivate()
 {
 }
 
+bool SceneOpenGL::TexturePrivate::update(const QRegion &damage)
+{
+    Q_UNUSED(damage)
+    return true;
+}
+
 //****************************************
 // SceneOpenGL::Window
 //****************************************
@@ -1803,9 +1830,11 @@ bool OpenGLWindowPixmap::bind()
 {
     if (!m_texture->isNull()) {
         if (!toplevel()->damage().isEmpty()) {
+            const bool success = m_texture->update(toplevel()->damage());
             // mipmaps need to be updated
             m_texture->setDirty();
             toplevel()->resetDamage();
+            return success;
         }
         return true;
     }
diff --git a/kwin/scene_opengl.h b/kwin/scene_opengl.h
index 043ef11..33a59b9 100644
--- a/kwin/scene_opengl.h
+++ b/kwin/scene_opengl.h
@@ -173,6 +173,7 @@ public:
     virtual void findTarget() = 0;
     virtual bool loadTexture(const Pixmap& pix, const QSize& size, int depth) = 0;
     virtual OpenGLBackend *backend() = 0;
+    virtual bool update(const QRegion &damage);
 
 protected:
     TexturePrivate();
@@ -195,6 +196,7 @@ public:
     virtual bool load(const QImage& image, GLenum target = GL_TEXTURE_2D);
     virtual bool load(const QPixmap& pixmap, GLenum target = GL_TEXTURE_2D);
     virtual void discard();
+    bool update(const QRegion &damage);
 
 protected:
     void findTarget();


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic