[PATCH weston 2/2] window: handle insufficient buffer space

ppaalanen at gmail.com ppaalanen at gmail.com
Fri Nov 29 07:48:52 PST 2013


From: Pekka Paalanen <pekka.paalanen at collabora.co.uk>

It is quite possible for os_create_anonymous_file() to fail when trying
to allocate a new wl_shm buffer. Propagate this failure out from
shm_surface_prepare. Most parts of toytoolkit are already avoiding NULL
cairo surfaces.

If cairo surface allocation fails, do not try to call the widget redraw
functions, those are not prepared to deal with NULL. Also do not
schedule a frame callback, this allows us to retry drawing the next
time.

If redraw fails for the main_surface of a window, restore the widget
geometry to what the compositor currently is showing. This keeps the
window visual appearance in sync with application state, so interacting
with the application does not break too badly.

If the very first draw of any window fails, then forcefully exit the
program. E.g. if weston-desktop-shell fails to allocate buffers for the
unlock dialog, w-d-s exits, and weston unlocks the screen automatically.

This patch allows e.g. weston-terminal to stop from enlarging while
resizing, if new sized buffers can no longer the allocated. Even then,
the application stays usable, as it can often repaint in the last
successful size. It does not crash, and the user is able to resize it
smaller, too.

Signed-off-by: Pekka Paalanen <pekka.paalanen at collabora.co.uk>
---
 clients/window.c | 93 +++++++++++++++++++++++++++++++++++++++++++++-----------
 1 file changed, 76 insertions(+), 17 deletions(-)

diff --git a/clients/window.c b/clients/window.c
index a201ebb..0e40ab4 100644
--- a/clients/window.c
+++ b/clients/window.c
@@ -1083,6 +1083,9 @@ shm_surface_prepare(struct toysurface *base, int dx, int dy,
 					   surface->flags,
 					   leaf->resize_pool,
 					   &leaf->data);
+	if (!leaf->cairo_surface)
+		return NULL;
+
 	wl_buffer_add_listener(leaf->data->buffer,
 			       &shm_surface_buffer_listener, surface);
 
@@ -3724,21 +3727,10 @@ hack_prevent_EGL_sub_surface_deadlock(struct window *window)
 }
 
 static void
-idle_resize(struct window *window)
+window_do_resize(struct window *window)
 {
 	struct surface *surface;
 
-	window->resize_needed = 0;
-	window->redraw_needed = 1;
-
-	DBG("from %dx%d to %dx%d\n",
-	    window->main_surface->server_allocation.width,
-	    window->main_surface->server_allocation.height,
-	    window->pending_allocation.width,
-	    window->pending_allocation.height);
-
-	hack_prevent_EGL_sub_surface_deadlock(window);
-
 	widget_set_allocation(window->main_surface->widget,
 			      window->pending_allocation.x,
 			      window->pending_allocation.y,
@@ -3761,6 +3753,46 @@ idle_resize(struct window *window)
 	}
 }
 
+static void
+idle_resize(struct window *window)
+{
+	window->resize_needed = 0;
+	window->redraw_needed = 1;
+
+	DBG("from %dx%d to %dx%d\n",
+	    window->main_surface->server_allocation.width,
+	    window->main_surface->server_allocation.height,
+	    window->pending_allocation.width,
+	    window->pending_allocation.height);
+
+	hack_prevent_EGL_sub_surface_deadlock(window);
+
+	window_do_resize(window);
+}
+
+static void
+undo_resize(struct window *window)
+{
+	window->pending_allocation.width =
+		window->main_surface->server_allocation.width;
+	window->pending_allocation.height =
+		window->main_surface->server_allocation.height;
+
+	DBG("back to %dx%d\n",
+	    window->main_surface->server_allocation.width,
+	    window->main_surface->server_allocation.height);
+
+	window_do_resize(window);
+
+	if (window->pending_allocation.width == 0 &&
+	    window->pending_allocation.height == 0) {
+		fprintf(stderr, "Error: Could not draw a surface, "
+			"most likely due to insufficient disk space in "
+			"%s (XDG_RUNTIME_DIR).\n", getenv("XDG_RUNTIME_DIR"));
+		exit(EXIT_FAILURE);
+	}
+}
+
 void
 window_schedule_resize(struct window *window, int width, int height)
 {
@@ -3885,25 +3917,30 @@ static const struct wl_callback_listener listener = {
 	frame_callback
 };
 
-static void
+static int
 surface_redraw(struct surface *surface)
 {
 	DBG_OBJ(surface->surface, "begin\n");
 
 	if (!surface->window->redraw_needed && !surface->redraw_needed)
-		return;
+		return 0;
 
 	/* Whole-window redraw forces a redraw even if the previous has
 	 * not yet hit the screen.
 	 */
 	if (surface->frame_cb) {
 		if (!surface->window->redraw_needed)
-			return;
+			return 0;
 
 		DBG_OBJ(surface->frame_cb, "cancelled\n");
 		wl_callback_destroy(surface->frame_cb);
 	}
 
+	if (!widget_get_cairo_surface(surface->widget)) {
+		DBG_OBJ(surface->surface, "cancelled due buffer failure\n");
+		return -1;
+	}
+
 	surface->frame_cb = wl_surface_frame(surface->surface);
 	wl_callback_add_listener(surface->frame_cb, &listener, surface);
 	DBG_OBJ(surface->frame_cb, "new\n");
@@ -3912,6 +3949,7 @@ surface_redraw(struct surface *surface)
 	DBG_OBJ(surface->surface, "-> widget_redraw\n");
 	widget_redraw(surface->widget);
 	DBG_OBJ(surface->surface, "done\n");
+	return 0;
 }
 
 static void
@@ -3919,6 +3957,8 @@ idle_redraw(struct task *task, uint32_t events)
 {
 	struct window *window = container_of(task, struct window, redraw_task);
 	struct surface *surface;
+	int failed = 0;
+	int resized = 0;
 
 	DBG(" --------- \n");
 
@@ -3933,16 +3973,35 @@ idle_redraw(struct task *task, uint32_t events)
 		}
 
 		idle_resize(window);
+		resized = 1;
 	}
 
-	wl_list_for_each(surface, &window->subsurface_list, link)
-		surface_redraw(surface);
+	if (surface_redraw(window->main_surface) < 0) {
+		/*
+		 * Only main_surface failure will cause us to undo the resize.
+		 * If sub-surfaces fail, they will just be broken with old
+		 * content.
+		 */
+		failed = 1;
+	} else {
+		wl_list_for_each(surface, &window->subsurface_list, link) {
+			if (surface == window->main_surface)
+				continue;
+
+			surface_redraw(surface);
+		}
+	}
 
 	window->redraw_needed = 0;
 	window_flush(window);
 
 	wl_list_for_each(surface, &window->subsurface_list, link)
 		surface_set_synchronized_default(surface);
+
+	if (resized && failed) {
+		/* Restore widget tree to correspond to what is on screen. */
+		undo_resize(window);
+	}
 }
 
 static void
-- 
1.8.1.5



More information about the wayland-devel mailing list