diff options
Diffstat (limited to 'src/compositor/wayland_wrapper/qwlinputdevice.cpp')
-rw-r--r-- | src/compositor/wayland_wrapper/qwlinputdevice.cpp | 650 |
1 files changed, 650 insertions, 0 deletions
diff --git a/src/compositor/wayland_wrapper/qwlinputdevice.cpp b/src/compositor/wayland_wrapper/qwlinputdevice.cpp new file mode 100644 index 000000000..8591de254 --- /dev/null +++ b/src/compositor/wayland_wrapper/qwlinputdevice.cpp @@ -0,0 +1,650 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Compositor. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "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 COPYRIGHT +** OWNER OR CONTRIBUTORS 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." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qwlinputdevice_p.h" + +#include "qwlcompositor_p.h" +#include "qwldatadevice_p.h" +#include "qwlsurface_p.h" +#include "qwltouch_p.h" +#include "qwlqtkey_p.h" +#include "qwaylandcompositor.h" + +#include <QtGui/QTouchEvent> + +#ifndef QT_NO_WAYLAND_XKB +#include <sys/mman.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/epoll.h> +#include <unistd.h> +#include <errno.h> +#include <fcntl.h> +#include <stdlib.h> +#endif + +QT_BEGIN_NAMESPACE + +namespace QtWayland { + +InputDevice::InputDevice(QWaylandInputDevice *handle, Compositor *compositor) + : m_handle(handle) + , m_compositor(compositor) +{ + wl_seat_init(base()); + initDevices(); + wl_display_add_global(compositor->wl_display(), + &wl_seat_interface, + this, + InputDevice::bind_func); + +#ifndef QT_NO_WAYLAND_XKB + xkb_rule_names xkb_names; + xkb_context *context = xkb_context_new(xkb_context_flags(0)); + + memset(&xkb_names, 0, sizeof(xkb_names)); + xkb_names.rules = strdup("evdev"); + xkb_names.model = strdup("pc105"); + xkb_names.layout = strdup("us"); + + xkb_keymap *keymap = xkb_map_new_from_names(context, &xkb_names, xkb_map_compile_flags(0)); + if (!keymap) + qFatal("Failed to compile global XKB keymap"); + + char *keymap_str_data = xkb_map_get_as_string(keymap); + QByteArray keymap_str = keymap_str_data; + m_keymap_size = keymap_str.size() + 1; + free(keymap_str_data); + + const char *path = getenv("XDG_RUNTIME_DIR"); + if (!path) + qFatal("XDG_RUNTIME_DIR not set"); + + QByteArray name = QByteArray(path) + "/qtwayland-xkb-map-XXXXXX"; + + int fd = mkstemp(name.data()); + if (fd >= 0) { + long flags = fcntl(fd, F_GETFD); + if (flags == -1 || fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1) { + close(fd); + qFatal("Failed to set FD_CLOEXEC on anonymous file"); + } + unlink(name.data()); + } else { + qFatal("Failed to create anonymous file with name %s", name.constData()); + } + + if (ftruncate(fd, m_keymap_size) < 0) + qFatal("Failed to create anonymous file of size %lu", (unsigned long)m_keymap_size); + + m_keymap_fd = fd; + + m_keymap_area = (char *)mmap(0, m_keymap_size, PROT_READ | PROT_WRITE, MAP_SHARED, m_keymap_fd, 0); + if (m_keymap_area == MAP_FAILED) { + close(m_keymap_fd); + qFatal("Failed to map shared memory segment"); + } + + strcpy(m_keymap_area, keymap_str.constData()); + + m_state = xkb_state_new(keymap); + + free((char *)xkb_names.rules); + free((char *)xkb_names.model); + free((char *)xkb_names.layout); + xkb_map_unref(keymap); + xkb_context_unref(context); +#endif +} + +InputDevice::~InputDevice() +{ + qDeleteAll(m_data_devices); + releaseDevices(); + +#ifndef QT_NO_WAYLAND_XKB + if (m_keymap_area) + munmap(m_keymap_area, m_keymap_size); + close(m_keymap_fd); + xkb_state_unref(m_state); +#endif +} + +void InputDevice::initDevices() +{ + wl_pointer_init(&m_device_interfaces.pointer); + wl_seat_set_pointer(base(), &m_device_interfaces.pointer); + + wl_keyboard_init(&m_device_interfaces.keyboard); + wl_seat_set_keyboard(base(), &m_device_interfaces.keyboard); + + wl_touch_init(&m_device_interfaces.touch); + wl_seat_set_touch(base(), &m_device_interfaces.touch); +} + +void InputDevice::releaseDevices() +{ + wl_pointer_release(&m_device_interfaces.pointer); + wl_keyboard_release(&m_device_interfaces.keyboard); + wl_touch_release(&m_device_interfaces.touch); +} + +wl_pointer *InputDevice::pointerDevice() +{ + return &m_device_interfaces.pointer; +} + +wl_keyboard *InputDevice::keyboardDevice() +{ + return &m_device_interfaces.keyboard; +} + +wl_touch *InputDevice::touchDevice() +{ + return &m_device_interfaces.touch; +} + +const wl_pointer *InputDevice::pointerDevice() const +{ + return &m_device_interfaces.pointer; +} + +const wl_keyboard *InputDevice::keyboardDevice() const +{ + return &m_device_interfaces.keyboard; +} + +const wl_touch *InputDevice::touchDevice() const +{ + return &m_device_interfaces.touch; +} + +void InputDevice::destroy_resource(wl_resource *resource) +{ + InputDevice *input_device = static_cast<InputDevice *>(resource->data); + if (input_device->keyboardDevice()->focus_resource == resource) { + input_device->keyboardDevice()->focus_resource = 0; + } + if (input_device->pointerDevice()->focus_resource == resource) { + input_device->pointerDevice()->focus_resource = 0; + } + + input_device->cleanupDataDeviceForClient(resource->client, true); + + wl_list_remove(&resource->link); + + free(resource); +} + +void InputDevice::bind_func(struct wl_client *client, void *data, + uint32_t version, uint32_t id) +{ + Q_UNUSED(version); + struct wl_resource *resource = wl_client_add_object(client, + &wl_seat_interface, + &seat_interface, + id, + data); + + struct wl_seat *seat = static_cast<struct wl_seat *>(data); + resource->destroy = destroy_resource; + wl_list_insert(&seat->base_resource_list, &resource->link); + + uint32_t caps = WL_SEAT_CAPABILITY_POINTER | WL_SEAT_CAPABILITY_KEYBOARD; + if (!QTouchDevice::devices().isEmpty()) + caps |= WL_SEAT_CAPABILITY_TOUCH; + + wl_seat_send_capabilities(resource, caps); +} + +const struct wl_pointer_interface InputDevice::pointer_interface = { + InputDevice::set_cursor +}; + +void InputDevice::set_cursor(wl_client *client, wl_resource *resource, + uint32_t serial, wl_resource *surface_resource, + int32_t hotspot_x, int32_t hotspot_y) +{ + Q_UNUSED(client); + Q_UNUSED(serial); + + wl_pointer *pointer = reinterpret_cast<wl_pointer *>(resource->data); + InputDevice *inputDevice = wayland_cast<InputDevice>(pointer->seat); + QtWayland::Surface *surface = reinterpret_cast<QtWayland::Surface *>(surface_resource->data); + + surface->setCursorSurface(true); + inputDevice->m_compositor->waylandCompositor()->setCursorSurface(surface->waylandSurface(), hotspot_x, hotspot_y); +} + +const struct wl_seat_interface InputDevice::seat_interface = { + get_pointer, + get_keyboard, + get_touch +}; + +void InputDevice::destroy_device_resource(wl_resource *resource) +{ + wl_list_remove(&resource->link); + free(resource); +} + +void InputDevice::get_pointer(struct wl_client *client, + struct wl_resource *resource, + uint32_t id) +{ + InputDevice *inputDevice = static_cast<InputDevice *>(resource->data); + wl_pointer *pointer = inputDevice->pointerDevice(); + wl_resource *clientResource = wl_client_add_object(client, + &wl_pointer_interface, + &pointer_interface, + id, + pointer); + wl_list_insert(&pointer->resource_list, &clientResource->link); + clientResource->destroy = InputDevice::destroy_device_resource; +} + +void InputDevice::get_keyboard(struct wl_client *client, + struct wl_resource *resource, + uint32_t id) +{ + InputDevice *inputDevice = static_cast<InputDevice *>(resource->data); + wl_keyboard *keyboard = inputDevice->keyboardDevice(); + wl_resource *clientResource = wl_client_add_object(client, + &wl_keyboard_interface, + 0, + id, + keyboard); + wl_list_insert(&keyboard->resource_list, &clientResource->link); + clientResource->destroy = InputDevice::destroy_device_resource; + +#ifndef QT_NO_WAYLAND_XKB + wl_keyboard_send_keymap(clientResource, WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1, + inputDevice->m_keymap_fd, inputDevice->m_keymap_size); +#endif +} + +void InputDevice::get_touch(struct wl_client *client, + struct wl_resource *resource, + uint32_t id) +{ + InputDevice *inputDevice = static_cast<InputDevice *>(resource->data); + wl_touch *touch = inputDevice->touchDevice(); + wl_resource *clientResource = wl_client_add_object(client, + &wl_touch_interface, + 0, + id, + touch); + wl_list_insert(&touch->resource_list, &clientResource->link); + clientResource->destroy = InputDevice::destroy_device_resource; +} + +void InputDevice::sendMousePressEvent(Qt::MouseButton button, const QPointF &localPos, const QPointF &globalPos) +{ + sendMouseMoveEvent(localPos,globalPos); + wl_pointer *pointer = pointerDevice(); + pointer->button_count++; + uint32_t time = m_compositor->currentTimeMsecs(); + const struct wl_pointer_grab_interface *interface = pointer->grab->interface; + interface->button(pointer->grab, time, toWaylandButton(button), 1); +} + +void InputDevice::sendMouseReleaseEvent(Qt::MouseButton button, const QPointF &localPos, const QPointF &globalPos) +{ + sendMouseMoveEvent(localPos,globalPos); + wl_pointer *pointer = pointerDevice(); + pointer->button_count--; + uint32_t time = m_compositor->currentTimeMsecs(); + const struct wl_pointer_grab_interface *interface = pointer->grab->interface; + interface->button(pointer->grab, time, toWaylandButton(button), 0); +} + +void InputDevice::sendMouseMoveEvent(const QPointF &localPos, const QPointF &globalPos) +{ + Q_UNUSED(globalPos); + uint32_t time = m_compositor->currentTimeMsecs(); + wl_pointer *pointer = pointerDevice(); + const struct wl_pointer_grab_interface *interface = pointer->grab->interface; + pointer->x = wl_fixed_from_double(globalPos.x()); + pointer->y = wl_fixed_from_double(globalPos.y()); + interface->motion(pointer->grab, + time, + wl_fixed_from_double(localPos.x()), wl_fixed_from_double(localPos.y())); +} + +void InputDevice::sendMouseMoveEvent(Surface *surface, const QPointF &localPos, const QPointF &globalPos) +{ + setMouseFocus(surface,localPos,globalPos); + sendMouseMoveEvent(localPos,globalPos); +} + +void InputDevice::sendMouseWheelEvent(Qt::Orientation orientation, int delta) +{ + wl_pointer *pointer = pointerDevice(); + struct wl_resource *resource = pointer->focus_resource; + if (!resource) + return; + uint32_t time = m_compositor->currentTimeMsecs(); + uint32_t axis = orientation == Qt::Horizontal ? WL_POINTER_AXIS_HORIZONTAL_SCROLL + : WL_POINTER_AXIS_VERTICAL_SCROLL; + wl_pointer_send_axis(resource, time, axis, wl_fixed_from_int(-delta / 12)); +} + +void InputDevice::updateModifierState(uint code, int state) +{ +#ifndef QT_NO_WAYLAND_XKB + xkb_state_update_key(m_state, code, state ? XKB_KEY_DOWN : XKB_KEY_UP); + + uint32_t mods_depressed = xkb_state_serialize_mods(m_state, (xkb_state_component)XKB_STATE_DEPRESSED); + uint32_t mods_latched = xkb_state_serialize_mods(m_state, (xkb_state_component)XKB_STATE_LATCHED); + uint32_t mods_locked = xkb_state_serialize_mods(m_state, (xkb_state_component)XKB_STATE_LATCHED); + uint32_t group = xkb_state_serialize_group(m_state, (xkb_state_component)XKB_STATE_EFFECTIVE); + + wl_keyboard *keyboard = keyboardDevice(); + + if (mods_depressed == keyboard->modifiers.mods_depressed + && mods_latched == keyboard->modifiers.mods_latched + && mods_locked == keyboard->modifiers.mods_locked + && group == keyboard->modifiers.group) + { + return; // no change + } + + keyboard->modifiers.mods_depressed = mods_depressed; + keyboard->modifiers.mods_latched = mods_latched; + keyboard->modifiers.mods_locked = mods_locked; + keyboard->modifiers.group = group; + + if (keyboard->focus_resource) + sendKeyModifiers(keyboard->focus_resource); +#else + Q_UNUSED(code); + Q_UNUSED(state); +#endif +} + +void InputDevice::sendKeyModifiers(wl_resource *resource) +{ + wl_keyboard *keyboard = keyboardDevice(); + uint32_t serial = wl_display_next_serial(m_compositor->wl_display()); + wl_keyboard_send_modifiers(resource, serial, keyboard->modifiers.mods_depressed, + keyboard->modifiers.mods_latched, keyboard->modifiers.mods_locked, keyboard->modifiers.group); +} + +void InputDevice::sendKeyPressEvent(uint code) +{ + wl_keyboard *keyboard = keyboardDevice(); + if (keyboard->focus_resource) { + uint32_t time = m_compositor->currentTimeMsecs(); + uint32_t serial = wl_display_next_serial(m_compositor->wl_display()); + wl_keyboard_send_key(keyboard->focus_resource, + serial, time, code - 8, 1); + } + updateModifierState(code, 1); +} + +void InputDevice::sendKeyReleaseEvent(uint code) +{ + wl_keyboard *keyboard = keyboardDevice(); + if (keyboard->focus_resource) { + uint32_t time = m_compositor->currentTimeMsecs(); + uint32_t serial = wl_display_next_serial(m_compositor->wl_display()); + wl_keyboard_send_key(keyboard->focus_resource, + serial, time, code - 8, 0); + } + updateModifierState(code, 0); +} + +void InputDevice::sendTouchPointEvent(int id, double x, double y, Qt::TouchPointState state) +{ + uint32_t time = m_compositor->currentTimeMsecs(); + uint32_t serial = 0; + wl_touch *touch = touchDevice(); + wl_resource *resource = touch->focus_resource; + if (!resource) + return; + switch (state) { + case Qt::TouchPointPressed: + wl_touch_send_down(resource, serial, time, &touch->focus->resource, id, + wl_fixed_from_double(x), wl_fixed_from_double(y)); + break; + case Qt::TouchPointMoved: + wl_touch_send_motion(resource, time, id, + wl_fixed_from_double(x), wl_fixed_from_double(y)); + break; + case Qt::TouchPointReleased: + wl_touch_send_up(resource, serial, time, id); + break; + case Qt::TouchPointStationary: + // stationary points are not sent through wayland, the client must cache them + break; + default: + break; + } +} + +void InputDevice::sendTouchFrameEvent() +{ + wl_touch *touch = touchDevice(); + wl_resource *resource = touch->focus_resource; + if (resource) + wl_touch_send_frame(resource); +} + +void InputDevice::sendTouchCancelEvent() +{ + wl_touch *touch = touchDevice(); + wl_resource *resource = touch->focus_resource; + if (resource) + wl_touch_send_cancel(resource); +} + +void InputDevice::sendFullKeyEvent(QKeyEvent *event) +{ + if (!keyboardFocus()) { + qWarning("Cannot send key event, no keyboard focus, fix the compositor"); + return; + } + + QtKeyExtensionGlobal *ext = m_compositor->qtkeyExtension(); + if (ext && ext->postQtKeyEvent(event, keyboardFocus())) + return; + + if (event->type() == QEvent::KeyPress) + sendKeyPressEvent(event->nativeScanCode()); + else if (event->type() == QEvent::KeyRelease) + sendKeyReleaseEvent(event->nativeScanCode()); +} + +void InputDevice::sendFullTouchEvent(QTouchEvent *event) +{ + if (!mouseFocus()) { + qWarning("Cannot send touch event, no pointer focus, fix the compositor"); + return; + } + + if (event->type() == QEvent::TouchCancel) { + sendTouchCancelEvent(); + return; + } + + TouchExtensionGlobal *ext = m_compositor->touchExtension(); + if (ext && ext->postTouchEvent(event, mouseFocus())) + return; + + const QList<QTouchEvent::TouchPoint> points = event->touchPoints(); + if (points.isEmpty()) + return; + + const int pointCount = points.count(); + QPointF pos = mouseFocus()->pos(); + for (int i = 0; i < pointCount; ++i) { + const QTouchEvent::TouchPoint &tp(points.at(i)); + // Convert the local pos in the compositor window to surface-relative. + QPointF p = tp.pos() - pos; + sendTouchPointEvent(tp.id(), p.x(), p.y(), tp.state()); + } + sendTouchFrameEvent(); +} + +Surface *InputDevice::keyboardFocus() const +{ + return wayland_cast<Surface>(keyboardDevice()->focus); +} + +/*! + * \return True if the keyboard focus is changed successfully. False for inactive transient surfaces. + */ +bool InputDevice::setKeyboardFocus(Surface *surface) +{ + if (surface && surface->transientInactive()) + return false; + + sendSelectionFocus(surface); + wl_keyboard_set_focus(keyboardDevice(), surface ? surface->base() : 0); + return true; +} + +Surface *InputDevice::mouseFocus() const +{ + return wayland_cast<Surface>(pointerDevice()->focus); +} + +void InputDevice::setMouseFocus(Surface *surface, const QPointF &localPos, const QPointF &globalPos) +{ + wl_pointer *pointer = pointerDevice(); + pointer->x = wl_fixed_from_double(globalPos.x()); + pointer->y = wl_fixed_from_double(globalPos.y()); + pointer->current = surface ? surface->base() : 0; + pointer->current_x = wl_fixed_from_double(localPos.x()); + pointer->current_y = wl_fixed_from_double(localPos.y()); + pointer->grab->interface->focus(pointer->grab, + surface ? surface->base() : 0, + wl_fixed_from_double(localPos.x()), wl_fixed_from_double(localPos.y())); + + // We have no separate touch focus management so make it match the pointer focus always. + // No wl_touch_set_focus() is available so set it manually. + wl_touch *touch = touchDevice(); + touch->focus = surface ? surface->base() : 0; + touch->focus_resource = Compositor::resourceForSurface(&touch->resource_list, surface); +} + +void InputDevice::cleanupDataDeviceForClient(struct wl_client *client, bool destroyDev) +{ + for (int i = 0; i < m_data_devices.size(); i++) { + struct wl_resource *data_device_resource = + m_data_devices.at(i)->dataDeviceResource(); + if (data_device_resource->client == client) { + if (destroyDev) + delete m_data_devices.at(i); + m_data_devices.removeAt(i); + break; + } + } +} + +void InputDevice::clientRequestedDataDevice(DataDeviceManager *data_device_manager, struct wl_client *client, uint32_t id) +{ + cleanupDataDeviceForClient(client, false); + DataDevice *dataDevice = new DataDevice(data_device_manager,client,id); + m_data_devices.append(dataDevice); +} + +void InputDevice::sendSelectionFocus(Surface *surface) +{ + if (!surface) + return; + DataDevice *device = dataDevice(surface->base()->resource.client); + if (device) { + device->sendSelectionFocus(); + } +} + +Compositor *InputDevice::compositor() const +{ + return m_compositor; +} + +QWaylandInputDevice *InputDevice::handle() const +{ + return m_handle; +} + +uint32_t InputDevice::toWaylandButton(Qt::MouseButton button) +{ +#ifndef BTN_LEFT + uint32_t BTN_LEFT = 0x110; +#endif + // the range of valid buttons (evdev module) is from 0x110 + // through 0x11f. 0x120 is the first 'Joystick' button. + switch (button) { + case Qt::LeftButton: return BTN_LEFT; + case Qt::RightButton: return uint32_t(0x111); + case Qt::MiddleButton: return uint32_t(0x112); + case Qt::ExtraButton1: return uint32_t(0x113); // AKA Qt::BackButton, Qt::XButton1 + case Qt::ExtraButton2: return uint32_t(0x114); // AKA Qt::ForwardButton, Qt::XButton2 + case Qt::ExtraButton3: return uint32_t(0x115); + case Qt::ExtraButton4: return uint32_t(0x116); + case Qt::ExtraButton5: return uint32_t(0x117); + case Qt::ExtraButton6: return uint32_t(0x118); + case Qt::ExtraButton7: return uint32_t(0x119); + case Qt::ExtraButton8: return uint32_t(0x11a); + case Qt::ExtraButton9: return uint32_t(0x11b); + case Qt::ExtraButton10: return uint32_t(0x11c); + case Qt::ExtraButton11: return uint32_t(0x11d); + case Qt::ExtraButton12: return uint32_t(0x11e); + case Qt::ExtraButton13: return uint32_t(0x11f); + // default should not occur; but if it does, then return Wayland's highest possible button number. + default: return uint32_t(0x11f); + } +} + +DataDevice *InputDevice::dataDevice(struct wl_client *client) const +{ + for (int i = 0; i < m_data_devices.size();i++) { + if (m_data_devices.at(i)->dataDeviceResource()->client == client) { + return m_data_devices.at(i); + } + } + return 0; +} + +} + +QT_END_NAMESPACE |