Add client side pointer image handling
diff --git a/clients/window.c b/clients/window.c
index f71bf31..08a0183 100644
--- a/clients/window.c
+++ b/clients/window.c
@@ -31,6 +31,7 @@
 #include <cairo.h>
 #include <glib.h>
 #include <glib-object.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
 #include <xf86drm.h>
 
 #define EGL_EGLEXT_PROTOTYPES 1
@@ -69,6 +70,8 @@
 	char *device_name;
 	cairo_surface_t *active_frame, *inactive_frame, *shadow;
 	struct xkb_desc *xkb;
+	EGLImageKHR *pointer_images;
+	struct wl_buffer **pointer_buffers;
 };
 
 struct window {
@@ -124,6 +127,100 @@
 	cairo_close_path(cr);
 }
 
+static int
+texture_from_png(const char *filename, int width, int height)
+{
+	GdkPixbuf *pixbuf;
+	GError *error = NULL;
+	void *data;
+	GLenum format;
+
+	pixbuf = gdk_pixbuf_new_from_file_at_scale(filename,
+						   width, height,
+						   FALSE, &error);
+	if (error != NULL)
+		return -1;
+
+	data = gdk_pixbuf_get_pixels(pixbuf);
+
+	if (gdk_pixbuf_get_has_alpha(pixbuf))
+		format = GL_RGBA;
+	else
+		format = GL_RGB;
+
+	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
+		     width, height, 0, format, GL_UNSIGNED_BYTE, data);
+
+	gdk_pixbuf_unref(pixbuf);
+
+	return 0;
+}
+
+static const struct {
+	const char *filename;
+	int hotspot_x, hotspot_y;
+} pointer_images[] = {
+	{ DATADIR "/wayland/bottom_left_corner.png",	 6, 30 },
+	{ DATADIR "/wayland/bottom_right_corner.png",	28, 28 },
+	{ DATADIR "/wayland/bottom_side.png",		16, 20 },
+	{ DATADIR "/wayland/grabbing.png",		20, 17 },
+	{ DATADIR "/wayland/left_ptr.png",		10,  5 },
+	{ DATADIR "/wayland/left_side.png",		10, 20 },
+	{ DATADIR "/wayland/right_side.png",		30, 19 },
+	{ DATADIR "/wayland/top_left_corner.png",	 8,  8 },
+	{ DATADIR "/wayland/top_right_corner.png",	26,  8 },
+	{ DATADIR "/wayland/top_side.png",		18,  8 },
+	{ DATADIR "/wayland/xterm.png",			15, 15 }
+};
+
+static void
+create_pointer_images(struct display *d)
+{
+	int i, count;
+	GLuint texture;
+	const int width = 32, height = 32;
+	EGLint name, stride;
+	struct wl_visual *visual;
+
+	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
+	};
+
+	glGenTextures(1, &texture);
+	glBindTexture(GL_TEXTURE_2D, 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);
+
+	visual = wl_display_get_argb_visual(d->display);
+
+	image_attribs[1] = width;
+	image_attribs[3] = height;
+	count = ARRAY_LENGTH(pointer_images);
+	d->pointer_images = malloc(count * sizeof *d->pointer_images);
+	d->pointer_buffers = malloc(count * sizeof *d->pointer_buffers);
+	for (i = 0; i < count; i++) {
+		d->pointer_images[i] =
+			eglCreateDRMImageMESA(d->dpy, image_attribs);
+		glEGLImageTargetTexture2DOES(GL_TEXTURE_2D,
+					     d->pointer_images[i]);
+		eglExportDRMImageMESA(d->dpy,
+				      d->pointer_images[i],
+				      &name, NULL, &stride);
+		texture_from_png(pointer_images[i].filename, width, height);
+
+		d->pointer_buffers[i] =
+			wl_drm_create_buffer(d->drm, name,
+					     width, height, stride, visual);
+	}
+
+}
+
 static const cairo_user_data_key_t surface_data_key;
 struct surface_data {
 	EGLImageKHR image;
@@ -315,6 +412,100 @@
 	WINDOW_STABLE = 16,
 };
 
