First step towards drag and drop protocol

This commit is contained in:
Kristian Høgsberg 2010-08-17 21:23:10 -04:00
parent 084d41aac1
commit eef08fbb1a
11 changed files with 444 additions and 46 deletions

47
TODO
View file

@ -1,7 +1,7 @@
Core wayland protocol
- generate pointer_focus on raise/lower, move windows, all kinds of
changes in surface stacking.
- generate pointer_focus (and drag focus) on raise/lower, move
windows, all kinds of changes in surface stacking.
- glyph cache
@ -9,10 +9,53 @@ Core wayland protocol
pass an fd through the compositor to the other client and let them
sort it out?
- DnD issues:
How to roboustly handle failing drag, ie the case where an
application gets a button event, tries to activate a drag, but when
the server gets the drag request, the button has already been
released and the grab is no longer active. What's the concern:
- Application may set a drag cursor that doesn't revert back,
since a failed drag doesn't result in a pointer_focus event to
give focus back to the surface. We could just do that: if the
pointer_focus is the same surface as we tried to start a grab
for, just remove and give back pointer_focus.
Alternatively, set drag cursors only in response to drag events,
like drag focus. But drag_focus and drag_motion are sent to the
drag target, so the source surface won't always get those. We
may also end up setting the cursor after the drag ends, but in
this case the drag started and ended and we'll get a
pointer_focus event, which will make the application reset the
pointer image. Could introduce a drag start event that
indicates that the drag active.
How to handle drop decline (accept with type=NULL)
- Targets must send a NULL type in accept if they don't accept a
drop at the drag_focus/drag_motion position. Root window will
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?
- copy-n-paste, store data in server (only one mime-type available)
or do X style (content mime-type negotiation, but data goes away
when client quits).
- Optional pointer images.
- Discard buffer, as in "wayland discarded your buffer, it's no
longer visible, you can stop updating it now.", reattach, as in "oh
hey, I'm about to show your buffer that I threw away, what was it

View file

