/*
* Clutter.
*
* An OpenGL based 'interactive canvas' library.
*
* Copyright (C) 2010 Intel Corporation.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
* Authors:
* Matthew Allum
* Robert Bragg
* Kristian Høgsberg
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include
#include
#include
#include
#include
#include "clutter-debug.h"
#include "clutter-device-manager-private.h"
#include "clutter-event-private.h"
#include "clutter-private.h"
#include "clutter-keysyms.h"
#include "evdev/clutter-xkb-utils.h"
#include "clutter-input-device-wayland.h"
#include "clutter-backend-wayland.h"
#include "clutter-backend-wayland-priv.h"
#include "clutter-stage-private.h"
#include "clutter-stage-wayland.h"
#include "clutter-wayland.h"
#include "cogl/clutter-stage-cogl.h"
typedef struct _ClutterInputDeviceClass ClutterInputDeviceWaylandClass;
#define clutter_input_device_wayland_get_type \
_clutter_input_device_wayland_get_type
G_DEFINE_TYPE (ClutterInputDeviceWayland,
clutter_input_device_wayland,
CLUTTER_TYPE_INPUT_DEVICE);
/* This gives us a fake time source for higher level abstractions to have an
* understanding of when an event happens. All that matters are that this is a
* monotonic increasing millisecond accurate time for events to be compared with.
*/
static guint32
_clutter_wayland_get_time (void)
{
return g_get_monotonic_time () / 1000;
}
static void
clutter_wayland_handle_motion (void *data,
struct wl_pointer *pointer,
uint32_t _time,
wl_fixed_t x, wl_fixed_t y)
{
ClutterInputDeviceWayland *device = data;
ClutterStageCogl *stage_cogl;
ClutterEvent *event;
if (!device->pointer_focus)
return;
stage_cogl = device->pointer_focus;
event = clutter_event_new (CLUTTER_MOTION);
event->motion.stage = stage_cogl->wrapper;
event->motion.device = CLUTTER_INPUT_DEVICE (device);
event->motion.time = _clutter_wayland_get_time();
event->motion.modifier_state = 0;
event->motion.x = wl_fixed_to_double(x);
event->motion.y = wl_fixed_to_double(y);
device->x = event->motion.x;
device->y = event->motion.y;
_clutter_event_push (event, FALSE);
}
static void
clutter_wayland_handle_button (void *data,
struct wl_pointer *pointer,
uint32_t serial, uint32_t _time,
uint32_t button, uint32_t state)
{
ClutterInputDeviceWayland *device = data;
ClutterStageCogl *stage_cogl;
ClutterEvent *event;
ClutterEventType type;
ClutterModifierType modifier_mask = 0;
if (!device->pointer_focus)
return;
stage_cogl = device->pointer_focus;
if (state)
type = CLUTTER_BUTTON_PRESS;
else
type = CLUTTER_BUTTON_RELEASE;
event = clutter_event_new (type);
event->button.stage = stage_cogl->wrapper;
event->button.device = CLUTTER_INPUT_DEVICE (device);
event->button.time = _clutter_wayland_get_time();
event->button.x = device->x;
event->button.y = device->y;
_clutter_xkb_translate_state (event, device->xkb, 0);
/* evdev button codes */
switch (button) {
case 272:
event->button.button = 1;
modifier_mask = CLUTTER_BUTTON1_MASK;
break;
case 273:
event->button.button = 3;
modifier_mask = CLUTTER_BUTTON2_MASK;
break;
case 274:
event->button.button = 2;
modifier_mask = CLUTTER_BUTTON3_MASK;
break;
}
if (modifier_mask)
{
if (state)
device->button_modifier_state |= modifier_mask;
else
device->button_modifier_state &= ~modifier_mask;
}
event->button.modifier_state = device->button_modifier_state;
_clutter_event_push (event, FALSE);
}
static void
clutter_wayland_handle_axis (void *data,
struct wl_pointer *pointer,
uint32_t time,
uint32_t axis,
wl_fixed_t value)
{
ClutterInputDeviceWayland *device = data;
ClutterStageCogl *stage_cogl;
ClutterEvent *event;
gdouble delta_x, delta_y;
if (!device->pointer_focus)
return;
stage_cogl = device->pointer_focus;
event = clutter_event_new (CLUTTER_SCROLL);
event->scroll.time = _clutter_wayland_get_time();
event->scroll.stage = stage_cogl->wrapper;
event->scroll.direction = CLUTTER_SCROLL_SMOOTH;
event->scroll.x = device->x;
event->scroll.y = device->y;
if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL)
{
delta_x = -wl_fixed_to_double(value) * 23;
delta_y = 0;
}
else
{
delta_x = 0;
delta_y = -wl_fixed_to_double(value) * 23; /* XXX: based on my bcm5794 */
}
clutter_event_set_scroll_delta (event, delta_x, delta_y);
_clutter_xkb_translate_state (event, device->xkb, 0);
_clutter_event_push (event, FALSE);
}
static void
clutter_wayland_handle_keymap (void *data,
struct wl_keyboard *keyboard,
uint32_t format,
int32_t fd,
uint32_t size)
{
ClutterInputDeviceWayland *device = data;
struct xkb_context *ctx;
struct xkb_keymap *keymap;
char *map_str;
if (device->xkb)
{
xkb_state_unref (device->xkb);
device->xkb = NULL;
}
ctx = xkb_context_new (0);
if (!ctx)
{
close (fd);
return;
}
if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1)
{
close (fd);
return;
}
map_str = mmap (NULL, size, PROT_READ, MAP_SHARED, fd, 0);
if (map_str == MAP_FAILED)
{
close(fd);
return;
}
keymap = xkb_map_new_from_string (ctx,
map_str,
XKB_KEYMAP_FORMAT_TEXT_V1,
0);
xkb_context_unref (ctx);
munmap (map_str, size);
close (fd);
if (!keymap)
{
g_warning ("failed to compile keymap\n");
return;
}
device->xkb = xkb_state_new(keymap);
xkb_map_unref (keymap);
if (!device->xkb)
{
g_warning ("failed to create XKB state object\n");
return;
}
}
/* XXX: Need a wl_keyboard event to harmonise these across clients. */
#define KEY_REPEAT_DELAY 660
#define KEY_REPEAT_INTERVAL 40
static gboolean
clutter_wayland_repeat_key (void *data)
{
ClutterInputDeviceWayland *device = data;
ClutterStageCogl *stage_cogl = device->keyboard_focus;
ClutterEvent *event;
event = _clutter_key_event_new_from_evdev ((ClutterInputDevice *) device,
(ClutterInputDevice*) device,
stage_cogl->wrapper,
device->xkb, 0,
device->repeat_time,
device->repeat_key,
1);
device->repeat_time += KEY_REPEAT_INTERVAL;
_clutter_event_push (event, FALSE);
if (!device->is_initial_repeat)
return TRUE;
g_source_remove (device->repeat_source);
device->repeat_source = g_timeout_add (KEY_REPEAT_INTERVAL,
clutter_wayland_repeat_key,
device);
device->is_initial_repeat = FALSE;
return FALSE;
}
static void
clutter_wayland_handle_key (void *data,
struct wl_keyboard *keyboard,
uint32_t serial, uint32_t _time,
uint32_t key, uint32_t state)
{
ClutterInputDeviceWayland *device = data;
ClutterStageCogl *stage_cogl = device->keyboard_focus;
ClutterEvent *event;
if (!device->keyboard_focus)
return;
if (!device->xkb)
return;
event = _clutter_key_event_new_from_evdev ((ClutterInputDevice *) device,
(ClutterInputDevice *) device,
stage_cogl->wrapper,
device->xkb, 0,
_clutter_wayland_get_time(),
key, state);
_clutter_event_push (event, FALSE);
if (!xkb_key_repeats (xkb_state_get_map (device->xkb), key))
return;
if (state)
{
if (device->repeat_key != XKB_KEYCODE_INVALID)
g_source_remove (device->repeat_source);
device->repeat_key = key;
device->repeat_time = _time + KEY_REPEAT_DELAY;
device->repeat_source = g_timeout_add (KEY_REPEAT_DELAY,
clutter_wayland_repeat_key,
device);
device->is_initial_repeat = TRUE;
}
else if (device->repeat_key == key)
{
g_source_remove (device->repeat_source);
device->repeat_key = XKB_KEYCODE_INVALID;
}
}
static void
clutter_wayland_handle_modifiers (void *data,
struct wl_keyboard *keyboard,
uint32_t serial,
uint32_t mods_depressed,
uint32_t mods_latched,
uint32_t mods_locked,
uint32_t group)
{
ClutterInputDeviceWayland *device = data;
if (!device->xkb)
return;
xkb_state_update_mask (device->xkb,
mods_depressed,
mods_latched,
mods_locked,
0,
0,
group);
}
static void
clutter_wayland_handle_pointer_enter (void *data,
struct wl_pointer *pointer,
uint32_t serial,
struct wl_surface *surface,
wl_fixed_t x, wl_fixed_t y)
{
ClutterInputDeviceWayland *device = data;
ClutterStageWayland *stage_wayland;
ClutterStageCogl *stage_cogl;
ClutterEvent *event;
ClutterBackend *backend;
ClutterBackendWayland *backend_wayland;
stage_wayland = wl_surface_get_user_data (surface);
if (!CLUTTER_IS_STAGE_COGL (stage_wayland))
return;
stage_cogl = CLUTTER_STAGE_COGL (stage_wayland);
device->pointer_focus = stage_cogl;
_clutter_input_device_set_stage (CLUTTER_INPUT_DEVICE (device),
stage_cogl->wrapper);
event = clutter_event_new (CLUTTER_ENTER);
event->crossing.stage = stage_cogl->wrapper;
event->crossing.time = _clutter_wayland_get_time();
event->crossing.x = wl_fixed_to_double(x);
event->crossing.y = wl_fixed_to_double(y);
event->crossing.source = CLUTTER_ACTOR (stage_cogl->wrapper);
event->crossing.device = CLUTTER_INPUT_DEVICE (device);
device->x = event->crossing.x;
device->y = event->crossing.y;
_clutter_event_push (event, FALSE);
if (stage_wayland->cursor_visible)
{
/* Set the cursor to the cursor loaded at backend initialisation */
backend = clutter_get_default_backend ();
backend_wayland = CLUTTER_BACKEND_WAYLAND (backend);
_clutter_backend_wayland_ensure_cursor (backend_wayland);
wl_pointer_set_cursor (pointer,
serial,
backend_wayland->cursor_surface,
backend_wayland->cursor_x,
backend_wayland->cursor_y);
wl_surface_attach (backend_wayland->cursor_surface,
backend_wayland->cursor_buffer,
0,
0);
wl_surface_damage (backend_wayland->cursor_surface,
0,
0,
32, /* XXX: FFS */
32);
wl_surface_commit (backend_wayland->cursor_surface);
}
else
{
wl_pointer_set_cursor (pointer, serial, NULL, 0, 0);
}
}
static void
clutter_wayland_handle_pointer_leave (void *data,
struct wl_pointer *pointer,
uint32_t serial,
struct wl_surface *surface)
{
ClutterInputDeviceWayland *device = data;
ClutterStageCogl *stage_cogl;
ClutterEvent *event;
if (surface == NULL)
return;
if (!CLUTTER_IS_STAGE_COGL (wl_surface_get_user_data (surface)))
return;
stage_cogl = wl_surface_get_user_data (surface);
g_assert (device->pointer_focus == stage_cogl);
event = clutter_event_new (CLUTTER_LEAVE);
event->crossing.stage = stage_cogl->wrapper;
event->crossing.time = _clutter_wayland_get_time();
event->crossing.x = device->x;
event->crossing.y = device->y;
event->crossing.source = CLUTTER_ACTOR (stage_cogl->wrapper);
event->crossing.device = CLUTTER_INPUT_DEVICE (device);
_clutter_event_push (event, FALSE);
device->pointer_focus = NULL;
_clutter_input_device_set_stage (CLUTTER_INPUT_DEVICE (device), NULL);
}
static void
clutter_wayland_handle_keyboard_enter (void *data,
struct wl_keyboard *keyboard,
uint32_t serial,
struct wl_surface *surface,
struct wl_array *keys)
{
ClutterInputDeviceWayland *device = data;
ClutterStageCogl *stage_cogl;
if (!CLUTTER_IS_STAGE_COGL (wl_surface_get_user_data (surface)))
return;
stage_cogl = wl_surface_get_user_data (surface);
g_assert (device->keyboard_focus == NULL);
device->keyboard_focus = stage_cogl;
_clutter_stage_update_state (stage_cogl->wrapper,
0,
CLUTTER_STAGE_STATE_ACTIVATED);
}
static void
clutter_wayland_handle_keyboard_leave (void *data,
struct wl_keyboard *keyboard,
uint32_t serial,
struct wl_surface *surface)
{
ClutterInputDeviceWayland *device = data;
ClutterStageCogl *stage_cogl;
if (!surface)
return;
if (!CLUTTER_IS_STAGE_COGL (wl_surface_get_user_data (surface)))
return;
stage_cogl = wl_surface_get_user_data (surface);
g_assert (device->keyboard_focus == stage_cogl);
_clutter_stage_update_state (stage_cogl->wrapper,
CLUTTER_STAGE_STATE_ACTIVATED,
0);
if (device->repeat_key != XKB_KEYCODE_INVALID)
{
g_source_remove (device->repeat_source);
device->repeat_key = XKB_KEYCODE_INVALID;
}
device->keyboard_focus = NULL;
}
static const struct wl_keyboard_listener _clutter_keyboard_wayland_listener = {
clutter_wayland_handle_keymap,
clutter_wayland_handle_keyboard_enter,
clutter_wayland_handle_keyboard_leave,
clutter_wayland_handle_key,
clutter_wayland_handle_modifiers,
};
static const struct wl_pointer_listener _clutter_pointer_wayland_listener = {
clutter_wayland_handle_pointer_enter,
clutter_wayland_handle_pointer_leave,
clutter_wayland_handle_motion,
clutter_wayland_handle_button,
clutter_wayland_handle_axis,
};
static void
clutter_wayland_handle_seat (void *data,
struct wl_seat *seat,
uint32_t capabilities)
{
ClutterInputDeviceWayland *device = data;
/* XXX: Needs to handle removals too. */
if ((capabilities & WL_SEAT_CAPABILITY_POINTER) && !device->has_pointer)
{
struct wl_pointer *pointer;
pointer = wl_seat_get_pointer (seat);
if (pointer)
{
wl_pointer_add_listener (pointer,
&_clutter_pointer_wayland_listener,
device);
wl_pointer_set_user_data (pointer, device);
device->has_pointer = 1;
}
}
if ((capabilities & WL_SEAT_CAPABILITY_KEYBOARD) && !device->has_keyboard)
{
struct wl_keyboard *keyboard;
keyboard = wl_seat_get_keyboard (seat);
if (keyboard)
{
wl_keyboard_add_listener (keyboard,
&_clutter_keyboard_wayland_listener,
device);
wl_keyboard_set_user_data (keyboard, device);
device->has_keyboard = 1;
}
}
}
const struct wl_seat_listener _clutter_seat_wayland_listener = {
clutter_wayland_handle_seat,
};
static gboolean
clutter_input_device_wayland_keycode_to_evdev (ClutterInputDevice *device,
guint hardware_keycode,
guint *evdev_keycode)
{
/* The hardware keycodes from the wayland backend are already evdev
keycodes */
*evdev_keycode = hardware_keycode;
return TRUE;
}
static void
clutter_input_device_wayland_class_init (ClutterInputDeviceWaylandClass *klass)
{
klass->keycode_to_evdev = clutter_input_device_wayland_keycode_to_evdev;
}
static void
clutter_input_device_wayland_init (ClutterInputDeviceWayland *self)
{
self->repeat_key = XKB_KEYCODE_INVALID;
}
/**
* clutter_wayland_input_device_get_wl_seat: (skip)
* @device: a #ClutterInputDevice
*
* Access the underlying data structure representing the Wayland device that is
* backing this #ClutterInputDevice.
*
* Note: this function can only be called when running on the Wayland platform.
* Calling this function at any other time will return %NULL.
*
* Returns: (transfer none): the Wayland input device associated with the
* @device
*
* Since: 1.10
*/
struct wl_seat *
clutter_wayland_input_device_get_wl_seat (ClutterInputDevice *device)
{
ClutterInputDeviceWayland *wayland_device;
if (!CLUTTER_INPUT_DEVICE_WAYLAND (device))
return NULL;
wayland_device = CLUTTER_INPUT_DEVICE_WAYLAND (device);
return wayland_device->input_device;
}