Add protocol for setting the pointer image
diff --git a/TODO b/TODO
index 77a7bdd..820288a 100644
--- a/TODO
+++ b/TODO
@@ -13,17 +13,6 @@
    or do X style (content mime-type negotiation, but data goes away
    when client quits).
 
- - protocol for setting the cursor image
-
-    - should we have a mechanism to attach surface to cursor for
-      guaranteed non-laggy drag?
-
-    - drawing cursors, moving them, cursor themes, attaching surfaces
-      to cursors.  How do you change cursors when you mouse over a
-      text field if you don't have subwindows?  This is what we do: a
-      client can set a cursor for a surface and wayland will set that
-      on enter and revert to default on leave
-
  - 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 454d81e..0ecbb7d 100644
--- a/compositor.c
+++ b/compositor.c
@@ -125,12 +125,24 @@
 	*v = t;
 }
 
-static void
-wlsc_surface_init(struct wlsc_surface *surface,
-		  struct wlsc_compositor *compositor, struct wl_visual *visual,
-		  int32_t x, int32_t y, int32_t width, int32_t height)
+static struct wlsc_surface *
+wlsc_surface_create(struct wlsc_compositor *compositor,
+		    struct wl_visual *visual,
+		    int32_t x, int32_t y, int32_t width, int32_t height)
 {
+	struct wlsc_surface *surface;
+
+	surface = malloc(sizeof *surface);
+	if (surface == NULL)
+		return NULL;
+
 	glGenTextures(1, &surface->texture);
+	glBindTexture(GL_TEXTURE_2D, surface->texture);
+	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
 	surface->compositor = compositor;
 	surface->visual = visual;
 	wlsc_matrix_init(&surface->matrix);
@@ -140,35 +152,8 @@
 	wlsc_matrix_init(&surface->matrix_inv);
 	wlsc_matrix_translate(&surface->matrix_inv, -x, -y, 0);
 	wlsc_matrix_scale(&surface->matrix_inv, 1.0 / width, 1.0 / height, 1);
-}
 
-static struct wlsc_surface *
-wlsc_surface_create_from_cairo_surface(struct wlsc_compositor *ec,
-				      cairo_surface_t *surface,
-				      int x, int y, int width, int height)
-{
-	struct wlsc_surface *es;
-	int stride;
-	void *data;
-
-	stride = cairo_image_surface_get_stride(surface);
-	data = cairo_image_surface_get_data(surface);
-
-	es = malloc(sizeof *es);
-	if (es == NULL)
-		return NULL;
-
-	wlsc_surface_init(es, ec, &ec->premultiplied_argb_visual,
-			  x, y, width, height);
-	glBindTexture(GL_TEXTURE_2D, es->texture);
-	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
-	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0,
-		     GL_RGBA, GL_UNSIGNED_BYTE, data);
-
-	return es;
+	return surface;
 }
 
 static void
@@ -213,6 +198,16 @@
 	const int hotspot_x = 16, hotspot_y = 16;
 	cairo_surface_t *surface;
 	cairo_t *cr;
+	int stride;
+	void *data;
+
+	EGLint image_attribs[] = {
+		EGL_WIDTH,		0,
+		EGL_HEIGHT,		0,
+		EGL_IMAGE_FORMAT_MESA,	EGL_IMAGE_FORMAT_ARGB8888_MESA,
+		EGL_IMAGE_USE_MESA,	EGL_IMAGE_USE_SCANOUT_MESA,
+		EGL_NONE
+	};
 
 	surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
 					     width, height);
@@ -231,17 +226,37 @@
 	cairo_fill(cr);
 	cairo_destroy(cr);
 
-	es = wlsc_surface_create_from_cairo_surface(ec,
-						   surface,
-						   x - hotspot_x,
-						   y - hotspot_y,
-						   width, height);
-	
+	es = wlsc_surface_create(ec, &ec->premultiplied_argb_visual,
+				 x, y, width, height);
+
+	stride = cairo_image_surface_get_stride(surface);
+	data = cairo_image_surface_get_data(surface);
+
+	image_attribs[1] = width;
+	image_attribs[3] = height;
+	ec->default_pointer_image =
+		eglCreateDRMImageMESA(ec->display, image_attribs);
+	glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, ec->default_pointer_image);
+	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0,
+		     GL_RGBA, GL_UNSIGNED_BYTE, data);
+
 	cairo_surface_destroy(surface);
 
 	return es;
 }
 