@ -239,15 +239,17 @@ create_pointer_images(struct wlsc_compositor *ec)
image_attribs[1] = width;
image_attribs[3] = height;
count = ARRAY_LENGTH(pointer_images);
ec->pointer_images = malloc(count * sizeof *ec->pointer_images);
ec->pointer_buffers = malloc(count * sizeof *ec->pointer_buffers);
for (i = 0; i < count; i++) {
ec->pointer_images[i] =
ec->pointer_buffers[i].image =
eglCreateDRMImageMESA(ec->display, image_attribs);
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D,
ec->pointer_images[i]);
ec->pointer_buffers[i].image);
texture_from_png(pointer_images[i].filename, width, height);
ec->pointer_buffers[i].visual = &ec->argb_visual;
ec->pointer_buffers[i].width = width;
ec->pointer_buffers[i].height = height;
}
}
static struct wlsc_surface *
@ -457,29 +459,43 @@ const static struct wl_surface_interface surface_interface = {
};
static void
wlsc_input_device_set_pointer_image(struct wlsc_input_device *device,
enum wlsc_pointer_type type)
wlsc_input_device_attach(struct wlsc_input_device *device,
struct wlsc_buffer *buffer, int x, int y)
{
struct wlsc_compositor *ec = device->ec;
glBindTexture(GL_TEXTURE_2D, device->sprite->texture);
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, ec->pointer_images[type]);
device->sprite->visual = &ec->argb_visual;
device->hotspot_x = pointer_images[type].hotspot_x;
device->hotspot_y = pointer_images[type].hotspot_y;
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, buffer->image);
device->sprite->visual = buffer->visual;
device->hotspot_x = x;
device->hotspot_y = y;
device->sprite->x = device->x - device->hotspot_x;
device->sprite->y = device->y - device->hotspot_y;
device->sprite->width = buffer->width;
device->sprite->height = buffer->height;
wlsc_surface_update_matrix(device->sprite);
wlsc_compositor_schedule_repaint(ec);
}
static void
wlsc_input_device_set_pointer_image(struct wlsc_input_device *device,
enum wlsc_pointer_type type)
{
struct wlsc_compositor *compositor = device->ec;
wlsc_input_device_attach(device,
&compositor->pointer_buffers[type],
pointer_images[type].hotspot_x,
pointer_images[type].hotspot_y);
}
static void
wlsc_input_device_start_grab(struct wlsc_input_device *device,
uint32_t time,
enum wlsc_grab_type grab,
enum wlsc_pointer_type pointer)
enum wlsc_grab_type grab)
{
device->grab = grab;
device->grab_surface = device->pointer_focus;
@ -491,8 +507,6 @@ wlsc_input_device_start_grab(struct wlsc_input_device *device,
wlsc_input_device_set_pointer_focus(device,
(struct wlsc_surface *) &wl_grab_surface,
time, 0, 0, 0, 0);
wlsc_input_device_set_pointer_image(device, pointer);
}
static void
@ -507,9 +521,8 @@ shell_move(struct wl_client *client, struct wl_shell *shell,
&wd->pointer_focus->base != surface)
return;
wlsc_input_device_start_grab(wd, time,
WLSC_DEVICE_GRAB_MOVE,
WLSC_POINTER_DRAGGING);
wlsc_input_device_start_grab(wd, time, WLSC_DEVICE_GRAB_MOVE);
wlsc_input_device_set_pointer_image(wd, WLSC_POINTER_DRAGGING);
}
static void
@ -556,7 +569,8 @@ shell_resize(struct wl_client *client, struct wl_shell *shell,
break;
}
wlsc_input_device_start_grab(wd, time, edges, pointer);
wlsc_input_device_start_grab(wd, time, edges);
wlsc_input_device_set_pointer_image(wd, pointer);
}
const static struct wl_shell_interface shell_interface = {
@ -681,6 +695,13 @@ 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,
int32_t x, int32_t y, int32_t sx, int32_t sy);
void
notify_motion(struct wlsc_input_device *device, uint32_t time, int x, int y)
{
@ -773,6 +794,17 @@ notify_motion(struct wlsc_input_device *device, uint32_t time, int x, int y)
WL_SHELL_CONFIGURE, time, device->grab,
&es->base, sx, sy, width, height);
break;
case WLSC_DEVICE_GRAB_DRAG:
es = pick_surface(device, &sx, &sy);
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,
time, x, y, sx, sy);
break;
}
device->sprite->x = device->x - device->hotspot_x;
@ -785,9 +817,21 @@ 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 wlsc_surface *es;
int32_t sx, sy;
switch (device->grab) {
case WLSC_DEVICE_GRAB_DRAG:
wl_drag_set_pointer_focus(drag, NULL, time, 0, 0, 0, 0);
wl_surface_post_event(drag->source, &drag->base,
WL_DRAG_FINISH);
wl_drag_reset(drag);
break;
default:
break;
}
device->grab = WLSC_DEVICE_GRAB_NONE;
es = pick_surface(device, &sx, &sy);
wlsc_input_device_set_pointer_focus(device, es, time,
@ -836,7 +880,6 @@ notify_button(struct wlsc_input_device *device,
if (!state &&
device->grab != WLSC_DEVICE_GRAB_NONE &&
device->grab_button == button) {
device->grab = WLSC_DEVICE_GRAB_NONE;
wlsc_input_device_end_grab(device, time);
}
@ -919,17 +962,7 @@ input_device_attach(struct wl_client *client,
return;
}
glBindTexture(GL_TEXTURE_2D, device->sprite->texture);
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, buffer->image);
device->sprite->visual = buffer->visual;
device->hotspot_x = x;
device->hotspot_y = y;
device->sprite->x = device->x - device->hotspot_x;
device->sprite->y = device->y - device->hotspot_y;
wlsc_surface_update_matrix(device->sprite);
wlsc_compositor_schedule_repaint(device->ec);
wlsc_input_device_attach(device, buffer, x, y);
}
const static struct wl_input_device_interface input_device_interface = {
@ -962,6 +995,194 @@ handle_surface_destroy(struct wlsc_listener *listener,
wlsc_input_device_end_grab(device, time);
}
static void
wl_drag_set_pointer_focus(struct wl_drag *drag,
struct wlsc_surface *surface, uint32_t time,
int32_t x, int32_t y, int32_t sx, int32_t sy)
{
if (drag->pointer_focus == &surface->base)
return;
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,
time, NULL, 0, 0, 0, 0);
if (surface)
wl_surface_post_event(&surface->base,
&drag->base,
WL_DRAG_POINTER_FOCUS,
time, &surface->base,
x, y, sx, sy);
drag->pointer_focus = &surface->base;
}
static void
wl_drag_reset(struct wl_drag *drag)
{
char **p, **end;
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);
drag->source = NULL;
drag->target = NULL;
drag->time = 0;
drag->pointer_focus = NULL;
}
static void
drag_prepare(struct wl_client *client,
struct wl_drag *drag, struct wl_surface *surface, uint32_t time,
struct wl_buffer *buffer, int32_t hotspot_x, int32_t hotspot_y)
{
struct wlsc_input_device *device =
(struct wlsc_input_device *) drag->input_device;
if (&device->pointer_focus->base != surface ||
device->grab_time != time)
return;
wl_drag_reset(drag);
drag->source = surface;
drag->time = time;
drag->buffer = buffer;
drag->hotspot_x = hotspot_x;
drag->hotspot_y = hotspot_y;
}
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);
if (!p || !*p)
wl_client_post_event(client,
(struct wl_object *) display,
WL_DISPLAY_NO_MEMORY);
}
static void
drag_activate(struct wl_client *client,
struct wl_drag *drag)
{
struct wlsc_input_device *device =
(struct wlsc_input_device *) drag->input_device;
struct wlsc_surface *surface;
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)
return;
wlsc_input_device_start_grab(device, drag->time,
WLSC_DEVICE_GRAB_DRAG);
wlsc_input_device_attach(device, (struct wlsc_buffer *) drag->buffer,
drag->hotspot_x, drag->hotspot_y);
surface = pick_surface(device, &sx, &sy);
wl_drag_set_pointer_focus(&device->drag, surface, drag->time,
device->x, device->y, sx, sy);
}
static void
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)
return;
wlsc_input_device_end_grab(device, get_time());
}
static void
drag_send(struct wl_client *client,
struct wl_drag *drag, struct wl_array *contents)
{
wl_client_post_event(client, &drag->base, WL_DRAG_DROP, contents);
}
static void
drag_accept(struct wl_client *client,
struct wl_drag *drag, const char *type)
{
char **p, **end;
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;
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 const struct wl_drag_interface drag_interface = {
drag_prepare,
drag_offer,
drag_activate,
drag_cancel,
drag_send,
drag_accept
};
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)
@ -972,6 +1193,8 @@ 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;

