Index: HelenOS.config
===================================================================
--- HelenOS.config	(revision 28f8f6f2876b18cdd1a463452ab1f7e872051b5f)
+++ HelenOS.config	(revision afcf7049d7f35c862988f3e47d752144e2c6aaac)
@@ -611,4 +611,7 @@
 ! [CONFIG_FB=y] CONFIG_DISP_DOUBLE_BUF (y/n)
 
+% Window double buffering
+! [CONFIG_FB=y] CONFIG_WIN_DOUBLE_BUF (n/y)
+
 % Start AP processors by the loader
 ! [PLATFORM=sparc64&CONFIG_SMP=y] CONFIG_AP (y/n)
Index: uspace/drv/fb/amdm37x_dispc/amdm37x_dispc.c
===================================================================
--- uspace/drv/fb/amdm37x_dispc/amdm37x_dispc.c	(revision 28f8f6f2876b18cdd1a463452ab1f7e872051b5f)
+++ uspace/drv/fb/amdm37x_dispc/amdm37x_dispc.c	(revision afcf7049d7f35c862988f3e47d752144e2c6aaac)
@@ -382,4 +382,8 @@
 	errno_t rc;
 
+	/* Check that we support all required flags */
+	if ((params->flags & ~bmpf_color_key) != 0)
+		return ENOTSUP;
+
 	dcbm = calloc(1, sizeof(amdm37x_bitmap_t));
 	if (dcbm == NULL)
@@ -483,4 +487,5 @@
 	gfx_rect_clip(&srect, &skfbrect, &crect);
 
