Index: uspace/app/barber/barber.c
===================================================================
--- uspace/app/barber/barber.c	(revision 7a5825ba13ecf6c1685db24212c9f0857f03997c)
+++ uspace/app/barber/barber.c	(revision 12008adfe6b01422d866fedbe63b45f8cdb5d9a9)
@@ -1,3 +1,4 @@
 /*
+ * Copyright (c) 2020 Jiri Svoboda
  * Copyright (c) 2014 Martin Decky
  * All rights reserved.
@@ -33,19 +34,20 @@
  */
 
+#include <draw/surface.h>
+#include <draw/codec.h>
+#include <device/led_dev.h>
+#include <errno.h>
+#include <fibril_synch.h>
+#include <io/pixel.h>
+#include <loc.h>
+#include <stats.h>
 #include <stdbool.h>
-#include <errno.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <task.h>
-#include <loc.h>
-#include <stats.h>
 #include <str.h>
-#include <fibril_synch.h>
-#include <io/pixel.h>
-#include <device/led_dev.h>
-#include <window.h>
-#include <canvas.h>
-#include <draw/surface.h>
-#include <draw/codec.h>
+#include <ui/ui.h>
+#include <ui/wdecor.h>
+#include <ui/window.h>
+#include <ui/image.h>
 #include "images.h"
 
@@ -72,5 +74,7 @@
 } led_dev_t;
 
-static char *winreg = NULL;
+typedef struct {
+	ui_t *ui;
+} barber_t;
 
 static fibril_timer_t *led_timer = NULL;
@@ -89,6 +93,7 @@
 
 static fibril_timer_t *frame_timer = NULL;
-static canvas_t *frame_canvas;
+static ui_image_t *frame_img;
 static surface_t *frames[FRAMES];
+static gfx_bitmap_t *frame_bmp[FRAMES];
 
 static unsigned int frame = 0;
@@ -98,15 +103,67 @@
 static void frame_timer_callback(void *);
 