View file

@ -69,7 +69,8 @@ enum wlsc_grab_type {
WLSC_DEVICE_GRAB_RESIZE_BOTTOM_RIGHT = 10,
WLSC_DEVICE_GRAB_RESIZE_MASK = 15,
WLSC_DEVICE_GRAB_MOVE = 16,
WLSC_DEVICE_GRAB_MOTION = 17
WLSC_DEVICE_GRAB_MOTION = 17,
WLSC_DEVICE_GRAB_DRAG = 18
};
enum wlsc_pointer_type {
@ -106,6 +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 wlsc_listener listener;
};
@ -118,6 +120,7 @@ struct wlsc_drm {
struct wlsc_buffer {
struct wl_buffer base;
int32_t width, height;
EGLImageKHR image;
struct wl_visual *visual;
};
@ -131,7 +134,7 @@ struct wlsc_compositor {
EGLContext context;
GLuint fbo, vbo;
GLuint proj_uniform, tex_uniform;
EGLImageKHR *pointer_images;
struct wlsc_buffer *pointer_buffers;
struct wl_display *wl_display;
/* We implement the shell interface. */

View file

@ -11,8 +11,8 @@ cursor_images = \
top_left_corner.png \
top_right_corner.png \
top_side.png \
xterm.png
xterm.png \
hand1.png
all :

BIN
data/hand1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -96,6 +96,94 @@
</event>
</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"/>
<arg name="buffer" type="object" interface="buffer"/>
<arg name="hotspot_x" type="int"/>
<arg name="hotspot_y" type="int"/>
</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"/>
<!-- Cancel the drag. -->
<request name="cancel"/>
<!-- Send the data to the target that accepted the offer -->
<request name="send">
<arg name="contents" type="array"/>
</request>
<!-- Called by the drag target to accept the offer of the given
type -->
<request name="accept">
<arg name="type" type="string"/>
</request>
<!-- Sent at connect time to announce the association -->
<event name="device">
<arg name="device" type="object" interface="input_device"/>
</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. -->
<event name="pointer_focus">
<arg name="time" type="uint"/>
<arg name="surface" type="object" interface="surface"/>
<arg name="x" type="int"/>
<arg name="y" type="int"/>
<arg name="surface_x" type="int"/>
<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">
<arg name="time" type="uint"/>
<arg name="x" type="int"/>
<arg name="y" type="int"/>
<arg name="surface_x" type="int"/>
<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 drag originator when the drag is finished. It's also
sent in case an originator tries to activate a drag after the
grab was released. If the originator didn't receive a
'target' event before receiving the 'finish' event, no drag
target was found and the originator should not send data. -->
<event name="finish"/>
<!-- Sent to target, contains dragged data. Ends transaction on
the target side. -->
<event name="drop">
<arg name="contents" type="array"/>
</event>
</interface>
<interface name="surface" version="1">
<request name="destroy"/>

View file

@ -273,6 +273,22 @@ emit_stubs(struct wl_list *message_list, struct interface *interface)
interface->name,
interface->name);
printf("static inline void\n"
"wl_%s_set_user_data(struct wl_%s *%s, void *user_data)\n"
"{\n"
"\twl_proxy_set_user_data((struct wl_proxy *) %s, user_data);\n"
"}\n\n",
interface->name, interface->name, interface->name,
interface->name);
printf("static inline void *\n"
"wl_%s_get_user_data(struct wl_%s *%s)\n"
"{\n"
"\treturn wl_proxy_get_user_data((struct wl_proxy *) %s);\n"
"}\n\n",
interface->name, interface->name, interface->name,
interface->name);
if (wl_list_empty(message_list))
return;
@ -424,7 +440,14 @@ static const char client_prototypes[] =
"extern int\n"
"wl_proxy_add_listener(struct wl_proxy *proxy,\n"
"\t\t void (**implementation)(void), void *data);\n\n";
"\t\t void (**implementation)(void), void *data);\n\n"
"extern void\n"
"wl_proxy_set_user_data(struct wl_proxy *proxy, void *user_data);\n\n"
"extern void *\n"
"wl_proxy_get_user_data(struct wl_proxy *proxy);\n\n";
static void
emit_header(struct protocol *protocol, int server)

View file

@ -402,17 +402,13 @@ wl_display_allocate_id(struct wl_display *display)
}
WL_EXPORT void
wl_surface_set_user_data(struct wl_surface *surface, void *user_data)
wl_proxy_set_user_data(struct wl_proxy *proxy, void *user_data)
{
struct wl_proxy *proxy = (struct wl_proxy *) surface;
proxy->user_data = user_data;
}
WL_EXPORT void *
wl_surface_get_user_data(struct wl_surface *surface)
wl_proxy_get_user_data(struct wl_proxy *proxy)
{
struct wl_proxy *proxy = (struct wl_proxy *) surface;
return proxy->user_data;
}

