Index: HelenOS.config
===================================================================
--- HelenOS.config	(revision ef734b78b1ad5a89163b14e6f38f206aa9b6e636)
+++ HelenOS.config	(revision 2ab8ab3c0a1e9771074c6e9d07ec1bf5e87ddafc)
@@ -614,6 +614,9 @@
 ! [CONFIG_FB=y] CONFIG_DISP_DOUBLE_BUF (y/n)
 
+% Client-side UI rendering
+! [CONFIG_FB=y] CONFIG_UI_CS_RENDER (y/n)
+
 % Window double buffering
-! [CONFIG_FB=y] CONFIG_WIN_DOUBLE_BUF (n/y)
+! [CONFIG_UI_CS_RENDER=y] CONFIG_WIN_DOUBLE_BUF (n/y)
 
 % Start AP processors by the loader
Index: uspace/app/terminal/terminal.c
===================================================================
--- uspace/app/terminal/terminal.c	(revision ef734b78b1ad5a89163b14e6f38f206aa9b6e636)
+++ uspace/app/terminal/terminal.c	(revision 2ab8ab3c0a1e9771074c6e9d07ec1bf5e87ddafc)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * Copyright (c) 2012 Petr Koupy
  * All rights reserved.
@@ -42,4 +42,5 @@
 #include <gfx/bitmap.h>
 #include <gfx/context.h>
+#include <gfx/render.h>
 #include <io/con_srv.h>
 #include <io/concaps.h>
@@ -524,4 +525,5 @@
 		term_write_char(term, str_decode(data, &off, size));
 
+	gfx_update(term->gc);
 	*nwritten = size;
 	return EOK;
@@ -533,4 +535,5 @@
 
 	term_update(term);
+	gfx_update(term->gc);
 }
 
@@ -544,4 +547,5 @@
 
 	term_update(term);
+	gfx_update(term->gc);
 }
 
@@ -555,4 +559,5 @@
 
 	term_update(term);
+	gfx_update(term->gc);
 }
 
@@ -626,4 +631,5 @@
 
 	term_update(term);
+	gfx_update(term->gc);
 }
 
@@ -688,4 +694,5 @@
 	term->is_focused = true;
 	term_update(term);
+	gfx_update(term->gc);
 }
 
@@ -731,4 +738,5 @@
 	term->is_focused = false;
 	term_update(term);
+	gfx_update(term->gc);
 }
 
Index: uspace/lib/gfx/include/gfx/render.h
===================================================================
--- uspace/lib/gfx/include/gfx/render.h	(revision ef734b78b1ad5a89163b14e6f38f206aa9b6e636)
+++ uspace/lib/gfx/include/gfx/render.h	(revision 2ab8ab3c0a1e9771074c6e9d07ec1bf5e87ddafc)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2019 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -44,4 +44,5 @@
 extern errno_t gfx_set_color(gfx_context_t *, gfx_color_t *);
 extern errno_t gfx_fill_rect(gfx_context_t *, gfx_rect_t *);
+extern errno_t gfx_update(gfx_context_t *);
 
 #endif
Index: uspace/lib/gfx/include/types/gfx/ops/context.h
===================================================================
--- uspace/lib/gfx/include/types/gfx/ops/context.h	(revision ef734b78b1ad5a89163b14e6f38f206aa9b6e636)
+++ uspace/lib/gfx/include/types/gfx/ops/context.h	(revision 2ab8ab3c0a1e9771074c6e9d07ec1bf5e87ddafc)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2019 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -51,4 +51,6 @@
 	/** Fill rectangle using the current drawing color */
 	errno_t (*fill_rect)(void *, gfx_rect_t *);
+	/** Update display */
+	errno_t (*update)(void *);
 	/** Create bitmap */
 	errno_t (*bitmap_create)(void *, gfx_bitmap_params_t *,
Index: uspace/lib/gfx/src/render.c
===================================================================
--- uspace/lib/gfx/src/render.c	(revision ef734b78b1ad5a89163b14e6f38f206aa9b6e636)
+++ uspace/lib/gfx/src/render.c	(revision 2ab8ab3c0a1e9771074c6e9d07ec1bf5e87ddafc)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2019 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -63,4 +63,18 @@
 }
 
+/** Update display.
+ *
+ * Finish any deferred rendering.
+ *
+ * @param gc Graphic context
+ *
+ * @return EOK on success, ENOMEM if insufficient resources,
+ *         EIO if grahic device connection was lost
+ */
+errno_t gfx_update(gfx_context_t *gc)
+{
+	return gc->ops->update(gc->arg);
+}
+
 /** @}
  */
Index: uspace/lib/gfx/test/render.c
===================================================================
--- uspace/lib/gfx/test/render.c	(revision ef734b78b1ad5a89163b14e6f38f206aa9b6e636)
+++ uspace/lib/gfx/test/render.c	(revision 2ab8ab3c0a1e9771074c6e9d07ec1bf5e87ddafc)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2019 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -32,4 +32,5 @@
 #include <pcut/pcut.h>
 #include <mem.h>
+#include <stdbool.h>
 
 PCUT_INIT;
@@ -39,8 +40,10 @@
 static errno_t testgc_set_color(void *, gfx_color_t *);
 static errno_t testgc_fill_rect(void *, gfx_rect_t *);
+static errno_t testgc_update(void *);
 
 static gfx_context_ops_t ops = {
 	.set_color = testgc_set_color,
-	.fill_rect = testgc_fill_rect
+	.fill_rect = testgc_fill_rect,
+	.update = testgc_update
 };
 
@@ -49,4 +52,5 @@
 	gfx_color_t *dclr;
 	gfx_rect_t *rect;
+	bool updated;
 } test_gc_t;
 
@@ -107,4 +111,23 @@
 }
 
