gtk2/gdk/wayland/gdkdevice-wayland.c
Rob Bradford 1d080a01c1 wayland: Process the keymap that is sent over from the compositor
Load the keymap from the file descriptor that the compositor has sent us and
then save that into our internal object for future use.
2012-07-16 13:44:29 +01:00

1697 lines
49 KiB
C

/* GDK - The GIMP Drawing Kit
* Copyright (C) 2009 Carlos Garnacho <carlosg@gnome.org>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#define _GNU_SOURCE
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include "config.h"
#include <string.h>
#include <gdk/gdkwindow.h>
#include <gdk/gdktypes.h>
#include "gdkprivate-wayland.h"
#include "gdkwayland.h"
#include "gdkkeysyms.h"
#include "gdkdeviceprivate.h"
#include "gdkdevicemanagerprivate.h"
#include "gdkprivate-wayland.h"
#include <xkbcommon/xkbcommon.h>
#include <X11/keysym.h>
#include <sys/time.h>
#include <sys/mman.h>
#define GDK_TYPE_DEVICE_CORE (gdk_device_core_get_type ())
#define GDK_DEVICE_CORE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GDK_TYPE_DEVICE_CORE, GdkDeviceCore))
#define GDK_DEVICE_CORE_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), GDK_TYPE_DEVICE_CORE, GdkDeviceCoreClass))
#define GDK_IS_DEVICE_CORE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDK_TYPE_DEVICE_CORE))
#define GDK_IS_DEVICE_CORE_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), GDK_TYPE_DEVICE_CORE))
#define GDK_DEVICE_CORE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDK_TYPE_DEVICE_CORE, GdkDeviceCoreClass))
typedef struct _GdkDeviceCore GdkDeviceCore;
typedef struct _GdkDeviceCoreClass GdkDeviceCoreClass;
typedef struct _GdkWaylandDevice GdkWaylandDevice;
typedef struct _DataOffer DataOffer;
typedef struct _GdkWaylandSelectionOffer GdkWaylandSelectionOffer;
struct _GdkWaylandDevice
{
struct wl_seat *wl_seat;
struct wl_pointer *wl_pointer;
struct wl_keyboard *wl_keyboard;
GdkDisplay *display;
GdkDeviceManager *device_manager;
GdkDevice *pointer;
GdkDevice *keyboard;
GdkModifierType modifiers;
GdkWindow *pointer_focus;
GdkWindow *keyboard_focus;
struct wl_data_device *data_device;
double surface_x, surface_y;
uint32_t time;
GdkWindow *pointer_grab_window;
uint32_t pointer_grab_time;
guint32 repeat_timer;
guint32 repeat_key;
guint32 repeat_count;
DataOffer *drag_offer;
DataOffer *selection_offer;
GdkWaylandSelectionOffer *selection_offer_out;
struct wl_surface *pointer_surface;
};
struct _GdkDeviceCore
{
GdkDevice parent_instance;
GdkWaylandDevice *device;
};
struct _GdkDeviceCoreClass
{
GdkDeviceClass parent_class;
};
G_DEFINE_TYPE (GdkDeviceCore, gdk_device_core, GDK_TYPE_DEVICE)
#define GDK_TYPE_DEVICE_MANAGER_CORE (gdk_device_manager_core_get_type ())
#define GDK_DEVICE_MANAGER_CORE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GDK_TYPE_DEVICE_MANAGER_CORE, GdkDeviceManagerCore))
#define GDK_DEVICE_MANAGER_CORE_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), GDK_TYPE_DEVICE_MANAGER_CORE, GdkDeviceManagerCoreClass))
#define GDK_IS_DEVICE_MANAGER_CORE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDK_TYPE_DEVICE_MANAGER_CORE))
#define GDK_IS_DEVICE_MANAGER_CORE_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), GDK_TYPE_DEVICE_MANAGER_CORE))
#define GDK_DEVICE_MANAGER_CORE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDK_TYPE_DEVICE_MANAGER_CORE, GdkDeviceManagerCoreClass))
typedef struct _GdkDeviceManagerCore GdkDeviceManagerCore;
typedef struct _GdkDeviceManagerCoreClass GdkDeviceManagerCoreClass;
struct _GdkDeviceManagerCore
{
GdkDeviceManager parent_object;
GdkDevice *core_pointer;
GdkDevice *core_keyboard;
GList *devices;
};
struct _GdkDeviceManagerCoreClass
{
GdkDeviceManagerClass parent_class;
};
G_DEFINE_TYPE (GdkDeviceManagerCore,
gdk_device_manager_core, GDK_TYPE_DEVICE_MANAGER)
static gboolean
gdk_device_core_get_history (GdkDevice *device,
GdkWindow *window,
guint32 start,
guint32 stop,
GdkTimeCoord ***events,
gint *n_events)
{
return FALSE;
}
static void
gdk_device_core_get_state (GdkDevice *device,
GdkWindow *window,
gdouble *axes,
GdkModifierType *mask)
{
gint x_int, y_int;
gdk_window_get_device_position (window, device, &x_int, &y_int, mask);
if (axes)
{
axes[0] = x_int;
axes[1] = y_int;
}
}
static void
gdk_device_core_set_window_cursor (GdkDevice *device,
GdkWindow *window,
GdkCursor *cursor)
{
GdkWaylandDevice *wd = GDK_DEVICE_CORE(device)->device;
GdkWaylandDisplay *wayland_display =
GDK_WAYLAND_DISPLAY (gdk_window_get_display (window));
struct wl_buffer *buffer;
int x, y, w, h;
if (cursor)
g_object_ref (cursor);
/* Setting the cursor to NULL means that we should use the default cursor */
if (!cursor)
{
/* FIXME: Is this the best sensible default ? */
cursor = _gdk_wayland_display_get_cursor_for_type (device->display,
GDK_LEFT_PTR);
}
buffer = _gdk_wayland_cursor_get_buffer (cursor, &x, &y, &w, &h);
wl_pointer_set_cursor (wd->wl_pointer,
_gdk_wayland_display_get_serial (wayland_display),
wd->pointer_surface,
x, y);
wl_surface_attach (wd->pointer_surface, buffer, 0, 0);
wl_surface_damage (wd->pointer_surface, 0, 0, w, h);
g_object_unref (cursor);
}
static void
gdk_device_core_warp (GdkDevice *device,
GdkScreen *screen,
gint x,
gint y)
{
}
static void
gdk_device_core_query_state (GdkDevice *device,
GdkWindow *window,
GdkWindow **root_window,
GdkWindow **child_window,
gint *root_x,
gint *root_y,
gint *win_x,
gint *win_y,
GdkModifierType *mask)
{
GdkWaylandDevice *wd;
GdkScreen *default_screen;
wd = GDK_DEVICE_CORE(device)->device;
default_screen = gdk_display_get_default_screen (wd->display);
if (root_window)
*root_window = gdk_screen_get_root_window (default_screen);
if (child_window)
*child_window = wd->pointer_focus;
/* Do something clever for relative here */
#if 0
if (root_x)
*root_x = wd->x;
if (root_y)
*root_y = wd->y;
#endif
if (win_x)
*win_x = wd->surface_x;
if (win_y)
*win_y = wd->surface_y;
if (mask)
*mask = wd->modifiers;
}
static GdkGrabStatus
gdk_device_core_grab (GdkDevice *device,
GdkWindow *window,
gboolean owner_events,
GdkEventMask event_mask,
GdkWindow *confine_to,
GdkCursor *cursor,
guint32 time_)
{
GdkWaylandDevice *wayland_device = GDK_DEVICE_CORE (device)->device;
if (gdk_device_get_source (device) == GDK_SOURCE_KEYBOARD)
{
/* Device is a keyboard */
return GDK_GRAB_SUCCESS;
}
else
{
/* Device is a pointer */
if (wayland_device->pointer_grab_window != NULL &&
time_ != 0 && wayland_device->pointer_grab_time > time_)
{
return GDK_GRAB_ALREADY_GRABBED;
}
if (time_ == 0)
time_ = wayland_device->time;
wayland_device->pointer_grab_window = window;
wayland_device->pointer_grab_time = time_;
/* FIXME: This probably breaks if you end up with multiple grabs on the
* same window - but we need to know the input device for when we are
* asked to map a popup window so that the grab can be managed by the
* compositor.
*/
_gdk_wayland_window_set_device_grabbed (window,
wayland_device->wl_seat,
time_);
}
return GDK_GRAB_SUCCESS;
}
static void
gdk_device_core_ungrab (GdkDevice *device,
guint32 time_)
{
GdkWaylandDevice *wayland_device = GDK_DEVICE_CORE (device)->device;
GdkDisplay *display;
GdkDeviceGrabInfo *grab;
display = gdk_device_get_display (device);
if (gdk_device_get_source (device) == GDK_SOURCE_KEYBOARD)
{
/* Device is a keyboard */
}
else
{
/* Device is a pointer */
grab = _gdk_display_get_last_device_grab (display, device);
if (grab)
grab->serial_end = grab->serial_start;
if (wayland_device->pointer_grab_window)
_gdk_wayland_window_set_device_grabbed (wayland_device->pointer_grab_window,
NULL,
0);
}
}
static GdkWindow *
gdk_device_core_window_at_position (GdkDevice *device,
gint *win_x,
gint *win_y,
GdkModifierType *mask,
gboolean get_toplevel)
{
GdkWaylandDevice *wd;
wd = GDK_DEVICE_CORE(device)->device;
if (win_x)
*win_x = wd->surface_x;
if (win_y)
*win_y = wd->surface_y;
if (mask)
*mask = wd->modifiers;
return wd->pointer_focus;
}
static void
gdk_device_core_select_window_events (GdkDevice *device,
GdkWindow *window,
GdkEventMask event_mask)
{
}
static void
gdk_device_core_class_init (GdkDeviceCoreClass *klass)
{
GdkDeviceClass *device_class = GDK_DEVICE_CLASS (klass);
device_class->get_history = gdk_device_core_get_history;
device_class->get_state = gdk_device_core_get_state;
device_class->set_window_cursor = gdk_device_core_set_window_cursor;
device_class->warp = gdk_device_core_warp;
device_class->query_state = gdk_device_core_query_state;
device_class->grab = gdk_device_core_grab;
device_class->ungrab = gdk_device_core_ungrab;
device_class->window_at_position = gdk_device_core_window_at_position;
device_class->select_window_events = gdk_device_core_select_window_events;
}
static void
gdk_device_core_init (GdkDeviceCore *device_core)
{
GdkDevice *device;
device = GDK_DEVICE (device_core);
_gdk_device_add_axis (device, GDK_NONE, GDK_AXIS_X, 0, 0, 1);
_gdk_device_add_axis (device, GDK_NONE, GDK_AXIS_Y, 0, 0, 1);
}
struct wl_seat *
_gdk_wayland_device_get_wl_seat (GdkDevice *device)
{
return GDK_DEVICE_CORE (device)->device->wl_seat;
}
struct wl_pointer *
_gdk_wayland_device_get_wl_pointer (GdkDevice *device)
{
return GDK_DEVICE_CORE (device)->device->wl_pointer;
}
struct wl_keyboard *
_gdk_wayland_device_get_wl_keyboard (GdkDevice *device)
{
return GDK_DEVICE_CORE (device)->device->wl_keyboard;
}
#if 0
static void
input_handle_motion(void *data, struct wl_input_device *input_device,
uint32_t time,
int32_t x, int32_t y, int32_t sx, int32_t sy)
{
GdkWaylandDevice *device = data;
GdkWaylandDisplay *display = GDK_WAYLAND_DISPLAY (device->display);
GdkEvent *event;
event = gdk_event_new (GDK_NOTHING);
device->time = time;
device->x = x;
device->y = y;
device->surface_x = sx;
device->surface_y = sy;
event->motion.type = GDK_MOTION_NOTIFY;
event->motion.window = g_object_ref (device->pointer_focus);
gdk_event_set_device (event, device->pointer);
event->motion.time = time;
event->motion.x = (gdouble) sx;
event->motion.y = (gdouble) sy;
event->motion.x_root = (gdouble) x;
event->motion.y_root = (gdouble) y;
event->motion.axes = NULL;
event->motion.state = device->modifiers;
event->motion.is_hint = 0;
gdk_event_set_screen (event, display->screen);
GDK_NOTE (EVENTS,
g_message ("motion %d %d, state %d",
sx, sy, event->button.state));
_gdk_wayland_display_deliver_event (device->display, event);
}
static void
input_handle_button(void *data, struct wl_input_device *input_device,
uint32_t time, uint32_t button, uint32_t state)
{
GdkWaylandDevice *device = data;
GdkWaylandDisplay *display = GDK_WAYLAND_DISPLAY (device->display);
GdkEvent *event;
uint32_t modifier;
int gdk_button;
switch (button) {
case 273:
gdk_button = 3;
break;
case 274:
gdk_button = 2;
break;
default:
gdk_button = button - 271;
break;
}
device->time = time;
event = gdk_event_new (state ? GDK_BUTTON_PRESS : GDK_BUTTON_RELEASE);
event->button.window = g_object_ref (device->pointer_focus);
gdk_event_set_device (event, device->pointer);
event->button.time = time;
event->button.x = (gdouble) device->surface_x;
event->button.y = (gdouble) device->surface_y;
event->button.x_root = (gdouble) device->x;
event->button.y_root = (gdouble) device->y;
event->button.axes = NULL;
event->button.state = device->modifiers;
event->button.button = gdk_button;
gdk_event_set_screen (event, display->screen);
modifier = 1 << (8 + gdk_button - 1);
if (state)
device->modifiers |= modifier;
else
device->modifiers &= ~modifier;
GDK_NOTE (EVENTS,
g_message ("button %d %s, state %d",
event->button.button,
state ? "press" : "release", event->button.state));
_gdk_wayland_display_deliver_event (device->display, event);
}
static void
translate_keyboard_string (GdkEventKey *event)
{
gunichar c = 0;
gchar buf[7];
/* Fill in event->string crudely, since various programs
* depend on it.
*/
event->string = NULL;
if (event->keyval != GDK_KEY_VoidSymbol)
c = gdk_keyval_to_unicode (event->keyval);
if (c)
{
gsize bytes_written;
gint len;
/* Apply the control key - Taken from Xlib
*/
if (event->state & GDK_CONTROL_MASK)
{
if ((c >= '@' && c < '\177') || c == ' ') c &= 0x1F;
else if (c == '2')
{
event->string = g_memdup ("\0\0", 2);
event->length = 1;
buf[0] = '\0';
return;
}
else if (c >= '3' && c <= '7') c -= ('3' - '\033');
else if (c == '8') c = '\177';
else if (c == '/') c = '_' & 0x1F;
}
len = g_unichar_to_utf8 (c, buf);
buf[len] = '\0';
event->string = g_locale_from_utf8 (buf, len,
NULL, &bytes_written,
NULL);
if (event->string)
event->length = bytes_written;
}
else if (event->keyval == GDK_KEY_Escape)
{
event->length = 1;
event->string = g_strdup ("\033");
}
else if (event->keyval == GDK_KEY_Return ||
event->keyval == GDK_KEY_KP_Enter)
{
event->length = 1;
event->string = g_strdup ("\r");
}
if (!event->string)
{
event->length = 0;
event->string = g_strdup ("");
}
}
static gboolean
keyboard_repeat (gpointer data);
static gboolean
deliver_key_event(GdkWaylandDevice *device,
uint32_t time, uint32_t key, uint32_t state)
{
GdkEvent *event;
uint32_t code, modifier, level;
struct xkb_desc *xkb;
GdkKeymap *keymap;
keymap = gdk_keymap_get_for_display (device->display);
xkb = _gdk_wayland_keymap_get_xkb_desc (keymap);
device->time = time;
event = gdk_event_new (state ? GDK_KEY_PRESS : GDK_KEY_RELEASE);
event->key.window = g_object_ref (device->keyboard_focus);
gdk_event_set_device (event, device->keyboard);
event->button.time = time;
event->key.state = device->modifiers;
event->key.group = 0;
code = key + xkb->min_key_code;
event->key.hardware_keycode = code;
level = 0;
if (device->modifiers & XKB_COMMON_SHIFT_MASK &&
XkbKeyGroupWidth(xkb, code, 0) > 1)
level = 1;
event->key.keyval = XkbKeySymEntry(xkb, code, level, 0);
modifier = xkb->map->modmap[code];
if (state)
device->modifiers |= modifier;
else
device->modifiers &= ~modifier;
event->key.is_modifier = modifier > 0;
translate_keyboard_string (&event->key);
_gdk_wayland_display_deliver_event (device->display, event);
GDK_NOTE (EVENTS,
g_message ("keyboard event, code %d, sym %d, "
"string %s, mods 0x%x",
code, event->key.keyval,
event->key.string, event->key.state));
device->repeat_count++;
device->repeat_key = key;
if (state == 0)
{
if (device->repeat_timer)
{
g_source_remove (device->repeat_timer);
device->repeat_timer = 0;
}
return FALSE;
}
else if (modifier)
{
return FALSE;
}
else switch (device->repeat_count)
{
case 1:
if (device->repeat_timer)
{
g_source_remove (device->repeat_timer);
device->repeat_timer = 0;
}
device->repeat_timer =
gdk_threads_add_timeout (400, keyboard_repeat, device);
return TRUE;
case 2:
device->repeat_timer =
gdk_threads_add_timeout (80, keyboard_repeat, device);
return FALSE;
default:
return TRUE;
}
}
static gboolean
keyboard_repeat (gpointer data)
{
GdkWaylandDevice *device = data;
return deliver_key_event (device, device->time, device->repeat_key, 1);
}
static void
input_handle_key(void *data, struct wl_input_device *input_device,
uint32_t time, uint32_t key, uint32_t state)
{
GdkWaylandDevice *device = data;
device->repeat_count = 0;
deliver_key_event (data, time, key, state);
}
static void
input_handle_pointer_focus(void *data,
struct wl_input_device *input_device,
uint32_t time, struct wl_surface *surface,
int32_t x, int32_t y, int32_t sx, int32_t sy)
{
GdkWaylandDevice *device = data;
GdkEvent *event;
device->time = time;
if (device->pointer_focus)
{
event = gdk_event_new (GDK_LEAVE_NOTIFY);
event->crossing.window = g_object_ref (device->pointer_focus);
gdk_event_set_device (event, device->pointer);
event->crossing.subwindow = NULL;
event->crossing.time = time;
event->crossing.x = (gdouble) device->surface_x;
event->crossing.y = (gdouble) device->surface_y;
event->crossing.x_root = (gdouble) device->x;
event->crossing.y_root = (gdouble) device->y;
event->crossing.mode = GDK_CROSSING_NORMAL;
event->crossing.detail = GDK_NOTIFY_ANCESTOR;
event->crossing.focus = TRUE;
event->crossing.state = 0;
_gdk_wayland_display_deliver_event (device->display, event);
GDK_NOTE (EVENTS,
g_message ("leave, device %p surface %p",
device, device->pointer_focus));
g_object_unref(device->pointer_focus);
device->pointer_focus = NULL;
}
if (surface)
{
device->pointer_focus = wl_surface_get_user_data(surface);
g_object_ref(device->pointer_focus);
event = gdk_event_new (GDK_ENTER_NOTIFY);
event->crossing.window = g_object_ref (device->pointer_focus);
gdk_event_set_device (event, device->pointer);
event->crossing.subwindow = NULL;
event->crossing.time = time;
event->crossing.x = (gdouble) sx;
event->crossing.y = (gdouble) sy;
event->crossing.x_root = (gdouble) x;
event->crossing.y_root = (gdouble) y;
event->crossing.mode = GDK_CROSSING_NORMAL;
event->crossing.detail = GDK_NOTIFY_ANCESTOR;
event->crossing.focus = TRUE;
event->crossing.state = 0;
device->surface_x = sx;
device->surface_y = sy;
device->x = x;
device->y = y;
_gdk_wayland_display_deliver_event (device->display, event);
GDK_NOTE (EVENTS,
g_message ("enter, device %p surface %p",
device, device->pointer_focus));
}
}
static void
update_modifiers(GdkWaylandDevice *device, struct wl_array *keys)
{
uint32_t *k, *end;
GdkKeymap *keymap;
struct xkb_desc *xkb;
keymap = gdk_keymap_get_for_display (device->display);
xkb = _gdk_wayland_keymap_get_xkb_desc (keymap);
end = keys->data + keys->size;
device->modifiers = 0;
for (k = keys->data; k < end; k++)
device->modifiers |= xkb->map->modmap[*k];
}
static void
input_handle_keyboard_focus(void *data,
struct wl_input_device *input_device,
uint32_t time,
struct wl_surface *surface,
struct wl_array *keys)
{
GdkWaylandDevice *device = data;
GdkEvent *event;
device->time = time;
if (device->keyboard_focus)
{
_gdk_wayland_window_remove_focus (device->keyboard_focus);
event = gdk_event_new (GDK_FOCUS_CHANGE);
event->focus_change.window = g_object_ref (device->keyboard_focus);
event->focus_change.send_event = FALSE;
event->focus_change.in = FALSE;
gdk_event_set_device (event, device->keyboard);
g_object_unref(device->keyboard_focus);
device->keyboard_focus = NULL;
GDK_NOTE (EVENTS,
g_message ("focus out, device %p surface %p",
device, device->keyboard_focus));
_gdk_wayland_display_deliver_event (device->display, event);
}
if (surface)
{
device->keyboard_focus = wl_surface_get_user_data(surface);
g_object_ref(device->keyboard_focus);
event = gdk_event_new (GDK_FOCUS_CHANGE);
event->focus_change.window = g_object_ref (device->keyboard_focus);
event->focus_change.send_event = FALSE;
event->focus_change.in = TRUE;
gdk_event_set_device (event, device->keyboard);
update_modifiers (device, keys);
GDK_NOTE (EVENTS,
g_message ("focus int, device %p surface %p",
device, device->keyboard_focus));
_gdk_wayland_display_deliver_event (device->display, event);
_gdk_wayland_window_add_focus (device->keyboard_focus);
}
}
static const struct wl_input_device_listener input_device_listener = {
input_handle_motion,
input_handle_button,
input_handle_key,
input_handle_pointer_focus,
input_handle_keyboard_focus,
};
#endif
struct _DataOffer {
struct wl_data_offer *offer;
gint ref_count;
GPtrArray *types;
};
static void
data_offer_offer (void *data,
struct wl_data_offer *wl_data_offer,
const char *type)
{
DataOffer *offer = (DataOffer *)data;
g_debug (G_STRLOC ": %s wl_data_offer = %p type = %s",
G_STRFUNC, wl_data_offer, type);
g_ptr_array_add (offer->types, g_strdup (type));
}
static void
data_offer_unref (DataOffer *offer)
{
offer->ref_count--;
if (offer->ref_count == 0)
{
g_ptr_array_free (offer->types, TRUE);
g_free (offer);
}
}
static const struct wl_data_offer_listener data_offer_listener = {
data_offer_offer,
};
static void
data_device_data_offer (void *data,
struct wl_data_device *data_device,
uint32_t id)
{
DataOffer *offer;
g_debug (G_STRLOC ": %s data_device = %p id = %lu",
G_STRFUNC, data_device, (long unsigned int)id);
/* This structure is reference counted to handle the case where you get a
* leave but are in the middle of transferring data
*/
offer = g_new0 (DataOffer, 1);
offer->ref_count = 1;
offer->types = g_ptr_array_new_with_free_func (g_free);
offer->offer = (struct wl_data_offer *)
wl_proxy_create_for_id ((struct wl_proxy *) data_device,
id,
&wl_data_offer_interface);
/* The DataOffer structure is then retrieved later since this sets the user
* data.
*/
wl_data_offer_add_listener (offer->offer,
&data_offer_listener,
offer);
}
static void
data_device_enter (void *data,
struct wl_data_device *data_device,
uint32_t time,
struct wl_surface *surface,
int32_t x,
int32_t y,
struct wl_data_offer *offer)
{
GdkWaylandDevice *device = (GdkWaylandDevice *)data;
g_debug (G_STRLOC ": %s data_device = %p time = %d, surface = %p, x = %d y = %d, offer = %p",
G_STRFUNC, data_device, time, surface, x, y, offer);
/* Retrieve the DataOffer associated with with the wl_data_offer - this
* association is made when the listener is attached.
*/
g_assert (device->drag_offer == NULL);
device->drag_offer = wl_data_offer_get_user_data (offer);
}
static void
data_device_leave (void *data,
struct wl_data_device *data_device)
{
GdkWaylandDevice *device = (GdkWaylandDevice *)data;
g_debug (G_STRLOC ": %s data_device = %p",
G_STRFUNC, data_device);
data_offer_unref (device->drag_offer);
device->drag_offer = NULL;
}
static void
data_device_motion (void *data,
struct wl_data_device *data_device,
uint32_t time,
int32_t x,
int32_t y)
{
g_debug (G_STRLOC ": %s data_device = %p, time = %d, x = %d, y = %d",
G_STRFUNC, data_device, time, x, y);
}
static void
data_device_drop (void *data,
struct wl_data_device *data_device)
{
g_debug (G_STRLOC ": %s data_device = %p",
G_STRFUNC, data_device);
}
static void
data_device_selection (void *data,
struct wl_data_device *wl_data_device,
struct wl_data_offer *offer)
{
GdkWaylandDevice *device = (GdkWaylandDevice *)data;
g_debug (G_STRLOC ": %s wl_data_device = %p wl_data_offer = %p",
G_STRFUNC, wl_data_device, offer);
if (!offer)
{
if (device->selection_offer)
{
data_offer_unref (device->selection_offer);
device->selection_offer = NULL;
}
return;
}
if (device->selection_offer)
{
data_offer_unref (device->selection_offer);
device->selection_offer = NULL;
}
/* Retrieve the DataOffer associated with with the wl_data_offer - this
* association is made when the listener is attached.
*/
g_assert (device->selection_offer == NULL);
device->selection_offer = wl_data_offer_get_user_data (offer);
}
static const struct wl_data_device_listener data_device_listener = {
data_device_data_offer,
data_device_enter,
data_device_leave,
data_device_motion,
data_device_drop,
data_device_selection
};
static void
pointer_handle_enter (void *data,
struct wl_pointer *pointer,
uint32_t serial,
struct wl_surface *surface,
wl_fixed_t sx,
wl_fixed_t sy)
{
GdkWaylandDevice *device = data;
GdkEvent *event;
GdkWaylandDisplay *wayland_display =
GDK_WAYLAND_DISPLAY (device->display);
_gdk_wayland_display_update_serial (wayland_display, serial);
device->pointer_focus = wl_surface_get_user_data(surface);
g_object_ref(device->pointer_focus);
event = gdk_event_new (GDK_ENTER_NOTIFY);
event->crossing.window = g_object_ref (device->pointer_focus);
gdk_event_set_device (event, device->pointer);
event->crossing.subwindow = NULL;
event->crossing.time = (guint32)(g_get_monotonic_time () / 1000);
event->crossing.x = wl_fixed_to_double (sx);
event->crossing.y = wl_fixed_to_double (sy);
event->crossing.mode = GDK_CROSSING_NORMAL;
event->crossing.detail = GDK_NOTIFY_ANCESTOR;
event->crossing.focus = TRUE;
event->crossing.state = 0;
device->surface_x = wl_fixed_to_double (sx);
device->surface_y = wl_fixed_to_double (sy);
_gdk_wayland_display_deliver_event (device->display, event);
GDK_NOTE (EVENTS,
g_message ("enter, device %p surface %p",
device, device->pointer_focus));
}
static void
pointer_handle_leave (void *data,
struct wl_pointer *pointer,
uint32_t serial,
struct wl_surface *surface)
{
GdkWaylandDevice *device = data;
GdkEvent *event;
GdkWaylandDisplay *wayland_display =
GDK_WAYLAND_DISPLAY (device->display);
_gdk_wayland_display_update_serial (wayland_display, serial);
event = gdk_event_new (GDK_LEAVE_NOTIFY);
event->crossing.window = g_object_ref (device->pointer_focus);
gdk_event_set_device (event, device->pointer);
event->crossing.subwindow = NULL;
event->crossing.time = (guint32)(g_get_monotonic_time () / 1000);
event->crossing.x = device->surface_x;
event->crossing.y = device->surface_y;
event->crossing.mode = GDK_CROSSING_NORMAL;
event->crossing.detail = GDK_NOTIFY_ANCESTOR;
event->crossing.focus = TRUE;
event->crossing.state = 0;
_gdk_wayland_display_deliver_event (device->display, event);
GDK_NOTE (EVENTS,
g_message ("leave, device %p surface %p",
device, device->pointer_focus));
g_object_unref(device->pointer_focus);
device->pointer_focus = NULL;
}
static void
pointer_handle_motion (void *data,
struct wl_pointer *pointer,
uint32_t time,
wl_fixed_t sx,
wl_fixed_t sy)
{
GdkWaylandDevice *device = data;
GdkWaylandDisplay *display = GDK_WAYLAND_DISPLAY (device->display);
GdkEvent *event;
event = gdk_event_new (GDK_NOTHING);
device->time = time;
device->surface_x = wl_fixed_to_double (sx);
device->surface_y = wl_fixed_to_double (sy);
event->motion.type = GDK_MOTION_NOTIFY;
event->motion.window = g_object_ref (device->pointer_focus);
gdk_event_set_device (event, device->pointer);
event->motion.time = time;
event->motion.x = wl_fixed_to_double (sx);
event->motion.y = wl_fixed_to_double (sy);
event->motion.axes = NULL;
event->motion.state = device->modifiers;
event->motion.is_hint = 0;
gdk_event_set_screen (event, display->screen);
GDK_NOTE (EVENTS,
g_message ("motion %d %d, state %d",
sx, sy, event->button.state));
_gdk_wayland_display_deliver_event (device->display, event);
}
static void
pointer_handle_button (void *data,
struct wl_pointer *pointer,
uint32_t serial,
uint32_t time,
uint32_t button,
uint32_t state)
{
GdkWaylandDevice *device = data;
GdkWaylandDisplay *display = GDK_WAYLAND_DISPLAY (device->display);
GdkEvent *event;
uint32_t modifier;
int gdk_button;
GdkWaylandDisplay *wayland_display =
GDK_WAYLAND_DISPLAY (device->display);
_gdk_wayland_display_update_serial (wayland_display, serial);
switch (button) {
case 273:
gdk_button = 3;
break;
case 274:
gdk_button = 2;
break;
default:
gdk_button = button - 271;
break;
}
device->time = time;
event = gdk_event_new (state ? GDK_BUTTON_PRESS : GDK_BUTTON_RELEASE);
event->button.window = g_object_ref (device->pointer_focus);
gdk_event_set_device (event, device->pointer);
event->button.time = time;
event->button.x = device->surface_x;
event->button.y = device->surface_y;
event->button.axes = NULL;
event->button.state = device->modifiers;
event->button.button = gdk_button;
gdk_event_set_screen (event, display->screen);
modifier = 1 << (8 + gdk_button - 1);
if (state)
device->modifiers |= modifier;
else
device->modifiers &= ~modifier;
GDK_NOTE (EVENTS,
g_message ("button %d %s, state %d",
event->button.button,
state ? "press" : "release", event->button.state));
_gdk_wayland_display_deliver_event (device->display, event);
}
static void
pointer_handle_axis (void *data,
struct wl_pointer *pointer,
uint32_t time,
uint32_t axis,
wl_fixed_t value)
{
}
static void
keyboard_handle_keymap (void *data,
struct wl_keyboard *keyboard,
uint32_t format,
int fd,
uint32_t size)
{
GdkWaylandDevice *device = data;
GdkWaylandDisplay *display = GDK_WAYLAND_DISPLAY (device->display);
GdkKeymap *gdk_keymap;
gchar *keymap_data;
struct xkb_keymap *keymap;
if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1)
{
g_critical (G_STRLOC ": Unknown keymap format");
close (fd);
return;
}
keymap_data = mmap (NULL, size, PROT_READ, MAP_SHARED, fd, 0);
if (keymap_data == MAP_FAILED)
{
g_critical (G_STRLOC ": Unable to map fd for keymap %s", g_strerror (errno));
close (fd);
return;
}
keymap = xkb_map_new_from_string (display->xkb_context,
keymap_data,
format,
0);
munmap (keymap_data, size);
close (fd);
gdk_keymap = _gdk_wayland_display_get_keymap (device->display);
_gdk_wayland_keymap_update_keymap (gdk_keymap, keymap);
}
static void
keyboard_handle_enter (void *data,
struct wl_keyboard *keyboard,
uint32_t serial,
struct wl_surface *surface,
struct wl_array *keys)
{
}
static void
keyboard_handle_leave (void *data,
struct wl_keyboard *keyboard,
uint32_t serial,
struct wl_surface *surface)
{
}
static void
keyboard_handle_key (void *data,
struct wl_keyboard *keyboard,
uint32_t serial,
uint32_t time,
uint32_t key,
uint32_t state_w)
{
}
static void
keyboard_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)
{
}
static const struct wl_pointer_listener pointer_listener = {
pointer_handle_enter,
pointer_handle_leave,
pointer_handle_motion,
pointer_handle_button,
pointer_handle_axis,
};
static const struct wl_keyboard_listener keyboard_listener = {
keyboard_handle_keymap,
keyboard_handle_enter,
keyboard_handle_leave,
keyboard_handle_key,
keyboard_handle_modifiers,
};
static void
seat_handle_capabilities(void *data, struct wl_seat *seat,
enum wl_seat_capability caps)
{
GdkWaylandDevice *device = data;
GdkDeviceManagerCore *device_manager_core =
GDK_DEVICE_MANAGER_CORE(device->device_manager);
if ((caps & WL_SEAT_CAPABILITY_POINTER) && !device->wl_pointer)
{
device->wl_pointer = wl_seat_get_pointer(seat);
wl_pointer_set_user_data(device->wl_pointer, device);
wl_pointer_add_listener(device->wl_pointer, &pointer_listener,
device);
device->pointer = g_object_new (GDK_TYPE_DEVICE_CORE,
"name", "Core Pointer",
"type", GDK_DEVICE_TYPE_MASTER,
"input-source", GDK_SOURCE_MOUSE,
"input-mode", GDK_MODE_SCREEN,
"has-cursor", TRUE,
"display", device->display,
"device-manager", device->device_manager,
NULL);
GDK_DEVICE_CORE (device->pointer)->device = device;
device_manager_core->devices =
g_list_prepend (device_manager_core->devices, device->pointer);
}
else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && device->wl_pointer)
{
wl_pointer_destroy(device->wl_pointer);
device->wl_pointer = NULL;
device_manager_core->devices =
g_list_remove (device_manager_core->devices, device->pointer);
g_object_unref (device->pointer);
device->pointer = NULL;
}
if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !device->wl_keyboard)
{
device->wl_keyboard = wl_seat_get_keyboard(seat);
wl_keyboard_set_user_data(device->wl_keyboard, device);
wl_keyboard_add_listener(device->wl_keyboard, &keyboard_listener,
device);
device->keyboard = g_object_new (GDK_TYPE_DEVICE_CORE,
"name", "Core Keyboard",
"type", GDK_DEVICE_TYPE_MASTER,
"input-source", GDK_SOURCE_KEYBOARD,
"input-mode", GDK_MODE_SCREEN,
"has-cursor", FALSE,
"display", device->display,
"device-manager", device->device_manager,
NULL);
GDK_DEVICE_CORE (device->keyboard)->device = device;
device_manager_core->devices =
g_list_prepend (device_manager_core->devices, device->keyboard);
}
else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && device->wl_keyboard)
{
wl_keyboard_destroy(device->wl_keyboard);
device->wl_keyboard = NULL;
device_manager_core->devices =
g_list_remove (device_manager_core->devices, device->keyboard);
g_object_unref (device->keyboard);
device->keyboard = NULL;
}
if (device->keyboard && device->pointer)
{
_gdk_device_set_associated_device (device->pointer, device->keyboard);
_gdk_device_set_associated_device (device->keyboard, device->pointer);
}
}
static const struct wl_seat_listener seat_listener = {
seat_handle_capabilities,
};
void
_gdk_wayland_device_manager_add_device (GdkDeviceManager *device_manager,
struct wl_seat *wl_seat)
{
GdkDisplay *display;
GdkWaylandDisplay *display_wayland;
GdkWaylandDevice *device;
display = gdk_device_manager_get_display (device_manager);
display_wayland = GDK_WAYLAND_DISPLAY (display);
device = g_new0 (GdkWaylandDevice, 1);
device->display = display;
device->device_manager = device_manager;
device->wl_seat = wl_seat;
wl_seat_add_listener (device->wl_seat, &seat_listener, device);
wl_seat_set_user_data (device->wl_seat, device);
device->data_device =
wl_data_device_manager_get_data_device (display_wayland->data_device_manager,
device->wl_seat);
wl_data_device_add_listener (device->data_device,
&data_device_listener, device);
device->pointer_surface =
wl_compositor_create_surface (display_wayland->compositor);
}
static void
free_device (gpointer data)
{
g_object_unref (data);
}
static void
gdk_device_manager_core_finalize (GObject *object)
{
GdkDeviceManagerCore *device_manager_core;
device_manager_core = GDK_DEVICE_MANAGER_CORE (object);
g_list_free_full (device_manager_core->devices, free_device);
G_OBJECT_CLASS (gdk_device_manager_core_parent_class)->finalize (object);
}
static GList *
gdk_device_manager_core_list_devices (GdkDeviceManager *device_manager,
GdkDeviceType type)
{
GdkDeviceManagerCore *device_manager_core;
GList *devices = NULL;
if (type == GDK_DEVICE_TYPE_MASTER)
{
device_manager_core = (GdkDeviceManagerCore *) device_manager;
devices = g_list_copy(device_manager_core->devices);
}
return devices;
}
static GdkDevice *
gdk_device_manager_core_get_client_pointer (GdkDeviceManager *device_manager)
{
GdkDeviceManagerCore *device_manager_core;
GList *l;
device_manager_core = (GdkDeviceManagerCore *) device_manager;
/* Find the first pointer device */
for (l = device_manager_core->devices; l != NULL; l = l->next)
{
GdkDevice *device = l->data;
if (gdk_device_get_source (device) == GDK_SOURCE_MOUSE)
return device;
}
return NULL;
}
static void
gdk_device_manager_core_class_init (GdkDeviceManagerCoreClass *klass)
{
GdkDeviceManagerClass *device_manager_class = GDK_DEVICE_MANAGER_CLASS (klass);
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = gdk_device_manager_core_finalize;
device_manager_class->list_devices = gdk_device_manager_core_list_devices;
device_manager_class->get_client_pointer = gdk_device_manager_core_get_client_pointer;
}
static void
gdk_device_manager_core_init (GdkDeviceManagerCore *device_manager)
{
}
GdkDeviceManager *
_gdk_wayland_device_manager_new (GdkDisplay *display)
{
return g_object_new (GDK_TYPE_DEVICE_MANAGER_CORE,
"display", display,
NULL);
}
gint
gdk_wayland_device_get_selection_type_atoms (GdkDevice *gdk_device,
GdkAtom **atoms_out)
{
gint i;
GdkAtom *atoms;
GdkWaylandDevice *device;
g_return_val_if_fail (GDK_IS_DEVICE_CORE (gdk_device), 0);
g_return_val_if_fail (atoms_out != NULL, 0);
device = GDK_DEVICE_CORE (gdk_device)->device;
if (!device->selection_offer || device->selection_offer->types->len == 0)
{
*atoms_out = NULL;
return 0;
}
atoms = g_new0 (GdkAtom, device->selection_offer->types->len);
/* Convert list of targets to atoms */
for (i = 0; i < device->selection_offer->types->len; i++)
{
atoms[i] = gdk_atom_intern (device->selection_offer->types->pdata[i],
FALSE);
GDK_NOTE (MISC,
g_message (G_STRLOC ": Adding atom for %s",
(char *)device->selection_offer->types->pdata[i]));
}
*atoms_out = atoms;
return device->selection_offer->types->len;
}
typedef struct
{
GdkWaylandDevice *device;
DataOffer *offer;
GIOChannel *channel;
GdkDeviceWaylandRequestContentCallback cb;
gpointer userdata;
} RequestContentClosure;
static gboolean
_request_content_io_func (GIOChannel *channel,
GIOCondition condition,
gpointer userdata)
{
RequestContentClosure *closure = (RequestContentClosure *)userdata;
gchar *data = NULL;
gsize len = 0;
GError *error = NULL;
/* FIXME: We probably want to do something better than this to avoid
* blocking on the transfer of large pieces of data: call the callback
* multiple times I should think.
*/
if (g_io_channel_read_to_end (channel,
&data,
&len,
&error) != G_IO_STATUS_NORMAL)
{
g_warning (G_STRLOC ": Error reading content from pipe: %s", error->message);
g_clear_error (&error);
}
/* Since we use _read_to_end we've got a guaranteed EOF and thus can go
* ahead and close the fd
*/
g_io_channel_shutdown (channel, TRUE, NULL);
closure->cb (closure->device->pointer, data, len, closure->userdata);
g_free (data);
data_offer_unref (closure->offer);
g_io_channel_unref (channel);
g_free (closure);
return FALSE;
}
gboolean
gdk_wayland_device_request_selection_content (GdkDevice *gdk_device,
const gchar *requested_mime_type,
GdkDeviceWaylandRequestContentCallback cb,
gpointer userdata)
{
int pipe_fd[2];
RequestContentClosure *closure;
GdkWaylandDevice *device;
GError *error = NULL;
g_return_val_if_fail (GDK_IS_DEVICE_CORE (gdk_device), FALSE);
g_return_val_if_fail (requested_mime_type != NULL, FALSE);
g_return_val_if_fail (cb != NULL, FALSE);
device = GDK_DEVICE_CORE (gdk_device)->device;
if (!device->selection_offer)
return FALSE;
/* TODO: Check mimetypes */
closure = g_new0 (RequestContentClosure, 1);
device->selection_offer->ref_count++;
pipe2 (pipe_fd, O_CLOEXEC);
wl_data_offer_receive (device->selection_offer->offer,
requested_mime_type,
pipe_fd[1]);
close (pipe_fd[1]);
closure->device = device;
closure->offer = device->selection_offer;
closure->channel = g_io_channel_unix_new (pipe_fd[0]);
closure->cb = cb;
closure->userdata = userdata;
if (!g_io_channel_set_encoding (closure->channel, NULL, &error))
{
g_warning (G_STRLOC ": Error setting encoding on channel: %s",
error->message);
g_clear_error (&error);
goto error;
}
g_io_add_watch (closure->channel,
G_IO_IN,
_request_content_io_func,
closure);
return TRUE;
error:
data_offer_unref (closure->offer);
g_io_channel_unref (closure->channel);
close (pipe_fd[1]);
g_free (closure);
return FALSE;
}
struct _GdkWaylandSelectionOffer {
GdkDeviceWaylandOfferContentCallback cb;
gpointer userdata;
struct wl_data_source *source;
GdkWaylandDevice *device;
};
static void
data_source_target (void *data,
struct wl_data_source *source,
const char *mime_type)
{
g_debug (G_STRLOC ": %s source = %p, mime_type = %s",
G_STRFUNC, source, mime_type);
}
static void
data_source_send (void *data,
struct wl_data_source *source,
const char *mime_type,
int32_t fd)
{
GdkWaylandSelectionOffer *offer = (GdkWaylandSelectionOffer *)data;;
gchar *buf;
gssize len, bytes_written = 0;
g_debug (G_STRLOC ": %s source = %p, mime_type = %s fd = %d",
G_STRFUNC, source, mime_type, fd);
buf = offer->cb (offer->device->pointer, mime_type, &len, offer->userdata);
while (len > 0)
{
bytes_written += write (fd, buf + bytes_written, len);
if (bytes_written == -1)
goto error;
len -= bytes_written;
}
close (fd);
g_free (buf);
return;
error:
g_warning (G_STRLOC ": Error writing data to client: %s",
g_strerror (errno));
close (fd);
g_free (buf);
}
static void
data_source_cancelled (void *data,
struct wl_data_source *source)
{
g_debug (G_STRLOC ": %s source = %p",
G_STRFUNC, source);
}
static const struct wl_data_source_listener data_source_listener = {
data_source_target,
data_source_send,
data_source_cancelled
};
static guint32
_wl_time_now (void)
{
struct timeval tv;
gettimeofday(&tv, NULL);
return tv.tv_sec * 1000 + tv.tv_usec / 1000;
}
gboolean
gdk_wayland_device_offer_selection_content (GdkDevice *gdk_device,
const gchar **mime_types,
gint nr_mime_types,
GdkDeviceWaylandOfferContentCallback cb,
gpointer userdata)
{
GdkDisplay *display;
GdkWaylandDisplay *display_wayland;
GdkWaylandSelectionOffer *offer;
GdkWaylandDevice *device;
gint i;
g_return_val_if_fail (GDK_IS_DEVICE_CORE (gdk_device), 0);
device = GDK_DEVICE_CORE (gdk_device)->device;
display = device->display;
display_wayland = GDK_WAYLAND_DISPLAY (display);
offer = g_new0 (GdkWaylandSelectionOffer, 1);
offer->cb = cb;
offer->userdata = userdata;
offer->source =
wl_data_device_manager_create_data_source (display_wayland->data_device_manager);
offer->device = device;
for (i = 0; i < nr_mime_types; i++)
{
wl_data_source_offer (offer->source,
mime_types[i]);
}
wl_data_source_add_listener (offer->source,
&data_source_listener,
offer);
wl_data_device_set_selection (device->data_device,
offer->source,
_wl_time_now ());
device->selection_offer_out = offer;
return TRUE;
}
gboolean
gdk_wayland_device_clear_selection_content (GdkDevice *gdk_device)
{
GdkWaylandDevice *device;
g_return_val_if_fail (GDK_IS_DEVICE_CORE (gdk_device), 0);
device = GDK_DEVICE_CORE (gdk_device)->device;
if (!device->selection_offer_out)
return FALSE;
wl_data_device_set_selection (device->data_device,
NULL,
_wl_time_now ());
wl_data_source_destroy (device->selection_offer_out->source);
g_free (device->selection_offer_out);
device->selection_offer_out = NULL;
return TRUE;
}