Split out non-EGL functionality of WaylandBackend into own source files
The functionality to create the connection to a Wayland compositor and creating a fullscreen surface is moved into wayland_backend.(h|cpp). The wl_egl_window for the surface is moved into the EglWaylandBackend to have the actual WaylandBackend free from Egl. This will allow in future to implement other compositing backends for Wayland which do not use egl. This means that egl is no longer a build requirement for the wayland related functionality.
This commit is contained in:
parent
ff562dc8bf
commit
baf477ac00
5 changed files with 928 additions and 807 deletions
|
@ -65,7 +65,7 @@ endif()
|
|||
|
||||
include_directories(${XCB_INCLUDE_DIR})
|
||||
|
||||
if(WAYLAND_FOUND AND KWIN_HAVE_EGL)
|
||||
if(WAYLAND_FOUND)
|
||||
include_directories(${WAYLAND_INCLUDE_DIR})
|
||||
endif()
|
||||
|
||||
|
@ -214,8 +214,11 @@ if(KWIN_HAVE_EGL)
|
|||
set(kwin_KDEINIT_SRCS ${kwin_KDEINIT_SRCS} eglonxbackend.cpp)
|
||||
endif()
|
||||
|
||||
if(WAYLAND_FOUND AND KWIN_HAVE_EGL)
|
||||
set(kwin_KDEINIT_SRCS ${kwin_KDEINIT_SRCS} egl_wayland_backend.cpp)
|
||||
if(WAYLAND_FOUND)
|
||||
set(kwin_KDEINIT_SRCS ${kwin_KDEINIT_SRCS} wayland_backend.cpp)
|
||||
if(KWIN_HAVE_EGL)
|
||||
set(kwin_KDEINIT_SRCS ${kwin_KDEINIT_SRCS} egl_wayland_backend.cpp)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
kconfig_add_kcfg_files(kwin_KDEINIT_SRCS settings.kcfgc)
|
||||
|
@ -281,11 +284,14 @@ set(kwin_XCB_LIBS
|
|||
|
||||
set(kwin_WAYLAND_LIBS
|
||||
${WAYLAND_CLIENT_LIBRARIES}
|
||||
${WAYLAND_EGL_LIBRARIES}
|
||||
${XCB_SHM_LIBRARY}
|
||||
${XCB_XTEST_LIBRARY}
|
||||
)
|
||||
|
||||
set(kwin_WAYLAND_EGL_LIBS
|
||||
${WAYLAND_EGL_LIBRARIES}
|
||||
)
|
||||
|
||||
set(kwin_OPENGL_LIBS )
|
||||
|
||||
find_library(XF86VM_LIBRARY Xxf86vm)
|
||||
|
@ -316,8 +322,11 @@ set(kwinLibs
|
|||
${kwin_OPENGL_LIBS}
|
||||
)
|
||||
|
||||
if(WAYLAND_FOUND AND KWIN_HAVE_EGL)
|
||||
if(WAYLAND_FOUND)
|
||||
set(kwinLibs ${kwinLibs} ${kwin_WAYLAND_LIBS})
|
||||
if(KWIN_HAVE_EGL)
|
||||
set(kwinLibs ${kwinLibs} ${kwin_WAYLAND_EGL_LIBS})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
kf5_add_kdeinit_executable( kwin ${kwin_KDEINIT_SRCS})
|
||||
|
|
|
@ -20,622 +20,35 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
#define WL_EGL_PLATFORM 1
|
||||
#include "egl_wayland_backend.h"
|
||||
// kwin
|
||||
#include "cursor.h"
|
||||
#include "options.h"
|
||||
#include "wayland_backend.h"
|
||||
// kwin libs
|
||||
#include <kwinglplatform.h>
|
||||
// KDE
|
||||
#include <KDE/KDebug>
|
||||
// Qt
|
||||
#include <QSocketNotifier>
|
||||
#include <QTemporaryFile>
|
||||
#include <QOpenGLContext>
|
||||
// xcb
|
||||
#include <xcb/xtest.h>
|
||||
// Wayland
|
||||
#include <wayland-client-protocol.h>
|
||||
// system
|
||||
#include <linux/input.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/shm.h>
|
||||
#include <sys/types.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)));
|
||||
} else if (strcmp(interface, "wl_seat") == 0) {
|
||||
d->createSeat(name);
|
||||
} else if (strcmp(interface, "wl_shm") == 0) {
|
||||
d->createShm(name);
|
||||
}
|
||||
qDebug() << "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(shellSurface);
|
||||
reinterpret_cast<WaylandBackend*>(data)->ping(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)
|
||||
}
|
||||
|
||||
static void seatHandleCapabilities(void *data, wl_seat *seat, uint32_t capabilities)
|
||||
{
|
||||
WaylandSeat *s = reinterpret_cast<WaylandSeat*>(data);
|
||||
if (seat != s->seat()) {
|
||||
return;
|
||||
}
|
||||
s->changed(capabilities);
|
||||
}
|
||||
|
||||
static void pointerHandleEnter(void *data, wl_pointer *pointer, uint32_t serial, wl_surface *surface,
|
||||
wl_fixed_t sx, wl_fixed_t sy)
|
||||
{
|
||||
Q_UNUSED(data)
|
||||
Q_UNUSED(pointer)
|
||||
Q_UNUSED(surface)
|
||||
Q_UNUSED(sx)
|
||||
Q_UNUSED(sy)
|
||||
WaylandSeat *seat = reinterpret_cast<WaylandSeat*>(data);
|
||||
seat->pointerEntered(serial);
|
||||
}
|
||||
|
||||
static void pointerHandleLeave(void *data, wl_pointer *pointer, uint32_t serial, wl_surface *surface)
|
||||
{
|
||||
Q_UNUSED(data)
|
||||
Q_UNUSED(pointer)
|
||||
Q_UNUSED(serial)
|
||||
Q_UNUSED(surface)
|
||||
}
|
||||
|
||||
static void pointerHandleMotion(void *data, wl_pointer *pointer, uint32_t time, wl_fixed_t sx, wl_fixed_t sy)
|
||||
{
|
||||
Q_UNUSED(data)
|
||||
Q_UNUSED(pointer)
|
||||
Q_UNUSED(time)
|
||||
xcb_test_fake_input(connection(), XCB_MOTION_NOTIFY, 0, XCB_TIME_CURRENT_TIME, XCB_WINDOW_NONE,
|
||||
wl_fixed_to_int(sx), wl_fixed_to_int(sy), 0);
|
||||
}
|
||||
|
||||
static void pointerHandleButton(void *data, wl_pointer *pointer, uint32_t serial, uint32_t time,
|
||||
uint32_t button, uint32_t state)
|
||||
{
|
||||
Q_UNUSED(data)
|
||||
Q_UNUSED(pointer)
|
||||
Q_UNUSED(serial)
|
||||
Q_UNUSED(time)
|
||||
uint8_t type = XCB_BUTTON_PRESS;
|
||||
if (state == WL_POINTER_BUTTON_STATE_RELEASED) {
|
||||
type = XCB_BUTTON_RELEASE;
|
||||
}
|
||||
// TODO: there must be a better way for mapping
|
||||
uint8_t xButton = 0;
|
||||
switch (button) {
|
||||
case BTN_LEFT:
|
||||
xButton = XCB_BUTTON_INDEX_1;
|
||||
break;
|
||||
case BTN_RIGHT:
|
||||
xButton = XCB_BUTTON_INDEX_3;
|
||||
break;
|
||||
case BTN_MIDDLE:
|
||||
xButton = XCB_BUTTON_INDEX_2;
|
||||
break;
|
||||
default:
|
||||
// TODO: add more buttons
|
||||
return;
|
||||
}
|
||||
xcb_test_fake_input(connection(), type, xButton, XCB_TIME_CURRENT_TIME, XCB_WINDOW_NONE, 0, 0, 0);
|
||||
}
|
||||
|
||||
static void pointerHandleAxis(void *data, wl_pointer *pointer, uint32_t time, uint32_t axis, wl_fixed_t value)
|
||||
{
|
||||
Q_UNUSED(data)
|
||||
Q_UNUSED(pointer)
|
||||
Q_UNUSED(time)
|
||||
uint8_t xButton = 0;
|
||||
const int delta = wl_fixed_to_int(value);
|
||||
if (delta == 0) {
|
||||
return;
|
||||
}
|
||||
switch (axis) {
|
||||
case WL_POINTER_AXIS_VERTICAL_SCROLL:
|
||||
xButton = delta > 0 ? XCB_BUTTON_INDEX_5 : XCB_BUTTON_INDEX_4;
|
||||
break;
|
||||
case WL_POINTER_AXIS_HORIZONTAL_SCROLL:
|
||||
// no enum values defined for buttons larger than 5
|
||||
xButton = delta > 0 ? 7 : 6;
|
||||
break;
|
||||
default:
|
||||
// doesn't exist
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < qAbs(delta); ++i) {
|
||||
xcb_test_fake_input(connection(), XCB_BUTTON_PRESS, xButton, XCB_TIME_CURRENT_TIME, XCB_WINDOW_NONE, 0, 0, 0);
|
||||
xcb_test_fake_input(connection(), XCB_BUTTON_RELEASE, xButton, XCB_TIME_CURRENT_TIME, XCB_WINDOW_NONE, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void keyboardHandleKeymap(void *data, wl_keyboard *keyboard,
|
||||
uint32_t format, int fd, uint32_t size)
|
||||
{
|
||||
Q_UNUSED(data)
|
||||
Q_UNUSED(keyboard)
|
||||
Q_UNUSED(format)
|
||||
Q_UNUSED(fd)
|
||||
Q_UNUSED(size)
|
||||
}
|
||||
|
||||
static void keyboardHandleEnter(void *data, wl_keyboard *keyboard,
|
||||
uint32_t serial, wl_surface *surface,
|
||||
wl_array *keys)
|
||||
{
|
||||
Q_UNUSED(data)
|
||||
Q_UNUSED(keyboard)
|
||||
Q_UNUSED(serial)
|
||||
Q_UNUSED(surface)
|
||||
Q_UNUSED(keys)
|
||||
}
|
||||
|
||||
static void keyboardHandleLeave(void *data, wl_keyboard *keyboard, uint32_t serial, wl_surface *surface)
|
||||
{
|
||||
Q_UNUSED(data)
|
||||
Q_UNUSED(keyboard)
|
||||
Q_UNUSED(serial)
|
||||
Q_UNUSED(surface)
|
||||
}
|
||||
|
||||
static void keyboardHandleKey(void *data, wl_keyboard *keyboard, uint32_t serial, uint32_t time,
|
||||
uint32_t key, uint32_t state)
|
||||
{
|
||||
Q_UNUSED(data)
|
||||
Q_UNUSED(keyboard)
|
||||
Q_UNUSED(serial)
|
||||
Q_UNUSED(time)
|
||||
uint8_t type = XCB_KEY_PRESS;
|
||||
if (state == WL_KEYBOARD_KEY_STATE_RELEASED) {
|
||||
type = XCB_KEY_RELEASE;
|
||||
}
|
||||
xcb_test_fake_input(connection(), type, key + 8 /*magic*/, XCB_TIME_CURRENT_TIME, XCB_WINDOW_NONE, 0, 0, 0);
|
||||
}
|
||||
|
||||
static void keyboardHandleModifiers(void *data, wl_keyboard *keyboard, uint32_t serial, uint32_t modsDepressed,
|
||||
uint32_t modsLatched, uint32_t modsLocked, uint32_t group)
|
||||
{
|
||||
Q_UNUSED(data)
|
||||
Q_UNUSED(keyboard)
|
||||
Q_UNUSED(serial)
|
||||
Q_UNUSED(modsDepressed)
|
||||
Q_UNUSED(modsLatched)
|
||||
Q_UNUSED(modsLocked)
|
||||
Q_UNUSED(group)
|
||||
}
|
||||
|
||||
// handlers
|
||||
static const struct wl_registry_listener s_registryListener = {
|
||||
registryHandleGlobal,
|
||||
registryHandleGlobalRemove
|
||||
};
|
||||
|
||||
static const struct wl_shell_surface_listener s_shellSurfaceListener = {
|
||||
handlePing,
|
||||
handleConfigure,
|
||||
handlePopupDone
|
||||
};
|
||||
|
||||
static const struct wl_pointer_listener s_pointerListener = {
|
||||
pointerHandleEnter,
|
||||
pointerHandleLeave,
|
||||
pointerHandleMotion,
|
||||
pointerHandleButton,
|
||||
pointerHandleAxis
|
||||
};
|
||||
|
||||
static const struct wl_keyboard_listener s_keyboardListener = {
|
||||
keyboardHandleKeymap,
|
||||
keyboardHandleEnter,
|
||||
keyboardHandleLeave,
|
||||
keyboardHandleKey,
|
||||
keyboardHandleModifiers,
|
||||
};
|
||||
|
||||
static const struct wl_seat_listener s_seatListener = {
|
||||
seatHandleCapabilities
|
||||
};
|
||||
|
||||
CursorData::CursorData(ShmPool *pool)
|
||||
: m_cursor(NULL)
|
||||
, m_valid(init(pool))
|
||||
{
|
||||
}
|
||||
|
||||
CursorData::~CursorData()
|
||||
{
|
||||
}
|
||||
|
||||
bool CursorData::init(ShmPool *pool)
|
||||
{
|
||||
QScopedPointer<xcb_xfixes_get_cursor_image_reply_t, QScopedPointerPodDeleter> cursor(
|
||||
xcb_xfixes_get_cursor_image_reply(connection(),
|
||||
xcb_xfixes_get_cursor_image_unchecked(connection()),
|
||||
NULL));
|
||||
if (cursor.isNull()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QImage cursorImage((uchar *) xcb_xfixes_get_cursor_image_cursor_image(cursor.data()), cursor->width, cursor->height,
|
||||
QImage::Format_ARGB32_Premultiplied);
|
||||
if (cursorImage.isNull()) {
|
||||
return false;
|
||||
}
|
||||
m_size = QSize(cursor->width, cursor->height);
|
||||
|
||||
m_cursor = pool->createBuffer(cursorImage);
|
||||
if (!m_cursor) {
|
||||
qDebug() << "Creating cursor buffer failed";
|
||||
return false;
|
||||
}
|
||||
|
||||
m_hotSpot = QPoint(cursor->xhot, cursor->yhot);
|
||||
return true;
|
||||
}
|
||||
|
||||
X11CursorTracker::X11CursorTracker(wl_pointer *pointer, WaylandBackend *backend, QObject* parent)
|
||||
: QObject(parent)
|
||||
, m_pointer(pointer)
|
||||
, m_backend(backend)
|
||||
, m_cursor(wl_compositor_create_surface(backend->compositor()))
|
||||
, m_enteredSerial(0)
|
||||
, m_lastX11Cursor(0)
|
||||
{
|
||||
Cursor::self()->startCursorTracking();
|
||||
connect(Cursor::self(), SIGNAL(cursorChanged(uint32_t)), SLOT(cursorChanged(uint32_t)));
|
||||
}
|
||||
|
||||
X11CursorTracker::~X11CursorTracker()
|
||||
{
|
||||
Cursor::self()->stopCursorTracking();
|
||||
if (m_cursor) {
|
||||
wl_surface_destroy(m_cursor);
|
||||
}
|
||||
}
|
||||
|
||||
void X11CursorTracker::cursorChanged(uint32_t serial)
|
||||
{
|
||||
if (m_lastX11Cursor == serial) {
|
||||
// not changed;
|
||||
return;
|
||||
}
|
||||
m_lastX11Cursor = serial;
|
||||
QHash<uint32_t, CursorData>::iterator it = m_cursors.find(serial);
|
||||
if (it != m_cursors.end()) {
|
||||
installCursor(it.value());
|
||||
return;
|
||||
}
|
||||
ShmPool *pool = m_backend->shmPool();
|
||||
if (!pool) {
|
||||
return;
|
||||
}
|
||||
CursorData cursor(pool);
|
||||
if (cursor.isValid()) {
|
||||
// TODO: discard unused cursors after some time?
|
||||
m_cursors.insert(serial, cursor);
|
||||
}
|
||||
installCursor(cursor);
|
||||
}
|
||||
|
||||
void X11CursorTracker::installCursor(const CursorData& cursor)
|
||||
{
|
||||
wl_pointer_set_cursor(m_pointer, m_enteredSerial, m_cursor, cursor.hotSpot().x(), cursor.hotSpot().y());
|
||||
wl_surface_attach(m_cursor, cursor.cursor(), 0, 0);
|
||||
wl_surface_damage(m_cursor, 0, 0, cursor.size().width(), cursor.size().height());
|
||||
wl_surface_commit(m_cursor);
|
||||
}
|
||||
|
||||
void X11CursorTracker::setEnteredSerial(uint32_t serial)
|
||||
{
|
||||
m_enteredSerial = serial;
|
||||
}
|
||||
|
||||
void X11CursorTracker::resetCursor()
|
||||
{
|
||||
QHash<uint32_t, CursorData>::iterator it = m_cursors.find(m_lastX11Cursor);
|
||||
if (it != m_cursors.end()) {
|
||||
installCursor(it.value());
|
||||
}
|
||||
}
|
||||
|
||||
ShmPool::ShmPool(wl_shm *shm)
|
||||
: m_shm(shm)
|
||||
, m_pool(NULL)
|
||||
, m_poolData(NULL)
|
||||
, m_size(1024 * 1024) // TODO: useful size?
|
||||
, m_tmpFile(new QTemporaryFile())
|
||||
, m_valid(createPool())
|
||||
, m_offset(0)
|
||||
{
|
||||
}
|
||||
|
||||
ShmPool::~ShmPool()
|
||||
{
|
||||
if (m_poolData) {
|
||||
munmap(m_poolData, m_size);
|
||||
}
|
||||
if (m_pool) {
|
||||
wl_shm_pool_destroy(m_pool);
|
||||
}
|
||||
if (m_shm) {
|
||||
wl_shm_destroy(m_shm);
|
||||
}
|
||||
}
|
||||
|
||||
bool ShmPool::createPool()
|
||||
{
|
||||
if (!m_tmpFile->open()) {
|
||||
qDebug() << "Could not open temporary file for Shm pool";
|
||||
return false;
|
||||
}
|
||||
if (ftruncate(m_tmpFile->handle(), m_size) < 0) {
|
||||
qDebug() << "Could not set size for Shm pool file";
|
||||
return false;
|
||||
}
|
||||
m_poolData = mmap(NULL, m_size, PROT_READ | PROT_WRITE, MAP_SHARED, m_tmpFile->handle(), 0);
|
||||
m_pool = wl_shm_create_pool(m_shm, m_tmpFile->handle(), m_size);
|
||||
|
||||
if (!m_poolData || !m_pool) {
|
||||
qDebug() << "Creating Shm pool failed";
|
||||
return false;
|
||||
}
|
||||
m_tmpFile->close();
|
||||
return true;
|
||||
}
|
||||
|
||||
wl_buffer *ShmPool::createBuffer(const QImage& image)
|
||||
{
|
||||
if (image.isNull() || !m_valid) {
|
||||
return NULL;
|
||||
}
|
||||
// TODO: test whether buffer needs resizing
|
||||
wl_buffer *buffer = wl_shm_pool_create_buffer(m_pool, m_offset, image.width(), image.height(),
|
||||
image.bytesPerLine(), WL_SHM_FORMAT_ARGB8888);
|
||||
if (buffer) {
|
||||
memcpy((char *)m_poolData + m_offset, image.bits(), image.byteCount());
|
||||
m_offset += image.byteCount();
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
WaylandSeat::WaylandSeat(wl_seat *seat, WaylandBackend *backend)
|
||||
: m_seat(seat)
|
||||
, m_pointer(NULL)
|
||||
, m_keyboard(NULL)
|
||||
, m_cursorTracker()
|
||||
, m_backend(backend)
|
||||
{
|
||||
if (m_seat) {
|
||||
wl_seat_add_listener(m_seat, &s_seatListener, this);
|
||||
}
|
||||
}
|
||||
|
||||
WaylandSeat::~WaylandSeat()
|
||||
{
|
||||
destroyPointer();
|
||||
destroyKeyboard();
|
||||
if (m_seat) {
|
||||
wl_seat_destroy(m_seat);
|
||||
}
|
||||
}
|
||||
|
||||
void WaylandSeat::destroyPointer()
|
||||
{
|
||||
if (m_pointer) {
|
||||
wl_pointer_destroy(m_pointer);
|
||||
m_pointer = NULL;
|
||||
m_cursorTracker.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void WaylandSeat::destroyKeyboard()
|
||||
{
|
||||
if (m_keyboard) {
|
||||
wl_keyboard_destroy(m_keyboard);
|
||||
m_keyboard = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void WaylandSeat::changed(uint32_t capabilities)
|
||||
{
|
||||
if ((capabilities & WL_SEAT_CAPABILITY_POINTER) && !m_pointer) {
|
||||
m_pointer = wl_seat_get_pointer(m_seat);
|
||||
wl_pointer_add_listener(m_pointer, &s_pointerListener, this);
|
||||
m_cursorTracker.reset(new X11CursorTracker(m_pointer, m_backend));
|
||||
} else if (!(capabilities & WL_SEAT_CAPABILITY_POINTER)) {
|
||||
destroyPointer();
|
||||
}
|
||||
if ((capabilities & WL_SEAT_CAPABILITY_KEYBOARD)) {
|
||||
m_keyboard = wl_seat_get_keyboard(m_seat);
|
||||
wl_keyboard_add_listener(m_keyboard, &s_keyboardListener, this);
|
||||
} else if (!(capabilities & WL_SEAT_CAPABILITY_KEYBOARD)) {
|
||||
destroyKeyboard();
|
||||
}
|
||||
}
|
||||
|
||||
void WaylandSeat::pointerEntered(uint32_t serial)
|
||||
{
|
||||
if (m_cursorTracker.isNull()) {
|
||||
return;
|
||||
}
|
||||
m_cursorTracker->setEnteredSerial(serial);
|
||||
}
|
||||
|
||||
void WaylandSeat::resetCursor()
|
||||
{
|
||||
if (!m_cursorTracker.isNull()) {
|
||||
m_cursorTracker->resetCursor();
|
||||
}
|
||||
}
|
||||
|
||||
WaylandBackend::WaylandBackend()
|
||||
: QObject(NULL)
|
||||
, 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)
|
||||
, m_seat()
|
||||
, m_shm()
|
||||
{
|
||||
qDebug() << "Created Wayland display";
|
||||
// setup the registry
|
||||
wl_registry_add_listener(m_registry, &s_registryListener, this);
|
||||
wl_display_dispatch(m_display);
|
||||
int fd = wl_display_get_fd(m_display);
|
||||
QSocketNotifier *notifier = new QSocketNotifier(fd, QSocketNotifier::Read, this);
|
||||
connect(notifier, SIGNAL(activated(int)), SLOT(readEvents()));
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
qDebug() << "Destroyed Wayland display";
|
||||
}
|
||||
|
||||
void WaylandBackend::readEvents()
|
||||
{
|
||||
// TODO: this still seems to block
|
||||
wl_display_flush(m_display);
|
||||
wl_display_dispatch(m_display);
|
||||
}
|
||||
|
||||
void WaylandBackend::createSeat(uint32_t name)
|
||||
{
|
||||
wl_seat *seat = reinterpret_cast<wl_seat*>(wl_registry_bind(m_registry, name, &wl_seat_interface, 1));
|
||||
m_seat.reset(new WaylandSeat(seat, this));
|
||||
}
|
||||
|
||||
bool WaylandBackend::createSurface()
|
||||
{
|
||||
m_surface = wl_compositor_create_surface(m_compositor);
|
||||
if (!m_surface) {
|
||||
qCritical() << "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) {
|
||||
qCritical() << "Creating Wayland Egl window failed";
|
||||
return false;
|
||||
}
|
||||
wl_shell_surface_set_fullscreen(m_shellSurface, WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT, 0, NULL);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void WaylandBackend::createShm(uint32_t name)
|
||||
{
|
||||
m_shm.reset(new ShmPool(reinterpret_cast<wl_shm *>(wl_registry_bind(m_registry, name, &wl_shm_interface, 1))));
|
||||
if (!m_shm->isValid()) {
|
||||
m_shm.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void WaylandBackend::ping(uint32_t serial)
|
||||
{
|
||||
wl_shell_surface_pong(m_shellSurface, serial);
|
||||
if (!m_seat.isNull()) {
|
||||
m_seat->resetCursor();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
EglWaylandBackend::EglWaylandBackend()
|
||||
: OpenGLBackend()
|
||||
: QObject(NULL)
|
||||
, OpenGLBackend()
|
||||
, m_context(EGL_NO_CONTEXT)
|
||||
, m_bufferAge(0)
|
||||
, m_wayland(new Wayland::WaylandBackend)
|
||||
, m_overlay(NULL)
|
||||
{
|
||||
qDebug() << "Connected to Wayland display?" << (m_wayland->display() ? "yes" : "no" );
|
||||
if (!m_wayland->display()) {
|
||||
setFailed("Could not connect to Wayland compositor");
|
||||
return;
|
||||
}
|
||||
connect(m_wayland.data(), SIGNAL(shellSurfaceSizeChanged(QSize)), SLOT(overlaySizeChanged(QSize)));
|
||||
initializeEgl();
|
||||
init();
|
||||
// Egl is always direct rendering
|
||||
|
@ -654,6 +67,9 @@ EglWaylandBackend::~EglWaylandBackend()
|
|||
eglDestroySurface(m_display, m_surface);
|
||||
eglTerminate(m_display);
|
||||
eglReleaseThread();
|
||||
if (m_overlay) {
|
||||
wl_egl_window_destroy(m_overlay);
|
||||
}
|
||||
}
|
||||
|
||||
bool EglWaylandBackend::initializeEgl()
|
||||
|
@ -746,11 +162,18 @@ bool EglWaylandBackend::initRenderingContext()
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!m_wayland->createSurface()) {
|
||||
if (!m_wayland->surface()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
m_surface = eglCreateWindowSurface(m_display, m_config, m_wayland->overlay(), NULL);
|
||||
const QSize &size = m_wayland->shellSurfaceSize();
|
||||
m_overlay = wl_egl_window_create(m_wayland->surface(), size.width(), size.height());
|
||||
if (!m_overlay) {
|
||||
qCritical() << "Creating Wayland Egl window failed";
|
||||
return false;
|
||||
}
|
||||
|
||||
m_surface = eglCreateWindowSurface(m_display, m_config, m_overlay, NULL);
|
||||
if (m_surface == EGL_NO_SURFACE) {
|
||||
qCritical() << "Create Window Surface failed";
|
||||
return false;
|
||||
|
@ -809,8 +232,7 @@ bool EglWaylandBackend::initBufferConfigs()
|
|||
void EglWaylandBackend::present()
|
||||
{
|
||||
// need to dispatch pending events as eglSwapBuffers can block
|
||||
wl_display_dispatch_pending(m_wayland->display());
|
||||
wl_display_flush(m_wayland->display());
|
||||
m_wayland->dispatchEvents();
|
||||
|
||||
if (supportsBufferAge()) {
|
||||
eglSwapBuffers(m_display, m_surface);
|
||||
|
@ -909,6 +331,11 @@ Shm *EglWaylandBackend::shm()
|
|||
return m_shm.data();
|
||||
}
|
||||
|
||||
void EglWaylandBackend::overlaySizeChanged(const QSize &size)
|
||||
{
|
||||
wl_egl_window_resize(m_overlay, size.width(), size.height(), 0, 0);
|
||||
}
|
||||
|
||||
/************************************************
|
||||
* EglTexture
|
||||
************************************************/
|
||||
|
|
|
@ -21,7 +21,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
#define KWIN_EGL_WAYLAND_BACKEND_H
|
||||
#include "scene_opengl.h"
|
||||
// wayland
|
||||
#include <wayland-client.h>
|
||||
#include <wayland-egl.h>
|
||||
// xcb
|
||||
#include <xcb/shm.h>
|
||||
|
@ -33,212 +32,10 @@ struct wl_shm;
|
|||
namespace KWin
|
||||
{
|
||||
|
||||
namespace Wayland
|
||||
{
|
||||
class ShmPool;
|
||||
class WaylandBackend;
|
||||
|
||||
class CursorData
|
||||
{
|
||||
public:
|
||||
CursorData(ShmPool *pool);
|
||||
~CursorData();
|
||||
bool isValid() const;
|
||||
const QPoint &hotSpot() const;
|
||||
const QSize &size() const;
|
||||
wl_buffer *cursor() const;
|
||||
private:
|
||||
bool init(ShmPool *pool);
|
||||
wl_buffer *m_cursor;
|
||||
QPoint m_hotSpot;
|
||||
QSize m_size;
|
||||
bool m_valid;
|
||||
};
|
||||
|
||||
class X11CursorTracker : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit X11CursorTracker(wl_pointer *pointer, WaylandBackend *backend, QObject* parent = 0);
|
||||
virtual ~X11CursorTracker();
|
||||
void setEnteredSerial(uint32_t serial);
|
||||
void resetCursor();
|
||||
private Q_SLOTS:
|
||||
void cursorChanged(uint32_t serial);
|
||||
private:
|
||||
void installCursor(const CursorData &cursor);
|
||||
wl_pointer *m_pointer;
|
||||
QHash<uint32_t, CursorData> m_cursors;
|
||||
WaylandBackend *m_backend;
|
||||
wl_surface *m_cursor;
|
||||
uint32_t m_enteredSerial;
|
||||
uint32_t m_installedCursor;
|
||||
uint32_t m_lastX11Cursor;
|
||||
};
|
||||
|
||||
class ShmPool
|
||||
{
|
||||
public:
|
||||
ShmPool(wl_shm *shm);
|
||||
~ShmPool();
|
||||
bool isValid() const;
|
||||
wl_buffer *createBuffer(const QImage &image);
|
||||
private:
|
||||
bool createPool();
|
||||
wl_shm *m_shm;
|
||||
wl_shm_pool *m_pool;
|
||||
void *m_poolData;
|
||||
size_t m_size;
|
||||
QScopedPointer<QTemporaryFile> m_tmpFile;
|
||||
bool m_valid;
|
||||
int m_offset;
|
||||
};
|
||||
|
||||
class WaylandSeat
|
||||
{
|
||||
public:
|
||||
WaylandSeat(wl_seat *seat, WaylandBackend *backend);
|
||||
virtual ~WaylandSeat();
|
||||
|
||||
void changed(uint32_t capabilities);
|
||||
wl_seat *seat();
|
||||
void pointerEntered(uint32_t serial);
|
||||
void resetCursor();
|
||||
private:
|
||||
void destroyPointer();
|
||||
void destroyKeyboard();
|
||||
wl_seat *m_seat;
|
||||
wl_pointer *m_pointer;
|
||||
wl_keyboard *m_keyboard;
|
||||
QScopedPointer<X11CursorTracker> m_cursorTracker;
|
||||
WaylandBackend *m_backend;
|
||||
};
|
||||
|
||||
/**
|
||||
* @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 QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
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();
|
||||
ShmPool *shmPool();
|
||||
void createSeat(uint32_t name);
|
||||
void createShm(uint32_t name);
|
||||
void ping(uint32_t serial);
|
||||
|
||||
bool createSurface();
|
||||
private Q_SLOTS:
|
||||
void readEvents();
|
||||
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;
|
||||
QScopedPointer<WaylandSeat> m_seat;
|
||||
QScopedPointer<ShmPool> m_shm;
|
||||
};
|
||||
|
||||
inline
|
||||
bool CursorData::isValid() const
|
||||
{
|
||||
return m_valid;
|
||||
namespace Wayland {
|
||||
class WaylandBackend;
|
||||
}
|
||||
|
||||
inline
|
||||
const QPoint& CursorData::hotSpot() const
|
||||
{
|
||||
return m_hotSpot;
|
||||
}
|
||||
|
||||
inline
|
||||
wl_buffer* CursorData::cursor() const
|
||||
{
|
||||
return m_cursor;
|
||||
}
|
||||
|
||||
inline
|
||||
const QSize& CursorData::size() const
|
||||
{
|
||||
return m_size;
|
||||
}
|
||||
|
||||
inline
|
||||
wl_seat *WaylandSeat::seat()
|
||||
{
|
||||
return m_seat;
|
||||
}
|
||||
|
||||
inline
|
||||
bool ShmPool::isValid() const
|
||||
{
|
||||
return m_valid;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
inline
|
||||
ShmPool* WaylandBackend::shmPool()
|
||||
{
|
||||
return m_shm.data();
|
||||
}
|
||||
|
||||
} // namespace Wayland
|
||||
|
||||
class Shm;
|
||||
|
||||
/**
|
||||
|
@ -257,8 +54,9 @@ class Shm;
|
|||
* 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
|
||||
class EglWaylandBackend : public QObject, public OpenGLBackend
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
EglWaylandBackend();
|
||||
virtual ~EglWaylandBackend();
|
||||
|
@ -273,6 +71,9 @@ public:
|
|||
protected:
|
||||
virtual void present();
|
||||
|
||||
private Q_SLOTS:
|
||||
void overlaySizeChanged(const QSize &size);
|
||||
|
||||
private:
|
||||
void init();
|
||||
bool initializeEgl();
|
||||
|
@ -285,6 +86,7 @@ private:
|
|||
EGLContext m_context;
|
||||
int m_bufferAge;
|
||||
QScopedPointer<Wayland::WaylandBackend> m_wayland;
|
||||
wl_egl_window *m_overlay;
|
||||
QScopedPointer<Shm> m_shm;
|
||||
friend class EglWaylandTexture;
|
||||
};
|
||||
|
|
632
wayland_backend.cpp
Normal file
632
wayland_backend.cpp
Normal file
|
@ -0,0 +1,632 @@
|
|||
/********************************************************************
|
||||
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/>.
|
||||
*********************************************************************/
|
||||
// own
|
||||
#include "wayland_backend.h"
|
||||
// KWin
|
||||
#include <kwinglobals.h>
|
||||
#include "cursor.h"
|
||||
// Qt
|
||||
#include <QDebug>
|
||||
#include <QImage>
|
||||
#include <QSocketNotifier>
|
||||
#include <QTemporaryFile>
|
||||
// xcb
|
||||
#include <xcb/xtest.h>
|
||||
#include <xcb/xfixes.h>
|
||||
// Wayland
|
||||
#include <wayland-client-protocol.h>
|
||||
// system
|
||||
#include <linux/input.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/mman.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)));
|
||||
} else if (strcmp(interface, "wl_seat") == 0) {
|
||||
d->createSeat(name);
|
||||
} else if (strcmp(interface, "wl_shm") == 0) {
|
||||
d->createShm(name);
|
||||
}
|
||||
qDebug() << "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(shellSurface);
|
||||
reinterpret_cast<WaylandBackend*>(data)->ping(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);
|
||||
display->setShellSurfaceSize(QSize(width, height));
|
||||
// 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)
|
||||
}
|
||||
|
||||
static void seatHandleCapabilities(void *data, wl_seat *seat, uint32_t capabilities)
|
||||
{
|
||||
WaylandSeat *s = reinterpret_cast<WaylandSeat*>(data);
|
||||
if (seat != s->seat()) {
|
||||
return;
|
||||
}
|
||||
s->changed(capabilities);
|
||||
}
|
||||
|
||||
static void pointerHandleEnter(void *data, wl_pointer *pointer, uint32_t serial, wl_surface *surface,
|
||||
wl_fixed_t sx, wl_fixed_t sy)
|
||||
{
|
||||
Q_UNUSED(data)
|
||||
Q_UNUSED(pointer)
|
||||
Q_UNUSED(surface)
|
||||
Q_UNUSED(sx)
|
||||
Q_UNUSED(sy)
|
||||
WaylandSeat *seat = reinterpret_cast<WaylandSeat*>(data);
|
||||
seat->pointerEntered(serial);
|
||||
}
|
||||
|
||||
static void pointerHandleLeave(void *data, wl_pointer *pointer, uint32_t serial, wl_surface *surface)
|
||||
{
|
||||
Q_UNUSED(data)
|
||||
Q_UNUSED(pointer)
|
||||
Q_UNUSED(serial)
|
||||
Q_UNUSED(surface)
|
||||
}
|
||||
|
||||
static void pointerHandleMotion(void *data, wl_pointer *pointer, uint32_t time, wl_fixed_t sx, wl_fixed_t sy)
|
||||
{
|
||||
Q_UNUSED(data)
|
||||
Q_UNUSED(pointer)
|
||||
Q_UNUSED(time)
|
||||
xcb_test_fake_input(connection(), XCB_MOTION_NOTIFY, 0, XCB_TIME_CURRENT_TIME, XCB_WINDOW_NONE,
|
||||
wl_fixed_to_int(sx), wl_fixed_to_int(sy), 0);
|
||||
}
|
||||
|
||||
static void pointerHandleButton(void *data, wl_pointer *pointer, uint32_t serial, uint32_t time,
|
||||
uint32_t button, uint32_t state)
|
||||
{
|
||||
Q_UNUSED(data)
|
||||
Q_UNUSED(pointer)
|
||||
Q_UNUSED(serial)
|
||||
Q_UNUSED(time)
|
||||
uint8_t type = XCB_BUTTON_PRESS;
|
||||
if (state == WL_POINTER_BUTTON_STATE_RELEASED) {
|
||||
type = XCB_BUTTON_RELEASE;
|
||||
}
|
||||
// TODO: there must be a better way for mapping
|
||||
uint8_t xButton = 0;
|
||||
switch (button) {
|
||||
case BTN_LEFT:
|
||||
xButton = XCB_BUTTON_INDEX_1;
|
||||
break;
|
||||
case BTN_RIGHT:
|
||||
xButton = XCB_BUTTON_INDEX_3;
|
||||
break;
|
||||
case BTN_MIDDLE:
|
||||
xButton = XCB_BUTTON_INDEX_2;
|
||||
break;
|
||||
default:
|
||||
// TODO: add more buttons
|
||||
return;
|
||||
}
|
||||
xcb_test_fake_input(connection(), type, xButton, XCB_TIME_CURRENT_TIME, XCB_WINDOW_NONE, 0, 0, 0);
|
||||
}
|
||||
|
||||
static void pointerHandleAxis(void *data, wl_pointer *pointer, uint32_t time, uint32_t axis, wl_fixed_t value)
|
||||
{
|
||||
Q_UNUSED(data)
|
||||
Q_UNUSED(pointer)
|
||||
Q_UNUSED(time)
|
||||
uint8_t xButton = 0;
|
||||
const int delta = wl_fixed_to_int(value);
|
||||
if (delta == 0) {
|
||||
return;
|
||||
}
|
||||
switch (axis) {
|
||||
case WL_POINTER_AXIS_VERTICAL_SCROLL:
|
||||
xButton = delta > 0 ? XCB_BUTTON_INDEX_5 : XCB_BUTTON_INDEX_4;
|
||||
break;
|
||||
case WL_POINTER_AXIS_HORIZONTAL_SCROLL:
|
||||
// no enum values defined for buttons larger than 5
|
||||
xButton = delta > 0 ? 7 : 6;
|
||||
break;
|
||||
default:
|
||||
// doesn't exist
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < qAbs(delta); ++i) {
|
||||
xcb_test_fake_input(connection(), XCB_BUTTON_PRESS, xButton, XCB_TIME_CURRENT_TIME, XCB_WINDOW_NONE, 0, 0, 0);
|
||||
xcb_test_fake_input(connection(), XCB_BUTTON_RELEASE, xButton, XCB_TIME_CURRENT_TIME, XCB_WINDOW_NONE, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void keyboardHandleKeymap(void *data, wl_keyboard *keyboard,
|
||||
uint32_t format, int fd, uint32_t size)
|
||||
{
|
||||
Q_UNUSED(data)
|
||||
Q_UNUSED(keyboard)
|
||||
Q_UNUSED(format)
|
||||
Q_UNUSED(fd)
|
||||
Q_UNUSED(size)
|
||||
}
|
||||
|
||||
static void keyboardHandleEnter(void *data, wl_keyboard *keyboard,
|
||||
uint32_t serial, wl_surface *surface,
|
||||
wl_array *keys)
|
||||
{
|
||||
Q_UNUSED(data)
|
||||
Q_UNUSED(keyboard)
|
||||
Q_UNUSED(serial)
|
||||
Q_UNUSED(surface)
|
||||
Q_UNUSED(keys)
|
||||
}
|
||||
|
||||
static void keyboardHandleLeave(void *data, wl_keyboard *keyboard, uint32_t serial, wl_surface *surface)
|
||||
{
|
||||
Q_UNUSED(data)
|
||||
Q_UNUSED(keyboard)
|
||||
Q_UNUSED(serial)
|
||||
Q_UNUSED(surface)
|
||||
}
|
||||
|
||||
static void keyboardHandleKey(void *data, wl_keyboard *keyboard, uint32_t serial, uint32_t time,
|
||||
uint32_t key, uint32_t state)
|
||||
{
|
||||
Q_UNUSED(data)
|
||||
Q_UNUSED(keyboard)
|
||||
Q_UNUSED(serial)
|
||||
Q_UNUSED(time)
|
||||
uint8_t type = XCB_KEY_PRESS;
|
||||
if (state == WL_KEYBOARD_KEY_STATE_RELEASED) {
|
||||
type = XCB_KEY_RELEASE;
|
||||
}
|
||||
xcb_test_fake_input(connection(), type, key + 8 /*magic*/, XCB_TIME_CURRENT_TIME, XCB_WINDOW_NONE, 0, 0, 0);
|
||||
}
|
||||
|
||||
static void keyboardHandleModifiers(void *data, wl_keyboard *keyboard, uint32_t serial, uint32_t modsDepressed,
|
||||
uint32_t modsLatched, uint32_t modsLocked, uint32_t group)
|
||||
{
|
||||
Q_UNUSED(data)
|
||||
Q_UNUSED(keyboard)
|
||||
Q_UNUSED(serial)
|
||||
Q_UNUSED(modsDepressed)
|
||||
Q_UNUSED(modsLatched)
|
||||
Q_UNUSED(modsLocked)
|
||||
Q_UNUSED(group)
|
||||
}
|
||||
|
||||
// handlers
|
||||
static const struct wl_registry_listener s_registryListener = {
|
||||
registryHandleGlobal,
|
||||
registryHandleGlobalRemove
|
||||
};
|
||||
|
||||
static const struct wl_shell_surface_listener s_shellSurfaceListener = {
|
||||
handlePing,
|
||||
handleConfigure,
|
||||
handlePopupDone
|
||||
};
|
||||
|
||||
static const struct wl_pointer_listener s_pointerListener = {
|
||||
pointerHandleEnter,
|
||||
pointerHandleLeave,
|
||||
pointerHandleMotion,
|
||||
pointerHandleButton,
|
||||
pointerHandleAxis
|
||||
};
|
||||
|
||||
static const struct wl_keyboard_listener s_keyboardListener = {
|
||||
keyboardHandleKeymap,
|
||||
keyboardHandleEnter,
|
||||
keyboardHandleLeave,
|
||||
keyboardHandleKey,
|
||||
keyboardHandleModifiers,
|
||||
};
|
||||
|
||||
static const struct wl_seat_listener s_seatListener = {
|
||||
seatHandleCapabilities
|
||||
};
|
||||
|
||||
CursorData::CursorData(ShmPool *pool)
|
||||
: m_cursor(NULL)
|
||||
, m_valid(init(pool))
|
||||
{
|
||||
}
|
||||
|
||||
CursorData::~CursorData()
|
||||
{
|
||||
}
|
||||
|
||||
bool CursorData::init(ShmPool *pool)
|
||||
{
|
||||
QScopedPointer<xcb_xfixes_get_cursor_image_reply_t, QScopedPointerPodDeleter> cursor(
|
||||
xcb_xfixes_get_cursor_image_reply(connection(),
|
||||
xcb_xfixes_get_cursor_image_unchecked(connection()),
|
||||
NULL));
|
||||
if (cursor.isNull()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QImage cursorImage((uchar *) xcb_xfixes_get_cursor_image_cursor_image(cursor.data()), cursor->width, cursor->height,
|
||||
QImage::Format_ARGB32_Premultiplied);
|
||||
if (cursorImage.isNull()) {
|
||||
return false;
|
||||
}
|
||||
m_size = QSize(cursor->width, cursor->height);
|
||||
|
||||
m_cursor = pool->createBuffer(cursorImage);
|
||||
if (!m_cursor) {
|
||||
qDebug() << "Creating cursor buffer failed";
|
||||
return false;
|
||||
}
|
||||
|
||||
m_hotSpot = QPoint(cursor->xhot, cursor->yhot);
|
||||
return true;
|
||||
}
|
||||
|
||||
X11CursorTracker::X11CursorTracker(wl_pointer *pointer, WaylandBackend *backend, QObject* parent)
|
||||
: QObject(parent)
|
||||
, m_pointer(pointer)
|
||||
, m_backend(backend)
|
||||
, m_cursor(wl_compositor_create_surface(backend->compositor()))
|
||||
, m_enteredSerial(0)
|
||||
, m_lastX11Cursor(0)
|
||||
{
|
||||
Cursor::self()->startCursorTracking();
|
||||
connect(Cursor::self(), SIGNAL(cursorChanged(uint32_t)), SLOT(cursorChanged(uint32_t)));
|
||||
}
|
||||
|
||||
X11CursorTracker::~X11CursorTracker()
|
||||
{
|
||||
Cursor::self()->stopCursorTracking();
|
||||
if (m_cursor) {
|
||||
wl_surface_destroy(m_cursor);
|
||||
}
|
||||
}
|
||||
|
||||
void X11CursorTracker::cursorChanged(uint32_t serial)
|
||||
{
|
||||
if (m_lastX11Cursor == serial) {
|
||||
// not changed;
|
||||
return;
|
||||
}
|
||||
m_lastX11Cursor = serial;
|
||||
QHash<uint32_t, CursorData>::iterator it = m_cursors.find(serial);
|
||||
if (it != m_cursors.end()) {
|
||||
installCursor(it.value());
|
||||
return;
|
||||
}
|
||||
ShmPool *pool = m_backend->shmPool();
|
||||
if (!pool) {
|
||||
return;
|
||||
}
|
||||
CursorData cursor(pool);
|
||||
if (cursor.isValid()) {
|
||||
// TODO: discard unused cursors after some time?
|
||||
m_cursors.insert(serial, cursor);
|
||||
}
|
||||
installCursor(cursor);
|
||||
}
|
||||
|
||||
void X11CursorTracker::installCursor(const CursorData& cursor)
|
||||
{
|
||||
wl_pointer_set_cursor(m_pointer, m_enteredSerial, m_cursor, cursor.hotSpot().x(), cursor.hotSpot().y());
|
||||
wl_surface_attach(m_cursor, cursor.cursor(), 0, 0);
|
||||
wl_surface_damage(m_cursor, 0, 0, cursor.size().width(), cursor.size().height());
|
||||
wl_surface_commit(m_cursor);
|
||||
}
|
||||
|
||||
void X11CursorTracker::setEnteredSerial(uint32_t serial)
|
||||
{
|
||||
m_enteredSerial = serial;
|
||||
}
|
||||
|
||||
void X11CursorTracker::resetCursor()
|
||||
{
|
||||
QHash<uint32_t, CursorData>::iterator it = m_cursors.find(m_lastX11Cursor);
|
||||
if (it != m_cursors.end()) {
|
||||
installCursor(it.value());
|
||||
}
|
||||
}
|
||||
|
||||
ShmPool::ShmPool(wl_shm *shm)
|
||||
: m_shm(shm)
|
||||
, m_pool(NULL)
|
||||
, m_poolData(NULL)
|
||||
, m_size(1024 * 1024) // TODO: useful size?
|
||||
, m_tmpFile(new QTemporaryFile())
|
||||
, m_valid(createPool())
|
||||
, m_offset(0)
|
||||
{
|
||||
}
|
||||
|
||||
ShmPool::~ShmPool()
|
||||
{
|
||||
if (m_poolData) {
|
||||
munmap(m_poolData, m_size);
|
||||
}
|
||||
if (m_pool) {
|
||||
wl_shm_pool_destroy(m_pool);
|
||||
}
|
||||
if (m_shm) {
|
||||
wl_shm_destroy(m_shm);
|
||||
}
|
||||
}
|
||||
|
||||
bool ShmPool::createPool()
|
||||
{
|
||||
if (!m_tmpFile->open()) {
|
||||
qDebug() << "Could not open temporary file for Shm pool";
|
||||
return false;
|
||||
}
|
||||
if (ftruncate(m_tmpFile->handle(), m_size) < 0) {
|
||||
qDebug() << "Could not set size for Shm pool file";
|
||||
return false;
|
||||
}
|
||||
m_poolData = mmap(NULL, m_size, PROT_READ | PROT_WRITE, MAP_SHARED, m_tmpFile->handle(), 0);
|
||||
m_pool = wl_shm_create_pool(m_shm, m_tmpFile->handle(), m_size);
|
||||
|
||||
if (!m_poolData || !m_pool) {
|
||||
qDebug() << "Creating Shm pool failed";
|
||||
return false;
|
||||
}
|
||||
m_tmpFile->close();
|
||||
return true;
|
||||
}
|
||||
|
||||
wl_buffer *ShmPool::createBuffer(const QImage& image)
|
||||
{
|
||||
if (image.isNull() || !m_valid) {
|
||||
return NULL;
|
||||
}
|
||||
// TODO: test whether buffer needs resizing
|
||||
wl_buffer *buffer = wl_shm_pool_create_buffer(m_pool, m_offset, image.width(), image.height(),
|
||||
image.bytesPerLine(), WL_SHM_FORMAT_ARGB8888);
|
||||
if (buffer) {
|
||||
memcpy((char *)m_poolData + m_offset, image.bits(), image.byteCount());
|
||||
m_offset += image.byteCount();
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
WaylandSeat::WaylandSeat(wl_seat *seat, WaylandBackend *backend)
|
||||
: m_seat(seat)
|
||||
, m_pointer(NULL)
|
||||
, m_keyboard(NULL)
|
||||
, m_cursorTracker()
|
||||
, m_backend(backend)
|
||||
{
|
||||
if (m_seat) {
|
||||
wl_seat_add_listener(m_seat, &s_seatListener, this);
|
||||
}
|
||||
}
|
||||
|
||||
WaylandSeat::~WaylandSeat()
|
||||
{
|
||||
destroyPointer();
|
||||
destroyKeyboard();
|
||||
if (m_seat) {
|
||||
wl_seat_destroy(m_seat);
|
||||
}
|
||||
}
|
||||
|
||||
void WaylandSeat::destroyPointer()
|
||||
{
|
||||
if (m_pointer) {
|
||||
wl_pointer_destroy(m_pointer);
|
||||
m_pointer = NULL;
|
||||
m_cursorTracker.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void WaylandSeat::destroyKeyboard()
|
||||
{
|
||||
if (m_keyboard) {
|
||||
wl_keyboard_destroy(m_keyboard);
|
||||
m_keyboard = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void WaylandSeat::changed(uint32_t capabilities)
|
||||
{
|
||||
if ((capabilities & WL_SEAT_CAPABILITY_POINTER) && !m_pointer) {
|
||||
m_pointer = wl_seat_get_pointer(m_seat);
|
||||
wl_pointer_add_listener(m_pointer, &s_pointerListener, this);
|
||||
m_cursorTracker.reset(new X11CursorTracker(m_pointer, m_backend));
|
||||
} else if (!(capabilities & WL_SEAT_CAPABILITY_POINTER)) {
|
||||
destroyPointer();
|
||||
}
|
||||
if ((capabilities & WL_SEAT_CAPABILITY_KEYBOARD)) {
|
||||
m_keyboard = wl_seat_get_keyboard(m_seat);
|
||||
wl_keyboard_add_listener(m_keyboard, &s_keyboardListener, this);
|
||||
} else if (!(capabilities & WL_SEAT_CAPABILITY_KEYBOARD)) {
|
||||
destroyKeyboard();
|
||||
}
|
||||
}
|
||||
|
||||
void WaylandSeat::pointerEntered(uint32_t serial)
|
||||
{
|
||||
if (m_cursorTracker.isNull()) {
|
||||
return;
|
||||
}
|
||||
m_cursorTracker->setEnteredSerial(serial);
|
||||
}
|
||||
|
||||
void WaylandSeat::resetCursor()
|
||||
{
|
||||
if (!m_cursorTracker.isNull()) {
|
||||
m_cursorTracker->resetCursor();
|
||||
}
|
||||
}
|
||||
|
||||
WaylandBackend::WaylandBackend()
|
||||
: QObject(NULL)
|
||||
, m_display(wl_display_connect(NULL))
|
||||
, m_registry(wl_display_get_registry(m_display))
|
||||
, m_compositor(NULL)
|
||||
, m_shell(NULL)
|
||||
, m_surface(NULL)
|
||||
, m_shellSurface(NULL)
|
||||
, m_shellSurfaceSize(displayWidth(), displayHeight())
|
||||
, m_seat()
|
||||
, m_shm()
|
||||
{
|
||||
qDebug() << "Created Wayland display";
|
||||
// setup the registry
|
||||
wl_registry_add_listener(m_registry, &s_registryListener, this);
|
||||
wl_display_dispatch(m_display);
|
||||
int fd = wl_display_get_fd(m_display);
|
||||
QSocketNotifier *notifier = new QSocketNotifier(fd, QSocketNotifier::Read, this);
|
||||
connect(notifier, SIGNAL(activated(int)), SLOT(readEvents()));
|
||||
}
|
||||
|
||||
WaylandBackend::~WaylandBackend()
|
||||
{
|
||||
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);
|
||||
}
|
||||
qDebug() << "Destroyed Wayland display";
|
||||
}
|
||||
|
||||
void WaylandBackend::readEvents()
|
||||
{
|
||||
// TODO: this still seems to block
|
||||
wl_display_flush(m_display);
|
||||
wl_display_dispatch(m_display);
|
||||
}
|
||||
|
||||
void WaylandBackend::createSeat(uint32_t name)
|
||||
{
|
||||
wl_seat *seat = reinterpret_cast<wl_seat*>(wl_registry_bind(m_registry, name, &wl_seat_interface, 1));
|
||||
m_seat.reset(new WaylandSeat(seat, this));
|
||||
}
|
||||
|
||||
void WaylandBackend::createSurface()
|
||||
{
|
||||
m_surface = wl_compositor_create_surface(m_compositor);
|
||||
if (!m_surface) {
|
||||
qCritical() << "Creating Wayland Surface failed";
|
||||
return;
|
||||
}
|
||||
// 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);
|
||||
wl_shell_surface_set_fullscreen(m_shellSurface, WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT, 0, NULL);
|
||||
}
|
||||
|
||||
void WaylandBackend::createShm(uint32_t name)
|
||||
{
|
||||
m_shm.reset(new ShmPool(reinterpret_cast<wl_shm *>(wl_registry_bind(m_registry, name, &wl_shm_interface, 1))));
|
||||
if (!m_shm->isValid()) {
|
||||
m_shm.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void WaylandBackend::ping(uint32_t serial)
|
||||
{
|
||||
wl_shell_surface_pong(m_shellSurface, serial);
|
||||
if (!m_seat.isNull()) {
|
||||
m_seat->resetCursor();
|
||||
}
|
||||
}
|
||||
|
||||
void WaylandBackend::setShell(wl_shell *s)
|
||||
{
|
||||
m_shell = s;
|
||||
createSurface();
|
||||
}
|
||||
|
||||
void WaylandBackend::setShellSurfaceSize(const QSize &size)
|
||||
{
|
||||
if (m_shellSurfaceSize == size) {
|
||||
return;
|
||||
}
|
||||
m_shellSurfaceSize = size;
|
||||
emit shellSurfaceSizeChanged(m_shellSurfaceSize);
|
||||
}
|
||||
|
||||
void WaylandBackend::dispatchEvents()
|
||||
{
|
||||
wl_display_dispatch_pending(m_display);
|
||||
wl_display_flush(m_display);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // KWin
|
251
wayland_backend.h
Normal file
251
wayland_backend.h
Normal file
|
@ -0,0 +1,251 @@
|
|||
/********************************************************************
|
||||
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_WAYLAND_BACKEND_H
|
||||
#define KWIN_WAYLAND_BACKEND_H
|
||||
// Qt
|
||||
#include <QHash>
|
||||
#include <QObject>
|
||||
#include <QPoint>
|
||||
#include <QSize>
|
||||
// wayland
|
||||
#include <wayland-client.h>
|
||||
|
||||
class QTemporaryFile;
|
||||
class QImage;
|
||||
struct wl_buffer;
|
||||
struct wl_shm;
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
namespace Wayland
|
||||
{
|
||||
class ShmPool;
|
||||
class WaylandBackend;
|
||||
|
||||
class CursorData
|
||||
{
|
||||
public:
|
||||
CursorData(ShmPool *pool);
|
||||
~CursorData();
|
||||
bool isValid() const;
|
||||
const QPoint &hotSpot() const;
|
||||
const QSize &size() const;
|
||||
wl_buffer *cursor() const;
|
||||
private:
|
||||
bool init(ShmPool *pool);
|
||||
wl_buffer *m_cursor;
|
||||
QPoint m_hotSpot;
|
||||
QSize m_size;
|
||||
bool m_valid;
|
||||
};
|
||||
|
||||
class X11CursorTracker : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit X11CursorTracker(wl_pointer *pointer, WaylandBackend *backend, QObject* parent = 0);
|
||||
virtual ~X11CursorTracker();
|
||||
void setEnteredSerial(uint32_t serial);
|
||||
void resetCursor();
|
||||
private Q_SLOTS:
|
||||
void cursorChanged(uint32_t serial);
|
||||
private:
|
||||
void installCursor(const CursorData &cursor);
|
||||
wl_pointer *m_pointer;
|
||||
QHash<uint32_t, CursorData> m_cursors;
|
||||
WaylandBackend *m_backend;
|
||||
wl_surface *m_cursor;
|
||||
uint32_t m_enteredSerial;
|
||||
uint32_t m_installedCursor;
|
||||
uint32_t m_lastX11Cursor;
|
||||
};
|
||||
|
||||
class ShmPool
|
||||
{
|
||||
public:
|
||||
ShmPool(wl_shm *shm);
|
||||
~ShmPool();
|
||||
bool isValid() const;
|
||||
wl_buffer *createBuffer(const QImage &image);
|
||||
private:
|
||||
bool createPool();
|
||||
wl_shm *m_shm;
|
||||
wl_shm_pool *m_pool;
|
||||
void *m_poolData;
|
||||
size_t m_size;
|
||||
QScopedPointer<QTemporaryFile> m_tmpFile;
|
||||
bool m_valid;
|
||||
int m_offset;
|
||||
};
|
||||
|
||||
class WaylandSeat
|
||||
{
|
||||
public:
|
||||
WaylandSeat(wl_seat *seat, WaylandBackend *backend);
|
||||
virtual ~WaylandSeat();
|
||||
|
||||
void changed(uint32_t capabilities);
|
||||
wl_seat *seat();
|
||||
void pointerEntered(uint32_t serial);
|
||||
void resetCursor();
|
||||
private:
|
||||
void destroyPointer();
|
||||
void destroyKeyboard();
|
||||
wl_seat *m_seat;
|
||||
wl_pointer *m_pointer;
|
||||
wl_keyboard *m_keyboard;
|
||||
QScopedPointer<X11CursorTracker> m_cursorTracker;
|
||||
WaylandBackend *m_backend;
|
||||
};
|
||||
|
||||
/**
|
||||
* @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 mapping.
|
||||
*/
|
||||
class WaylandBackend : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
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();
|
||||
ShmPool *shmPool();
|
||||
void createSeat(uint32_t name);
|
||||
void createShm(uint32_t name);
|
||||
void ping(uint32_t serial);
|
||||
|
||||
wl_surface *surface() const;
|
||||
const QSize &shellSurfaceSize() const;
|
||||
void setShellSurfaceSize(const QSize &size);
|
||||
|
||||
void dispatchEvents();
|
||||
Q_SIGNALS:
|
||||
void shellSurfaceSizeChanged(const QSize &size);
|
||||
private Q_SLOTS:
|
||||
void readEvents();
|
||||
private:
|
||||
void createSurface();
|
||||
wl_display *m_display;
|
||||
wl_registry *m_registry;
|
||||
wl_compositor *m_compositor;
|
||||
wl_shell *m_shell;
|
||||
wl_surface *m_surface;
|
||||
wl_shell_surface *m_shellSurface;
|
||||
QSize m_shellSurfaceSize;
|
||||
QScopedPointer<WaylandSeat> m_seat;
|
||||
QScopedPointer<ShmPool> m_shm;
|
||||
};
|
||||
|
||||
inline
|
||||
bool CursorData::isValid() const
|
||||
{
|
||||
return m_valid;
|
||||
}
|
||||
|
||||
inline
|
||||
const QPoint& CursorData::hotSpot() const
|
||||
{
|
||||
return m_hotSpot;
|
||||
}
|
||||
|
||||
inline
|
||||
wl_buffer* CursorData::cursor() const
|
||||
{
|
||||
return m_cursor;
|
||||
}
|
||||
|
||||
inline
|
||||
const QSize& CursorData::size() const
|
||||
{
|
||||
return m_size;
|
||||
}
|
||||
|
||||
inline
|
||||
wl_seat *WaylandSeat::seat()
|
||||
{
|
||||
return m_seat;
|
||||
}
|
||||
|
||||
inline
|
||||
bool ShmPool::isValid() const
|
||||
{
|
||||
return m_valid;
|
||||
}
|
||||
|
||||
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_shell *WaylandBackend::shell()
|
||||
{
|
||||
return m_shell;
|
||||
}
|
||||
|
||||
inline
|
||||
ShmPool* WaylandBackend::shmPool()
|
||||
{
|
||||
return m_shm.data();
|
||||
}
|
||||
|
||||
inline
|
||||
wl_surface *WaylandBackend::surface() const
|
||||
{
|
||||
return m_surface;
|
||||
}
|
||||
|
||||
inline
|
||||
const QSize &WaylandBackend::shellSurfaceSize() const
|
||||
{
|
||||
return m_shellSurfaceSize;
|
||||
}
|
||||
|
||||
} // namespace Wayland
|
||||
} // namespace KWin
|
||||
|
||||
#endif // KWIN_WAYLAND_BACKEND_H
|
Loading…
Reference in a new issue