+PCUT_TEST(update)
+{
+	errno_t rc;
+	gfx_context_t *gc = NULL;
+	test_gc_t tgc;
+
+	memset(&tgc, 0, sizeof(tgc));
+
+	rc = gfx_context_new(&ops, &tgc, &gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	PCUT_ASSERT_FALSE(tgc.updated);
+	gfx_update(gc);
+	PCUT_ASSERT_TRUE(tgc.updated);
+
+	rc = gfx_context_delete(gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
 static errno_t testgc_set_color(void *arg, gfx_color_t *color)
 {
@@ -125,3 +148,11 @@
 }
 
+static errno_t testgc_update(void *arg)
+{
+	test_gc_t *tgc = (test_gc_t *) arg;
+
+	tgc->updated = true;
+	return EOK;
+}
+
 PCUT_EXPORT(render);
Index: uspace/lib/ipcgfx/include/ipcgfx/ipc/gc.h
===================================================================
--- uspace/lib/ipcgfx/include/ipcgfx/ipc/gc.h	(revision ef734b78b1ad5a89163b14e6f38f206aa9b6e636)
+++ uspace/lib/ipcgfx/include/ipcgfx/ipc/gc.h	(revision 2ab8ab3c0a1e9771074c6e9d07ec1bf5e87ddafc)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2019 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -41,9 +41,10 @@
 	GC_SET_RGB_COLOR = IPC_FIRST_USER_METHOD,
 	GC_FILL_RECT,
+	GC_UPDATE,
 	GC_BITMAP_CREATE,
 	GC_BITMAP_CREATE_DOUTPUT,
 	GC_BITMAP_DESTROY,
 	GC_BITMAP_RENDER,
-	GC_BITMAP_GET_ALLOC
+	GC_BITMAP_GET_ALLOC,
 } gc_request_t;
 
Index: uspace/lib/ipcgfx/src/client.c
===================================================================
--- uspace/lib/ipcgfx/src/client.c	(revision ef734b78b1ad5a89163b14e6f38f206aa9b6e636)
+++ uspace/lib/ipcgfx/src/client.c	(revision 2ab8ab3c0a1e9771074c6e9d07ec1bf5e87ddafc)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2019 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -47,4 +47,5 @@
 static errno_t ipc_gc_set_color(void *, gfx_color_t *);
 static errno_t ipc_gc_fill_rect(void *, gfx_rect_t *);
+static errno_t ipc_gc_update(void *);
 static errno_t ipc_gc_bitmap_create(void *, gfx_bitmap_params_t *,
     gfx_bitmap_alloc_t *, void **);
@@ -56,4 +57,5 @@
 	.set_color = ipc_gc_set_color,
 	.fill_rect = ipc_gc_fill_rect,
+	.update = ipc_gc_update,
 	.bitmap_create = ipc_gc_bitmap_create,
 	.bitmap_destroy = ipc_gc_bitmap_destroy,
@@ -103,4 +105,23 @@
 	rc = async_req_4_0(exch, GC_FILL_RECT, rect->p0.x, rect->p0.y,
 	    rect->p1.x, rect->p1.y);
+	async_exchange_end(exch);
+
+	return rc;
+}
+
+/** Update display on IPC GC.
+ *
+ * @param arg IPC GC
+ *
+ * @return EOK on success or an error code
+ */
+static errno_t ipc_gc_update(void *arg)
+{
+	ipc_gc_t *ipcgc = (ipc_gc_t *) arg;
+	async_exch_t *exch;
+	errno_t rc;
+
+	exch = async_exchange_begin(ipcgc->sess);
+	rc = async_req_0_0(exch, GC_UPDATE);
 	async_exchange_end(exch);
 
Index: uspace/lib/ipcgfx/src/server.c
===================================================================
--- uspace/lib/ipcgfx/src/server.c	(revision ef734b78b1ad5a89163b14e6f38f206aa9b6e636)
+++ uspace/lib/ipcgfx/src/server.c	(revision 2ab8ab3c0a1e9771074c6e9d07ec1bf5e87ddafc)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2019 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -86,4 +86,12 @@
 }
 
+static void gc_update_srv(ipc_gc_srv_t *srvgc, ipc_call_t *call)
+{
+	errno_t rc;
+
+	rc = gfx_update(srvgc->gc);
+	async_answer_0(call, rc);
+}
+
 static void gc_bitmap_create_srv(ipc_gc_srv_t *srvgc, ipc_call_t *icall)
 {
@@ -358,4 +366,7 @@
 		case GC_FILL_RECT:
 			gc_fill_rect_srv(&srvgc, &call);
+			break;
+		case GC_UPDATE:
+			gc_update_srv(&srvgc, &call);
 			break;
 		case GC_BITMAP_CREATE:
Index: uspace/lib/ipcgfx/test/ipcgfx.c
===================================================================
--- uspace/lib/ipcgfx/test/ipcgfx.c	(revision ef734b78b1ad5a89163b14e6f38f206aa9b6e636)
+++ uspace/lib/ipcgfx/test/ipcgfx.c	(revision 2ab8ab3c0a1e9771074c6e9d07ec1bf5e87ddafc)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2019 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -52,4 +52,5 @@
 static errno_t test_gc_set_color(void *, gfx_color_t *);
 static errno_t test_gc_fill_rect(void *, gfx_rect_t *);
+static errno_t test_gc_update(void *);
 static errno_t test_gc_bitmap_create(void *, gfx_bitmap_params_t *,
     gfx_bitmap_alloc_t *, void **);
@@ -61,4 +62,5 @@
 	.set_color = test_gc_set_color,
 	.fill_rect = test_gc_fill_rect,
+	.update = test_gc_update,
 	.bitmap_create = test_gc_bitmap_create,
 	.bitmap_destroy = test_gc_bitmap_destroy,
@@ -80,4 +82,6 @@
 	bool fill_rect_called;
 	gfx_rect_t fill_rect_rect;
+
+	bool update_called;
 
 	bool bitmap_create_called;
@@ -292,4 +296,86 @@
 	PCUT_ASSERT_EQUALS(rect.p1.x, resp.fill_rect_rect.p1.x);
 	PCUT_ASSERT_EQUALS(rect.p1.y, resp.fill_rect_rect.p1.y);
+
+	ipc_gc_delete(ipcgc);
+	async_hangup(sess);
+
+	rc = loc_service_unregister(sid);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+/** gfx_update with server returning failure */
+PCUT_TEST(update_failure)
+{
+	errno_t rc;
+	service_id_t sid;
+	test_response_t resp;
+	gfx_context_t *gc;
+	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.update_called = false;
+	rc = gfx_update(gc);
+	PCUT_ASSERT_ERRNO_VAL(resp.rc, rc);
+	PCUT_ASSERT_TRUE(resp.update_called);
+
+	ipc_gc_delete(ipcgc);
+	async_hangup(sess);
+
+	rc = loc_service_unregister(sid);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+/** gfx_update with server returning success */
+PCUT_TEST(update_success)
+{
+	errno_t rc;
+	service_id_t sid;
+	test_response_t resp;
+	gfx_context_t *gc;
+	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.update_called = false;
+	rc = gfx_update(gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_TRUE(resp.update_called);
 
 	ipc_gc_delete(ipcgc);
@@ -847,4 +933,18 @@
 }
 
+/** Update test GC.
+ *
+ * @param arg Test GC
+ *
+ * @return EOK on success or an error code
+ */
+static errno_t test_gc_update(void *arg)
+{
+	test_response_t *resp = (test_response_t *) arg;
+
+	resp->update_called = true;
+	return resp->rc;
+}
+
 /** Create bitmap in test GC.
  *
Index: uspace/lib/memgfx/include/memgfx/memgc.h
===================================================================
--- uspace/lib/memgfx/include/memgfx/memgc.h	(revision ef734b78b1ad5a89163b14e6f38f206aa9b6e636)
+++ uspace/lib/memgfx/include/memgfx/memgc.h	(revision 2ab8ab3c0a1e9771074c6e9d07ec1bf5e87ddafc)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -46,5 +46,5 @@
 
 extern errno_t mem_gc_create(gfx_rect_t *, gfx_bitmap_alloc_t *,
-    mem_gc_update_cb_t, void *, mem_gc_t **);
+    mem_gc_invalidate_cb_t, mem_gc_update_cb_t, void *, mem_gc_t **);
 extern errno_t mem_gc_delete(mem_gc_t *);
 extern void mem_gc_retarget(mem_gc_t *, gfx_rect_t *, gfx_bitmap_alloc_t *);
Index: uspace/lib/memgfx/include/types/memgfx/memgc.h
===================================================================
--- uspace/lib/memgfx/include/types/memgfx/memgc.h	(revision ef734b78b1ad5a89163b14e6f38f206aa9b6e636)
+++ uspace/lib/memgfx/include/types/memgfx/memgc.h	(revision 2ab8ab3c0a1e9771074c6e9d07ec1bf5e87ddafc)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -42,5 +42,6 @@
 typedef struct mem_gc mem_gc_t;
 
-typedef void (*mem_gc_update_cb_t)(void *, gfx_rect_t *);
+typedef void (*mem_gc_invalidate_cb_t)(void *, gfx_rect_t *);
+typedef void (*mem_gc_update_cb_t)(void *);
 
 #endif
Index: uspace/lib/memgfx/private/memgc.h
===================================================================
--- uspace/lib/memgfx/private/memgc.h	(revision ef734b78b1ad5a89163b14e6f38f206aa9b6e636)
+++ uspace/lib/memgfx/private/memgc.h	(revision 2ab8ab3c0a1e9771074c6e9d07ec1bf5e87ddafc)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -51,4 +51,6 @@
 	/** Allocation info */
 	gfx_bitmap_alloc_t alloc;
+	/** Invalidate callback */
+	mem_gc_invalidate_cb_t invalidate;
 	/** Update callback */
 	mem_gc_update_cb_t update;
Index: uspace/lib/memgfx/src/memgc.c
===================================================================
--- uspace/lib/memgfx/src/memgc.c	(revision ef734b78b1ad5a89163b14e6f38f206aa9b6e636)
+++ uspace/lib/memgfx/src/memgc.c	(revision 2ab8ab3c0a1e9771074c6e9d07ec1bf5e87ddafc)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -50,4 +50,5 @@
 static errno_t mem_gc_set_color(void *, gfx_color_t *);
 static errno_t mem_gc_fill_rect(void *, gfx_rect_t *);
+static errno_t mem_gc_update(void *);
 static errno_t mem_gc_bitmap_create(void *, gfx_bitmap_params_t *,
     gfx_bitmap_alloc_t *, void **);
@@ -60,4 +61,5 @@
 	.set_color = mem_gc_set_color,
 	.fill_rect = mem_gc_fill_rect,
+	.update = mem_gc_update,
 	.bitmap_create = mem_gc_bitmap_create,
 	.bitmap_destroy = mem_gc_bitmap_destroy,
@@ -119,4 +121,18 @@
 }
 
+/** Update memory GC.
+ *
+ * @param arg Memory GC
+ *
+ * @return EOK on success or an error code
+ */
+static errno_t mem_gc_update(void *arg)
+{
+	mem_gc_t *mgc = (mem_gc_t *) arg;
+
+	mgc->update(mgc->cb_arg);
+	return EOK;
+}
+
 /** Create memory GC.
  *
@@ -132,5 +148,6 @@
  */
 errno_t mem_gc_create(gfx_rect_t *rect, gfx_bitmap_alloc_t *alloc,
-    mem_gc_update_cb_t update_cb, void *cb_arg, mem_gc_t **rgc)
+    mem_gc_invalidate_cb_t invalidate_cb, mem_gc_update_cb_t update_cb,
+    void *cb_arg, mem_gc_t **rgc)
 {
 	mem_gc_t *mgc = NULL;
@@ -152,4 +169,5 @@
 	mgc->alloc = *alloc;
 
+	mgc->invalidate = invalidate_cb;
 	mgc->update = update_cb;
 	mgc->cb_arg = cb_arg;
@@ -205,5 +223,5 @@
 static void mem_gc_invalidate_rect(mem_gc_t *mgc, gfx_rect_t *rect)
 {
-	mgc->update(mgc->cb_arg, rect);
+	mgc->invalidate(mgc->cb_arg, rect);
 }
 
Index: uspace/lib/memgfx/test/memgfx.c
===================================================================
--- uspace/lib/memgfx/test/memgfx.c	(revision ef734b78b1ad5a89163b14e6f38f206aa9b6e636)
+++ uspace/lib/memgfx/test/memgfx.c	(revision 2ab8ab3c0a1e9771074c6e9d07ec1bf5e87ddafc)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -42,12 +42,15 @@
 PCUT_TEST_SUITE(memgfx);
 
-static void test_update_rect(void *arg, gfx_rect_t *rect);
+static void test_invalidate_rect(void *arg, gfx_rect_t *rect);
+static void test_update(void *arg);
 
 typedef struct {
+	/** True if invalidate was called */
+	bool invalidate_called;
+	/** Invalidate rectangle */
+	gfx_rect_t inv_rect;
 	/** True if update was called */
 	bool update_called;
-	/** Update rectangle */
-	gfx_rect_t rect;
-} test_update_t;
+} test_resp_t;
 
 /** Test creating and deleting a memory GC */
@@ -69,5 +72,5 @@
 	PCUT_ASSERT_NOT_NULL(alloc.pixels);
 
-	rc = mem_gc_create(&rect, &alloc, NULL, NULL, &mgc);
+	rc = mem_gc_create(&rect, &alloc, NULL, NULL, NULL, &mgc);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -89,5 +92,5 @@
 	pixel_t pixel;
 	pixel_t expected;
-	test_update_t update;
+	test_resp_t resp;
 	errno_t rc;
 
@@ -103,5 +106,6 @@
 	PCUT_ASSERT_NOT_NULL(alloc.pixels);
 
-	rc = mem_gc_create(&rect, &alloc, test_update_rect, &update, &mgc);
+	rc = mem_gc_create(&rect, &alloc, test_invalidate_rect,
+	    test_update, &resp, &mgc);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -122,5 +126,5 @@
 	frect.p1.y = 5;
 
-	memset(&update, 0, sizeof(update));
+	memset(&resp, 0, sizeof(resp));
 
 	rc = gfx_fill_rect(gc, &frect);
@@ -141,10 +145,10 @@
 	}
 
-	/* Check that the update rect is equal to the filled rect */
-	PCUT_ASSERT_TRUE(update.update_called);
-	PCUT_ASSERT_INT_EQUALS(frect.p0.x, update.rect.p0.x);
-	PCUT_ASSERT_INT_EQUALS(frect.p0.y, update.rect.p0.y);
-	PCUT_ASSERT_INT_EQUALS(frect.p1.x, update.rect.p1.x);
-	PCUT_ASSERT_INT_EQUALS(frect.p1.y, update.rect.p1.y);
+	/* Check that the invalidate rect is equal to the filled rect */
+	PCUT_ASSERT_TRUE(resp.invalidate_called);
+	PCUT_ASSERT_INT_EQUALS(frect.p0.x, resp.inv_rect.p0.x);
+	PCUT_ASSERT_INT_EQUALS(frect.p0.y, resp.inv_rect.p0.y);
+	PCUT_ASSERT_INT_EQUALS(frect.p1.x, resp.inv_rect.p1.x);
+	PCUT_ASSERT_INT_EQUALS(frect.p1.y, resp.inv_rect.p1.y);
 
 	/* TODO: Check clipping once memgc can support pitch != width etc. */
@@ -169,5 +173,5 @@
 	pixel_t pixel;
 	pixel_t expected;
-	test_update_t update;
+	test_resp_t resp;
 	errno_t rc;
 
@@ -183,5 +187,6 @@
 	PCUT_ASSERT_NOT_NULL(alloc.pixels);
 
-	rc = mem_gc_create(&rect, &alloc, test_update_rect, &update, &mgc);
+	rc = mem_gc_create(&rect, &alloc, test_invalidate_rect,
+	    test_update, &resp, &mgc);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -191,4 +196,5 @@
 	/* Create bitmap */
 
+	gfx_bitmap_params_init(&params);
 	params.rect.p0.x = 0;
 	params.rect.p0.y = 0;
@@ -219,5 +225,5 @@
 	dpmap.data = alloc.pixels;
 
-	memset(&update, 0, sizeof(update));
+	memset(&resp, 0, sizeof(resp));
 
 	/* Render the bitmap */
@@ -237,10 +243,10 @@
 	}
 
