compositor: introduce weston_buffer_reference

The wl_buffer reference counting API has been inconsistent. You would
manually increment the refcount and register a destroy listener, as
opposed to calling weston_buffer_post_release(), which internally
decremented the refcount, and then removing a list item.

Replace both cases with a single function:
weston_buffer_reference(weston_buffer_reference *ref, wl_buffer *buffer)

Buffer is assigned to ref->buffer, while taking care of all the refcounting
and release posting. You take a reference by passing a non-NULL buffer, and
release a reference by passing NULL as buffer. The function uses an
internal wl_buffer destroy listener, so the pointer gets reset on
destruction automatically.

This is inspired by the pipe_resource_reference() of Mesa, and modified
by krh's suggestion to add struct weston_buffer_reference.

Additionally, when a surface gets destroyed, the associated wl_buffer
will send a release event. Often the buffer is already destroyed on
client side, so the event will be discarded by libwayland-client.

Compositor-drm.c is converted to use weston_buffer_reference.

Signed-off-by: Pekka Paalanen <ppaalanen@gmail.com>
diff --git a/src/compositor.c b/src/compositor.c
index d80d929..869fffa 100644
--- a/src/compositor.c
+++ b/src/compositor.c
@@ -181,16 +181,6 @@
 }
 
 static void
-surface_handle_buffer_destroy(struct wl_listener *listener, void *data)
-{
-	struct weston_surface *es =
-		container_of(listener, struct weston_surface, 
-			     buffer_destroy_listener);
-
-	es->buffer = NULL;
-}
-
-static void
 surface_handle_pending_buffer_destroy(struct wl_listener *listener, void *data)
 {
 	struct weston_surface *surface =
@@ -241,7 +231,6 @@
 
 	pixman_region32_init(&surface->texture_damage);
 
-	surface->buffer = NULL;
 	surface->buffer_transform = WL_OUTPUT_TRANSFORM_NORMAL;
 	surface->pending.buffer_transform = surface->buffer_transform;
 	surface->output = NULL;
@@ -254,9 +243,6 @@
 	pixman_region32_init(&surface->transform.opaque);
 	wl_list_init(&surface->frame_callback_list);
 
-	surface->buffer_destroy_listener.notify =
-		surface_handle_buffer_destroy;
-
 	wl_list_init(&surface->geometry.transformation_list);
 	wl_list_insert(&surface->geometry.transformation_list,
 		       &surface->transform.position.link);
@@ -731,9 +717,9 @@
 	case WL_OUTPUT_TRANSFORM_270:
 	case WL_OUTPUT_TRANSFORM_FLIPPED_90:
 	case WL_OUTPUT_TRANSFORM_FLIPPED_270:
-		return surface->buffer->height;
+		return surface->buffer_ref.buffer->height;
 	default:
-		return surface->buffer->width;
+		return surface->buffer_ref.buffer->width;
 	}
 }
 
@@ -745,9 +731,9 @@
 	case WL_OUTPUT_TRANSFORM_270:
 	case WL_OUTPUT_TRANSFORM_FLIPPED_90:
 	case WL_OUTPUT_TRANSFORM_FLIPPED_270:
-		return surface->buffer->width;
+		return surface->buffer_ref.buffer->width;
 	default:
-		return surface->buffer->height;
+		return surface->buffer_ref.buffer->height;
 	}
 }
 
@@ -877,8 +863,7 @@
 	if (surface->pending.buffer)
 		wl_list_remove(&surface->pending.buffer_destroy_listener.link);
 
-	if (surface->buffer)
-		wl_list_remove(&surface->buffer_destroy_listener.link);
+	weston_buffer_reference(&surface->buffer_ref, NULL);
 
 	pixman_region32_fini(&surface->texture_damage);
 	compositor->renderer->destroy_surface(surface);
@@ -907,26 +892,47 @@
 }
 
 static void
