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 00000000..c13aa36b
Binary files /dev/null and b/data/hand1.png differ
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);