-	/* Check that the update rect is equal to the filled rect */
-	PCUT_ASSERT_TRUE(update.update_called);
-	PCUT_ASSERT_INT_EQUALS(params.rect.p0.x, update.rect.p0.x);
-	PCUT_ASSERT_INT_EQUALS(params.rect.p0.y, update.rect.p0.y);
-	PCUT_ASSERT_INT_EQUALS(params.rect.p1.x, update.rect.p1.x);
-	PCUT_ASSERT_INT_EQUALS(params.rect.p1.y, update.rect.p1.y);
+	/* Check that the invalidate rect is equal to the filled rect */
+	PCUT_ASSERT_TRUE(resp.invalidate_called);
+	PCUT_ASSERT_INT_EQUALS(params.rect.p0.x, resp.inv_rect.p0.x);
+	PCUT_ASSERT_INT_EQUALS(params.rect.p0.y, resp.inv_rect.p0.y);
+	PCUT_ASSERT_INT_EQUALS(params.rect.p1.x, resp.inv_rect.p1.x);
+	PCUT_ASSERT_INT_EQUALS(params.rect.p1.y, resp.inv_rect.p1.y);
 
 	/* TODO: Check clipping once memgc can support pitch != width etc. */
@@ -250,11 +256,57 @@
 }
 