+enum pointer_type {
+	POINTER_BOTTOM_LEFT,
+	POINTER_BOTTOM_RIGHT,
+	POINTER_BOTTOM,
+	POINTER_DRAGGING,
+	POINTER_LEFT_PTR,
+	POINTER_LEFT,
+	POINTER_RIGHT,
+	POINTER_TOP_LEFT,
+	POINTER_TOP_RIGHT,
+	POINTER_TOP,
+	POINTER_IBEAM,
+};
+
+static int
+get_pointer_location(struct window *window, int32_t x, int32_t y)
+{
+	int vlocation, hlocation;
+	const int grip_size = 8;
+
+	if (x < window->margin)
+		hlocation = WINDOW_STABLE;
+	else if (window->margin <= x && x < window->margin + grip_size)
+		hlocation = WINDOW_RESIZING_LEFT;
+	else if (x < window->allocation.width - window->margin - grip_size)
+		hlocation = WINDOW_MOVING;
+	else if (x < window->allocation.width - window->margin)
+		hlocation = WINDOW_RESIZING_RIGHT;
+	else
+		hlocation = WINDOW_STABLE;
+
+	if (y < window->margin)
+		vlocation = WINDOW_STABLE;
+	else if (window->margin <= y && y < window->margin + grip_size)
+		vlocation = WINDOW_RESIZING_TOP;
+	else if (y < window->allocation.height - window->margin - grip_size)
+		vlocation = WINDOW_MOVING;
+	else if (y < window->allocation.height - window->margin)
+		vlocation = WINDOW_RESIZING_BOTTOM;
+	else
+		vlocation = WINDOW_STABLE;
+
+	return vlocation | hlocation;
+}
+
+static void
+set_pointer_image(struct input *input)
+{
+	struct display *display = input->display;
+	int pointer, location;
+
+	location = get_pointer_location(input->pointer_focus,
+					input->sx, input->sy);
+	switch (location) {
+	case WINDOW_RESIZING_TOP:
+		pointer = POINTER_TOP;
+		break;
+	case WINDOW_RESIZING_BOTTOM:
+		pointer = POINTER_BOTTOM;
+		break;
+	case WINDOW_RESIZING_LEFT:
+		pointer = POINTER_LEFT;
+		break;
+	case WINDOW_RESIZING_RIGHT:
+		pointer = POINTER_RIGHT;
+		break;
+	case WINDOW_RESIZING_TOP_LEFT:
+		pointer = POINTER_TOP_LEFT;
+		break;
+	case WINDOW_RESIZING_TOP_RIGHT:
+		pointer = POINTER_TOP_RIGHT;
+		break;
+	case WINDOW_RESIZING_BOTTOM_LEFT:
+		pointer = POINTER_BOTTOM_LEFT;
+		break;
+	case WINDOW_RESIZING_BOTTOM_RIGHT:
+		pointer = POINTER_BOTTOM_RIGHT;
+		break;
+	case WINDOW_STABLE:
+		wl_input_device_attach(input->input_device,
+				       NULL, 0, 0);
+		return;
+	default:
+		/* FIXME: We should ask the application here. */
+		pointer = POINTER_IBEAM;
+		break;
+	}
+
+	wl_input_device_attach(input->input_device,
+			       display->pointer_buffers[pointer],
+			       pointer_images[pointer].hotspot_x,
+			       pointer_images[pointer].hotspot_y);
+}
+
 static void
 window_handle_motion(void *data, struct wl_input_device *input_device,
 		     uint32_t time,
@@ -326,6 +517,8 @@
 	input->y = y;
 	input->sx = sx;
 	input->sy = sy;
+
+	set_pointer_image(input);
 }
 
 static void window_handle_button(void *data, struct wl_input_device *input_device,
@@ -333,28 +526,12 @@
 {
 	struct input *input = data;
 	struct window *window = input->pointer_focus;
-	int grip_size = 8, vlocation, hlocation;
+	int location;
 
-	if (window->margin <= input->sx && input->sx < window->margin + grip_size)
-		hlocation = WINDOW_RESIZING_LEFT;
-	else if (input->sx < window->allocation.width - window->margin - grip_size)
-		hlocation = WINDOW_MOVING;
-	else if (input->sx < window->allocation.width - window->margin)
-		hlocation = WINDOW_RESIZING_RIGHT;
-	else
-		hlocation = WINDOW_STABLE;
-
-	if (window->margin <= input->sy && input->sy < window->margin + grip_size)
-		vlocation = WINDOW_RESIZING_TOP;
-	else if (input->sy < window->allocation.height - window->margin - grip_size)
-		vlocation = WINDOW_MOVING;
-	else if (input->sy < window->allocation.height - window->margin)
-		vlocation = WINDOW_RESIZING_BOTTOM;
-	else
-		vlocation = WINDOW_STABLE;
+	location = get_pointer_location(window, input->sx, input->sy);
 
 	if (button == BTN_LEFT && state == 1) {
-		switch (hlocation | vlocation) {
+		switch (location) {
 		case WINDOW_MOVING:
 			wl_shell_move(window->display->shell,
 				      window->surface, input_device, time);
@@ -369,7 +546,7 @@
 		case WINDOW_RESIZING_BOTTOM_RIGHT:
 			wl_shell_resize(window->display->shell,
 					window->surface, input_device, time,
-					hlocation | vlocation);
+					location);
 			break;
 		}
 	}
@@ -413,10 +590,12 @@
 {
 	struct input *input = data;
 
-	if (surface)
+	if (surface) {
 		input->pointer_focus = wl_surface_get_user_data(surface);
-	else
+		set_pointer_image(input);
+	} else {
 		input->pointer_focus = NULL;
+	}
 }
 
 static void
@@ -945,6 +1124,8 @@
 		return NULL;
 	}
 
+	create_pointer_images(d);
+
 	display_render_frame(d);
 
 	d->loop = g_main_loop_new(NULL, FALSE);