View file

@ -62,9 +62,6 @@ wl_display_get_premultiplied_argb_visual(struct wl_display *display);
struct wl_visual *
wl_display_get_rgb_visual(struct wl_display *display);
void wl_surface_set_user_data(struct wl_surface *surface, void *user_data);
void *wl_surface_get_user_data(struct wl_surface *surface);
#ifdef __cplusplus
}
#endif

View file

@ -161,6 +161,12 @@ wl_client_connection_update(struct wl_connection *connection,
return wl_event_source_fd_update(client->source, mask);
}
WL_EXPORT struct wl_display *
wl_client_get_display(struct wl_client *client)
{
return client->display;
}
static void
wl_display_post_range(struct wl_display *display, struct wl_client *client)
{

View file

@ -124,6 +124,22 @@ struct wl_visual {
struct wl_object base;
};
struct wl_drag {
struct wl_object base;
struct wl_surface *source;
struct wl_surface *pointer_focus;
struct wl_client *target;
int32_t x, y, sx, sy;
struct wl_input_device *input_device;
struct wl_array types;
const char *type;
uint32_t time;
struct wl_buffer *buffer;
int32_t hotspot_x;
int32_t hotspot_y;
};
void
wl_client_post_event(struct wl_client *client,
struct wl_object *sender,
@ -159,6 +175,9 @@ void
wl_client_add_resource(struct wl_client *client,
struct wl_resource *resource);
struct wl_display *
wl_client_get_display(struct wl_client *client);
void
wl_resource_destroy(struct wl_resource *resource, struct wl_client *client);