-/** Called by memory GC when a rectangle is updated. */
-static void test_update_rect(void *arg, gfx_rect_t *rect)
-{
-	test_update_t *update = (test_update_t *)arg;
-
-	update->update_called = true;
-	update->rect = *rect;
+/** Test gfx_update() on a memory GC */
+PCUT_TEST(gfx_update)
+{
+	mem_gc_t *mgc;
+	gfx_rect_t rect;
+	gfx_bitmap_alloc_t alloc;
+	gfx_context_t *gc;
+	test_resp_t resp;
+	errno_t rc;
+
+	/* Bounding rectangle for memory GC */
+	rect.p0.x = 0;
+	rect.p0.y = 0;
+	rect.p1.x = 10;
+	rect.p1.y = 10;
+
+	alloc.pitch = (rect.p1.x - rect.p0.x) * sizeof(uint32_t);
+	alloc.off0 = 0;
+	alloc.pixels = calloc(1, alloc.pitch * (rect.p1.y - rect.p0.y));
+	PCUT_ASSERT_NOT_NULL(alloc.pixels);
+
+	rc = mem_gc_create(&rect, &alloc, test_invalidate_rect,
+	    test_update, &resp, &mgc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	gc = mem_gc_get_ctx(mgc);
+	PCUT_ASSERT_NOT_NULL(gc);
+
+	memset(&resp, 0, sizeof(resp));
+	PCUT_ASSERT_FALSE(resp.update_called);
+
+	gfx_update(gc);
+	PCUT_ASSERT_TRUE(resp.update_called);
+
+	mem_gc_delete(mgc);
+	free(alloc.pixels);
+}
+
+/** Called by memory GC when a rectangle is modified. */
+static void test_invalidate_rect(void *arg, gfx_rect_t *rect)
+{
+	test_resp_t *resp = (test_resp_t *)arg;
+
+	resp->invalidate_called = true;
+	resp->inv_rect = *rect;
+}
+
+/** Called by memory GC when update is called. */
+static void test_update(void *arg)
+{
+	test_resp_t *resp = (test_resp_t *)arg;
+
+	resp->update_called = true;
 }
 
Index: uspace/lib/ui/private/window.h
===================================================================
--- uspace/lib/ui/private/window.h	(revision ef734b78b1ad5a89163b14e6f38f206aa9b6e636)
+++ uspace/lib/ui/private/window.h	(revision 2ab8ab3c0a1e9771074c6e9d07ec1bf5e87ddafc)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -60,4 +60,10 @@
 	/** Window GC */
 	gfx_context_t *gc;
+	/** Window bitmap (if client-side rendering) */
+	gfx_bitmap_t *bmp;
+	/** Window memory GC (if client-side rendering) */
+	mem_gc_t *mgc;
+	/** Real window GC (if client-side rendering) */
+	gfx_context_t *realgc;
 	/** Window rectangle */
 	gfx_rect_t rect;
@@ -68,4 +74,6 @@
 	/** Application area GC */
 	gfx_context_t *app_gc;
+	/** Dirty rectangle */
+	gfx_rect_t dirty_rect;
 	/** UI resource. Ideally this would be in ui_t. */
 	struct ui_resource *res;
Index: uspace/lib/ui/src/checkbox.c
===================================================================
--- uspace/lib/ui/src/checkbox.c	(revision ef734b78b1ad5a89163b14e6f38f206aa9b6e636)
+++ uspace/lib/ui/src/checkbox.c	(revision 2ab8ab3c0a1e9771074c6e9d07ec1bf5e87ddafc)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -220,4 +220,8 @@
 
 	rc = gfx_puttext(checkbox->res->font, &pos, &fmt, checkbox->caption);
+	if (rc != EOK)
+		goto error;
+
+	rc = gfx_update(checkbox->res->gc);
 	if (rc != EOK)
 		goto error;
Index: uspace/lib/ui/src/dummygc.c
===================================================================
--- uspace/lib/ui/src/dummygc.c	(revision ef734b78b1ad5a89163b14e6f38f206aa9b6e636)
+++ uspace/lib/ui/src/dummygc.c	(revision 2ab8ab3c0a1e9771074c6e9d07ec1bf5e87ddafc)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -199,7 +199,21 @@
 {
 	dummygc_bitmap_t *tbm = (dummygc_bitmap_t *)bm;
+
 	tbm->dgc->bm_rendered = true;
-	tbm->dgc->bm_srect = *srect;
-	tbm->dgc->bm_offs = *offs;
+
+	tbm->dgc->bm_srect.p0.x = 0;
+	tbm->dgc->bm_srect.p0.y = 0;
+	tbm->dgc->bm_srect.p1.x = 0;
+	tbm->dgc->bm_srect.p1.y = 0;
+
+	tbm->dgc->bm_offs.x = 0;
+	tbm->dgc->bm_offs.y = 0;
+
+	if (srect != NULL)
+		tbm->dgc->bm_srect = *srect;
+
+	if (offs != NULL)
+		tbm->dgc->bm_offs = *offs;
+
 	return EOK;
 }
Index: uspace/lib/ui/src/entry.c
===================================================================
--- uspace/lib/ui/src/entry.c	(revision ef734b78b1ad5a89163b14e6f38f206aa9b6e636)
+++ uspace/lib/ui/src/entry.c	(revision 2ab8ab3c0a1e9771074c6e9d07ec1bf5e87ddafc)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -219,4 +219,8 @@
 		goto error;
 
+	rc = gfx_update(entry->res->gc);
+	if (rc != EOK)
+		goto error;
+
 	return EOK;
 error:
Index: uspace/lib/ui/src/image.c
===================================================================
--- uspace/lib/ui/src/image.c	(revision ef734b78b1ad5a89163b14e6f38f206aa9b6e636)
+++ uspace/lib/ui/src/image.c	(revision 2ab8ab3c0a1e9771074c6e9d07ec1bf5e87ddafc)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -37,4 +37,5 @@
 #include <gfx/bitmap.h>
 #include <gfx/context.h>
+#include <gfx/render.h>
 #include <gfx/text.h>
 #include <stdlib.h>
@@ -177,6 +178,9 @@
 	 */
 	gfx_rect_rtranslate(&offs, &irect, &srect);
-	return gfx_bitmap_render(image->bitmap, &srect, &offs);
-
+	rc = gfx_bitmap_render(image->bitmap, &srect, &offs);
+	if (rc != EOK)
+		return rc;
+
+	return gfx_update(image->res->gc);
 }
 
Index: uspace/lib/ui/src/label.c
===================================================================
--- uspace/lib/ui/src/label.c	(revision ef734b78b1ad5a89163b14e6f38f206aa9b6e636)
+++ uspace/lib/ui/src/label.c	(revision 2ab8ab3c0a1e9771074c6e9d07ec1bf5e87ddafc)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -204,4 +204,8 @@
 		goto error;
 
+	rc = gfx_update(label->res->gc);
+	if (rc != EOK)
+		goto error;
+
 	return EOK;
 error:
Index: uspace/lib/ui/src/pbutton.c
===================================================================
--- uspace/lib/ui/src/pbutton.c	(revision ef734b78b1ad5a89163b14e6f38f206aa9b6e636)
+++ uspace/lib/ui/src/pbutton.c	(revision 2ab8ab3c0a1e9771074c6e9d07ec1bf5e87ddafc)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -303,4 +303,8 @@
 			goto error;
 	}
