Use a transient object for the dnd session

dev
Kristian Høgsberg 15 years ago
parent 5c63df7f1e
commit e9d37bdc5f
  1. 24
      TODO
  2. 245
      clients/dnd.c
  3. 109
      clients/window.c
  4. 14
      clients/window.h
  5. 275
      compositor.c
  6. 2
      compositor.h
  7. 104
      protocol.xml
  8. 42
      scanner.c
  9. 12
      wayland-client.c
  10. 9
      wayland-server.h

24
TODO

@ -27,18 +27,6 @@ Core wayland protocol
send a NULL type or x-wayland/root-something type if the source
offers that.
Races between pointer motion, ending the drag, the target sending
accept request and the source receiving the target event.
- We've sent a drag focus or motion event to the source, but
haven't received an accept request corresponding to that event
and now the button is release. The compositor could wait for
the source to reply to outstanding focus/motion events before
sending the finish event to the source. Or we could send the
finish event through the source so that it needs to reply to the
finish event too. Either way, the state of the drag blocks on
the client. What if we drag to a client that doesn't doo dnd?
How do we animate the drag icon back to the drag origin in case of
a failed drag?
@ -48,18 +36,6 @@ Core wayland protocol
that will participate in dnd. Or just assume client is not
participating until we receive an accept request.
May need to look at all offer events before we can decide which one
to go with. Problem is, we don't know when we've seen that last
offer event.
Create transient dnd object when a client starts a drag. Announce
the dnd object to clients first time the drag enters one of its
surfaces. Track if we've already announced the object by comparing
the drag start timestamp/serial with the clients last-event
timestamp/serial? Wont work if we send other events to a client
after creating the drag object. Maybe just keep the transient
object on the initiator side?
- Pointer image issue:
- A touch input device doesn't have a pointer; indicate that