+	// XXX bmpf_color_key
 	for (pos.y = crect.p0.y; pos.y < crect.p1.y; pos.y++) {
 		for (pos.x = crect.p0.x; pos.x < crect.p1.x; pos.x++) {
Index: uspace/drv/fb/kfb/port.c
===================================================================
--- uspace/drv/fb/kfb/port.c	(revision 28f8f6f2876b18cdd1a463452ab1f7e872051b5f)
+++ uspace/drv/fb/kfb/port.c	(revision afcf7049d7f35c862988f3e47d752144e2c6aaac)
@@ -199,4 +199,8 @@
 	errno_t rc;
 
+	/* Check that we support all required flags */
+	if ((params->flags & ~bmpf_color_key) != 0)
+		return ENOTSUP;
+
 	kfbbm = calloc(1, sizeof(kfb_bitmap_t));
 	if (kfbbm == NULL)
Index: uspace/lib/congfx/src/console.c
===================================================================
--- uspace/lib/congfx/src/console.c	(revision 28f8f6f2876b18cdd1a463452ab1f7e872051b5f)
+++ uspace/lib/congfx/src/console.c	(revision afcf7049d7f35c862988f3e47d752144e2c6aaac)
@@ -206,4 +206,8 @@
 	errno_t rc;
 
+	/* Check that we support all requested flags */
+	if ((params->flags & ~bmpf_color_key) != 0)
+		return ENOTSUP;
+
 	cbm = calloc(1, sizeof(console_gc_bitmap_t));
 	if (cbm == NULL)
Index: uspace/lib/gfx/include/gfx/coord.h
===================================================================
--- uspace/lib/gfx/include/gfx/coord.h	(revision 28f8f6f2876b18cdd1a463452ab1f7e872051b5f)
+++ uspace/lib/gfx/include/gfx/coord.h	(revision afcf7049d7f35c862988f3e47d752144e2c6aaac)
@@ -55,4 +55,5 @@
 extern bool gfx_rect_is_empty(gfx_rect_t *);
 extern bool gfx_rect_is_incident(gfx_rect_t *, gfx_rect_t *);
+extern bool gfx_rect_is_inside(gfx_rect_t *, gfx_rect_t *);
 extern bool gfx_pix_inside_rect(gfx_coord2_t *, gfx_rect_t *);
 
Index: uspace/lib/gfx/include/types/gfx/bitmap.h
===================================================================
--- uspace/lib/gfx/include/types/gfx/bitmap.h	(revision 28f8f6f2876b18cdd1a463452ab1f7e872051b5f)
+++ uspace/lib/gfx/include/types/gfx/bitmap.h	(revision afcf7049d7f35c862988f3e47d752144e2c6aaac)
@@ -48,5 +48,7 @@
 typedef enum {
 	/** Enable color key */
-	bmpf_color_key = 0x1
+	bmpf_color_key = 0x1,
+	/** Directly map GC output into this bitmap */
+	bmpf_direct_output = 0x2
 } gfx_bitmap_flags_t;
 
Index: uspace/lib/gfx/src/coord.c
===================================================================
--- uspace/lib/gfx/src/coord.c	(revision 28f8f6f2876b18cdd1a463452ab1f7e872051b5f)
+++ uspace/lib/gfx/src/coord.c	(revision afcf7049d7f35c862988f3e47d752144e2c6aaac)
@@ -255,4 +255,27 @@
 }
 
+/** Return true if rectangle @a a is contained in rectangle @a b.
+ *
+ * @param a Inside rectangle
+ * @param b Outside rectangle
+ * @return @c true iff @a a is (non-strictly) inside @a b
+ */
+bool gfx_rect_is_inside(gfx_rect_t *a, gfx_rect_t *b)
+{
+	gfx_rect_t sa;
+	gfx_rect_t sb;
+
+	gfx_rect_points_sort(a, &sa);
+	gfx_rect_points_sort(b, &sb);
+
+	if (sa.p0.x < sb.p0.x || sa.p0.y < sb.p0.y)
+		return false;
+
+	if (sa.p1.x > sb.p1.x || sa.p1.y > sb.p1.y)
+		return false;
+
+	return true;
+}
+
 /** Get rectangle dimensions.
  *
Index: uspace/lib/gfx/test/coord.c
===================================================================
--- uspace/lib/gfx/test/coord.c	(revision 28f8f6f2876b18cdd1a463452ab1f7e872051b5f)
+++ uspace/lib/gfx/test/coord.c	(revision afcf7049d7f35c862988f3e47d752144e2c6aaac)
@@ -775,4 +775,104 @@
 }
 
+/** gfx_rect_is_inside is true for rectangle strictly inside */
+PCUT_TEST(rect_is_inside_strict)
+{
+	gfx_rect_t a;
+	gfx_rect_t b;
+
+	a.p0.x = 2;
+	a.p0.y = 3;
+	a.p1.x = 4;
+	a.p1.y = 5;
+
+	b.p0.x = 1;
+	b.p0.y = 2;
+	b.p1.x = 5;
+	b.p1.y = 6;
+
+	PCUT_ASSERT_TRUE(gfx_rect_is_inside(&a, &b));
+}
+
+/** gfx_rect_is_inside is true for two equal rectangles */
+PCUT_TEST(rect_is_inside_same)
+{
+	gfx_rect_t a;
+	gfx_rect_t b;
+
+	a.p0.x = 1;
+	a.p0.y = 2;
+	a.p1.x = 3;
+	a.p1.y = 4;
+
+	b.p0.x = 1;
+	b.p0.y = 2;
+	b.p1.x = 3;
+	b.p1.y = 4;
+
+	PCUT_ASSERT_TRUE(gfx_rect_is_inside(&a, &b));
+}
+
+/** gfx_rect_is_inside is false for @c a.p0 outside */
+PCUT_TEST(rect_is_inside_p0_outside)
+{
+	gfx_rect_t a;
+	gfx_rect_t b;
+
+	a.p0.x = 0;
+	a.p0.y = 2;
+	a.p1.x = 3;
+	a.p1.y = 4;
+
+	b.p0.x = 1;
+	b.p0.y = 2;
+	b.p1.x = 3;
+	b.p1.y = 4;
+
+	PCUT_ASSERT_FALSE(gfx_rect_is_inside(&a, &b));
+
+	a.p0.x = 1;
+	a.p0.y = 1;
+	a.p1.x = 3;
+	a.p1.y = 4;
+
+	b.p0.x = 1;
+	b.p0.y = 2;
+	b.p1.x = 3;
+	b.p1.y = 4;
+
+	PCUT_ASSERT_FALSE(gfx_rect_is_inside(&a, &b));
+}
+
+/** gfx_rect_is_inside is false for @c a.p1 outside */
+PCUT_TEST(rect_is_inside_p1_outside)
+{
+	gfx_rect_t a;
+	gfx_rect_t b;
+
+	a.p0.x = 1;
+	a.p0.y = 2;
+	a.p1.x = 4;
+	a.p1.y = 4;
+
+	b.p0.x = 1;
+	b.p0.y = 2;
+	b.p1.x = 3;
+	b.p1.y = 4;
+
+	PCUT_ASSERT_FALSE(gfx_rect_is_inside(&a, &b));
+
+	a.p0.x = 1;
+	a.p0.y = 2;
+	a.p1.x = 3;
+	a.p1.y = 5;
+
+	b.p0.x = 1;
+	b.p0.y = 2;
+	b.p1.x = 3;
+	b.p1.y = 4;
+
+	PCUT_ASSERT_FALSE(gfx_rect_is_inside(&a, &b));
+}
+
 /** gfx_pix_inside_rect for  */
 PCUT_TEST(pix_inside_rect)
Index: uspace/lib/gui/window.c
===================================================================
--- uspace/lib/gui/window.c	(revision 28f8f6f2876b18cdd1a463452ab1f7e872051b5f)
+++ uspace/lib/gui/window.c	(revision afcf7049d7f35c862988f3e47d752144e2c6aaac)
@@ -414,43 +414,17 @@
 	}
 