+
+	rc = gfx_update(pbutton->res->gc);
+	if (rc != EOK)
+		goto error;
 
 	return EOK;
Index: uspace/lib/ui/src/rbutton.c
===================================================================
--- uspace/lib/ui/src/rbutton.c	(revision ef734b78b1ad5a89163b14e6f38f206aa9b6e636)
+++ uspace/lib/ui/src/rbutton.c	(revision 2ab8ab3c0a1e9771074c6e9d07ec1bf5e87ddafc)
@@ -289,4 +289,8 @@
 	rc = gfx_puttext(rbutton->group->res->font, &pos, &fmt,
 	    rbutton->caption);
+	if (rc != EOK)
+		goto error;
+
+	rc = gfx_update(rbutton->group->res->gc);
 	if (rc != EOK)
 		goto error;
Index: uspace/lib/ui/src/slider.c
===================================================================
--- uspace/lib/ui/src/slider.c	(revision ef734b78b1ad5a89163b14e6f38f206aa9b6e636)
+++ uspace/lib/ui/src/slider.c	(revision 2ab8ab3c0a1e9771074c6e9d07ec1bf5e87ddafc)
@@ -303,4 +303,8 @@
 
 	rc = gfx_fill_rect(slider->res->gc, &irect);
+	if (rc != EOK)
+		goto error;
+
+	rc = gfx_update(slider->res->gc);
 	if (rc != EOK)
 		goto error;
