* 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, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * 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 <X11/extensions/XKBcommon.h>
#include <X11/keysym.h>
+#include <sys/time.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))
typedef struct _GdkDeviceCoreClass GdkDeviceCoreClass;
typedef struct _GdkWaylandDevice GdkWaylandDevice;
+typedef struct _DataOffer DataOffer;
+
+typedef struct _GdkWaylandSelectionOffer GdkWaylandSelectionOffer;
+
struct _GdkWaylandDevice
{
GdkDisplay *display;
GdkWindow *pointer_focus;
GdkWindow *keyboard_focus;
struct wl_input_device *device;
+ struct wl_data_device *data_device;
int32_t x, y, 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 _GdkDeviceCore
{
gint x_int, y_int;
- gdk_window_get_pointer (window, &x_int, &y_int, mask);
+ gdk_window_get_device_position (window, device, &x_int, &y_int, mask);
if (axes)
{
int x, y;
if (cursor)
+ g_object_ref (cursor);
+
+ /* Setting the cursor to NULL means that we should use the default cursor */
+ if (!cursor)
{
- buffer = _gdk_wayland_cursor_get_buffer(cursor, &x, &y);
- wl_input_device_attach(wd->device, wd->time, buffer, x, y);
- }
- else
- {
- wl_input_device_attach(wd->device, wd->time, NULL, 0, 0);
+ /* 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);
+ wl_input_device_attach(wd->device, wd->time, buffer, x, y);
+
+ g_object_unref (cursor);
}
static void
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_;
+ }
+
return GDK_GRAB_SUCCESS;
}
gdk_device_core_ungrab (GdkDevice *device,
guint32 time_)
{
+ 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;
+ }
}
static GdkWindow *
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;
}
int32_t x, int32_t y, int32_t sx, int32_t sy)
{
GdkWaylandDevice *device = data;
+ GdkDisplayWayland *display = GDK_DISPLAY_WAYLAND (device->display);
GdkEvent *event;
event = gdk_event_new (GDK_NOTHING);
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);
}
uint32_t time, uint32_t button, uint32_t state)
{
GdkWaylandDevice *device = data;
+ GdkDisplayWayland *display = GDK_DISPLAY_WAYLAND (device->display);
GdkEvent *event;
uint32_t modifier;
-
- fprintf (stderr, "button event %d, state %d\n", button, state);
+ 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.y_root = (gdouble) device->y;
event->button.axes = NULL;
event->button.state = device->modifiers;
- event->button.button = button - 271;
+ event->button.button = gdk_button;
+ gdk_event_set_screen (event, display->screen);
- modifier = 1 << (8 + button - 272);
+ 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
-input_handle_key(void *data, struct wl_input_device *input_device,
- uint32_t time, uint32_t key, uint32_t state)
+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)
{
- GdkWaylandDevice *device = data;
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);
event->button.time = time;
event->key.state = device->modifiers;
event->key.group = 0;
- event->key.hardware_keycode = key;
-
- keymap = gdk_keymap_get_for_display (device->display);
- xkb = _gdk_wayland_keymap_get_xkb_desc (keymap);
-
code = key + xkb->min_key_code;
+ event->key.hardware_keycode = code;
level = 0;
if (device->modifiers & XKB_COMMON_SHIFT_MASK &&
event->key.is_modifier = modifier > 0;
- if (event->key.keyval == GDK_KEY_Escape)
+ 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)
{
- event->key.length = 1;
- event->key.string = g_strdup ("\033");
+ if (device->repeat_timer)
+ {
+ g_source_remove (device->repeat_timer);
+ device->repeat_timer = 0;
+ }
+ return FALSE;
}
- else if (event->key.keyval == GDK_KEY_Return ||
- event->key.keyval == GDK_KEY_KP_Enter)
+ else if (modifier)
{
- event->key.length = 1;
- event->key.string = g_strdup ("\r");
+ return FALSE;
}
- else if (event->key.state & GDK_CONTROL_MASK)
+ else switch (device->repeat_count)
{
- gsize bytes_written;
- gint len;
- gchar buf[7];
- int c = event->key.keyval;
-
- /* Apply the control key - Taken from Xlib */
- if ((c >= XK_at && c < '\177') || c == ' ')
- c &= 0x1F;
- else if (c == XK_2)
+ case 1:
+ if (device->repeat_timer)
{
- event->key.string = g_memdup ("\0\0", 2);
- event->key.length = 1;
- buf[0] = '\0';
- goto out;
+ g_source_remove (device->repeat_timer);
+ device->repeat_timer = 0;
}
- else if (c >= XK_3 && c <= XK_7)
- c -= (XK_3 - '\033');
- else if (c == XK_8)
- c = '\177';
- else if (c == XK_slash)
- c = '_' & 0x1F;
- len = g_unichar_to_utf8 (c, buf);
- buf[len] = '\0';
-
- event->key.string = g_locale_from_utf8 (buf, len,
- NULL, &bytes_written,
- NULL);
- if (event->key.string)
- event->key.length = bytes_written;
- }
- else
- {
- char buffer[128];
- xkb_keysym_to_string(event->key.keyval, buffer, sizeof buffer);
- event->key.string = g_strdup (buffer);
- event->key.length = strlen(event->key.string);
+ 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;
}
+}
- out:
- _gdk_wayland_display_deliver_event (device->display, event);
+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;
- fprintf (stderr, "keyboard event, code %d, sym %d, string %s, mods 0x%x\n",
- code, event->key.keyval, event->key.string, event->key.state);
+ device->repeat_count = 0;
+ deliver_key_event (data, time, key, state);
}
static void
_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;
}
device->y = y;
_gdk_wayland_display_deliver_event (device->display, event);
- }
- fprintf (stderr, "pointer focus surface %p, window %p\n",
- surface, device->pointer_focus);
+ GDK_NOTE (EVENTS,
+ g_message ("enter, device %p surface %p",
+ device, device->pointer_focus));
+ }
}
static void
device->modifiers = 0;
for (k = keys->data; k < end; k++)
device->modifiers |= xkb->map->modmap[*k];
-
- fprintf (stderr, "modifiers: 0x%x\n", device->modifiers);
}
static void
GdkWaylandDevice *device = data;
GdkEvent *event;
- fprintf (stderr, "keyboard focus surface %p\n", surface);
-
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->pointer_focus);
+ 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);
}
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,
+ input_handle_motion,
+ input_handle_button,
+ input_handle_key,
+ input_handle_pointer_focus,
+ input_handle_keyboard_focus,
+};
+
+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
};
void
struct wl_input_device *wl_device)
{
GdkDisplay *display;
+ GdkDisplayWayland *display_wayland;
GdkDeviceManagerCore *device_manager_core =
GDK_DEVICE_MANAGER_CORE(device_manager);
GdkWaylandDevice *device;
device = g_new0 (GdkWaylandDevice, 1);
display = gdk_device_manager_get_display (device_manager);
+ display_wayland = GDK_DISPLAY_WAYLAND (display);
device->display = display;
device->pointer = g_object_new (GDK_TYPE_DEVICE_CORE,
wl_input_device_add_listener(device->device,
&input_device_listener, device);
+ device->data_device =
+ wl_data_device_manager_get_data_device (display_wayland->data_device_manager,
+ device->device);
+ wl_data_device_add_listener (device->data_device,
+ &data_device_listener, device);
+
device_manager_core->devices =
g_list_prepend (device_manager_core->devices, device->keyboard);
device_manager_core->devices =
}
static void
-free_device (void *data, void *user_data)
+free_device (gpointer data)
{
g_object_unref (data);
}
device_manager_core = GDK_DEVICE_MANAGER_CORE (object);
- g_list_foreach (device_manager_core->devices, free_device, NULL);
- g_list_free (device_manager_core->devices);
+ g_list_free_full (device_manager_core->devices, free_device);
G_OBJECT_CLASS (gdk_device_manager_core_parent_class)->finalize (object);
}
"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;
+ GdkDisplayWayland *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_DISPLAY_WAYLAND (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;
+}