From eef08fbb1a32d206c32608c95fe8a80db0fbc081 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20H=C3=B8gsberg?= Date: Tue, 17 Aug 2010 21:23:10 -0400 Subject: [PATCH] First step towards drag and drop protocol --- TODO | 47 +++++++- compositor.c | 283 ++++++++++++++++++++++++++++++++++++++++++----- compositor.h | 7 +- data/Makefile | 4 +- data/hand1.png | Bin 0 -> 1117 bytes protocol.xml | 88 +++++++++++++++ scanner.c | 25 ++++- wayland-client.c | 8 +- wayland-client.h | 3 - wayland-server.c | 6 + wayland-server.h | 19 ++++ 11 files changed, 444 insertions(+), 46 deletions(-) create mode 100644 data/hand1.png diff --git a/TODO b/TODO index 820288ae..5cb6ab4b 100644 --- a/TODO +++ b/TODO @@ -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 diff --git a/compositor.c b/compositor.c index 5a2bada3..93aad964 100644 --- a/compositor.c +++ b/compositor.c @@ -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; diff --git a/compositor.h b/compositor.h index 315cb36a..44fc1a8d 100644 --- a/compositor.h +++ b/compositor.h @@ -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. */ diff --git a/data/Makefile b/data/Makefile index 3a44a1b8..bc57d232 100644 --- a/data/Makefile +++ b/data/Makefile @@ -11,8 +11,8 @@ cursor_images = \ top_left_corner.png \ top_right_corner.png \ top_side.png \ - xterm.png - + xterm.png \ + hand1.png all : diff --git a/data/hand1.png b/data/hand1.png new file mode 100644 index 0000000000000000000000000000000000000000..c13aa36b34f4249bac44e5e6c850a7b7bbce9e0d GIT binary patch literal 1117 zcmV-j1fu(iP)8exT{OVNK86lP*_fP@gjUj$W^Rjq&!LIs`yD)5o2s-Mr!&iW#e2=nvvALsM=Ebt@nj}YQl z;BTr?MM`;R1K#)h{jUI7Xj4;DvS5>wlXmg5Kp^lNkOG2$xGi?ITkvTznT#DB9g)do zn3Xnv$KLL<}R0vJgr$AbkB>~aY)I>{5i&Z=}7z~z16{6%+)YsR0guQ8ANoY9$05mo>y5z^l z$K-N3&d$zUKby^Rad82_>FFtN-n=25PCp31wk*qa8X6jua5xNrs;a1}%KrX7wrvv* zhtV{R-Q8W%>9qTf*xuf*3cza%Y|}Jd1(uhWT?JNFR*+KC*Vl)lC@d{4;q&Xw)&ub7-yamerYZ%8SJdV71NuIp~nIC+dzU2EtJ3Ks;mzS49O8GVL z3-AYUE~UJx2;e~Yfh6z+@B!DCNZ}|JN-xTK>G3QL`~C>C%#BOw9x#>zkttsMRsaX0Pz~Ly*7pJ` jF2$+>_%H0q{~P`bh2jP=IyL|`00000NkvXXu0mjfGgkKN literal 0 HcmV?d00001 diff --git a/protocol.xml b/protocol.xml index d8c644c3..9abeffa6 100644 --- a/protocol.xml +++ b/protocol.xml @@ -96,6 +96,94 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/scanner.c b/scanner.c index 2ce88afd..0fa5079e 100644 --- a/scanner.c +++ b/scanner.c @@ -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) diff --git a/wayland-client.c b/wayland-client.c index 44358681..c79cc651 100644 --- a/wayland-client.c +++ b/wayland-client.c @@ -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; } diff --git a/wayland-client.h b/wayland-client.h index 1a49fd79..68e59367 100644 --- a/wayland-client.h +++ b/wayland-client.h @@ -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 diff --git a/wayland-server.c b/wayland-server.c index fe24f3ff..196e67f8 100644 --- a/wayland-server.c +++ b/wayland-server.c @@ -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) { diff --git a/wayland-server.h b/wayland-server.h index a871d5ae..9b7cef33 100644 --- a/wayland-server.h +++ b/wayland-server.h @@ -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);