-static bool decode_frames(void)
-{
+static void wnd_close(ui_window_t *, void *);
+
+static ui_window_cb_t window_cb = {
+	.close = wnd_close
+};
+
+/** Window close button was clicked.
+ *
+ * @param window Window
+ * @param arg Argument (launcher)
+ */
+static void wnd_close(ui_window_t *window, void *arg)
+{
+	barber_t *barber = (barber_t *) arg;
+
+	ui_quit(barber->ui);
+}
+
+static bool decode_frames(gfx_context_t *gc)
+{
+	gfx_bitmap_alloc_t alloc;
+	surface_coord_t w, h;
+	gfx_bitmap_params_t params;
+	errno_t rc;
+
 	for (unsigned int i = 0; i < FRAMES; i++) {
-		frames[i] = decode_tga_gz(images[i].addr, images[i].size, 0);
+		frames[i] = decode_tga_gz(images[i].addr, images[i].size,
+		    SURFACE_FLAG_SHARED);
 		if (frames[i] == NULL) {
 			printf("Unable to decode frame %u.\n", i);
 			return false;
 		}
+
+		surface_get_resolution(frames[i], &w, &h);
+		gfx_bitmap_params_init(&params);
+		params.rect.p1.x = w;
+		params.rect.p1.y = h;
+
+		alloc.pitch = sizeof(uint32_t) * w;
+		alloc.off0 = 0;
+		alloc.pixels = surface_direct_access(frames[i]);
+
+		rc = gfx_bitmap_create(gc, &params, &alloc, &frame_bmp[i]);
+		if (rc != EOK) {
+			printf("Error creating bitmap.\n");
+			return false;
+		}
 	}
 
 	return true;
+}
+
+static void destroy_frames(void)
+{
+	unsigned i;
+
+	for (i = 0; i < FRAMES; i++) {
+		gfx_bitmap_destroy(frame_bmp[i]);
+		frame_bmp[i] = NULL;
+
+		surface_destroy(frames[i]);
+		frames[i] = NULL;
+	}
 }
 
@@ -192,4 +249,5 @@
 {
 	struct timespec prev;
+	gfx_rect_t rect;
 	getuptime(&prev);
 
@@ -198,5 +256,11 @@
 		frame = 0;
 
-	update_canvas(frame_canvas, frames[frame]);
+	rect.p0.x = 0;
+	rect.p0.y = 0;
+	rect.p1.x = FRAME_WIDTH;
+	rect.p1.y = FRAME_HEIGHT;
+
+	ui_image_set_bmp(frame_img, frame_bmp[frame], &rect);
+	(void) ui_image_paint(frame_img);
 
 	struct timespec cur;
@@ -255,5 +319,15 @@
 int main(int argc, char *argv[])
 {
-	const char *display_svc = DISPLAY_DEFAULT;
+	const char *display_spec = UI_DISPLAY_DEFAULT;
+	barber_t barber;
+	ui_t *ui;
+	ui_wnd_params_t params;
+	ui_window_t *window;
+	ui_resource_t *ui_res;
+	gfx_rect_t rect;
+	gfx_rect_t wrect;
+	gfx_rect_t app_rect;
+	gfx_context_t *gc;
+	gfx_coord2_t off;
 	int i;
 
@@ -268,5 +342,5 @@
 			}
 
-			display_svc = argv[i++];
+			display_spec = argv[i++];
 		} else {
 			printf("Invalid option '%s'.\n", argv[i]);
@@ -295,33 +369,70 @@
 	}
 
-	if (!decode_frames())
-		return 1;
-
-	winreg = argv[1];
-	window_t *main_window = window_open(display_svc, NULL,
-	    WINDOW_MAIN | WINDOW_DECORATED, "barber");
-	if (!main_window) {
-		printf("Cannot open main window.\n");
-		return 1;
-	}
-
-	frame_canvas = create_canvas(window_root(main_window), NULL,
-	    FRAME_WIDTH, FRAME_HEIGHT, frames[frame]);
-
-	if (!frame_canvas) {
-		window_close(main_window);
-		printf("Cannot create widgets.\n");
-		return 1;
-	}
-
-	window_resize(main_window, 0, 0, FRAME_WIDTH + 8, FRAME_HEIGHT + 28,
-	    WINDOW_PLACEMENT_RIGHT | WINDOW_PLACEMENT_BOTTOM);
-	window_exec(main_window);
+	rc = ui_create(display_spec, &ui);
+	if (rc != EOK) {
+		printf("Error creating UI on display %s.\n", display_spec);
+		return 1;
+	}
+
+	rect.p0.x = 0;
+	rect.p0.y = 0;
+	rect.p1.x = FRAME_WIDTH;
+	rect.p1.y = FRAME_HEIGHT;
+
+	ui_wnd_params_init(&params);
+	params.caption = "";
+	/*
+	 * Compute window rectangle such that application area corresponds
+	 * to rect
+	 */
+	ui_wdecor_rect_from_app(&rect, &wrect);
+	off = wrect.p0;
+	gfx_rect_rtranslate(&off, &wrect, &params.rect);
+
+	barber.ui = ui;
+
+	rc = ui_window_create(ui, &params, &window);
+	if (rc != EOK) {
+		printf("Error creating window.\n");
+		return 1;
+	}
+
+	ui_res = ui_window_get_res(window);
+	gc = ui_window_get_gc(window);
+	ui_window_get_app_rect(window, &app_rect);
+	ui_window_set_cb(window, &window_cb, (void *) &barber);
+
+	if (!decode_frames(gc))
+		return 1;
+
+	rc = ui_image_create(ui_res, frame_bmp[frame], &rect,
+	    &frame_img);
+	if (rc != EOK) {
+		printf("Error creating UI.\n");
+		return 1;
+	}
+
+	ui_image_set_rect(frame_img, &app_rect);
+
+	ui_window_add(window, ui_image_ctl(frame_img));
+
+	rc = ui_window_paint(window);
+	if (rc != EOK) {
+		printf("Error painting window.\n");
+		return 1;
+	}
 
 	plan_led_timer();
 	plan_frame_timer(0);
 
-	task_retval(0);
-	async_manager();
+	ui_run(ui);
+
+	/* Unlink bitmap from image so it is not destroyed along with it */
+	ui_image_set_bmp(frame_img, NULL, &rect);
+
+	ui_window_destroy(window);
+	ui_destroy(ui);
+
+	destroy_frames();
 
 	return 0;
Index: uspace/app/barber/meson.build
===================================================================
--- uspace/app/barber/meson.build	(revision 7a5825ba13ecf6c1685db24212c9f0857f03997c)
+++ uspace/app/barber/meson.build	(revision 12008adfe6b01422d866fedbe63b45f8cdb5d9a9)
@@ -27,5 +27,5 @@
 #
 
-deps = [ 'gui', 'draw', 'compress', 'softrend', 'math' ]
+deps = [ 'ui', 'draw', 'compress' ]
 
 _images = files(
Index: uspace/lib/ui/include/ui/image.h
===================================================================
--- uspace/lib/ui/include/ui/image.h	(revision 7a5825ba13ecf6c1685db24212c9f0857f03997c)
+++ uspace/lib/ui/include/ui/image.h	(revision 12008adfe6b01422d866fedbe63b45f8cdb5d9a9)
@@ -48,4 +48,5 @@
 extern void ui_image_destroy(ui_image_t *);
 extern ui_control_t *ui_image_ctl(ui_image_t *);
+extern void ui_image_set_bmp(ui_image_t *, gfx_bitmap_t *, gfx_rect_t *);
 extern void ui_image_set_rect(ui_image_t *, gfx_rect_t *);
 extern errno_t ui_image_paint(ui_image_t *);
Index: uspace/lib/ui/src/image.c
===================================================================
--- uspace/lib/ui/src/image.c	(revision 7a5825ba13ecf6c1685db24212c9f0857f03997c)
+++ uspace/lib/ui/src/image.c	(revision 12008adfe6b01422d866fedbe63b45f8cdb5d9a9)
@@ -134,4 +134,7 @@
 	gfx_coord2_t offs;
 
+	if (image->bitmap == NULL)
+		return EOK;
+
 	/*
 	 * UI image position does not depend on bitmap rectangle p0, so
@@ -149,4 +152,21 @@
 }
 
+/** Change image bitmap.
+ *
+ * Note that the caller must have saved the pointer to the previous bitmap
+ * in the image, because this causes it to be unlinked from the image and
+ * not destroyed (the ownership is transferred back to the caller).
+ *
+ * @param image Image
+ * @param bitmap New bitmap (ownership transferred to image) or @c NULL
+ * @param brect New bitmap rectangle
+ */
+void ui_image_set_bmp(ui_image_t *image, gfx_bitmap_t *bitmap,
+    gfx_rect_t *brect)
+{
+	image->bitmap = bitmap;
+	image->brect = *brect;
+}
+
 /** Destroy image control.
  *
Index: uspace/lib/ui/test/image.c
===================================================================
--- uspace/lib/ui/test/image.c	(revision 7a5825ba13ecf6c1685db24212c9f0857f03997c)
+++ uspace/lib/ui/test/image.c	(revision 12008adfe6b01422d866fedbe63b45f8cdb5d9a9)
@@ -87,5 +87,4 @@
 PCUT_TEST(set_rect)
 {
-
 	ui_image_t *image = NULL;
 	gfx_rect_t brect;
@@ -107,4 +106,49 @@
 	PCUT_ASSERT_INT_EQUALS(rect.p1.x, image->rect.p1.x);
 	PCUT_ASSERT_INT_EQUALS(rect.p1.y, image->rect.p1.y);
+
+	ui_image_destroy(image);
+}
+
+/** Set image bitmap */
+PCUT_TEST(set_bmp)
+{
+	ui_image_t *image = NULL;
+	gfx_rect_t brect;
+	gfx_rect_t rect;
+	gfx_bitmap_t *bitmap;
+	gfx_bitmap_params_t params;
+	dummy_gc_t *dgc;
+	gfx_context_t *gc;
+	errno_t rc;
+
+	rc = dummygc_create(&dgc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	gc = dummygc_get_ctx(dgc);
+
+	rc = ui_image_create(NULL, NULL, &brect, &image);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(image);
+
+	rect.p0.x = 1;
+	rect.p0.y = 2;
+	rect.p1.x = 3;
+	rect.p1.y = 4;
+
+	ui_image_set_rect(image, &rect);
+	PCUT_ASSERT_INT_EQUALS(rect.p0.x, image->rect.p0.x);
+	PCUT_ASSERT_INT_EQUALS(rect.p0.y, image->rect.p0.y);
+	PCUT_ASSERT_INT_EQUALS(rect.p1.x, image->rect.p1.x);
+	PCUT_ASSERT_INT_EQUALS(rect.p1.y, image->rect.p1.y);
+
+	gfx_bitmap_params_init(&params);
+	rc = gfx_bitmap_create(gc, &params, NULL, &bitmap);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_image_set_bmp(image, bitmap, &brect);
+	PCUT_ASSERT_EQUALS(bitmap, image->bitmap);
+
+	rc = ui_image_paint(image);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
 	ui_image_destroy(image);
@@ -138,4 +182,11 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
+	/* Check that we can paint image after setting bitmap to NULL */
+
+	ui_image_set_bmp(image, NULL, &brect);
+
+	rc = ui_image_paint(image);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
 	ui_image_destroy(image);
 }