+static void
+wlsc_input_device_set_default_pointer_image(struct wlsc_input_device *device)
+{
+	struct wlsc_compositor *ec = device->ec;
+
+	glBindTexture(GL_TEXTURE_2D, device->sprite->texture);
+	glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, ec->default_pointer_image);
+	device->sprite->visual = &ec->premultiplied_argb_visual;
+	device->hotspot_x = 16;
+	device->hotspot_y = 16;
+}
+
 static struct wlsc_surface *
 background_create(struct wlsc_output *output, const char *filename)
 {
@@ -251,11 +266,12 @@
 	void *data;
 	GLenum format;
 
-	background = malloc(sizeof *background);
+	background = wlsc_surface_create(output->compositor,
+					 &output->compositor->rgb_visual,
+					 output->x, output->y,
+					 output->width, output->height);
 	if (background == NULL)
 		return NULL;
-	
-	g_type_init();
 
 	pixbuf = gdk_pixbuf_new_from_file_at_scale(filename,
 						   output->width,
@@ -268,17 +284,7 @@
 
 	data = gdk_pixbuf_get_pixels(pixbuf);
 
-	wlsc_surface_init(background, output->compositor,
-			  &output->compositor->rgb_visual,
-			  output->x, output->y, output->width, output->height);
-
-	glBindTexture(GL_TEXTURE_2D, background->texture);
-	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
-	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-
-        if (gdk_pixbuf_get_has_alpha(pixbuf))
+	if (gdk_pixbuf_get_has_alpha(pixbuf))
 		format = GL_RGBA;
 	else
 		format = GL_RGB;
@@ -432,10 +438,6 @@
 	struct wlsc_buffer *buffer = (struct wlsc_buffer *) buffer_base;
 
 	glBindTexture(GL_TEXTURE_2D, es->texture);
-	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
-	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
 	glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, buffer->image);
 	es->visual = buffer->visual;
 }
@@ -520,7 +522,7 @@
 	struct wlsc_compositor *ec = (struct wlsc_compositor *) compositor;
 	struct wlsc_surface *surface;
 
-	surface = malloc(sizeof *surface);
+	surface = wlsc_surface_create(ec, NULL, 0, 0, 0, 0);
 	if (surface == NULL) {
 		wl_client_post_event(client,
 				     (struct wl_object *) ec->wl_display,
@@ -528,8 +530,6 @@
 		return;
 	}
 
-	wlsc_surface_init(surface, ec, NULL, 0, 0, 0, 0);
-
 	wl_list_insert(ec->surface_list.prev, &surface->link);
 	surface->base.base.destroy = destroy_surface;
 	wl_client_add_surface(client, &surface->base,
@@ -609,6 +609,9 @@
 				      time, &surface->base,
 				      x, y, sx, sy);
 
+	if (!surface)
+		wlsc_input_device_set_default_pointer_image(device);
+
 	device->pointer_focus = surface;
 }
 
@@ -640,7 +643,6 @@
 	struct wlsc_surface *es;
 	struct wlsc_compositor *ec = device->ec;
 	struct wlsc_output *output;
-	const int hotspot_x = 16, hotspot_y = 16;
 	int32_t sx, sy, width, height;
 
 	/* FIXME: We need some multi head love here. */
@@ -727,7 +729,7 @@
 	wlsc_matrix_init(&device->sprite->matrix);
 	wlsc_matrix_scale(&device->sprite->matrix, 64, 64, 1);
 	wlsc_matrix_translate(&device->sprite->matrix,
-			      x - hotspot_x, y - hotspot_y, 0);
+			      x - device->hotspot_x, y - device->hotspot_y, 0);
 
 	wlsc_compositor_schedule_repaint(device->ec);
 }
@@ -829,6 +831,30 @@
 				      WL_INPUT_DEVICE_KEY, time, key, state);
 }
 