-weston_surface_attach(struct weston_surface *surface, struct wl_buffer *buffer)
+weston_buffer_reference_handle_destroy(struct wl_listener *listener,
+				       void *data)
 {
-	if (surface->buffer && buffer != surface->buffer) {
-		weston_buffer_post_release(surface->buffer);
-		wl_list_remove(&surface->buffer_destroy_listener.link);
+	struct weston_buffer_reference *ref =
+		container_of(listener, struct weston_buffer_reference,
+			     destroy_listener);
+
+	assert((struct wl_buffer *)data == ref->buffer);
+	ref->buffer = NULL;
+}
+
+WL_EXPORT void
+weston_buffer_reference(struct weston_buffer_reference *ref,
+			struct wl_buffer *buffer)
+{
+	if (ref->buffer && buffer != ref->buffer) {
+		weston_buffer_post_release(ref->buffer);
+		wl_list_remove(&ref->destroy_listener.link);
 	}
 
-	if (buffer && buffer != surface->buffer) {
+	if (buffer && buffer != ref->buffer) {
 		buffer->busy_count++;
 		wl_signal_add(&buffer->resource.destroy_signal,
-			      &surface->buffer_destroy_listener);
+			      &ref->destroy_listener);
 	}
 
+	ref->buffer = buffer;
+	ref->destroy_listener.notify = weston_buffer_reference_handle_destroy;
+}
+
+static void
+weston_surface_attach(struct weston_surface *surface, struct wl_buffer *buffer)
+{
+	weston_buffer_reference(&surface->buffer_ref, buffer);
+
 	if (!buffer) {
 		if (weston_surface_is_mapped(surface))
 			weston_surface_unmap(surface);
 	}
 
 	surface->compositor->renderer->attach(surface, buffer);
-	surface->buffer = buffer;
 }
 
 WL_EXPORT void
@@ -1006,7 +1012,8 @@
 surface_accumulate_damage(struct weston_surface *surface,
 			  pixman_region32_t *opaque)
 {
-	if (surface->buffer && wl_buffer_is_shm(surface->buffer))
+	if (surface->buffer_ref.buffer &&
+	    wl_buffer_is_shm(surface->buffer_ref.buffer))
 		surface->compositor->renderer->flush_damage(surface);
 
 	if (surface->transform.enabled) {
@@ -1243,6 +1250,8 @@
 	if (buffer_resource)
 		buffer = buffer_resource->data;
 
+	/* Attach, attach, without commit in between does not send
+	 * wl_buffer.release. */
 	if (surface->pending.buffer)
 		wl_list_remove(&surface->pending.buffer_destroy_listener.link);
 
@@ -1380,7 +1389,7 @@
 	if (surface->pending.buffer || surface->pending.remove_contents)
 		weston_surface_attach(surface, surface->pending.buffer);
 
-	if (surface->buffer && surface->configure)
+	if (surface->buffer_ref.buffer && surface->configure)
 		surface->configure(surface, surface->pending.sx,
 				   surface->pending.sy);
 	surface->pending.sx = 0;
@@ -2126,7 +2135,8 @@
 	y = wl_fixed_to_int(seat->seat.pointer->y) - seat->hotspot_y;
 
 	weston_surface_configure(seat->sprite, x, y,
-				 es->buffer->width, es->buffer->height);
+				 es->buffer_ref.buffer->width,
+				 es->buffer_ref.buffer->height);
 
 	empty_region(&es->pending.input);
 
@@ -2192,7 +2202,7 @@
 	seat->hotspot_x = x;
 	seat->hotspot_y = y;
 
-	if (surface->buffer)
+	if (surface->buffer_ref.buffer)
 		pointer_cursor_surface_configure(surface, 0, 0);
 }
 
@@ -2567,7 +2577,8 @@
 
 	weston_surface_configure(es,
 				 es->geometry.x + sx, es->geometry.y + sy,
-				 es->buffer->width, es->buffer->height);
+				 es->buffer_ref.buffer->width,
+				 es->buffer_ref.buffer->height);
 }
 
 static int
@@ -2615,7 +2626,7 @@
 	struct wl_list *list;
 
 	if (weston_surface_is_mapped(seat->drag_surface) ||
-	    !seat->drag_surface->buffer)
+	    !seat->drag_surface->buffer_ref.buffer)
 		return;
 
 	if (seat->sprite && weston_surface_is_mapped(seat->sprite))