-	/* Allocate resources for new surface. */
-	surface_t *new_surface = surface_create(width, height, NULL,
-	    SURFACE_FLAG_SHARED);
-	if (!new_surface)
-		return;
-
-	gfx_bitmap_params_init(&params);
-	params.rect.p0.x = 0;
-	params.rect.p0.y = 0;
-	params.rect.p1.x = width;
-	params.rect.p1.y = height;
-
-	alloc.pitch = width * sizeof(uint32_t);
-	alloc.off0 = 0;
-	alloc.pixels = surface_direct_access(new_surface);
-
-	rc = gfx_bitmap_create(win->gc, &params, &alloc, &new_bitmap);
-	if (rc != EOK) {
-		surface_destroy(new_surface);
-		return;
-	}
-
-	/* Switch new and old surface. */
 	fibril_mutex_lock(&win->guard);
-	surface_t *old_surface = win->surface;
-	gfx_bitmap_t *old_bitmap = win->bitmap;
-	win->surface = new_surface;
-	win->bitmap = new_bitmap;
-	fibril_mutex_unlock(&win->guard);
-
-	/*
-	 * Let all widgets in the tree alter their position and size.
-	 * Widgets might also paint themselves onto the new surface.
-	 */
-	win->root.rearrange(&win->root, 0, 0, width, height);
-
-	fibril_mutex_lock(&win->guard);
-	surface_reset_damaged_region(win->surface);
-	fibril_mutex_unlock(&win->guard);
+
+	/* Deallocate old bitmap. */
+	if (win->bitmap != NULL) {
+		gfx_bitmap_destroy(win->bitmap);
+		win->bitmap = NULL;
+	}
+
+	/* Deallocate old surface. */
+	if (win->surface != NULL) {
+		surface_destroy(win->surface);
+		win->surface = NULL;
+	}
 
 	/* Resize the display window. */
@@ -463,35 +437,59 @@
 
 	rc = display_window_resize(win->dwindow, &offs, &nrect);
+	if (rc != EOK)
+		return;
+
+	gfx_bitmap_params_init(&params);
+#ifndef CONFIG_WIN_DOUBLE_BUF
+	params.flags = bmpf_direct_output;
+#else
+	params.flags = 0;
+#endif
+	params.rect.p0.x = 0;
+	params.rect.p0.y = 0;
+	params.rect.p1.x = width;
+	params.rect.p1.y = height;
+
+	rc = gfx_bitmap_create(win->gc, &params, NULL, &new_bitmap);
 	if (rc != EOK) {
-		/* Rollback to old surface. Reverse all changes. */
-
-		sysarg_t old_width = 0;
-		sysarg_t old_height = 0;
-		if (old_surface)
-			surface_get_resolution(old_surface, &old_width, &old_height);
-
-		fibril_mutex_lock(&win->guard);
-		new_surface = win->surface;
-		win->surface = old_surface;
-		win->bitmap = old_bitmap;
+		if (rc == ENOTSUP) {
+			/* Direct output is not supported */
+			params.flags &= ~bmpf_direct_output;
+			rc = gfx_bitmap_create(win->gc, &params, NULL, &new_bitmap);
+			if (rc != EOK) {
+				fibril_mutex_unlock(&win->guard);
+				return;
+			}
+		}
+	}
+
+	rc = gfx_bitmap_get_alloc(new_bitmap, &alloc);
+	if (rc != EOK) {
 		fibril_mutex_unlock(&win->guard);
-
-		win->root.rearrange(&win->root, 0, 0, old_width, old_height);
-
-		if (win->surface) {
-			fibril_mutex_lock(&win->guard);
-			surface_reset_damaged_region(win->surface);
-			fibril_mutex_unlock(&win->guard);
-		}
-
-		surface_destroy(new_surface);
-		return;
-	}
-
-	if (old_bitmap != NULL)
-		gfx_bitmap_destroy(old_bitmap);
-	/* Deallocate old surface. */
-	if (old_surface)
-		surface_destroy(old_surface);
+		return;
+	}
+
+	/* Allocate new surface. */
+	surface_t *new_surface = surface_create(width, height, alloc.pixels, 0);
+	if (!new_surface) {
+		gfx_bitmap_destroy(new_bitmap);
+		fibril_mutex_unlock(&win->guard);
+		return;
+	}
+
+	/* Switch in new surface and bitmap. */
+	win->surface = new_surface;
+	win->bitmap = new_bitmap;
+	fibril_mutex_unlock(&win->guard);
+
+	/*
+	 * Let all widgets in the tree alter their position and size.
+	 * Widgets might also paint themselves onto the new surface.
+	 */
+	win->root.rearrange(&win->root, 0, 0, width, height);
+
+	fibril_mutex_lock(&win->guard);
+	surface_reset_damaged_region(win->surface);
+	fibril_mutex_unlock(&win->guard);
 
 	if (placement_flags != WINDOW_PLACEMENT_ANY) {
Index: uspace/lib/ipcgfx/include/ipcgfx/ipc/gc.h
===================================================================
--- uspace/lib/ipcgfx/include/ipcgfx/ipc/gc.h	(revision 28f8f6f2876b18cdd1a463452ab1f7e872051b5f)
+++ uspace/lib/ipcgfx/include/ipcgfx/ipc/gc.h	(revision afcf7049d7f35c862988f3e47d752144e2c6aaac)
@@ -42,4 +42,5 @@
 	GC_FILL_RECT,
 	GC_BITMAP_CREATE,
+	GC_BITMAP_CREATE_DOUTPUT,
 	GC_BITMAP_DESTROY,
 	GC_BITMAP_RENDER,
Index: uspace/lib/ipcgfx/src/client.c
===================================================================
--- uspace/lib/ipcgfx/src/client.c	(revision 28f8f6f2876b18cdd1a463452ab1f7e872051b5f)
+++ uspace/lib/ipcgfx/src/client.c	(revision afcf7049d7f35c862988f3e47d752144e2c6aaac)
@@ -108,5 +108,5 @@
 }
 
