diff --git a/CMakeLists.txt b/CMakeLists.txt
index f9d9c21147..b4324aed25 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -260,6 +260,7 @@ set(kwin_WAYLAND_LIBS
${WAYLAND_CLIENT_LIBRARIES}
${WAYLAND_EGL_LIBRARIES}
${XCB_SHM_LIBRARIES}
+ ${XCB_XTEST_LIBRARIES}
)
set(kwin_OPENGL_LIBS )
diff --git a/egl_wayland_backend.cpp b/egl_wayland_backend.cpp
index 1d49f8974c..3bb1c576c9 100644
--- a/egl_wayland_backend.cpp
+++ b/egl_wayland_backend.cpp
@@ -25,7 +25,10 @@ along with this program. If not, see .
#include
// KDE
#include
+// xcb
+#include
// system
+#include
#include
namespace KWin
@@ -47,6 +50,8 @@ static void registryHandleGlobal(void *data, struct wl_registry *registry,
d->setCompositor(reinterpret_cast(wl_registry_bind(registry, name, &wl_compositor_interface, 1)));
} else if (strcmp(interface, "wl_shell") == 0) {
d->setShell(reinterpret_cast(wl_registry_bind(registry, name, &wl_shell_interface, 1)));
+ } else if (strcmp(interface, "wl_seat") == 0) {
+ d->createSeat(name);
}
kDebug(1212) << "Wayland Interface: " << interface;
}
@@ -92,6 +97,138 @@ static void handlePopupDone(void *data, struct wl_shell_surface *shellSurface)
Q_UNUSED(shellSurface)
}
+static void seatHandleCapabilities(void *data, wl_seat *seat, uint32_t capabilities)
+{
+ WaylandSeat *s = reinterpret_cast(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(serial)
+ Q_UNUSED(surface)
+ Q_UNUSED(sx)
+ Q_UNUSED(sy)
+}
+
+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)
+ Q_UNUSED(axis)
+ Q_UNUSED(value)
+ // TODO: implement mouse wheel support
+}
+
+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,
@@ -104,6 +241,77 @@ static const struct wl_shell_surface_listener s_shellSurfaceListener = {
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
+};
+
+WaylandSeat::WaylandSeat(wl_seat *seat)
+ : m_seat(seat)
+ , m_pointer(NULL)
+ , m_keyboard(NULL)
+{
+ 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;
+ }
+}
+
+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);
+ } 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();
+ }
+}
+
WaylandBackend::WaylandBackend()
: m_display(wl_display_connect(NULL))
, m_registry(wl_display_get_registry(m_display))
@@ -112,6 +320,7 @@ WaylandBackend::WaylandBackend()
, m_surface(NULL)
, m_overlay(NULL)
, m_shellSurface(NULL)
+ , m_seat()
{
kDebug(1212) << "Created Wayland display";
// setup the registry
@@ -146,6 +355,12 @@ WaylandBackend::~WaylandBackend()
kDebug(1212) << "Destroyed Wayland display";
}
+void WaylandBackend::createSeat(uint32_t name)
+{
+ wl_seat *seat = reinterpret_cast(wl_registry_bind(m_registry, name, &wl_seat_interface, 1));
+ m_seat.reset(new WaylandSeat(seat));
+}
+
bool WaylandBackend::createSurface()
{
m_surface = wl_compositor_create_surface(m_compositor);
diff --git a/egl_wayland_backend.h b/egl_wayland_backend.h
index 711ad1d576..b0c23a8e2a 100644
--- a/egl_wayland_backend.h
+++ b/egl_wayland_backend.h
@@ -31,6 +31,23 @@ namespace KWin
namespace Wayland
{
+
+class WaylandSeat
+{
+public:
+ WaylandSeat(wl_seat *seat);
+ virtual ~WaylandSeat();
+
+ void changed(uint32_t capabilities);
+ wl_seat *seat();
+private:
+ void destroyPointer();
+ void destroyKeyboard();
+ wl_seat *m_seat;
+ wl_pointer *m_pointer;
+ wl_keyboard *m_keyboard;
+};
+
/**
* @brief Class encapsulating all Wayland data structures needed by the Egl backend.
*
@@ -49,6 +66,7 @@ public:
void setShell(wl_shell *s);
wl_shell *shell();
wl_egl_window *overlay();
+ void createSeat(uint32_t name);
bool createSurface();
private:
@@ -59,8 +77,15 @@ private:
wl_surface *m_surface;
wl_egl_window *m_overlay;
wl_shell_surface *m_shellSurface;
+ QScopedPointer m_seat;
};
+inline
+wl_seat *WaylandSeat::seat()
+{
+ return m_seat;
+}
+
inline
wl_display *WaylandBackend::display()
{