diff --git a/CMakeLists.txt b/CMakeLists.txt index c4745df217..a09dd86107 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -147,6 +147,20 @@ set_package_properties(XKB PROPERTIES PURPOSE "Required for building KWin with Wayland support" ) +find_package(Libinput 0.5) +set_package_properties(Libinput PROPERTIES TYPE OPTIONAL PURPOSE "Required for input handling on Wayland.") + +find_package(UDev) +set_package_properties(UDev PROPERTIES URL "http://www.freedesktop.org/software/systemd/libudev/" + DESCRIPTION "Linux device library." + TYPE OPTIONAL + PURPOSE "Required for input handling on Wayland." + ) +set(HAVE_INPUT FALSE) +if (Libinput_FOUND AND UDEV_FOUND) + set(HAVE_INPUT TRUE) +endif() + find_package(X11) set_package_properties(X11 PROPERTIES DESCRIPTION "X11 libraries" URL "http://www.x.org" @@ -410,6 +424,15 @@ if(HAVE_WAYLAND) endif() endif() +if(HAVE_INPUT) + set(kwin_KDEINIT_SRCS + ${kwin_KDEINIT_SRCS} + libinput/context.cpp + libinput/connection.cpp + libinput/events.cpp + ) +endif() + kconfig_add_kcfg_files(kwin_KDEINIT_SRCS settings.kcfgc) qt5_add_dbus_adaptor( kwin_KDEINIT_SRCS org.kde.KWin.xml dbusinterface.h KWin::DBusInterface ) @@ -515,6 +538,10 @@ if(HAVE_WAYLAND) endif() endif() +if(HAVE_INPUT) + set(kwinLibs ${kwinLibs} Libinput::Libinput ${UDEV_LIBS}) +endif() + add_library(kwin SHARED ${kwin_KDEINIT_SRCS}) set_target_properties(kwin PROPERTIES diff --git a/cmake/modules/FindLibinput.cmake b/cmake/modules/FindLibinput.cmake new file mode 100644 index 0000000000..b856e0bbcb --- /dev/null +++ b/cmake/modules/FindLibinput.cmake @@ -0,0 +1,125 @@ +#.rst: +# FindLibinput +# ------- +# +# Try to find libinput on a Unix system. +# +# This will define the following variables: +# +# ``Libinput_FOUND`` +# True if (the requested version of) libinput is available +# ``Libinput_VERSION`` +# The version of libinput +# ``Libinput_LIBRARIES`` +# This can be passed to target_link_libraries() instead of the ``Libinput::Libinput`` +# target +# ``Libinput_INCLUDE_DIRS`` +# This should be passed to target_include_directories() if the target is not +# used for linking +# ``Libinput_DEFINITIONS`` +# This should be passed to target_compile_options() if the target is not +# used for linking +# +# If ``Libinput_FOUND`` is TRUE, it will also define the following imported target: +# +# ``Libinput::Libinput`` +# The libinput library +# +# In general we recommend using the imported target, as it is easier to use. +# Bear in mind, however, that if the target is in the link interface of an +# exported library, it must be made available by the package config file. + +#============================================================================= +# Copyright 2014 Alex Merry +# Copyright 2014 Martin Gräßlin +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#============================================================================= + +if(CMAKE_VERSION VERSION_LESS 2.8.12) + message(FATAL_ERROR "CMake 2.8.12 is required by FindLibinput.cmake") +endif() +if(CMAKE_MINIMUM_REQUIRED_VERSION VERSION_LESS 2.8.12) + message(AUTHOR_WARNING "Your project should require at least CMake 2.8.12 to use FindLibinput.cmake") +endif() + +if(NOT WIN32) + # Use pkg-config to get the directories and then use these values + # in the FIND_PATH() and FIND_LIBRARY() calls + find_package(PkgConfig) + pkg_check_modules(PKG_Libinput QUIET libinput) + + set(Libinput_DEFINITIONS ${PKG_Libinput_CFLAGS_OTHER}) + set(Libinput_VERSION ${PKG_Libinput_VERSION}) + + find_path(Libinput_INCLUDE_DIR + NAMES + libinput.h + HINTS + ${PKG_Libinput_INCLUDE_DIRS} + ) + find_library(Libinput_LIBRARY + NAMES + input + HINTS + ${PKG_Libinput_LIBRARY_DIRS} + ) + + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(Libinput + FOUND_VAR + Libinput_FOUND + REQUIRED_VARS + Libinput_LIBRARY + Libinput_INCLUDE_DIR + VERSION_VAR + Libinput_VERSION + ) + + if(Libinput_FOUND AND NOT TARGET Libinput::Libinput) + add_library(Libinput::Libinput UNKNOWN IMPORTED) + set_target_properties(Libinput::Libinput PROPERTIES + IMPORTED_LOCATION "${Libinput_LIBRARY}" + INTERFACE_COMPILE_OPTIONS "${Libinput_DEFINITIONS}" + INTERFACE_INCLUDE_DIRECTORIES "${Libinput_INCLUDE_DIR}" + ) + endif() + + mark_as_advanced(Libinput_LIBRARY Libinput_INCLUDE_DIR) + + # compatibility variables + set(Libinput_LIBRARIES ${Libinput_LIBRARY}) + set(Libinput_INCLUDE_DIRS ${Libinput_INCLUDE_DIR}) + set(Libinput_VERSION_STRING ${Libinput_VERSION}) + +else() + message(STATUS "FindLibinput.cmake cannot find libinput on Windows systems.") + set(Libinput_FOUND FALSE) +endif() + +include(FeatureSummary) +set_package_properties(Libinput PROPERTIES + URL "http://www.freedesktop.org/wiki/Software/libinput/" + DESCRIPTION "Library to handle input devices in Wayland compositors and to provide a generic X.Org input driver." +) diff --git a/cmake/modules/FindUDev.cmake b/cmake/modules/FindUDev.cmake new file mode 100644 index 0000000000..402ef7ec08 --- /dev/null +++ b/cmake/modules/FindUDev.cmake @@ -0,0 +1,29 @@ +# - Try to find the UDev library +# Once done this will define +# +# UDEV_FOUND - system has UDev +# UDEV_INCLUDE_DIR - the libudev include directory +# UDEV_LIBS - The libudev libraries + +# Copyright (c) 2010, Rafael Fernández López, +# +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. + +find_path(UDEV_INCLUDE_DIR libudev.h) +find_library(UDEV_LIBS udev) + +if(UDEV_INCLUDE_DIR AND UDEV_LIBS) + include(CheckFunctionExists) + include(CMakePushCheckState) + cmake_push_check_state() + set(CMAKE_REQUIRED_LIBRARIES ${UDEV_LIBS} ) + + cmake_pop_check_state() + +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(UDev DEFAULT_MSG UDEV_INCLUDE_DIR UDEV_LIBS) + +mark_as_advanced(UDEV_INCLUDE_DIR UDEV_LIBS) diff --git a/config-kwin.h.cmake b/config-kwin.h.cmake index 3e2338eb4e..26c2b0a89d 100644 --- a/config-kwin.h.cmake +++ b/config-kwin.h.cmake @@ -12,6 +12,7 @@ #cmakedefine01 HAVE_WAYLAND #cmakedefine01 HAVE_WAYLAND_EGL #cmakedefine01 HAVE_XKB +#cmakedefine01 HAVE_INPUT /* Define to 1 if you have the header file. */ #cmakedefine HAVE_UNISTD_H 1 diff --git a/input.cpp b/input.cpp index dce42a7ff2..80d4b476b3 100644 --- a/input.cpp +++ b/input.cpp @@ -26,6 +26,9 @@ along with this program. If not, see . #endif #include "unmanaged.h" #include "workspace.h" +#if HAVE_INPUT +#include "libinput/connection.h" +#endif // KDE #include // TODO: remove xtest @@ -166,6 +169,16 @@ InputRedirection::InputRedirection(QObject *parent) , m_pointerWindow() , m_shortcuts(new GlobalShortcutsManager(this)) { +#if HAVE_INPUT + LibInput::Connection *conn = LibInput::Connection::create(this); + if (conn) { + // TODO: connect the motion notifiers + conn->setup(); + connect(conn, &LibInput::Connection::pointerButtonChanged, this, &InputRedirection::processPointerButton); + connect(conn, &LibInput::Connection::pointerAxisChanged, this, &InputRedirection::processPointerAxis); + connect(conn, &LibInput::Connection::keyChanged, this, &InputRedirection::processKeyboardKey); + } +#endif } InputRedirection::~InputRedirection() diff --git a/libinput/connection.cpp b/libinput/connection.cpp new file mode 100644 index 0000000000..ca71e563f9 --- /dev/null +++ b/libinput/connection.cpp @@ -0,0 +1,137 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2014 Martin Gräßlin + +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 . +*********************************************************************/ +#include "connection.h" +#include "context.h" +#include "events.h" + +#include +#include + +#include + +namespace KWin +{ +namespace LibInput +{ + +Connection *Connection::s_self = nullptr; + +static Context *s_context = nullptr; + +Connection::Connection(QObject *parent) = delete; + +Connection *Connection::create(QObject *parent) +{ + Q_ASSERT(!s_self); + static Udev s_udev; + if (!s_udev.isValid()) { + qWarning() << "Failed to initialize udev"; + return nullptr; + } + if (!s_context) { + s_context = new Context(s_udev); + if (!s_context->isValid()) { + qWarning() << "Failed to create context from udev"; + delete s_context; + s_context = nullptr; + return nullptr; + } + // TODO: don't hardcode seat name + if (!s_context->assignSeat("seat0")) { + qWarning() << "Failed to assign seat seat0"; + delete s_context; + s_context = nullptr; + return nullptr; + } + } + s_self = new Connection(s_context, parent); + return s_self; +} + +Connection::Connection(Context *input, QObject *parent) + : QObject(parent) + , m_input(input) + , m_notifier(nullptr) +{ + Q_ASSERT(m_input); +} + +Connection::~Connection() +{ + s_self = nullptr; + delete s_context; + s_context = nullptr; +} + +void Connection::setup() +{ + Q_ASSERT(!m_notifier); + m_notifier = new QSocketNotifier(m_input->fileDescriptor(), QSocketNotifier::Read, this); + connect(m_notifier, &QSocketNotifier::activated, this, &Connection::handleEvent); +} + +void Connection::handleEvent() +{ + do { + m_input->dispatch(); + QScopedPointer event(m_input->event()); + if (event.isNull()) { + break; + } + switch (event->type()) { + case LIBINPUT_EVENT_KEYBOARD_KEY: { + KeyEvent *ke = static_cast(event.data()); + emit keyChanged(ke->key(), ke->state(), ke->time()); + break; + } + case LIBINPUT_EVENT_POINTER_AXIS: { + PointerEvent *pe = static_cast(event.data()); + emit pointerAxisChanged(pe->axis(), pe->axisValue(), pe->time()); + break; + } + case LIBINPUT_EVENT_POINTER_BUTTON: { + PointerEvent *pe = static_cast(event.data()); + emit pointerButtonChanged(pe->button(), pe->buttonState(), pe->time()); + break; + } + case LIBINPUT_EVENT_POINTER_MOTION: { + PointerEvent *pe = static_cast(event.data()); + emit pointerMotion(pe->delta(), pe->time()); + break; + } + case LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE: { + PointerEvent *pe = static_cast(event.data()); + emit pointerMotionAbsolute(pe->absolutePos(), pe->absolutePos(m_size), pe->time()); + break; + } + default: + // nothing + break; + } + } while (true); +} + +void Connection::setScreenSize(const QSize &size) +{ + m_size = size; +} + +} +} diff --git a/libinput/connection.h b/libinput/connection.h new file mode 100644 index 0000000000..9bc0a57f0f --- /dev/null +++ b/libinput/connection.h @@ -0,0 +1,71 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2014 Martin Gräßlin + +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 . +*********************************************************************/ +#ifndef KWIN_LIBINPUT_CONNECTION_H +#define KWIN_LIBINPUT_CONNECTION_H + +#include "../input.h" +#include + +#include +#include + +class QSocketNotifier; + +namespace KWin +{ +namespace LibInput +{ + +class Context; + +class Connection : public QObject +{ + Q_OBJECT +public: + ~Connection(); + + void setup(); + /** + * Sets the screen @p size. This is needed for mapping absolute pointer events to + * the screen data. + **/ + void setScreenSize(const QSize &size); + +Q_SIGNALS: + void keyChanged(uint32_t key, InputRedirection::KeyboardKeyState, uint32_t time); + void pointerButtonChanged(uint32_t button, InputRedirection::PointerButtonState state, uint32_t time); + void pointerMotionAbsolute(QPointF orig, QPointF screen, uint32_t time); + void pointerMotion(QPointF delta, uint32_t time); + void pointerAxisChanged(InputRedirection::PointerAxis axis, qreal delta, uint32_t time); + +private: + Connection(Context *input, QObject *parent = nullptr); + void handleEvent(); + Context *m_input; + QSocketNotifier *m_notifier; + QSize m_size; + + KWIN_SINGLETON(Connection) +}; + +} +} + +#endif diff --git a/libinput/context.cpp b/libinput/context.cpp new file mode 100644 index 0000000000..286d145ff1 --- /dev/null +++ b/libinput/context.cpp @@ -0,0 +1,111 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2014 Martin Gräßlin + +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 . +*********************************************************************/ +#include "context.h" +#include "events.h" + +#include +#include +#include +#include + +namespace KWin +{ +namespace LibInput +{ + +Udev::Udev() + : m_udev(udev_new()) +{ +} + +Udev::~Udev() +{ + if (m_udev) { + udev_unref(m_udev); + } +} + +Context::Context(const Udev &udev) + : m_libinput(libinput_udev_create_context(&Context::s_interface, this, udev)) +{ + libinput_log_set_priority(m_libinput, LIBINPUT_LOG_PRIORITY_DEBUG); +} + +Context::~Context() +{ + if (m_libinput) { + libinput_unref(m_libinput); + } +} + +bool Context::assignSeat(const char *seat) +{ + if (!isValid()) { + return false; + } + return libinput_udev_assign_seat(m_libinput, seat) == 0; +} + +int Context::fileDescriptor() +{ + if (!isValid()) { + return 0; + } + return libinput_get_fd(m_libinput); +} + +void Context::dispatch() +{ + libinput_dispatch(m_libinput); +} + +const struct libinput_interface Context::s_interface = { + Context::openRestrictedCallback, + Context::closeRestrictedCallBack +}; + +int Context::openRestrictedCallback(const char *path, int flags, void *user_data) +{ + return ((Context*)user_data)->openRestricted(path, flags); +} + +void Context::closeRestrictedCallBack(int fd, void *user_data) +{ + ((Context*)user_data)->closeRestricted(fd); +} + +int Context::openRestricted(const char *path, int flags) +{ + int fd = open(path, flags); + return fd < 0 ? -errno : fd; +} + +void Context::closeRestricted(int fd) +{ + close(fd); +} + +Event *Context::event() +{ + return Event::create(libinput_get_event(m_libinput)); +} + +} +} diff --git a/libinput/context.h b/libinput/context.h new file mode 100644 index 0000000000..d0c65f8c56 --- /dev/null +++ b/libinput/context.h @@ -0,0 +1,92 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2014 Martin Gräßlin + +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 . +*********************************************************************/ +#ifndef KWIN_LIBINPUT_CONTEXT_H +#define KWIN_LIBINPUT_CONTEXT_H + +#include + +struct udev; + +namespace KWin +{ +namespace LibInput +{ + +class Event; + +class Udev +{ +public: + Udev(); + ~Udev(); + + bool isValid() const { + return m_udev != nullptr; + } + operator udev*() const { + return m_udev; + } + operator udev*() { + return m_udev; + } +private: + struct udev *m_udev; +}; + +class Context +{ +public: + Context(const Udev &udev); + ~Context(); + bool assignSeat(const char *seat); + bool isValid() const { + return m_libinput != nullptr; + } + + int fileDescriptor(); + void dispatch(); + + operator libinput*() { + return m_libinput; + } + operator libinput*() const { + return m_libinput; + } + + /** + * Gets the next event, if there is no new event @c null is returned. + * The caller takes ownership of the returned pointer. + **/ + Event *event(); + + static int openRestrictedCallback(const char *path, int flags, void *user_data); + static void closeRestrictedCallBack(int fd, void *user_data); + static const struct libinput_interface s_interface; + +private: + int openRestricted(const char *path, int flags); + void closeRestricted(int fd); + struct libinput *m_libinput; +}; + +} +} + +#endif diff --git a/libinput/events.cpp b/libinput/events.cpp new file mode 100644 index 0000000000..02db1937a6 --- /dev/null +++ b/libinput/events.cpp @@ -0,0 +1,165 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2014 Martin Gräßlin + +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 . +*********************************************************************/ +#include "events.h" + +#include + +namespace KWin +{ +namespace LibInput +{ + +Event *Event::create(libinput_event *event) +{ + if (!event) { + return nullptr; + } + const auto t = libinput_event_get_type(event); + // TODO: add touch events + // TODO: add device notify events + switch (t) { + case LIBINPUT_EVENT_KEYBOARD_KEY: + return new KeyEvent(event); + case LIBINPUT_EVENT_POINTER_AXIS: + case LIBINPUT_EVENT_POINTER_BUTTON: + case LIBINPUT_EVENT_POINTER_MOTION: + case LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE: + return new PointerEvent(event, t); + default: + return new Event(event, t); + } +} + +Event::Event(libinput_event *event, libinput_event_type type) + : m_event(event) + , m_type(type) +{ +} + +Event::~Event() +{ + libinput_event_destroy(m_event); +} + +libinput_device *Event::device() const +{ + return libinput_event_get_device(m_event); +} + +KeyEvent::KeyEvent(libinput_event *event) + : Event(event, LIBINPUT_EVENT_KEYBOARD_KEY) + , m_keyboardEvent(libinput_event_get_keyboard_event(event)) +{ +} + +KeyEvent::~KeyEvent() = default; + +uint32_t KeyEvent::key() const +{ + return libinput_event_keyboard_get_key(m_keyboardEvent); +} + +InputRedirection::KeyboardKeyState KeyEvent::state() const +{ + switch (libinput_event_keyboard_get_key_state(m_keyboardEvent)) { + case LIBINPUT_KEY_STATE_PRESSED: + return InputRedirection::KeyboardKeyPressed; + case LIBINPUT_KEY_STATE_RELEASED: + return InputRedirection::KeyboardKeyReleased; + } + abort(); +} + +uint32_t KeyEvent::time() const +{ + return libinput_event_keyboard_get_time(m_keyboardEvent); +} + +PointerEvent::PointerEvent(libinput_event *event, libinput_event_type type) + : Event(event, type) + , m_pointerEvent(libinput_event_get_pointer_event(event)) +{ +} + +PointerEvent::~PointerEvent() = default; + +QPointF PointerEvent::absolutePos() const +{ + Q_ASSERT(type() == LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE); + return QPointF(libinput_event_pointer_get_absolute_x(m_pointerEvent), + libinput_event_pointer_get_absolute_y(m_pointerEvent)); +} + +QPointF PointerEvent::absolutePos(const QSize &size) const +{ + Q_ASSERT(type() == LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE); + return QPointF(libinput_event_pointer_get_absolute_x_transformed(m_pointerEvent, size.width()), + libinput_event_pointer_get_absolute_y_transformed(m_pointerEvent, size.height())); +} + +QPointF PointerEvent::delta() const +{ + Q_ASSERT(type() == LIBINPUT_EVENT_POINTER_MOTION); + return QPointF(libinput_event_pointer_get_dx(m_pointerEvent), libinput_event_pointer_get_dy(m_pointerEvent)); +} + +uint32_t PointerEvent::time() const +{ + return libinput_event_pointer_get_time(m_pointerEvent); +} + +uint32_t PointerEvent::button() const +{ + Q_ASSERT(type() == LIBINPUT_EVENT_POINTER_BUTTON); + return libinput_event_pointer_get_button(m_pointerEvent); +} + +InputRedirection::PointerButtonState PointerEvent::buttonState() const +{ + Q_ASSERT(type() == LIBINPUT_EVENT_POINTER_BUTTON); + switch (libinput_event_pointer_get_button_state(m_pointerEvent)) { + case LIBINPUT_BUTTON_STATE_PRESSED: + return InputRedirection::PointerButtonPressed; + case LIBINPUT_BUTTON_STATE_RELEASED: + return InputRedirection::PointerButtonReleased; + } + abort(); +} + +InputRedirection::PointerAxis PointerEvent::axis() const +{ + Q_ASSERT(type() == LIBINPUT_EVENT_POINTER_AXIS); + switch (libinput_event_pointer_get_axis(m_pointerEvent)) { + case LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL: + return InputRedirection::PointerAxisHorizontal; + case LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL: + return InputRedirection::PointerAxisVertical; + } + abort(); +} + +qreal PointerEvent::axisValue() const +{ + Q_ASSERT(type() == LIBINPUT_EVENT_POINTER_AXIS); + return libinput_event_pointer_get_axis_value(m_pointerEvent); +} + +} +} diff --git a/libinput/events.h b/libinput/events.h new file mode 100644 index 0000000000..60335069f9 --- /dev/null +++ b/libinput/events.h @@ -0,0 +1,113 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2014 Martin Gräßlin + +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 . +*********************************************************************/ +#ifndef KWIN_LIBINPUT_EVENTS_H +#define KWIN_LIBINPUT_EVENTS_H + +#include "../input.h" + +#include + +namespace KWin +{ +namespace LibInput +{ + +class Event +{ +public: + virtual ~Event(); + + libinput_event_type type() const; + libinput_device *device() const; + + operator libinput_event*() { + return m_event; + } + operator libinput_event*() const { + return m_event; + } + + static Event *create(libinput_event *event); + +protected: + Event(libinput_event *event, libinput_event_type type); + +private: + libinput_event *m_event; + libinput_event_type m_type; +}; + +class KeyEvent : public Event +{ +public: + KeyEvent(libinput_event *event); + virtual ~KeyEvent(); + + uint32_t key() const; + InputRedirection::KeyboardKeyState state() const; + uint32_t time() const; + + operator libinput_event_keyboard*() { + return m_keyboardEvent; + } + operator libinput_event_keyboard*() const { + return m_keyboardEvent; + } + +private: + libinput_event_keyboard *m_keyboardEvent; +}; + +class PointerEvent : public Event +{ +public: + PointerEvent(libinput_event* event, libinput_event_type type); + virtual ~PointerEvent(); + + QPointF absolutePos() const; + QPointF absolutePos(const QSize &size) const; + QPointF delta() const; + uint32_t button() const; + InputRedirection::PointerButtonState buttonState() const; + uint32_t time() const; + InputRedirection::PointerAxis axis() const; + qreal axisValue() const; + + operator libinput_event_pointer*() { + return m_pointerEvent; + } + operator libinput_event_pointer*() const { + return m_pointerEvent; + } + +private: + libinput_event_pointer *m_pointerEvent; +}; + +inline +libinput_event_type Event::type() const +{ + return m_type; +} + +} +} + +#endif diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index ae5d353587..74d905d7b1 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -17,3 +17,14 @@ if (Wayland_Client_FOUND AND XKB_FOUND AND KF5Wayland_FOUND) add_executable(waylandclienttest ${waylandclienttest_SRCS}) target_link_libraries(waylandclienttest Qt5::Core Qt5::Gui Wayland::Client KF5::WaylandClient) endif() + +if (HAVE_INPUT) + set(libinputtest_SRCS + libinputtest.cpp + ${KWIN_SOURCE_DIR}/libinput/context.cpp + ${KWIN_SOURCE_DIR}/libinput/connection.cpp + ${KWIN_SOURCE_DIR}/libinput/events.cpp + ) + add_executable(libinputtest ${libinputtest_SRCS}) + target_link_libraries(libinputtest Qt5::Core Libinput::Libinput ${UDEV_LIBS}) +endif() diff --git a/tests/libinputtest.cpp b/tests/libinputtest.cpp new file mode 100644 index 0000000000..0e04779d94 --- /dev/null +++ b/tests/libinputtest.cpp @@ -0,0 +1,69 @@ +/* + * Copyright 2014 Martin Gräßlin + * + * 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) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * 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 . +*/ +#include "../input.h" +#include "../libinput/connection.h" + +#include + +#include + +#include + +int main(int argc, char **argv) +{ + using namespace KWin::LibInput; + + QCoreApplication app(argc, argv); + Connection *conn = Connection::create(&app); + if (!conn) { + std::cerr << "Failed to create LibInput integration" << std::endl; + return 1; + } + conn->setScreenSize(QSize(100, 100)); + conn->setup(); + QObject::connect(conn, &Connection::keyChanged, + [](uint32_t key, KWin::InputRedirection::KeyboardKeyState state) { + std::cout << "Got key event" << std::endl;; + if (key == KEY_Q && state == KWin::InputRedirection::KeyboardKeyReleased) { + QCoreApplication::instance()->quit(); + } + } + ); + QObject::connect(conn, &Connection::pointerButtonChanged, + [](uint32_t button, KWin::InputRedirection::PointerButtonState state) { + std::cout << "Got pointer button event" << std::endl; + if (button == BTN_MIDDLE && state == KWin::InputRedirection::PointerButtonReleased) { + QCoreApplication::instance()->quit(); + } + } + ); + QObject::connect(conn, &Connection::pointerMotion, + [](QPointF delta) { + std::cout << "Got pointer motion: " << delta.x() << "/" << delta.y() << std::endl; + } + ); + QObject::connect(conn, &Connection::pointerAxisChanged, + [](KWin::InputRedirection::PointerAxis axis, qreal delta) { + std::cout << "Axis: " << axis << " with delta" << delta << std::endl; + } + ); + + return app.exec(); +}