-/** Create bitmap in IPC GC.
+/** Create normal bitmap in IPC GC.
  *
  * @param arg IPC GC
@@ -116,5 +116,6 @@
  * @return EOK on success or an error code
  */
-errno_t ipc_gc_bitmap_create(void *arg, gfx_bitmap_params_t *params,
+static errno_t ipc_gc_bitmap_create_normal(void *arg,
+    gfx_bitmap_params_t *params,
     gfx_bitmap_alloc_t *alloc, void **rbm)
 {
@@ -209,4 +210,98 @@
 }
 
+/** Create direct output bitmap in IPC GC.
+ *
+ * @param arg IPC GC
+ * @param params Bitmap params
+ * @param alloc Bitmap allocation info or @c NULL
+ * @param rbm Place to store pointer to new bitmap
+ * @return EOK on success or an error code
+ */
+static errno_t ipc_gc_bitmap_create_direct_output(void *arg,
+    gfx_bitmap_params_t *params,
+    gfx_bitmap_alloc_t *alloc, void **rbm)
+{
+	ipc_gc_t *ipcgc = (ipc_gc_t *) arg;
+	ipc_gc_bitmap_t *ipcbm = NULL;
+	gfx_coord2_t dim;
+	async_exch_t *exch = NULL;
+	void *pixels;
+	ipc_call_t answer;
+	size_t asize;
+	aid_t req;
+	errno_t rc;
+
+	/* Cannot specify allocation for direct output bitmap */
+	if (alloc != NULL)
+		return EINVAL;
+
+	ipcbm = calloc(1, sizeof(ipc_gc_bitmap_t));
+	if (ipcbm == NULL)
+		return ENOMEM;
+
+	gfx_coord2_subtract(&params->rect.p1, &params->rect.p0, &dim);
+	ipcbm->rect = params->rect;
+
+	ipcbm->alloc.pitch = dim.x * sizeof(uint32_t);
+	ipcbm->alloc.off0 = 0;
+	ipcbm->myalloc = true;
+
+	asize = PAGES2SIZE(SIZE2PAGES(ipcbm->alloc.pitch * dim.y));
+
+	exch = async_exchange_begin(ipcgc->sess);
+	req = async_send_0(exch, GC_BITMAP_CREATE_DOUTPUT, &answer);
+	rc = async_data_write_start(exch, params, sizeof (gfx_bitmap_params_t));
+	if (rc != EOK) {
+		async_forget(req);
+		goto error;
+	}
+
+	rc = async_share_in_start_0_0(exch, asize, &pixels);
+	if (rc != EOK) {
+		async_forget(req);
+		goto error;
+	}
+	async_exchange_end(exch);
+	exch = NULL;
+
+	async_wait_for(req, &rc);
+	if (rc != EOK)
+		goto error;
+
+	ipcbm->ipcgc = ipcgc;
+	ipcbm->bmp_id = ipc_get_arg1(&answer);
+	ipcbm->alloc.pixels = pixels;
+	*rbm = (void *)ipcbm;
+	return EOK;
+error:
+	if (exch != NULL)
+		async_exchange_end(exch);
+	if (ipcbm != NULL) {
+		if (ipcbm->alloc.pixels != NULL)
+			as_area_destroy(ipcbm->alloc.pixels);
+		free(ipcbm);
+	}
+	return rc;
+}
+
+/** Create bitmap in IPC GC.
+ *
+ * @param arg IPC GC
+ * @param params Bitmap params
+ * @param alloc Bitmap allocation info or @c NULL
+ * @param rbm Place to store pointer to new bitmap
+ * @return EOK on success or an error code
+ */
+errno_t ipc_gc_bitmap_create(void *arg, gfx_bitmap_params_t *params,
+    gfx_bitmap_alloc_t *alloc, void **rbm)
+{
+	if ((params->flags & bmpf_direct_output) != 0) {
+		return ipc_gc_bitmap_create_direct_output(arg, params, alloc,
+		    rbm);
+	} else {
+		return ipc_gc_bitmap_create_normal(arg, params, alloc, rbm);
+	}
+}
+
 /** Destroy bitmap in IPC GC.
  *
Index: uspace/lib/ipcgfx/src/server.c
===================================================================
--- uspace/lib/ipcgfx/src/server.c	(revision 28f8f6f2876b18cdd1a463452ab1f7e872051b5f)
+++ uspace/lib/ipcgfx/src/server.c	(revision afcf7049d7f35c862988f3e47d752144e2c6aaac)
@@ -128,5 +128,4 @@
 	/* Check size */
 	if (size != PAGES2SIZE(SIZE2PAGES(dim.x * dim.y * sizeof(uint32_t)))) {
-		printf("size=%zu, expected=%zu\n", size, dim.x * dim.y * sizeof(uint32_t));
 		async_answer_0(icall, EINVAL);
 		return;
@@ -168,4 +167,89 @@
 }
 
+static void gc_bitmap_create_doutput_srv(ipc_gc_srv_t *srvgc, ipc_call_t *icall)
+{
+	gfx_bitmap_params_t params;
+	gfx_bitmap_alloc_t alloc;
+	gfx_bitmap_t *bitmap;
+	gfx_coord2_t dim;
+	ipc_gc_srv_bitmap_t *srvbmp = NULL;
+	ipc_call_t call;
+	size_t size;
+	errno_t rc;
+
+	if (!async_data_write_receive(&call, &size)) {
+		async_answer_0(&call, EREFUSED);
+		async_answer_0(icall, EREFUSED);
+		return;
+	}
+
+	if (size != sizeof(gfx_bitmap_params_t)) {
+		async_answer_0(&call, EINVAL);
+		async_answer_0(icall, EINVAL);
+		return;
+	}
+
+	rc = async_data_write_finalize(&call, &params, size);
+	if (rc != EOK) {
+		async_answer_0(&call, rc);
+		async_answer_0(icall, rc);
+		return;
+	}
+
+	/* Bitmap dimensions */
+	gfx_coord2_subtract(&params.rect.p1, &params.rect.p0, &dim);
+
+	if (!async_share_in_receive(&call, &size)) {
+		async_answer_0(icall, EINVAL);
+		return;
+	}
+
+	/* Check size */
+	if (size != PAGES2SIZE(SIZE2PAGES(dim.x * dim.y * sizeof(uint32_t)))) {
+		async_answer_0(&call, EINVAL);
+		async_answer_0(icall, EINVAL);
+		return;
+	}
+
+	rc = gfx_bitmap_create(srvgc->gc, &params, NULL, &bitmap);
+	if (rc != EOK) {
+		async_answer_0(&call, rc);
+		async_answer_0(icall, rc);
+		return;
+	}
+
+	rc = gfx_bitmap_get_alloc(bitmap, &alloc);
+	if (rc != EOK) {
+		gfx_bitmap_destroy(bitmap);
+		async_answer_0(&call, rc);
+		async_answer_0(icall, rc);
+		return;
+	}
+
+	rc = async_share_in_finalize(&call, alloc.pixels, AS_AREA_READ |
+	    AS_AREA_WRITE | AS_AREA_CACHEABLE);
+	if (rc != EOK) {
+		gfx_bitmap_destroy(bitmap);
+		async_answer_0(icall, EIO);
+		return;
+	}
+
+	srvbmp = calloc(1, sizeof(ipc_gc_srv_bitmap_t));
+	if (srvbmp == NULL) {
+		gfx_bitmap_destroy(bitmap);
+		async_answer_0(icall, ENOMEM);
+		return;
+	}
+
+	srvbmp->srvgc = srvgc;
+	list_append(&srvbmp->lbitmaps, &srvgc->bitmaps);
+	srvbmp->bmp = bitmap;
+	srvbmp->bmp_id = srvgc->next_bmp_id++;
+	printf("gc_bitmap_create_doutput_srv: storing bmp_id=%u\n",
+	    (unsigned) srvbmp->bmp_id);
+
+	async_answer_1(icall, EOK, srvbmp->bmp_id);
+}
+
 static void gc_bitmap_destroy_srv(ipc_gc_srv_t *srvgc, ipc_call_t *call)
 {
@@ -270,4 +354,7 @@
 			gc_bitmap_create_srv(&srvgc, &call);
 			break;
+		case GC_BITMAP_CREATE_DOUTPUT:
+			gc_bitmap_create_doutput_srv(&srvgc, &call);
+			break;
 		case GC_BITMAP_DESTROY:
 			gc_bitmap_destroy_srv(&srvgc, &call);
Index: uspace/lib/ipcgfx/test/ipcgfx.c
===================================================================
--- uspace/lib/ipcgfx/test/ipcgfx.c	(revision 28f8f6f2876b18cdd1a463452ab1f7e872051b5f)
+++ uspace/lib/ipcgfx/test/ipcgfx.c	(revision afcf7049d7f35c862988f3e47d752144e2c6aaac)
@@ -27,4 +27,5 @@
  */
 
+#include <as.h>
 #include <async.h>
 #include <errno.h>
@@ -96,4 +97,5 @@
 typedef struct {
 	test_response_t *resp;
+	gfx_bitmap_alloc_t alloc;
 } test_bitmap_t;
 
@@ -474,4 +476,129 @@
 }
 
+/** gfx_bitmap_create direct output bitmap with server returning failure */
+PCUT_TEST(bitmap_create_dout_failure)
+{
+	errno_t rc;
+	service_id_t sid;
+	test_response_t resp;
+	gfx_context_t *gc;
+	gfx_bitmap_params_t params;
+	gfx_bitmap_t *bitmap;
+	async_sess_t *sess;
+	ipc_gc_t *ipcgc;
+
+	async_set_fallback_port_handler(test_ipcgc_conn, &resp);
+
+	// FIXME This causes this test to be non-reentrant!
+	rc = loc_server_register(test_ipcgfx_server);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = loc_service_register(test_ipcgfx_svc, &sid);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	sess = loc_service_connect(sid, INTERFACE_GC, 0);
+	PCUT_ASSERT_NOT_NULL(sess);
+
+	rc = ipc_gc_create(sess, &ipcgc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	gc = ipc_gc_get_ctx(ipcgc);
+	PCUT_ASSERT_NOT_NULL(gc);
+
+	resp.rc = ENOMEM;
+	resp.bitmap_create_called = false;
+
+	gfx_bitmap_params_init(&params);
+	params.flags = bmpf_direct_output;
+	params.rect.p0.x = 1;
+	params.rect.p0.y = 2;
+	params.rect.p1.x = 3;
+	params.rect.p1.y = 4;
+	bitmap = NULL;
+	rc = gfx_bitmap_create(gc, &params, NULL, &bitmap);
+	PCUT_ASSERT_ERRNO_VAL(resp.rc, rc);
+	PCUT_ASSERT_TRUE(resp.bitmap_create_called);
+	PCUT_ASSERT_EQUALS(params.rect.p0.x, resp.bitmap_create_params.rect.p0.x);
+	PCUT_ASSERT_EQUALS(params.rect.p0.y, resp.bitmap_create_params.rect.p0.y);
+	PCUT_ASSERT_EQUALS(params.rect.p1.x, resp.bitmap_create_params.rect.p1.x);
+	PCUT_ASSERT_EQUALS(params.rect.p1.y, resp.bitmap_create_params.rect.p1.y);
+	PCUT_ASSERT_EQUALS((params.rect.p1.x - params.rect.p0.x) *
+	    sizeof(uint32_t), (unsigned) resp.bitmap_create_alloc.pitch);
+	PCUT_ASSERT_EQUALS(0, resp.bitmap_create_alloc.off0);
+	PCUT_ASSERT_NOT_NULL(resp.bitmap_create_alloc.pixels);
+	PCUT_ASSERT_NULL(bitmap);
+
+	ipc_gc_delete(ipcgc);
+	async_hangup(sess);
+
+	rc = loc_service_unregister(sid);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+/** gfx_bitmap_create direct output bitmap with server returning success */
+PCUT_TEST(bitmap_create_dout_success)
+{
+	errno_t rc;
+	service_id_t sid;
+	test_response_t resp;
+	gfx_context_t *gc;
+	gfx_bitmap_params_t params;
+	gfx_bitmap_t *bitmap;
+	async_sess_t *sess;
+	ipc_gc_t *ipcgc;
+
+	async_set_fallback_port_handler(test_ipcgc_conn, &resp);
+
+	// FIXME This causes this test to be non-reentrant!
+	rc = loc_server_register(test_ipcgfx_server);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = loc_service_register(test_ipcgfx_svc, &sid);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	sess = loc_service_connect(sid, INTERFACE_GC, 0);
+	PCUT_ASSERT_NOT_NULL(sess);
+
+	rc = ipc_gc_create(sess, &ipcgc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	gc = ipc_gc_get_ctx(ipcgc);
+	PCUT_ASSERT_NOT_NULL(gc);
+
+	resp.rc = EOK;
+	resp.bitmap_create_called = false;
+
+	gfx_bitmap_params_init(&params);
+	params.flags = bmpf_direct_output;
+	params.rect.p0.x = 1;
+	params.rect.p0.y = 2;
+	params.rect.p1.x = 3;
+	params.rect.p1.y = 4;
+	bitmap = NULL;
+	rc = gfx_bitmap_create(gc, &params, NULL, &bitmap);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_TRUE(resp.bitmap_create_called);
+	PCUT_ASSERT_EQUALS(params.rect.p0.x, resp.bitmap_create_params.rect.p0.x);
+	PCUT_ASSERT_EQUALS(params.rect.p0.y, resp.bitmap_create_params.rect.p0.y);
+	PCUT_ASSERT_EQUALS(params.rect.p1.x, resp.bitmap_create_params.rect.p1.x);
+	PCUT_ASSERT_EQUALS(params.rect.p1.y, resp.bitmap_create_params.rect.p1.y);
+	PCUT_ASSERT_EQUALS((params.rect.p1.x - params.rect.p0.x) *
+	    sizeof(uint32_t), (unsigned) resp.bitmap_create_alloc.pitch);
+	PCUT_ASSERT_EQUALS(0, resp.bitmap_create_alloc.off0);
+	PCUT_ASSERT_NOT_NULL(resp.bitmap_create_alloc.pixels);
+	PCUT_ASSERT_NOT_NULL(bitmap);
+
+	resp.bitmap_destroy_called = false;
+	rc = gfx_bitmap_destroy(bitmap);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_TRUE(resp.bitmap_destroy_called);
+
+	ipc_gc_delete(ipcgc);
+	async_hangup(sess);
+
+	rc = loc_service_unregister(sid);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
 /** gfx_bitmap_render with server returning failure */
 PCUT_TEST(bitmap_render_failure)
@@ -733,8 +860,22 @@
 	test_response_t *resp = (test_response_t *) arg;
 	test_bitmap_t *bitmap;
+	gfx_coord2_t dim;
 
 	resp->bitmap_create_called = true;
 	resp->bitmap_create_params = *params;
-	resp->bitmap_create_alloc = *alloc;
+
+	if ((params->flags & bmpf_direct_output) != 0) {
+		gfx_coord2_subtract(&params->rect.p1, &params->rect.p0, &dim);
+
+		resp->bitmap_create_alloc.pitch = dim.x * sizeof(uint32_t);
+		resp->bitmap_create_alloc.off0 = 0;
+		resp->bitmap_create_alloc.pixels = as_area_create(AS_AREA_ANY,
+		    dim.x * dim.y * sizeof(uint32_t), AS_AREA_READ |
+		    AS_AREA_WRITE | AS_AREA_CACHEABLE, AS_AREA_UNPAGED);
+		if (resp->bitmap_create_alloc.pixels == AS_MAP_FAILED)
+			return ENOMEM;
+	} else {
+		resp->bitmap_create_alloc = *alloc;
+	}
 
 	if (resp->rc != EOK)
@@ -746,4 +887,5 @@
 
 	bitmap->resp = resp;
+	bitmap->alloc = resp->bitmap_create_alloc;
 	*rbm = (void *) bitmap;
 	return EOK;
@@ -763,4 +905,7 @@
 	if (resp->rc != EOK)
 		return resp->rc;
+
+	if ((resp->bitmap_create_params.flags & bmpf_direct_output) != 0)
+		as_area_destroy(resp->bitmap_create_alloc.pixels);
 
 	free(bitmap);
@@ -795,6 +940,8 @@
 static errno_t test_gc_bitmap_get_alloc(void *bm, gfx_bitmap_alloc_t *alloc)
 {
-	/* Currently IPC GC does not pass this method to the server */
-	return ENOTSUP;
+	test_bitmap_t *bitmap = (test_bitmap_t *) bm;
+
+	*alloc = bitmap->alloc;
+	return EOK;
 }
 
Index: uspace/lib/memgfx/src/memgc.c
===================================================================
--- uspace/lib/memgfx/src/memgc.c	(revision 28f8f6f2876b18cdd1a463452ab1f7e872051b5f)
+++ uspace/lib/memgfx/src/memgc.c	(revision afcf7049d7f35c862988f3e47d752144e2c6aaac)
@@ -37,4 +37,5 @@
  */
 
+#include <as.h>
 #include <assert.h>
 #include <gfx/color.h>
@@ -223,4 +224,8 @@
 	errno_t rc;
 
+	/* Check that we support all requested flags */
+	if ((params->flags & ~(bmpf_color_key | bmpf_direct_output)) != 0)
+		return ENOTSUP;
+
 	mbm = calloc(1, sizeof(mem_gc_bitmap_t));
 	if (mbm == NULL)
@@ -232,5 +237,30 @@
 	mbm->key_color = params->key_color;
 
-	if (alloc == NULL) {
+	if ((params->flags & bmpf_direct_output) != 0) {
+		/* Caller cannot specify allocation for direct output */
+		if (alloc != NULL) {
+			rc = EINVAL;
+			goto error;
+		}
+
+		/* Bounding rectangle must be within GC bounding rectangle */
+		if (!gfx_rect_is_inside(&mbm->rect, &mgc->rect)) {
+			rc = EINVAL;
+			goto error;
+		}
+
+		mbm->alloc = mgc->alloc;
+
+		/* Don't free pixel array when destroying bitmap */
+		mbm->myalloc = false;
+	} else if (alloc == NULL) {
+#if 0
+		/*
+		 * TODO: If the bitmap is not required to be sharable,
+		 * we could allocate it with a simple malloc.
+		 * Need to have a bitmap flag specifying that the
+		 * allocation should be sharable. IPC GC could
+		 * automatically add this flag
+		 */
 		mbm->alloc.pitch = dim.x * sizeof(uint32_t);
 		mbm->alloc.off0 = 0;
@@ -242,4 +272,16 @@
 			goto error;
 		}
+#endif
+		mbm->alloc.pitch = dim.x * sizeof(uint32_t);
+		mbm->alloc.off0 = 0;
+		mbm->alloc.pixels = as_area_create(AS_AREA_ANY,
+		    dim.x * dim.y * sizeof(uint32_t), AS_AREA_READ |
+		    AS_AREA_WRITE | AS_AREA_CACHEABLE, AS_AREA_UNPAGED);
+		mbm->myalloc = true;
+
+		if (mbm->alloc.pixels == AS_MAP_FAILED) {
+			rc = ENOMEM;
+			goto error;
+		}
 	} else {
 		mbm->alloc = *alloc;
@@ -263,6 +305,11 @@
 {
 	mem_gc_bitmap_t *mbm = (mem_gc_bitmap_t *)bm;
-	if (mbm->myalloc)
+	if (mbm->myalloc) {
+#if 0
+		/* TODO: if we alloc allocating the bitmap with malloc */
 		free(mbm->alloc.pixels);
+#endif
+		as_area_destroy(mbm->alloc.pixels);
+	}
 
 	free(mbm);
@@ -317,5 +364,7 @@
 	dmap.data = mbm->mgc->alloc.pixels;
 
-	if ((mbm->flags & bmpf_color_key) == 0) {
+	if ((mbm->flags & bmpf_direct_output) != 0) {
+		/* Nothing to do */
+	} else if ((mbm->flags & bmpf_color_key) == 0) {
 		for (y = drect.p0.y; y < drect.p1.y; y++) {
 			for (x = drect.p0.x; x < drect.p1.x; x++) {
Index: uspace/srv/hid/rfb/main.c
===================================================================
--- uspace/srv/hid/rfb/main.c	(revision 28f8f6f2876b18cdd1a463452ab1f7e872051b5f)
+++ uspace/srv/hid/rfb/main.c	(revision afcf7049d7f35c862988f3e47d752144e2c6aaac)
@@ -195,4 +195,8 @@
 	errno_t rc;
 
+	/* Check that we support all required flags */
+	if ((params->flags & ~bmpf_color_key) != 0)
+		return ENOTSUP;
+
 	rfbbm = calloc(1, sizeof(rfb_bitmap_t));
 	if (rfbbm == NULL)