+static void
+input_device_attach(struct wl_client *client,
+		    struct wl_input_device *device_base,
+		    struct wl_buffer *buffer_base, int32_t x, int32_t y)
+{
+	struct wlsc_input_device *device =
+		(struct wlsc_input_device *) device_base;
+	struct wlsc_buffer *buffer = (struct wlsc_buffer *) buffer_base;
+
+	if (device->pointer_focus == NULL ||
+	    device->pointer_focus->base.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;
+}
+
+const static struct wl_input_device_interface input_device_interface = {
+	input_device_attach,
+};
+
 static uint32_t
 get_time(void)
 {
@@ -866,7 +892,8 @@
 		       struct wlsc_compositor *ec)
 {
 	device->base.interface = &wl_input_device_interface;
-	device->base.implementation = NULL;
+	device->base.implementation =
+		(void (**)(void)) &input_device_interface;
 	wl_display_add_object(ec->wl_display, &device->base);
 	wl_display_add_global(ec->wl_display, &device->base, NULL);
 
@@ -874,6 +901,8 @@
 	device->y = 100;
 	device->ec = ec;
 	device->sprite = pointer_create(ec, device->x, device->y, 64, 64);
+	device->hotspot_x = 16;
+	device->hotspot_y = 16;
 
 	device->listener.func = handle_surface_destroy;
 	wl_list_insert(ec->surface_destroy_listener_list.prev,
@@ -1092,6 +1121,8 @@
 	GError *error = NULL;
 	GOptionContext *context;
 
+	g_type_init(); /* GdkPixbuf needs this, it seems. */
+
 	context = g_option_context_new(NULL);
 	g_option_context_add_main_entries(context, option_entries, "Wayland");
 	if (!g_option_context_parse(context, &argc, &argv, &error)) {
diff --git a/compositor.h b/compositor.h
index 1237fbc..ac812dc 100644
--- a/compositor.h
+++ b/compositor.h
@@ -81,6 +81,7 @@
 	int32_t x, y;
 	struct wlsc_compositor *ec;
 	struct wlsc_surface *sprite;
+	int32_t hotspot_x, hotspot_y;
 	struct wl_list link;
 
 	struct wlsc_surface *pointer_focus;
@@ -118,6 +119,7 @@
 	EGLContext context;
 	GLuint fbo, vbo;
 	GLuint proj_uniform, tex_uniform;
+	EGLImageKHR default_pointer_image;
 	struct wl_display *wl_display;
 
 	/* We implement the shell interface. */
diff --git a/protocol.xml b/protocol.xml
index be2e79b..d8c644c 100644
--- a/protocol.xml
+++ b/protocol.xml
@@ -119,6 +119,12 @@
   </interface>
 
   <interface name="input_device" version="1">
+    <request name="attach">
+      <arg name="buffer" type="object" interface="buffer"/>
+      <arg name="hotspot_x" type="int"/>
+      <arg name="hotspot_y" type="int"/>
+    </request>
+
     <event name="motion">
       <arg name="time" type="uint"/>
       <arg name="x" type="int"/>
diff --git a/spec/main.tex b/spec/main.tex
index 330d672..41e0367 100644
--- a/spec/main.tex
+++ b/spec/main.tex
@@ -159,7 +159,7 @@
   \hline 
   Interface \texttt{cache} \\ \hline 
   Requests \\ \hline 
-  no requests \\ \hline
+  \texttt{attach(buffer, x, y)} \\
   Events \\ \hline
   \texttt{motion(x, y, sx, sy)} \\
   \texttt{button(button, state, x, y, sx, sy)} \\
@@ -179,14 +179,14 @@
 A surface can change the pointer image when the surface is the pointer
 focus of the input device.  Wayland doesn't automatically change the
 pointer image when a pointer enters a surface, but expects the
-application to set the cursor it wants in response the the motion
-event.  The rationale is that a client has to manage changing pointer
-images for UI elements within the surface in response to motion events
-anyway, so we'll make that the only mechanism for setting changing the
-pointer image.  If the server receives a request to set the pointer
-image after the surface loses pointer focus, the request is ignored.
-To the client this will look like it successfully set the pointer
-image.
+application to set the cursor it wants in response the the pointer
+focus and motion events.  The rationale is that a client has to manage
+changing pointer images for UI elements within the surface in response
+to motion events anyway, so we'll make that the only mechanism for
+setting changing the pointer image.  If the server receives a request
+to set the pointer image after the surface loses pointer focus, the
+request is ignored.  To the client this will look like it successfully
+set the pointer image.
 
 The compositor will revert the pointer image back to a default image
 when no surface has the pointer focus for that device.  Clients can