Index: uspace/lib/ui/src/wdecor.c
===================================================================
--- uspace/lib/ui/src/wdecor.c	(revision ef734b78b1ad5a89163b14e6f38f206aa9b6e636)
+++ uspace/lib/ui/src/wdecor.c	(revision 2ab8ab3c0a1e9771074c6e9d07ec1bf5e87ddafc)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -236,4 +236,8 @@
 	}
 
+	rc = gfx_update(wdecor->res->gc);
+	if (rc != EOK)
+		return rc;
+
 	return EOK;
 }
Index: uspace/lib/ui/src/window.c
===================================================================
--- uspace/lib/ui/src/window.c	(revision ef734b78b1ad5a89163b14e6f38f206aa9b6e636)
+++ uspace/lib/ui/src/window.c	(revision 2ab8ab3c0a1e9771074c6e9d07ec1bf5e87ddafc)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -84,5 +84,8 @@
 };
 
-static void ui_window_app_update(void *, gfx_rect_t *);
+static void ui_window_invalidate(void *, gfx_rect_t *);
+static void ui_window_update(void *);
+static void ui_window_app_invalidate(void *, gfx_rect_t *);
+static void ui_window_app_update(void *);
 
 /** Initialize window parameters structure.
@@ -123,4 +126,8 @@
 	ui_wdecor_t *wdecor = NULL;
 	dummy_gc_t *dgc = NULL;
+	gfx_bitmap_params_t bparams;
+	gfx_bitmap_alloc_t alloc;
+	gfx_bitmap_t *bmp = NULL;
+	mem_gc_t *memgc = NULL;
 	errno_t rc;
 
@@ -196,5 +203,44 @@
 	}
 
-	rc = ui_resource_create(gc, &res);
+#ifdef CONFIG_UI_CS_RENDER
+	/* Create window bitmap */
+	gfx_bitmap_params_init(&bparams);
+#ifndef CONFIG_WIN_DOUBLE_BUF
+	bparams.flags |= bmpf_direct_output;
+#endif
+
+	/* Move rectangle so that top-left corner is 0,0 */
+	gfx_rect_rtranslate(&params->rect.p0, &params->rect, &bparams.rect);
+
+	rc = gfx_bitmap_create(gc, &bparams, NULL, &bmp);
+	if (rc != EOK)
+		goto error;
+
+	/* Create memory GC */
+	rc = gfx_bitmap_get_alloc(bmp, &alloc);
+	if (rc != EOK) {
+		gfx_bitmap_destroy(window->app_bmp);
+		return rc;
+	}
+
+	rc = mem_gc_create(&bparams.rect, &alloc, ui_window_invalidate,
+	    ui_window_update, (void *) window, &memgc);
+	if (rc != EOK) {
+		gfx_bitmap_destroy(window->app_bmp);
+		return rc;
+	}
+
+	window->bmp = bmp;
+	window->mgc = memgc;
+	window->gc = mem_gc_get_ctx(memgc);
+	window->realgc = gc;
+#else
+	(void) ui_window_update;
+	(void) ui_window_invalidate;
+	(void) alloc;
+	(void) bparams;
+	window->gc = gc;
+#endif
+	rc = ui_resource_create(window->gc, &res);
 	if (rc != EOK)
 		goto error;