@ -44,12 +44,21 @@ struct dnd {
struct display *display;
uint32_t key;
struct item *items[16];
};
struct wl_buffer *translucent_buffer;
struct wl_buffer *opaque_buffer;
struct dnd_drag {
cairo_surface_t *translucent;
cairo_surface_t *opaque;
int hotspot_x, hotspot_y;
uint32_t tag;
struct dnd *dnd;
struct input *input;
};
struct dnd_offer {
struct dnd *dnd;
struct wl_array types;
const char *drag_type;
uint32_t tag;
};
struct item {
@ -108,7 +117,7 @@ item_create(struct display *display, int x, int y)
cairo_curve_to(cr,
x1 - y1 * u, y1 + x1 * u,
x2 + y2 * v, y2 - x2 * v,
x2, y2);
x2, y2);
cairo_curve_to(cr,
x2 - y2 * v, y2 + x2 * v,
@ -219,90 +228,130 @@ dnd_get_item(struct dnd *dnd, int32_t x, int32_t y)
}
static void
drag_handle_device(void *data,
struct wl_drag *drag, struct wl_input_device *device)
drag_target(void *data,
struct wl_drag *drag, const char *mime_type)
{
struct dnd_drag *dnd_drag = data;
struct dnd *dnd = dnd_drag->dnd;
struct wl_input_device *device;
cairo_surface_t *surface;
struct wl_buffer *buffer;
fprintf(stderr, "target %s\n", mime_type);
device = input_get_input_device(dnd_drag->input);
if (mime_type)
surface = dnd_drag->opaque;
else
surface = dnd_drag->translucent;
buffer = display_get_buffer_for_surface(dnd->display, surface);
wl_input_device_attach(device, buffer,
dnd_drag->hotspot_x, dnd_drag->hotspot_y);
}
static void
drag_pointer_focus(void *data,
struct wl_drag *drag,
uint32_t time, struct wl_surface *surface,
int32_t x, int32_t y, int32_t surface_x, int32_t surface_y)
drag_finish(void *data, struct wl_drag *drag, int fd)
{
struct dnd *dnd = data;
/* FIXME: We need the offered types before we get the
* pointer_focus event so we know which one we want and can
* send the accept request back. */
struct dnd_drag *dnd_drag = data;
char text[] = "[drop data]";
fprintf(stderr, "drag pointer focus %p\n", surface);
fprintf(stderr, "got 'finish', fd %d, sending message\n", fd);
if (!surface) {
dnd->drag_type = NULL;
return;
}
write(fd, text, sizeof text);
close(fd);
if (!dnd_get_item(dnd, surface_x, surface_y)) {
wl_drag_accept(drag, "text/plain");
dnd->drag_type = "text/plain";
} else {
wl_drag_accept(drag, NULL);
dnd->drag_type = NULL;
}
/* The 'finish' event marks the end of the session on the drag
* source side and we need to clean up the drag object created
* and the local state. */
wl_drag_destroy(drag);
cairo_surface_destroy(dnd_drag->translucent);
cairo_surface_destroy(dnd_drag->opaque);
free(dnd_drag);
}
static const struct wl_drag_listener drag_listener = {
drag_target,
drag_finish
};
static void
drag_offer(void *data,
struct wl_drag *drag, const char *type)
drag_offer_offer(void *data,
struct wl_drag_offer *offer, const char *type)
{
fprintf(stderr, "drag offer %s\n", type);
struct dnd_offer *dnd_offer = data;
char **p;
p = wl_array_add(&dnd_offer->types, sizeof *p);
if (p)
*p = strdup(type);
}
static void
drag_motion(void *data,
struct wl_drag *drag,
uint32_t time,
int32_t x, int32_t y, int32_t surface_x, int32_t surface_y)
drag_offer_pointer_focus(void *data,
struct wl_drag_offer *offer,
uint32_t time, struct wl_surface *surface,
int32_t x, int32_t y,
int32_t surface_x, int32_t surface_y)
{
struct dnd *dnd = data;
struct dnd_offer *dnd_offer = data;
struct window *window;
char **p, **end;
/* FIXME: Need to correlate this with the offer event.
* Problem is, we don't know when we've seen that last offer
* event, and we might need to look at all of them before we
* can decide which one to go with. */
if (!dnd_get_item(dnd, surface_x, surface_y)) {
wl_drag_accept(drag, "text/plain");
dnd->drag_type = "text/plain";
/* The last event in a dnd session is pointer_focus with a
* NULL surface, whether or not we get the drop event. We
* need to clean up the dnd_offer proxy and whatever state we
* allocated. */
if (!surface) {
fprintf(stderr, "pointer focus NULL, session over\n");
wl_array_release(&dnd_offer->types);
free(dnd_offer);
wl_drag_offer_destroy(offer);
return;
}
fprintf(stderr, "drag pointer focus %p\n", surface);
fprintf(stderr, "offered types:\n");
end = dnd_offer->types.data + dnd_offer->types.size;
for (p = dnd_offer->types.data; p < end; p++)
fprintf(stderr, "\%s\n", *p);
window = wl_surface_get_user_data(surface);
dnd_offer->dnd = window_get_user_data(window);
if (!dnd_get_item(dnd_offer->dnd, surface_x, surface_y)) {
wl_drag_offer_accept(offer, time, "text/plain");
dnd_offer->drag_type = "text/plain";
} else {
wl_drag_accept(drag, NULL);
dnd->drag_type = NULL;
wl_drag_offer_accept(offer, time, NULL);
dnd_offer->drag_type = NULL;
}
}
static void
drag_target(void *data,
struct wl_drag *drag, const char *mime_type)
drag_offer_motion(void *data,
struct wl_drag_offer *offer, uint32_t time,
int32_t x, int32_t y, int32_t surface_x, int32_t surface_y)
{
struct dnd *dnd = data;
struct input *input;
struct wl_input_device *device;
struct dnd_offer *dnd_offer = data;
struct dnd *dnd = dnd_offer->dnd;
fprintf(stderr, "target %s\n", mime_type);
input = wl_drag_get_user_data(drag);
device = input_get_input_device(input);
if (mime_type)
wl_input_device_attach(device, dnd->opaque_buffer,
dnd->hotspot_x, dnd->hotspot_y);
else
wl_input_device_attach(device, dnd->translucent_buffer,
dnd->hotspot_x, dnd->hotspot_y);
if (!dnd_get_item(dnd, surface_x, surface_y)) {
fprintf(stderr, "drag offer motion %d, %d, accepting\n",
surface_x, surface_y);
wl_drag_offer_accept(offer, time, "text/plain");
dnd_offer->drag_type = "text/plain";
} else {
fprintf(stderr, "drag offer motion %d, %d, declining\n",
surface_x, surface_y);
wl_drag_offer_accept(offer, time, NULL);
dnd_offer->drag_type = NULL;
}
}
static gboolean
drop_io_func(GIOChannel *source, GIOCondition condition, gpointer data)
{
struct dnd *dnd = data;
struct dnd_offer *dnd_offer = data;
char buffer[256];
int fd;
unsigned int len;
@ -312,7 +361,7 @@ drop_io_func(GIOChannel *source, GIOCondition condition, gpointer data)
fprintf(stderr, "read %d bytes: %s\n", len, buffer);
fd = g_io_channel_unix_get_fd(source);
close(fd);
g_source_remove(dnd->tag);
g_source_remove(dnd_offer->tag);
g_io_channel_unref(source);
@ -320,13 +369,13 @@ drop_io_func(GIOChannel *source, GIOCondition condition, gpointer data)
}
static void
drag_drop(void *data, struct wl_drag *drag)
drag_offer_drop(void *data, struct wl_drag_offer *offer)
{
struct dnd *dnd = data;
int p[2];
struct dnd_offer *dnd_offer = data;
GIOChannel *channel;
int p[2];
if (!dnd->drag_type) {
if (!dnd_offer->drag_type) {
fprintf(stderr, "got 'drop', but no target\n");
/* FIXME: Should send response so compositor and
* source knows it's over. Can't send -1 to indicate
@ -338,38 +387,39 @@ drag_drop(void *data, struct wl_drag *drag)
fprintf(stderr, "got 'drop', sending write end of pipe\n");
pipe(p);
wl_drag_receive(drag, p[1]);
wl_drag_offer_receive(offer, p[1]);
close(p[1]);
channel = g_io_channel_unix_new(p[0]);
dnd->tag = g_io_add_watch(channel, G_IO_IN, drop_io_func, dnd);
dnd_offer->tag = g_io_add_watch(channel, G_IO_IN,
drop_io_func, dnd_offer);
}
static const struct wl_drag_offer_listener drag_offer_listener = {
drag_offer_offer,
drag_offer_pointer_focus,
drag_offer_motion,
drag_offer_drop,
};
static void
drag_finish(void *data, struct wl_drag *drag, int fd)
drag_offer_handler(struct wl_drag_offer *offer, struct display *display)
{
char text[] = "[drop data]";
struct dnd_offer *dnd_offer;
fprintf(stderr, "got 'finish', fd %d, sending message\n", fd);
dnd_offer = malloc(sizeof *dnd_offer);
if (dnd_offer == NULL)
return;
write(fd, text, sizeof text);
close(fd);
wl_drag_offer_add_listener(offer, &drag_offer_listener, dnd_offer);
wl_array_init(&dnd_offer->types);
}
static const struct wl_drag_listener drag_listener = {
drag_handle_device,
drag_pointer_focus,
drag_offer,
drag_motion,
drag_target,
drag_drop,
drag_finish
};
static cairo_surface_t *
create_drag_cursor(struct dnd *dnd, struct item *item, int32_t x, int32_t y,
double opacity)
create_drag_cursor(struct dnd_drag *dnd_drag,
struct item *item, int32_t x, int32_t y, double opacity)
{
struct dnd *dnd = dnd_drag->dnd;
cairo_surface_t *surface, *pointer;
int32_t pointer_width, pointer_height, hotspot_x, hotspot_y;
struct rectangle rectangle;
@ -409,8 +459,8 @@ create_drag_cursor(struct dnd *dnd, struct item *item, int32_t x, int32_t y,
display_flush_cairo_device(dnd->display);
cairo_destroy(cr);
dnd->hotspot_x = pointer_width + x - item->x;
dnd->hotspot_y = pointer_height + y - item->y;
dnd_drag->hotspot_x = pointer_width + x - item->x;
dnd_drag->hotspot_y = pointer_height + y - item->y;
return surface;
}
@ -423,8 +473,8 @@ dnd_button_handler(struct window *window,
struct dnd *dnd = data;
int32_t x, y;
struct item *item;
cairo_surface_t *surface;
struct rectangle rectangle;
struct dnd_drag *dnd_drag;
window_get_child_rectangle(dnd->window, &rectangle);
input_get_position(input, &x, &y);
@ -435,18 +485,17 @@ dnd_button_handler(struct window *window,
if (item && state == 1) {
fprintf(stderr, "start drag, item %p\n", item);
surface = create_drag_cursor(dnd, item, x, y, 1);
dnd->opaque_buffer =
display_get_buffer_for_surface(dnd->display, surface);
surface = create_drag_cursor(dnd, item, x, y, 0.2);
dnd->translucent_buffer =
display_get_buffer_for_surface(dnd->display, surface);
dnd_drag = malloc(sizeof *dnd_drag);
dnd_drag->dnd = dnd;
dnd_drag->input = input;
window_start_drag(window, input, time);
dnd_drag->opaque =
create_drag_cursor(dnd_drag, item, x, y, 1);
dnd_drag->translucent =
create_drag_cursor(dnd_drag, item, x, y, 0.2);
/* FIXME: We leak the surface because we can't free it
* until the server has referenced it. */
window_start_drag(window, input, time,
&drag_listener, dnd_drag);
}
}
@ -509,8 +558,6 @@ dnd_create(struct display *display)
rectangle.height = 4 * (item_height + item_padding) + item_padding;
window_set_child_size(dnd->window, &rectangle);
display_add_drag_listener(display, &drag_listener, dnd);
dnd_draw(dnd);
return dnd;
@ -532,6 +579,8 @@ main(int argc, char *argv[])
d = display_create(&argc, &argv, option_entries);
display_set_drag_offer_handler(d, drag_offer_handler);
dnd = dnd_create (d);
display_run(d);

@ -71,6 +71,8 @@ struct display {
cairo_surface_t *active_frame, *inactive_frame, *shadow;
struct xkb_desc *xkb;
cairo_surface_t **pointer_surfaces;
display_drag_offer_handler_t drag_offer_handler;
};
struct window {
@ -107,7 +109,6 @@ struct window {
struct input {
struct display *display;
struct wl_input_device *input_device;
struct wl_drag *drag;
struct window *pointer_focus;
struct window *keyboard_focus;
uint32_t modifiers;
@ -744,25 +745,21 @@ input_get_input_device(struct input *input)
return input->input_device;
}
void
display_add_drag_listener(struct display *display,
const struct wl_drag_listener *drag_listener,
void *data)
struct wl_drag *
window_start_drag(struct window *window, struct input *input, uint32_t time,
const struct wl_drag_listener *listener, void *data)
{
struct input *input;
wl_list_for_each(input, &display->input_list, link)
wl_drag_add_listener(input->drag, drag_listener, data);
}
struct wl_drag *drag;
void
window_start_drag(struct window *window, struct input *input, uint32_t time)
{
cairo_device_flush (window->display->device);
wl_drag_prepare(input->drag, window->surface, time);
wl_drag_offer(input->drag, "text/plain");
wl_drag_activate(input->drag);
drag = wl_shell_create_drag(window->display->shell);
wl_drag_offer(drag, "text/plain");
wl_drag_offer(drag, "text/html");
wl_drag_activate(drag, window->surface, input->input_device, time);
wl_drag_add_listener(drag, listener, data);
return drag;
}
static void
@ -902,6 +899,12 @@ window_set_user_data(struct window *window, void *data)
window->user_data = data;
}
void *
window_get_user_data(struct window *window)
{
return window->user_data;
}
void
window_set_resize_handler(struct window *window,
window_resize_handler_t handler)
@ -1099,71 +1102,12 @@ display_add_input(struct display *d, uint32_t id)
wl_input_device_set_user_data(input->input_device, input);
}
static void
drag_handle_device(void *data,
struct wl_drag *drag, struct wl_input_device *device)
{
struct input *input;
input = wl_input_device_get_user_data(device);
input->drag = drag;
wl_drag_set_user_data(drag, input);
}
static void
drag_pointer_focus(void *data,
struct wl_drag *drag,
uint32_t time, struct wl_surface *surface,
int32_t x, int32_t y, int32_t surface_x, int32_t surface_y)
{
}
static void
drag_offer(void *data,
struct wl_drag *drag, const char *type)
{
}
static void
drag_motion(void *data,
struct wl_drag *drag,
uint32_t time,
int32_t x, int32_t y, int32_t surface_x, int32_t surface_y)
{
}
static void
drag_target(void *data,
struct wl_drag *drag, const char *mime_type)
{
}
static void
drag_drop(void *data, struct wl_drag *drag)
{
}
static void
drag_finish(void *data, struct wl_drag *drag, int fd)
{
}
static const struct wl_drag_listener drag_listener = {
drag_handle_device,
drag_pointer_focus,
drag_offer,
drag_motion,
drag_target,
drag_drop,
drag_finish
};
static void
display_handle_global(struct wl_display *display, uint32_t id,
const char *interface, uint32_t version, void *data)
{
struct display *d = data;
struct wl_drag *drag;
struct wl_drag_offer *offer;
if (strcmp(interface, "compositor") == 0) {
d->compositor = wl_compositor_create(display, id);
@ -1180,9 +1124,9 @@ display_handle_global(struct wl_display *display, uint32_t id,
} else if (strcmp(interface, "drm") == 0) {
d->drm = wl_drm_create(display, id);
wl_drm_add_listener(d->drm, &drm_listener, d);
} else if (strcmp(interface, "drag") == 0) {
drag = wl_drag_create(display, id);
wl_drag_add_listener(drag, &drag_listener, NULL);
} else if (strcmp(interface, "drag_offer") == 0) {
offer = wl_drag_offer_create(display, id);
d->drag_offer_handler(offer, d);
}
}
@ -1363,3 +1307,10 @@ display_run(struct display *d)
{
g_main_loop_run(d->loop);
}
void
display_set_drag_offer_handler(struct display *display,
display_drag_offer_handler_t handler)
{
display->drag_offer_handler = handler;
}

@ -111,6 +111,9 @@ typedef int (*window_motion_handler_t)(struct window *window,
int32_t x, int32_t y,
int32_t sx, int32_t sy, void *data);
typedef void (*display_drag_offer_handler_t)(struct wl_drag_offer *offer,
struct display *display);
struct window *
window_create(struct display *display, const char *title,
int32_t x, int32_t y, int32_t width, int32_t height);
@ -149,6 +152,9 @@ window_set_fullscreen(struct window *window, int fullscreen);
void
window_set_user_data(struct window *window, void *data);
void *
window_get_user_data(struct window *window);
void
window_set_redraw_handler(struct window *window,
window_redraw_handler_t handler);
@ -191,7 +197,13 @@ window_set_frame_handler(struct window *window,
window_frame_handler_t handler);
void
window_start_drag(struct window *window, struct input *input, uint32_t time);
display_set_drag_offer_handler(struct display *display,
display_drag_offer_handler_t handler);
struct wl_drag *
window_start_drag(struct window *window, struct input *input, uint32_t time,
const struct wl_drag_listener *listener, void *data);
void
input_get_position(struct input *input, int32_t *x, int32_t *y);

@ -573,9 +573,49 @@ shell_resize(struct wl_client *client, struct wl_shell *shell,
wlsc_input_device_set_pointer_image(wd, pointer);
}
static void
destroy_drag(struct wl_resource *resource, struct wl_client *client)
{
struct wl_drag *drag =
container_of(resource, struct wl_drag, resource);
/* FIXME: More stuff */
free(drag);
}
const static struct wl_drag_interface drag_interface;
static void
shell_create_drag(struct wl_client *client,
struct wl_shell *shell, uint32_t id)
{
struct wl_display *display = wl_client_get_display(client);
struct wl_drag *drag;
drag = malloc(sizeof *drag);
if (drag == NULL) {
wl_client_post_event(client,
(struct wl_object *) display,
WL_DISPLAY_NO_MEMORY);
return;
}
memset(drag, 0, sizeof *drag);
drag->resource.base.id = id;
drag->resource.base.interface = &wl_drag_interface;
drag->resource.base.implementation =
(void (**)(void)) &drag_interface;
drag->resource.destroy = destroy_drag;
wl_client_add_resource(client, &drag->resource);
}
const static struct wl_shell_interface shell_interface = {
shell_move,
shell_resize
shell_resize,
shell_create_drag
};
static void
@ -589,7 +629,7 @@ compositor_create_surface(struct wl_client *client,
if (surface == NULL) {
wl_client_post_event(client,
(struct wl_object *) ec->wl_display,
WL_DISPLAY_NO_MEMORY, 0);
WL_DISPLAY_NO_MEMORY);
return;
}
@ -695,8 +735,6 @@ pick_surface(struct wlsc_input_device *device, int32_t *sx, int32_t *sy)
return NULL;
}
static void
wl_drag_reset(struct wl_drag *drag);
static void
wl_drag_set_pointer_focus(struct wl_drag *drag,
struct wlsc_surface *surface, uint32_t time,
@ -797,11 +835,12 @@ notify_motion(struct wlsc_input_device *device, uint32_t time, int x, int y)
case WLSC_DEVICE_GRAB_DRAG:
es = pick_surface(device, &sx, &sy);
wl_drag_set_pointer_focus(&device->drag,
wl_drag_set_pointer_focus(device->drag,
es, time, x, y, sx, sy);
if (es)
wl_surface_post_event(&es->base, &device->drag.base,
WL_DRAG_MOTION,
wl_surface_post_event(&es->base,
&device->drag->drag_offer.base,
WL_DRAG_OFFER_MOTION,
time, x, y, sx, sy);
break;
@ -817,7 +856,7 @@ notify_motion(struct wlsc_input_device *device, uint32_t time, int x, int y)
static void
wlsc_input_device_end_grab(struct wlsc_input_device *device, uint32_t time)
{
struct wl_drag *drag = &device->drag;
struct wl_drag *drag = device->drag;
struct wlsc_surface *es;
int32_t sx, sy;
@ -825,9 +864,10 @@ wlsc_input_device_end_grab(struct wlsc_input_device *device, uint32_t time)
case WLSC_DEVICE_GRAB_DRAG:
if (drag->target)
wl_client_post_event(drag->target,
&drag->base, WL_DRAG_DROP);
&drag->drag_offer.base,
WL_DRAG_OFFER_DROP);
wl_drag_set_pointer_focus(drag, NULL, time, 0, 0, 0, 0);
wl_drag_reset(drag);
device->drag = NULL;
break;
default:
break;
@ -1012,80 +1052,89 @@ wl_drag_set_pointer_focus(struct wl_drag *drag,
if (drag->pointer_focus &&
(!surface || drag->pointer_focus->client != surface->base.client))
wl_surface_post_event(drag->pointer_focus,
&drag->base,
WL_DRAG_POINTER_FOCUS,
&drag->drag_offer.base,
WL_DRAG_OFFER_POINTER_FOCUS,
time, NULL, 0, 0, 0, 0);
if (surface) {
if (surface &&
(!drag->pointer_focus ||
drag->pointer_focus->client != surface->base.client)) {
wl_surface_post_event(&surface->base,
&drag->base,
WL_DRAG_POINTER_FOCUS,
time, &surface->base,
x, y, sx, sy);
(struct wl_object *) surface->compositor->wl_display,
WL_DISPLAY_GLOBAL,
&drag->drag_offer.base,
drag->drag_offer.base.interface->name,
drag->drag_offer.base.interface->version);
end = drag->types.data + drag->types.size;
for (p = drag->types.data; p < end; p++)
wl_surface_post_event(&surface->base,
&drag->base,
WL_DRAG_OFFER, *p);
&drag->drag_offer.base,
WL_DRAG_OFFER_OFFER, *p);
}
if (surface) {
wl_surface_post_event(&surface->base,
&drag->drag_offer.base,
WL_DRAG_OFFER_POINTER_FOCUS,
time, &surface->base,
x, y, sx, sy);
}
drag->pointer_focus = &surface->base;
drag->pointer_focus_time = time;
drag->target = NULL;
}
static void
wl_drag_reset(struct wl_drag *drag)
drag_offer_accept(struct wl_client *client,
struct wl_drag_offer *offer, uint32_t time, const char *type)
{
struct wl_drag *drag = container_of(offer, struct wl_drag, drag_offer);
char **p, **end;
/* If the client responds to drag pointer_focus or motion
* events after the pointer has left the surface, we just
* discard the accept requests. The drag source just won't
* get the corresponding 'target' events and eventually the
* next surface/root will start sending events. */
if (time < drag->pointer_focus_time)
return;
drag->target = client;
drag->type = NULL;
end = drag->types.data + drag->types.size;
for (p = drag->types.data; p < end; p++)
free(*p);
wl_array_release(&drag->types);
wl_array_init(&drag->types);
/* FIXME: We need to reset drag->target too, but can't right
* now because we need it for send/drop.
*
* drag->target = NULL;
* drag->source = NULL;
*/
drag->time = 0;
drag->pointer_focus = NULL;
if (type && strcmp(*p, type) == 0)
drag->type = *p;
wl_surface_post_event(drag->source, &drag->resource.base,
WL_DRAG_TARGET, drag->type);
}
static void
drag_prepare(struct wl_client *client,
struct wl_drag *drag, struct wl_surface *surface, uint32_t time)
drag_offer_receive(struct wl_client *client,
struct wl_drag_offer *offer, int fd)
{
struct wlsc_input_device *device =
(struct wlsc_input_device *) drag->input_device;
if (&device->pointer_focus->base != surface ||
device->grab_time != time)
return;
struct wl_drag *drag = container_of(offer, struct wl_drag, drag_offer);
wl_drag_reset(drag);
drag->source = surface;
drag->time = time;
wl_surface_post_event(drag->source, &drag->resource.base,
WL_DRAG_FINISH, fd);
close(fd);
}
static const struct wl_drag_offer_interface drag_offer_interface = {
drag_offer_accept,
drag_offer_receive
};
static void
drag_offer(struct wl_client *client, struct wl_drag *drag, const char *type)
{
struct wl_display *display = wl_client_get_display (client);
struct wlsc_input_device *device =
(struct wlsc_input_device *) drag->input_device;
char **p;
if (drag->source == NULL ||
drag->source->client != client ||
device->grab != WLSC_DEVICE_GRAB_MOTION ||
&device->pointer_focus->base != drag->source ||
device->grab_time != drag->time)
return;
p = wl_array_add(&drag->types, sizeof *p);
if (p)
*p = strdup(type);
@ -1097,25 +1146,36 @@ drag_offer(struct wl_client *client, struct wl_drag *drag, const char *type)
static void
drag_activate(struct wl_client *client,
struct wl_drag *drag)
struct wl_drag *drag,
struct wl_surface *surface,
struct wl_input_device *input_device, uint32_t time)
{
struct wl_display *display = wl_client_get_display (client);
struct wlsc_input_device *device =
(struct wlsc_input_device *) drag->input_device;
struct wlsc_surface *surface;
(struct wlsc_input_device *) input_device;
struct wlsc_surface *target;
int32_t sx, sy;
if (drag->source == NULL ||
drag->source->client != client ||
device->grab != WLSC_DEVICE_GRAB_MOTION ||
&device->pointer_focus->base != drag->source ||
device->grab_time != drag->time)
if (device->grab != WLSC_DEVICE_GRAB_MOTION ||
&device->pointer_focus->base != surface ||
device->grab_time != time)
return;
wlsc_input_device_start_grab(device, drag->time,
drag->source = surface;
drag->input_device = input_device;
drag->drag_offer.base.interface = &wl_drag_offer_interface;
drag->drag_offer.base.implementation =
(void (**)(void)) &drag_offer_interface;
wl_display_add_object(display, &drag->drag_offer.base);
wlsc_input_device_start_grab(device, time,
WLSC_DEVICE_GRAB_DRAG);
surface = pick_surface(device, &sx, &sy);
wl_drag_set_pointer_focus(&device->drag, surface, drag->time,
device->drag = drag;
target = pick_surface(device, &sx, &sy);
wl_drag_set_pointer_focus(device->drag, target, time,
device->x, device->y, sx, sy);
}
@ -1125,102 +1185,19 @@ drag_cancel(struct wl_client *client, struct wl_drag *drag)
struct wlsc_input_device *device =
(struct wlsc_input_device *) drag->input_device;
if (drag->source == NULL ||
drag->source->client != client ||
device->grab != WLSC_DEVICE_GRAB_DRAG)
if (drag->source == NULL || drag->source->client != client)
return;
wlsc_input_device_end_grab(device, get_time());
}
static void
drag_accept(struct wl_client *client,
struct wl_drag *drag, const char *type)
{
char **p, **end;
/* If the client responds to drag motion events after the
* pointer has left the surface, we just discard the accept
* requests. The drag source just won't get the corresponding
* 'target' events and the next surface/root will start
* sending events. */
if (drag->pointer_focus == NULL)
return;
/* The accept request may arrive after the pointer has left
* the surface that received the drag motion events that
* triggered the request. But if the pointer re-enters the
* surface (or another surface from the same client) and we
* receive the 'accept' requests from the first visit to the
* surface later, this check will pass. Then we end up
* sending the 'target' event for the old 'accept' requests
* events *after* potentially sending 'target' from the root
* surface, out of order. So we need to track the time of
* pointer_focus and this request needs a time stamp so we can
* compare and reject 'accept' requests that are older than
* the pointer_focus in timestamp. */
if (drag->pointer_focus->client != client)
return;
/* FIXME: We need a serial number here to correlate the accept
* request with a pointer_focus/motion event. */
drag->target = client;
if (type == NULL) {
drag->type = NULL;
} else {
end = drag->types.data + drag->types.size;
for (p = drag->types.data; p < end; p++)
if (strcmp(*p, type) == 0)
drag->type = *p;
}
wl_surface_post_event(drag->source, &drag->base,
WL_DRAG_TARGET, drag->type);
}
static void
drag_receive(struct wl_client *client,
struct wl_drag *drag, int fd)
{
wl_surface_post_event(drag->source, &drag->base, WL_DRAG_FINISH, fd);
close(fd);
device->drag = NULL;
}
static const struct wl_drag_interface drag_interface = {
drag_prepare,
drag_offer,
drag_activate,
drag_cancel,
drag_accept,
drag_receive
};
static void
wl_drag_post_device(struct wl_client *client, struct wl_object *global)
{
struct wl_drag *drag = container_of(global, struct wl_drag, base);
wl_client_post_event(client, global,
WL_DRAG_DEVICE, drag->input_device);
}
static void
wl_drag_init(struct wl_drag *drag,
struct wl_display *display, struct wl_input_device *input_device)
{
drag->base.interface = &wl_drag_interface;
drag->base.implementation = (void (**)(void))
&drag_interface;
wl_display_add_object(display, &drag->base);
wl_display_add_global(display, &drag->base, wl_drag_post_device);
drag->source = NULL;
wl_array_init(&drag->types);
drag->input_device = input_device;
}
void
wlsc_input_device_init(struct wlsc_input_device *device,
struct wlsc_compositor *ec)
@ -1231,8 +1208,6 @@ wlsc_input_device_init(struct wlsc_input_device *device,
wl_display_add_object(ec->wl_display, &device->base.base);
wl_display_add_global(ec->wl_display, &device->base.base, NULL);
wl_drag_init(&device->drag, ec->wl_display, &device->base);
device->x = 100;
device->y = 100;
device->ec = ec;

@ -107,7 +107,7 @@ struct wlsc_input_device {
int32_t grab_width, grab_height;
int32_t grab_dx, grab_dy;
uint32_t grab_button;
struct wl_drag drag;
struct wl_drag *drag;
struct wlsc_listener listener;
};

@ -66,7 +66,7 @@
</interface>
<interface name="buffer" version="1">
<request name="destroy"/>
<request name="destroy" type="destructor"/>
</interface>
<interface name="shell" version="1">
@ -84,6 +84,10 @@
<arg name="edges" type="uint"/>
</request>
<request name="create_drag">
<arg name="id" type="new_id" interface="drag"/>
</request>
<event name="configure">
<arg name="time" type="uint"/>
<!-- Same as edges except also move (16) -->
@ -97,46 +101,65 @@
</interface>
<interface name="drag" version="1">
<request name="prepare">
<!-- Start a drag action from given surface and device for the
grab started by the button click at time -->
<arg name="surface" type="object" interface="surface"/>
<arg name="time" type="uint"/>
</request>
<!-- Add an offered mime type. Can be called several times to
offer multiple types, but must be called before 'activate'. -->
<request name="offer">
<arg name="type" type="string"/>
</request>
<request name="activate"/>
<request name="activate">
<arg name="surface" type="object" interface="surface"/>
<arg name="input_device" type="object" interface="input_device"/>
<arg name="time" type="uint"/>
</request>
<!-- Cancel the drag. -->
<request name="cancel"/>
<!-- Destroy the drag and cancel the session. -->
<request name="destroy" type="destructor"/>
<!-- Sent when a target accepts pointer_focus or motion events.
If a target does not accept any of the offered types, type is
NULL -->
<event name="target">
<arg name="mime_type" type="string"/>
</event>
<!-- Sent when the drag is finished. The final mime type is that
of the last target event. If that was NULL, no drag target
was valid when the drag finished, fd is undefined and the
source should not send data. The event is also sent in case
a drag source tries to activate a drag after the grab was
released, in which case mime_type will also be NULL. -->
<event name="finish">
<arg name="fd" type="fd"/>
</event>
</interface>
<!-- Called by the drag target to accept the offer of the given
type -->
<interface name="drag_offer" version="1">
<!-- Call to accept the offer of the given type -->
<request name="accept">
<arg name="time" type="uint"/>
<arg name="type" type="string"/>
</request>
<!-- Called by the drag target to initiate the drag finish
sequence. Send the pipe fd to the compositor, which forwards
it to the source in the 'finish' event -->
<!-- Called to initiate the drag finish sequence. Sends the pipe
fd to the compositor, which forwards it to the source in the
'finish' event -->
<request name="receive">
<arg name="fd" type="fd"/>
</request>
<!-- Sent at connect time to announce the association -->
<event name="device">
<arg name="device" type="object" interface="input_device"/>
<!-- Sent before the pointer_focus event to announce the types
offered. One event per offered mime type. -->
<event name="offer">
<arg name="type" type="string"/>
</event>
<!-- Similar to device::pointer_focus. Sent to potential
target surfaces to offer drag data. If the device
leaves the window, the drag stops or the originator cancels
the drag, this event is sent with the NULL surface. -->
<!-- Similar to device::pointer_focus. Sent to potential target
surfaces to offer drag data. If the device leaves the
window, the drag stops or the originator cancels the drag,
this event is sent with the NULL surface, at which point the
drag object may no longer be valid. -->
<event name="pointer_focus">
<arg name="time" type="uint"/>
<arg name="surface" type="object" interface="surface"/>
@ -146,12 +169,6 @@
<arg name="surface_y" type="int"/>
</event>
<!-- Sent after the pointer_focus event to announce the types
offered. One event per offered mime type. -->
<event name="offer">
<arg name="type" type="string"/>
</event>
<!-- Similar to device::motion. Sent to potential target surfaces
as the drag pointer moves around in the surface. -->
<event name="motion">
@ -162,34 +179,15 @@
<arg name="surface_y" type="int"/>
</event>
<!-- Sent to drag originator in response to pointer_focus and
motion events. If a target does not accept any of the
offered types, type is NULL -->
<event name="target">
<arg name="mime_type" type="string"/>
</event>
<!-- Sent to target, to indicate that the drag is finishing. The
last motion/pointer_focus event gives the location of the
drop. Target must respond with the 'receive' request, which
sends an fd to the source for writing the drag data. -->
<!-- Sent to indicate that the drag is finishing. The last
motion/pointer_focus event gives the location of the drop.
Target must respond with the 'receive' request, which sends
an fd to the source for writing the drag data. -->
<event name="drop"/>
<!-- Sent to drag source when the drag is finished. The final
mime type is that of the last target event. If that was
NULL, no drag target was valid when the drag finished, fd is
undefined and the source should not send data. The event is
also sent in case a drag source tries to activate a drag
after the grab was released, in which case mime_type will
also be NULL. -->
<event name="finish">
<arg name="fd" type="fd"/>
</event>
</interface>
<interface name="surface" version="1">
<request name="destroy"/>
<request name="destroy" type="destructor"/>
<request name="attach">
<arg name="buffer" type="object" interface="buffer"/>

@ -74,6 +74,7 @@ struct message {
char *uppercase_name;
struct wl_list arg_list;
struct wl_list link;
int destructor;
};
enum arg_type {
@ -177,6 +178,11 @@ start_element(void *data, const char *element_name, const char **atts)
wl_list_insert(ctx->interface->event_list.prev,
&message->link);
if (type != NULL && strcmp(type, "destructor") == 0)
message->destructor = 1;
else
message->destructor = 0;
ctx->message = message;
} else if (strcmp(element_name, "arg") == 0) {
arg = malloc(sizeof *arg);
@ -263,6 +269,7 @@ emit_stubs(struct wl_list *message_list, struct interface *interface)
{
struct message *m;
struct arg *a, *ret;
int has_destructor, has_destroy;
/* We provide a hand written constructor for the display object */
if (strcmp(interface->name, "display") != 0)
@ -293,6 +300,33 @@ emit_stubs(struct wl_list *message_list, struct interface *interface)
interface->name, interface->name, interface->name,
interface->name);
has_destructor = 0;
has_destroy = 0;
wl_list_for_each(m, message_list, link) {
if (m->destructor)
has_destructor = 1;
if (strcmp(m->name, "destroy)") == 0)
has_destroy = 1;
}
if (!has_destructor && has_destroy) {
fprintf(stderr,
"interface %s has method named destroy but"
"no destructor", interface->name);
exit(EXIT_FAILURE);
}
/* And we have a hand-written display destructor */
if (!has_destructor && strcmp(interface->name, "display") != 0)
printf("static inline void\n"
"wl_%s_destroy(struct wl_%s *%s)\n"
"{\n"
"\twl_proxy_destroy("
"(struct wl_proxy *) %s);\n"
"}\n\n",
interface->name, interface->name, interface->name,
interface->name);
if (wl_list_empty(message_list))
return;
@ -347,6 +381,11 @@ emit_stubs(struct wl_list *message_list, struct interface *interface)
}
printf(");\n");
if (m->destructor)
printf("\n\twl_proxy_destroy("
"(struct wl_proxy *) %s);\n",
interface->name);
if (ret)
printf("\n\treturn (struct wl_%s *) %s;\n",
ret->interface_name, ret->name);
@ -442,6 +481,9 @@ static const char client_prototypes[] =
"wl_proxy_create_for_id(struct wl_display *display,\n"
"\t\t const struct wl_interface *interface, uint32_t id);\n"
"extern void\n"
"wl_proxy_destroy(struct wl_proxy *proxy);\n\n"
"extern int\n"
"wl_proxy_add_listener(struct wl_proxy *proxy,\n"
"\t\t void (**implementation)(void), void *data);\n\n"

@ -146,6 +146,18 @@ wl_proxy_create(struct wl_proxy *factory,
wl_display_allocate_id(factory->display));
}
WL_EXPORT void
wl_proxy_destroy(struct wl_proxy *proxy)
{
struct wl_listener *listener, *next;
wl_list_for_each_safe(listener, next, &proxy->listener_list, link)
free(listener);
wl_hash_table_remove(proxy->display->objects, proxy->base.id);
free(proxy);
}
WL_EXPORT int
wl_proxy_add_listener(struct wl_proxy *proxy,
void (**implementation)(void), void *data)

@ -124,8 +124,13 @@ struct wl_visual {
struct wl_object base;
};
struct wl_drag {
struct wl_drag_offer {
struct wl_object base;
};
struct wl_drag {
struct wl_resource resource;
struct wl_drag_offer drag_offer;
struct wl_surface *source;
struct wl_surface *pointer_focus;
struct wl_client *target;
@ -133,7 +138,7 @@ struct wl_drag {
struct wl_input_device *input_device;
struct wl_array types;
const char *type;
uint32_t time;
uint32_t pointer_focus_time;
};
void

Loading…
Cancel
Save