@@ -211,5 +257,5 @@
 	window->dwindow = dwindow;
 	window->rect = dparams.rect;
-	window->gc = gc;
+
 	window->res = res;
 	window->wdecor = wdecor;
@@ -222,4 +268,8 @@
 	if (res != NULL)
 		ui_resource_destroy(res);
+	if (memgc != NULL)
+		mem_gc_delete(memgc);
+	if (bmp != NULL)
+		gfx_bitmap_destroy(bmp);
 	if (dgc != NULL)
 		dummygc_destroy(dgc);
@@ -242,4 +292,14 @@
 	ui_wdecor_destroy(window->wdecor);
 	ui_resource_destroy(window->res);
+	if (0 && window->app_mgc != NULL)
+		mem_gc_delete(window->app_mgc);
+	if (0 && window->app_bmp != NULL)
+		gfx_bitmap_destroy(window->app_bmp);
+	if (window->mgc != NULL) {
+		mem_gc_delete(window->mgc);
+		window->gc = NULL;
+	}
+	if (window->bmp != NULL)
+		gfx_bitmap_destroy(window->bmp);
 	gfx_context_delete(window->gc);
 	display_window_destroy(window->dwindow);
@@ -294,6 +354,9 @@
 	gfx_rect_t arect;
 	gfx_bitmap_t *app_bmp = NULL;
-	gfx_bitmap_params_t params;
-	gfx_bitmap_alloc_t alloc;
+	gfx_bitmap_t *win_bmp = NULL;
+	gfx_bitmap_params_t app_params;
+	gfx_bitmap_params_t win_params;
+	gfx_bitmap_alloc_t app_alloc;
+	gfx_bitmap_alloc_t win_alloc;
 	errno_t rc;
 
@@ -305,8 +368,31 @@
 	gfx_rect_rtranslate(&offs, rect, &nrect);
 
+	/* mgc != NULL iff client-side rendering */
+	if (window->mgc != NULL) {
+		/* Resize window bitmap */
+		assert(window->bmp != NULL);
+
+		gfx_bitmap_params_init(&win_params);
+#ifndef CONFIG_WIN_DOUBLE_BUF
+		win_params.flags |= bmpf_direct_output;
+#endif
+		win_params.rect = nrect;
+
+		rc = gfx_bitmap_create(window->realgc, &win_params, NULL,
+		    &win_bmp);
+		if (rc != EOK)
+			goto error;
+
+		rc = gfx_bitmap_get_alloc(win_bmp, &win_alloc);
+		if (rc != EOK)
+			goto error;
+	}
+
+	/* Application area GC? */
 	if (window->app_gc != NULL) {
+		/* Resize application bitmap */
 		assert(window->app_bmp != NULL);
 
-		gfx_bitmap_params_init(&params);
+		gfx_bitmap_params_init(&app_params);
 
 		/*
@@ -315,12 +401,12 @@
 		 */
 		ui_wdecor_app_from_rect(window->wdecor->style, &nrect, &arect);
-		gfx_rect_rtranslate(&arect.p0, &arect, &params.rect);
-
-		rc = gfx_bitmap_create(window->gc, &params, NULL,
+		gfx_rect_rtranslate(&arect.p0, &arect, &app_params.rect);
+
+		rc = gfx_bitmap_create(window->gc, &app_params, NULL,
 		    &app_bmp);
 		if (rc != EOK)
 			goto error;
 
-		rc = gfx_bitmap_get_alloc(app_bmp, &alloc);
+		rc = gfx_bitmap_get_alloc(app_bmp, &app_alloc);
 		if (rc != EOK)
 			goto error;
@@ -334,9 +420,19 @@
 	}
 
+	/* CLient side rendering? */
+	if (window->mgc != NULL) {
+		mem_gc_retarget(window->mgc, &win_params.rect, &win_alloc);
+
+		gfx_bitmap_destroy(window->bmp);
+		window->bmp = win_bmp;
+	}
+
 	ui_wdecor_set_rect(window->wdecor, &nrect);
 	ui_wdecor_paint(window->wdecor);
-
+	gfx_update(window->gc);
+
+	/* Application area GC? */
 	if (window->app_gc != NULL) {
-		mem_gc_retarget(window->app_mgc, &params.rect, &alloc);
+		mem_gc_retarget(window->app_mgc, &app_params.rect, &app_alloc);
 
 		gfx_bitmap_destroy(window->app_bmp);
@@ -348,4 +444,6 @@
 	if (app_bmp != NULL)
 		gfx_bitmap_destroy(app_bmp);
+	if (win_bmp != NULL)
+		gfx_bitmap_destroy(win_bmp);
 	return rc;
 }
@@ -420,6 +518,6 @@
 		}
 
-		rc = mem_gc_create(&params.rect, &alloc, ui_window_app_update,
-		    (void *) window, &memgc);
+		rc = mem_gc_create(&params.rect, &alloc, ui_window_app_invalidate,
+		    ui_window_app_update, (void *) window, &memgc);
 		if (rc != EOK) {
 			gfx_bitmap_destroy(window->app_bmp);
@@ -696,4 +794,8 @@
 		return ui_control_paint(window->control);
 
+	rc = gfx_update(window->res->gc);
+	if (rc != EOK)
+		return rc;
+
 	return EOK;
 }
@@ -710,10 +812,41 @@
 }
 
-/** Application area update callback
+/** Window invalidate callback
  *
  * @param arg Argument (ui_window_t *)
  * @param rect Rectangle to update
  */
-static void ui_window_app_update(void *arg, gfx_rect_t *rect)
+static void ui_window_invalidate(void *arg, gfx_rect_t *rect)
+{
+	ui_window_t *window = (ui_window_t *) arg;
+	gfx_rect_t env;
+
+	gfx_rect_envelope(&window->dirty_rect, rect, &env);
+	window->dirty_rect = env;
+}
+
+/** Window update callback
+ *
+ * @param arg Argument (ui_window_t *)
+ */
+static void ui_window_update(void *arg)
+{
+	ui_window_t *window = (ui_window_t *) arg;
+
+	if (!gfx_rect_is_empty(&window->dirty_rect))
+		(void) gfx_bitmap_render(window->bmp, &window->dirty_rect, NULL);
+
+	window->dirty_rect.p0.x = 0;
+	window->dirty_rect.p0.y = 0;
+	window->dirty_rect.p1.x = 0;
+	window->dirty_rect.p1.y = 0;
+}
+
+/** Application area invalidate callback
+ *
+ * @param arg Argument (ui_window_t *)
+ * @param rect Rectangle to update
+ */
+static void ui_window_app_invalidate(void *arg, gfx_rect_t *rect)
 {
 	ui_window_t *window = (ui_window_t *) arg;
@@ -724,4 +857,24 @@
 	/* Render bitmap rectangle inside the application area */
 	(void) gfx_bitmap_render(window->app_bmp, rect, &arect.p0);
+	/*
+	 * TODO Update applications to call gfx_update(), then
+	 * we can defer update to ui_window_app_update().
+	 */
+	(void) gfx_update(window->res->gc);
+}
+
+/** Application area update callback
+ *
+ * @param arg Argument (ui_window_t *)
+ */
+static void ui_window_app_update(void *arg)
+{
+	ui_window_t *window = (ui_window_t *) arg;
+
+	/*
+	 * Not used since display is updated immediately
+	 * in ui_window_app_invalidate
+	 */
+	(void) window;
 }
 
Index: uspace/lib/ui/test/window.c
===================================================================
--- uspace/lib/ui/test/window.c	(revision ef734b78b1ad5a89163b14e6f38f206aa9b6e636)
+++ uspace/lib/ui/test/window.c	(revision 2ab8ab3c0a1e9771074c6e9d07ec1bf5e87ddafc)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -258,6 +258,6 @@
 	params.rect.p0.x = 0;
 	params.rect.p0.y = 0;
-	params.rect.p0.x = 10;
-	params.rect.p0.y = 10;
+	params.rect.p0.x = 100;
+	params.rect.p0.y = 100;
 
 	rc = ui_window_create(ui, &params, &window);
Index: uspace/srv/hid/display/display.c
===================================================================
--- uspace/srv/hid/display/display.c	(revision ef734b78b1ad5a89163b14e6f38f206aa9b6e636)
+++ uspace/srv/hid/display/display.c	(revision 2ab8ab3c0a1e9771074c6e9d07ec1bf5e87ddafc)
@@ -50,5 +50,6 @@
 
 static gfx_context_t *ds_display_get_unbuf_gc(ds_display_t *);
-static void ds_display_update_cb(void *, gfx_rect_t *);
+static void ds_display_invalidate_cb(void *, gfx_rect_t *);
+static void ds_display_update_cb(void *);
 
 /** Create display.
@@ -459,5 +460,6 @@
 
 	rc = mem_gc_create(&disp->rect, &alloc,
-	    ds_display_update_cb, (void *) disp, &disp->bbgc);
+	    ds_display_invalidate_cb, ds_display_update_cb, (void *) disp,
+	    &disp->bbgc);
 	if (rc != EOK)
 		goto error;
@@ -732,5 +734,5 @@
 }
 
-/** Display update callback.
+/** Display invalidate callback.
  *
  * Called by backbuffer memory GC when something is rendered into it.
@@ -740,5 +742,5 @@
  * @param rect Rectangle to update
  */
-static void ds_display_update_cb(void *arg, gfx_rect_t *rect)
+static void ds_display_invalidate_cb(void *arg, gfx_rect_t *rect)
 {
 	ds_display_t *disp = (ds_display_t *) arg;
@@ -749,4 +751,15 @@
 }
 
+/** Display update callback.
+ *
+ * @param arg Argument (display cast as void *)
+ */
+static void ds_display_update_cb(void *arg)
+{
+	ds_display_t *disp = (ds_display_t *) arg;
+
+	(void) disp;
+}
+
 /** @}
  */
Index: uspace/srv/hid/display/window.c
===================================================================
--- uspace/srv/hid/display/window.c	(revision ef734b78b1ad5a89163b14e6f38f206aa9b6e636)
+++ uspace/srv/hid/display/window.c	(revision 2ab8ab3c0a1e9771074c6e9d07ec1bf5e87ddafc)
@@ -49,5 +49,6 @@
 #include "window.h"
 
-static void ds_window_update_cb(void *, gfx_rect_t *);
+static void ds_window_invalidate_cb(void *, gfx_rect_t *);
+static void ds_window_update_cb(void *);
 static void ds_window_get_preview_rect(ds_window_t *, gfx_rect_t *);
 
@@ -106,6 +107,6 @@
 	}
 
-	rc = mem_gc_create(&params->rect, &alloc, ds_window_update_cb,
-	    (void *)wnd, &wnd->mgc);
+	rc = mem_gc_create(&params->rect, &alloc, ds_window_invalidate_cb,
+	    ds_window_update_cb, (void *)wnd, &wnd->mgc);
 	if (rc != EOK)
 		goto error;
@@ -789,9 +790,9 @@
 }
 
-/** Window memory GC update callback.
- *
- * This is called by the window's memory GC when a rectangle us updated.
- */
-static void ds_window_update_cb(void *arg, gfx_rect_t *rect)
+/** Window memory GC invalidate callback.
+ *
+ * This is called by the window's memory GC when a rectangle is modified.
+ */
+static void ds_window_invalidate_cb(void *arg, gfx_rect_t *rect)
 {
 	ds_window_t *wnd = (ds_window_t *)arg;
@@ -806,4 +807,15 @@
 }
 
+/** Window memory GC update callback.
+ *
+ * This is called by the window's memory GC when it is to be updated.
+ */
+static void ds_window_update_cb(void *arg)
+{
+	ds_window_t *wnd = (ds_window_t *)arg;
+
+	(void) wnd;
+}
+
 /** @}
  */
