Index: uspace/app/uidemo/uidemo.c
===================================================================
--- uspace/app/uidemo/uidemo.c	(revision d4ea1f67c45352df9a960c7a2a7b061780108b7c)
+++ uspace/app/uidemo/uidemo.c	(revision 8965860ccae8af5b3d432226917c44dff1028ca8)
@@ -1070,5 +1070,5 @@
 	}
 
-	rc = ui_scrollbar_create(ui_res, &demo.scrollbar);
+	rc = ui_scrollbar_create(ui, window, &demo.scrollbar);
 	if (rc != EOK) {
 		printf("Error creating button.\n");
Index: uspace/lib/ui/include/types/ui/clickmatic.h
===================================================================
--- uspace/lib/ui/include/types/ui/clickmatic.h	(revision 8965860ccae8af5b3d432226917c44dff1028ca8)
+++ uspace/lib/ui/include/types/ui/clickmatic.h	(revision 8965860ccae8af5b3d432226917c44dff1028ca8)
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2022 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup libui
+ * @{
+ */
+/**
+ * @file Clickmatic
+ */
+
+#ifndef _UI_TYPES_CLICKMATIC_H
+#define _UI_TYPES_CLICKMATIC_H
+
+struct ui_clickmatic;
+typedef struct ui_clickmatic ui_clickmatic_t;
+
+/** Clickmatic callbacks */
+typedef struct ui_clickmatic_cb {
+	void (*clicked)(ui_clickmatic_t *, void *);
+} ui_clickmatic_cb_t;
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/ui/include/ui/clickmatic.h
===================================================================
--- uspace/lib/ui/include/ui/clickmatic.h	(revision 8965860ccae8af5b3d432226917c44dff1028ca8)
+++ uspace/lib/ui/include/ui/clickmatic.h	(revision 8965860ccae8af5b3d432226917c44dff1028ca8)
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2022 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup libui
+ * @{
+ */
+/**
+ * @file Clickmatic
+ */
+
+#ifndef _UI_CLICKMATIC_H
+#define _UI_CLICKMATIC_H
+
+#include <errno.h>
+#include <gfx/coord.h>
+#include <types/ui/clickmatic.h>
+#include <types/ui/ui.h>
+
+extern errno_t ui_clickmatic_create(ui_t *, ui_clickmatic_t **);
+extern void ui_clickmatic_set_cb(ui_clickmatic_t *, ui_clickmatic_cb_t *,
+    void *);
+extern void ui_clickmatic_destroy(ui_clickmatic_t *);
+extern void ui_clickmatic_press(ui_clickmatic_t *);
+extern void ui_clickmatic_release(ui_clickmatic_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/ui/include/ui/scrollbar.h
===================================================================
--- uspace/lib/ui/include/ui/scrollbar.h	(revision d4ea1f67c45352df9a960c7a2a7b061780108b7c)
+++ uspace/lib/ui/include/ui/scrollbar.h	(revision 8965860ccae8af5b3d432226917c44dff1028ca8)
@@ -43,8 +43,9 @@
 #include <types/ui/control.h>
 #include <types/ui/event.h>
-#include <types/ui/resource.h>
 #include <types/ui/scrollbar.h>
+#include <types/ui/ui.h>
+#include <types/ui/window.h>
 
-extern errno_t ui_scrollbar_create(ui_resource_t *, ui_scrollbar_t **);
+extern errno_t ui_scrollbar_create(ui_t *, ui_window_t *, ui_scrollbar_t **);
 extern void ui_scrollbar_destroy(ui_scrollbar_t *);
 extern ui_control_t *ui_scrollbar_ctl(ui_scrollbar_t *);
@@ -62,4 +63,5 @@
 extern void ui_scrollbar_release(ui_scrollbar_t *, gfx_coord2_t *);
 extern void ui_scrollbar_update(ui_scrollbar_t *, gfx_coord2_t *);
+extern void ui_scrollbar_throughs_update(ui_scrollbar_t *, gfx_coord2_t *);
 extern void ui_scrollbar_up(ui_scrollbar_t *);
 extern void ui_scrollbar_down(ui_scrollbar_t *);
Index: uspace/lib/ui/include/ui/ui.h
===================================================================
--- uspace/lib/ui/include/ui/ui.h	(revision d4ea1f67c45352df9a960c7a2a7b061780108b7c)
+++ uspace/lib/ui/include/ui/ui.h	(revision 8965860ccae8af5b3d432226917c44dff1028ca8)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2021 Jiri Svoboda
+ * Copyright (c) 2022 Jiri Svoboda
  * All rights reserved.
  *
@@ -41,4 +41,5 @@
 #include <io/console.h>
 #include <stdbool.h>
+#include <types/ui/clickmatic.h>
 #include <types/ui/ui.h>
 
@@ -54,4 +55,7 @@
 extern errno_t ui_suspend(ui_t *);
 extern errno_t ui_resume(ui_t *);
+extern void ui_lock(ui_t *);
+extern void ui_unlock(ui_t *);
+extern ui_clickmatic_t *ui_get_clickmatic(ui_t *);
 
 #endif
Index: uspace/lib/ui/meson.build
===================================================================
--- uspace/lib/ui/meson.build	(revision d4ea1f67c45352df9a960c7a2a7b061780108b7c)
+++ uspace/lib/ui/meson.build	(revision 8965860ccae8af5b3d432226917c44dff1028ca8)
@@ -1,4 +1,4 @@
 #
-# Copyright (c) 2021 Jiri Svoboda
+# Copyright (c) 2022 Jiri Svoboda
 # All rights reserved.
 #
@@ -31,4 +31,5 @@
 	'src/accel.c',
 	'src/checkbox.c',
+	'src/clickmatic.c',
 	'src/control.c',
 	'src/dummygc.c',
@@ -58,4 +59,5 @@
 	'test/accel.c',
 	'test/checkbox.c',
+	'test/clickmatic.c',
 	'test/control.c',
 	'test/entry.c',
Index: uspace/lib/ui/private/clickmatic.h
===================================================================
--- uspace/lib/ui/private/clickmatic.h	(revision 8965860ccae8af5b3d432226917c44dff1028ca8)
+++ uspace/lib/ui/private/clickmatic.h	(revision 8965860ccae8af5b3d432226917c44dff1028ca8)
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2022 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup libui
+ * @{
+ */
+/**
+ * @file Clickmatic structure
+ *
+ */
+
+#ifndef _UI_PRIVATE_CLICKMATIC_H
+#define _UI_PRIVATE_CLICKMATIC_H
+
+#include <fibril_synch.h>
+#include <gfx/coord.h>
+
+/** Actual structure of clickmatic.
+ *
+ * This is private to libui.
+ */
+struct ui_clickmatic {
+	/** Containing user interface */
+	struct ui *ui;
+	/** Callbacks */
+	struct ui_clickmatic_cb *cb;
+	/** Callback argument */
+	void *arg;
+	/** Click position */
+	gfx_coord2_t pos;
+	/** Timer */
+	fibril_timer_t *timer;
+};
+
+extern void ui_clickmatic_clicked(ui_clickmatic_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/ui/private/scrollbar.h
===================================================================
--- uspace/lib/ui/private/scrollbar.h	(revision d4ea1f67c45352df9a960c7a2a7b061780108b7c)
+++ uspace/lib/ui/private/scrollbar.h	(revision 8965860ccae8af5b3d432226917c44dff1028ca8)
@@ -48,6 +48,8 @@
 	/** Base control object */
 	struct ui_control *control;
-	/** UI resource */
-	struct ui_resource *res;
+	/** UI */
+	struct ui *ui;
+	/** UI window containing scrollbar */
+	struct ui_window *window;
 	/** Callbacks */
 	struct ui_scrollbar_cb *cb;
@@ -66,6 +68,10 @@
 	/** Up through is currently held down */
 	bool up_through_held;
+	/** Pointer is inside up through */
+	bool up_through_inside;
 	/** Down through is currently held down */
 	bool down_through_held;
+	/** Pointer is inside down through */
+	bool down_through_inside;
 	/** Position where thumb was pressed */
 	gfx_coord2_t press_pos;
@@ -74,4 +80,6 @@
 	/** Thumb position */
 	gfx_coord_t pos;
+	/** Last cursor position (when through is held) */
+	gfx_coord2_t last_curs_pos;
 };
 
@@ -97,5 +105,4 @@
 extern errno_t ui_scrollbar_paint_gfx(ui_scrollbar_t *);
 extern errno_t ui_scrollbar_paint_text(ui_scrollbar_t *);
-extern errno_t ui_scrollbar_thumb_clear(ui_scrollbar_t *);
 extern void ui_scrollbar_get_geom(ui_scrollbar_t *, ui_scrollbar_geom_t *);
 
Index: uspace/lib/ui/private/ui.h
===================================================================
--- uspace/lib/ui/private/ui.h	(revision d4ea1f67c45352df9a960c7a2a7b061780108b7c)
+++ uspace/lib/ui/private/ui.h	(revision 8965860ccae8af5b3d432226917c44dff1028ca8)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2021 Jiri Svoboda
+ * Copyright (c) 2022 Jiri Svoboda
  * All rights reserved.
  *
@@ -41,4 +41,5 @@
 #include <gfx/coord.h>
 #include <display.h>
+#include <fibril_synch.h>
 #include <io/console.h>
 #include <stdbool.h>
@@ -63,4 +64,8 @@
 	/** Windows (in stacking order, ui_window_t) */
 	list_t windows;
+	/** UI lock */
+	fibril_mutex_t lock;
+	/** Clickmatic */
+	struct ui_clickmatic *clickmatic;
 };
 
Index: uspace/lib/ui/src/clickmatic.c
===================================================================
--- uspace/lib/ui/src/clickmatic.c	(revision 8965860ccae8af5b3d432226917c44dff1028ca8)
+++ uspace/lib/ui/src/clickmatic.c	(revision 8965860ccae8af5b3d432226917c44dff1028ca8)
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 2022 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup libui
+ * @{
+ */
+/**
+ * @file Clickmatic.
+ *
+ * Clickmatic is used to periodically generate events in particular cases
+ * when a mouse button is held down, such as when holding the button or
+ * through of a scrollbar.
+ */
+
+#include <fibril_synch.h>
+#include <stdlib.h>
+#include <ui/clickmatic.h>
+#include <ui/ui.h>
+#include "../private/clickmatic.h"
+
+enum {
+	/** Initial clickmatic delay in MS */
+	clickmatic_delay_ms = 500,
+	/** Clickmatic repeat rate in clicks per second */
+	clickmatic_rate = 10
+};
+
+static void ui_clickmatic_timer_fun(void *);
+
+/** Create clickmatic.
+ *
+ * @param ui UI
+ * @param rclickmatic Place to store pointer to new clickmatic
+ * @return EOK on success or an error code
+ */
+errno_t ui_clickmatic_create(ui_t *ui, ui_clickmatic_t **rclickmatic)
+{
+	ui_clickmatic_t *clickmatic;
+
+	clickmatic = calloc(1, sizeof(ui_clickmatic_t));
+	if (clickmatic == NULL)
+		return ENOMEM;
+
+	clickmatic->ui = ui;
+	clickmatic->timer = fibril_timer_create(NULL);
+	if (clickmatic->timer == NULL) {
+		free(clickmatic);
+		return ENOMEM;
+	}
+
+	*rclickmatic = clickmatic;
+	return EOK;
+}
+
+/** Set clickmatick callbacks.
+ *
+ * @param clickmatic Clickmatic
+ * @param cb Callbacks
+ * @param arg Argument to callbacks
+ */
+void ui_clickmatic_set_cb(ui_clickmatic_t *clickmatic, ui_clickmatic_cb_t *cb,
+    void *arg)
+{
+	clickmatic->cb = cb;
+	clickmatic->arg = arg;
+}
+
+/** Destroy clickmatic.
+ *
+ * @param clickmatic Clickmatic or @c NULL
+ */
+void ui_clickmatic_destroy(ui_clickmatic_t *clickmatic)
+{
+	if (clickmatic == NULL)
+		return;
+
+	fibril_timer_destroy(clickmatic->timer);
+	free(clickmatic);
+}
+
+/** Activate clickmatic.
+ *
+ * This generates one click event, then starts repeating after delay.
+ *
+ * @param clickmatic Clickmatic
+ */
+void ui_clickmatic_press(ui_clickmatic_t *clickmatic)
+{
+	ui_clickmatic_clicked(clickmatic);
+
+	fibril_timer_set(clickmatic->timer, 1000 * clickmatic_delay_ms,
+	    ui_clickmatic_timer_fun, (void *)clickmatic);
+}
+
+/** Deactivate clickmatic.
+ *
+ * Stops generating events.
+ *
+ * @param clickmatic Clickmatic
+ */
+void ui_clickmatic_release(ui_clickmatic_t *clickmatic)
+{
+	(void) fibril_timer_clear(clickmatic->timer);
+}
+
+/** Clickmatic clicked event.
+ *
+ * @param clickmatic Clickmatic
+ */
+void ui_clickmatic_clicked(ui_clickmatic_t *clickmatic)
+{
+	if (clickmatic->cb != NULL && clickmatic->cb->clicked != NULL)
+		clickmatic->cb->clicked(clickmatic, clickmatic->arg);
+}
+
+/** Clickmatic timer function.
+ *
+ * @param arg Argument (ui_clickmatic_t *)
+ */
+static void ui_clickmatic_timer_fun(void *arg)
+{
+	ui_clickmatic_t *clickmatic = (ui_clickmatic_t *)arg;
+
+	/*
+	 * Because we are operating in a different fibril, we must lock
+	 * the UI to ensure mutual exclusion with normal UI events
+	 */
+
+	ui_lock(clickmatic->ui);
+	ui_clickmatic_clicked(clickmatic);
+	ui_unlock(clickmatic->ui);
+
+	fibril_timer_set(clickmatic->timer, 1000000 / clickmatic_rate,
+	    ui_clickmatic_timer_fun, (void *)clickmatic);
+}
+
+/** @}
+ */
Index: uspace/lib/ui/src/scrollbar.c
===================================================================
--- uspace/lib/ui/src/scrollbar.c	(revision d4ea1f67c45352df9a960c7a2a7b061780108b7c)
+++ uspace/lib/ui/src/scrollbar.c	(revision 8965860ccae8af5b3d432226917c44dff1028ca8)
@@ -53,6 +53,6 @@
  * button on the up/down through generates page up / page down events.
  *
- * TODO: Up/down buttons/throughs should be equipped with an autorepeat
- * mechanism: after an initial delay, start repeating at a preset rate.
+ * Pressing and holding down mouse button on up / down button or up / down
+ * through will auto-scroll (using clickmatic).
  */
 
@@ -65,8 +65,12 @@
 #include <stdlib.h>
 #include <str.h>
+#include <ui/clickmatic.h>
 #include <ui/control.h>
 #include <ui/paint.h>
 #include <ui/pbutton.h>
 #include <ui/scrollbar.h>
+#include <ui/ui.h>
+#include <ui/window.h>
+#include "../private/pbutton.h"
 #include "../private/resource.h"
 #include "../private/scrollbar.h"
@@ -92,19 +96,23 @@
 
 static void ui_scrollbar_up_btn_down(ui_pbutton_t *, void *);
+static void ui_scrollbar_up_btn_up(ui_pbutton_t *, void *);
 static void ui_scrollbar_down_btn_down(ui_pbutton_t *, void *);
+static void ui_scrollbar_down_btn_up(ui_pbutton_t *, void *);
 static void ui_scrollbar_ctl_destroy(void *);
 static errno_t ui_scrollbar_ctl_paint(void *);
 static ui_evclaim_t ui_scrollbar_ctl_pos_event(void *, pos_event_t *);
 
-ui_pbutton_cb_t ui_scrollbar_up_btn_cb = {
-	.down = ui_scrollbar_up_btn_down
+static ui_pbutton_cb_t ui_scrollbar_up_btn_cb = {
+	.down = ui_scrollbar_up_btn_down,
+	.up = ui_scrollbar_up_btn_up
 };
 
-ui_pbutton_cb_t ui_scrollbar_down_btn_cb = {
-	.down = ui_scrollbar_down_btn_down
+static ui_pbutton_cb_t ui_scrollbar_down_btn_cb = {
+	.down = ui_scrollbar_down_btn_down,
+	.up = ui_scrollbar_down_btn_up
 };
 
 /** Scrollbar control ops */
-ui_control_ops_t ui_scrollbar_ops = {
+static ui_control_ops_t ui_scrollbar_ops = {
 	.destroy = ui_scrollbar_ctl_destroy,
 	.paint = ui_scrollbar_ctl_paint,
@@ -112,15 +120,44 @@
 };
 
+static void ui_scrollbar_cm_up(ui_clickmatic_t *, void *);
+static void ui_scrollbar_cm_down(ui_clickmatic_t *, void *);
+static void ui_scrollbar_cm_page_up(ui_clickmatic_t *, void *);
+static void ui_scrollbar_cm_page_down(ui_clickmatic_t *, void *);
+
+/** Scrollbar clickmatic up callbacks */
+ui_clickmatic_cb_t ui_scrollbar_clickmatic_up_cb = {
+	.clicked = ui_scrollbar_cm_up
+};
+
+/** Scrollbar clickmatic down callbacks */
+ui_clickmatic_cb_t ui_scrollbar_clickmatic_down_cb = {
+	.clicked = ui_scrollbar_cm_down
+};
+
+/** Scrollbar clickmatic page up callbacks */
+ui_clickmatic_cb_t ui_scrollbar_clickmatic_page_up_cb = {
+	.clicked = ui_scrollbar_cm_page_up
+};
+
+/** Scrollbar clickmatic page down callbacks */
+ui_clickmatic_cb_t ui_scrollbar_clickmatic_page_down_cb = {
+	.clicked = ui_scrollbar_cm_page_down
+};
+
 /** Create new scrollbar.
  *
- * @param resource UI resource
+ * @param ui UI
+ * @param window Window containing scrollbar
  * @param rscrollbar Place to store pointer to new scrollbar
  * @return EOK on success, ENOMEM if out of memory
  */
-errno_t ui_scrollbar_create(ui_resource_t *resource,
+errno_t ui_scrollbar_create(ui_t *ui, ui_window_t *window,
     ui_scrollbar_t **rscrollbar)
 {
 	ui_scrollbar_t *scrollbar;
+	ui_resource_t *resource;
 	errno_t rc;
+
+	resource = ui_window_get_res(window);
 
 	scrollbar = calloc(1, sizeof(ui_scrollbar_t));
@@ -155,5 +192,6 @@
 	    ui_scrollbar_def_thumb_len;
 
-	scrollbar->res = resource;
+	scrollbar->ui = ui;
+	scrollbar->window = window;
 	*rscrollbar = scrollbar;
 	return EOK;
@@ -283,19 +321,9 @@
     gfx_rect_t *rect, gfx_rect_t *inside)
 {
-	return ui_paint_bevel(scrollbar->res->gc, rect,
-	    scrollbar->res->btn_highlight_color,
-	    scrollbar->res->btn_shadow_color, ui_scrollbar_thumb_bevel_width, inside);
-}
-
-/** Determine scrollbar thumb rectagle.
- *
- * @param scrollbar Scrollbar
- */
-static void ui_scrollbar_thumb_rect(ui_scrollbar_t *scrollbar, gfx_rect_t *rect)
-{
-	ui_scrollbar_geom_t geom;
-
-	ui_scrollbar_get_geom(scrollbar, &geom);
-	*rect = geom.thumb_rect;
+	ui_resource_t *resource = ui_window_get_res(scrollbar->window);
+
+	return ui_paint_bevel(resource->gc, rect,
+	    resource->btn_highlight_color,
+	    resource->btn_shadow_color, ui_scrollbar_thumb_bevel_width, inside);
 }
 
@@ -310,9 +338,12 @@
 gfx_coord_t ui_scrollbar_through_length(ui_scrollbar_t *scrollbar)
 {
+	ui_resource_t *resource;
 	gfx_coord2_t dims;
 	gfx_coord_t w;
 
+	resource = ui_window_get_res(scrollbar->window);
+
 	gfx_rect_dims(&scrollbar->rect, &dims);
-	w = scrollbar->res->textmode ? ui_scrollbar_btn_len_text :
+	w = resource->textmode ? ui_scrollbar_btn_len_text :
 	    ui_scrollbar_btn_len;
 	return dims.x - 2 * w;
@@ -339,8 +370,11 @@
 void ui_scrollbar_set_thumb_length(ui_scrollbar_t *scrollbar, gfx_coord_t len)
 {
+	ui_resource_t *resource;
 	gfx_coord_t min_len;
 	gfx_coord_t max_len;
 
-	min_len = scrollbar->res->textmode ?
+	resource = ui_window_get_res(scrollbar->window);
+
+	min_len = resource->textmode ?
 	    ui_scrollbar_min_thumb_len_text :
 	    ui_scrollbar_min_thumb_len;
@@ -353,5 +387,4 @@
 
 	if (len != scrollbar->thumb_len) {
-		(void) ui_scrollbar_thumb_clear(scrollbar);
 		scrollbar->thumb_len = len;
 		(void) ui_scrollbar_paint(scrollbar);
@@ -387,7 +420,8 @@
 
 	if (pos != scrollbar->pos) {
-		(void) ui_scrollbar_thumb_clear(scrollbar);
 		scrollbar->pos = pos;
 		(void) ui_scrollbar_paint(scrollbar);
+		ui_scrollbar_throughs_update(scrollbar,
+		    &scrollbar->last_curs_pos);
 		ui_scrollbar_moved(scrollbar, pos);
 	}
@@ -401,4 +435,5 @@
 errno_t ui_scrollbar_paint_gfx(ui_scrollbar_t *scrollbar)
 {
+	ui_resource_t *resource;
 	ui_scrollbar_geom_t geom;
 	gfx_rect_t brect;
@@ -406,21 +441,23 @@
 	errno_t rc;
 
+	resource = ui_window_get_res(scrollbar->window);
+
 	ui_scrollbar_get_geom(scrollbar, &geom);
 
 	/* Paint scrollbar frame */
 
-	rc = ui_paint_inset_frame(scrollbar->res, &scrollbar->rect, NULL);
+	rc = ui_paint_inset_frame(resource, &scrollbar->rect, NULL);
 	if (rc != EOK)
 		goto error;
 
 	/* Paint scrollbar up through */
-	rc = gfx_set_color(scrollbar->res->gc,
-	    scrollbar->up_through_held ?
-	    scrollbar->res->sbar_act_through_color :
-	    scrollbar->res->sbar_through_color);
-	if (rc != EOK)
-		goto error;
-
-	rc = gfx_fill_rect(scrollbar->res->gc, &geom.up_through_rect);
+	rc = gfx_set_color(resource->gc,
+	    scrollbar->up_through_held && scrollbar->up_through_inside ?
+	    resource->sbar_act_through_color :
+	    resource->sbar_through_color);
+	if (rc != EOK)
+		goto error;
+
+	rc = gfx_fill_rect(resource->gc, &geom.up_through_rect);
 	if (rc != EOK)
 		goto error;
@@ -428,12 +465,12 @@
 	/* Paint scrollbar down through */
 
-	rc = gfx_set_color(scrollbar->res->gc,
-	    scrollbar->down_through_held ?
-	    scrollbar->res->sbar_act_through_color :
-	    scrollbar->res->sbar_through_color);
-	if (rc != EOK)
-		goto error;
-
-	rc = gfx_fill_rect(scrollbar->res->gc, &geom.down_through_rect);
+	rc = gfx_set_color(resource->gc,
+	    scrollbar->down_through_held && scrollbar->down_through_inside ?
+	    resource->sbar_act_through_color :
+	    resource->sbar_through_color);
+	if (rc != EOK)
+		goto error;
+
+	rc = gfx_fill_rect(resource->gc, &geom.down_through_rect);
 	if (rc != EOK)
 		goto error;
@@ -441,5 +478,5 @@
 	/* Paint scrollbar thumb */
 
-	rc = ui_scrollbar_paint_thumb_frame(scrollbar->res, &geom.thumb_rect,
+	rc = ui_scrollbar_paint_thumb_frame(resource, &geom.thumb_rect,
 	    ui_scrollbar_thumb_frame_thickness, &brect);
 	if (rc != EOK)
@@ -450,9 +487,9 @@
 		goto error;
 
-	rc = gfx_set_color(scrollbar->res->gc, scrollbar->res->btn_face_color);
-	if (rc != EOK)
-		goto error;
-
-	rc = gfx_fill_rect(scrollbar->res->gc, &irect);
+	rc = gfx_set_color(resource->gc, resource->btn_face_color);
+	if (rc != EOK)
+		goto error;
+
+	rc = gfx_fill_rect(resource->gc, &irect);
 	if (rc != EOK)
 		goto error;
@@ -466,5 +503,5 @@
 		goto error;
 
-	rc = gfx_update(scrollbar->res->gc);
+	rc = gfx_update(resource->gc);
 	if (rc != EOK)
 		goto error;
@@ -482,4 +519,5 @@
 errno_t ui_scrollbar_paint_text(ui_scrollbar_t *scrollbar)
 {
+	ui_resource_t *resource;
 	gfx_coord2_t pos;
 	gfx_text_fmt_t fmt;
@@ -490,4 +528,6 @@
 	errno_t rc;
 
+	resource = ui_window_get_res(scrollbar->window);
+
 	/* Paint scrollbar through */
 
@@ -496,6 +536,6 @@
 
 	gfx_text_fmt_init(&fmt);
-	fmt.font = scrollbar->res->font;
-	fmt.color = scrollbar->res->sbar_through_color;
+	fmt.font = resource->font;
+	fmt.color = resource->sbar_through_color;
 	fmt.halign = gfx_halign_left;
 	fmt.valign = gfx_valign_top;
@@ -552,5 +592,5 @@
 		goto error;
 
-	rc = gfx_update(scrollbar->res->gc);
+	rc = gfx_update(resource->gc);
 	if (rc != EOK)
 		goto error;
@@ -568,5 +608,7 @@
 errno_t ui_scrollbar_paint(ui_scrollbar_t *scrollbar)
 {
-	if (scrollbar->res->textmode)
+	ui_resource_t *resource = ui_window_get_res(scrollbar->window);
+
+	if (resource->textmode)
 		return ui_scrollbar_paint_text(scrollbar);
 	else
@@ -581,9 +623,12 @@
 void ui_scrollbar_get_geom(ui_scrollbar_t *scrollbar, ui_scrollbar_geom_t *geom)
 {
+	ui_resource_t *resource;
 	gfx_coord_t btn_len;
 	gfx_rect_t orect;
 	gfx_rect_t irect;
 
-	if (scrollbar->res->textmode) {
+	resource = ui_window_get_res(scrollbar->window);
+
+	if (resource->textmode) {
 		btn_len = ui_scrollbar_btn_len_text;
 	} else {
@@ -591,11 +636,11 @@
 	}
 
-	if (scrollbar->res->textmode) {
+	if (resource->textmode) {
 		irect = scrollbar->rect;
 		orect = scrollbar->rect;
 	} else {
-		ui_paint_get_inset_frame_inside(scrollbar->res,
+		ui_paint_get_inset_frame_inside(resource,
 		    &scrollbar->rect, &irect);
-		ui_paint_get_bevel_inside(scrollbar->res->gc,
+		ui_paint_get_bevel_inside(resource->gc,
 		    &scrollbar->rect, 1, &orect);
 	}
@@ -636,33 +681,4 @@
 }
 
-/** Clear scrollbar thumb.
- *
- * @param scrollbar Scrollbar
- * @return EOK on success or an error code
- */
-errno_t ui_scrollbar_thumb_clear(ui_scrollbar_t *scrollbar)
-{
-	gfx_rect_t rect;
-	errno_t rc;
-
-	/* No need to clear thumb in text mode */
-	if (scrollbar->res->textmode)
-		return EOK;
-
-	ui_scrollbar_thumb_rect(scrollbar, &rect);
-
-	rc = gfx_set_color(scrollbar->res->gc, scrollbar->res->wnd_face_color);
-	if (rc != EOK)
-		goto error;
-
-	rc = gfx_fill_rect(scrollbar->res->gc, &rect);
-	if (rc != EOK)
-		goto error;
-
-	return EOK;
-error:
-	return rc;
-}
-
 /** Press down scrollbar thumb.
  *
@@ -688,11 +704,12 @@
 void ui_scrollbar_up_through_press(ui_scrollbar_t *scrollbar)
 {
-	if (scrollbar->up_through_held)
-		return;
+	ui_clickmatic_t *clickmatic = ui_get_clickmatic(scrollbar->ui);
 
 	scrollbar->up_through_held = true;
-	(void) ui_scrollbar_paint(scrollbar);
-
-	ui_scrollbar_page_up(scrollbar);
+	scrollbar->up_through_inside = true;
+
+	ui_clickmatic_set_cb(clickmatic, &ui_scrollbar_clickmatic_page_up_cb,
+	    (void *) scrollbar);
+	ui_clickmatic_press(clickmatic);
 }
 
@@ -703,11 +720,12 @@
 void ui_scrollbar_down_through_press(ui_scrollbar_t *scrollbar)
 {
-	if (scrollbar->down_through_held)
-		return;
+	ui_clickmatic_t *clickmatic = ui_get_clickmatic(scrollbar->ui);
 
 	scrollbar->down_through_held = true;
-	(void) ui_scrollbar_paint(scrollbar);
-
-	ui_scrollbar_page_down(scrollbar);
+	scrollbar->down_through_inside = true;
+
+	ui_clickmatic_set_cb(clickmatic, &ui_scrollbar_clickmatic_page_down_cb,
+	    (void *) scrollbar);
+	ui_clickmatic_press(clickmatic);
 }
 
@@ -719,4 +737,6 @@
 void ui_scrollbar_release(ui_scrollbar_t *scrollbar, gfx_coord2_t *pos)
 {
+	ui_clickmatic_t *clickmatic;
+
 	if (scrollbar->thumb_held) {
 		ui_scrollbar_update(scrollbar, pos);
@@ -725,4 +745,8 @@
 
 	if (scrollbar->up_through_held || scrollbar->down_through_held) {
+		clickmatic = ui_get_clickmatic(scrollbar->ui);
+		ui_clickmatic_release(clickmatic);
+		ui_clickmatic_set_cb(clickmatic, NULL, NULL);
+
 		scrollbar->up_through_held = false;
 		scrollbar->down_through_held = false;
@@ -731,4 +755,39 @@
 }
 
+/** Update state of scrollbar throuhgs.
+ *
+ * Update state of scrollbar throughs after mouse cursor or thumb has moved.
+ *
+ * @param scrollbar Scrollbar
+ * @param pos Mouse cursor position
+ */
+void ui_scrollbar_throughs_update(ui_scrollbar_t *scrollbar, gfx_coord2_t *pos)
+{
+	ui_scrollbar_geom_t geom;
+	bool inside_up;
+	bool inside_down;
+
+	ui_scrollbar_get_geom(scrollbar, &geom);
+
+	inside_up = gfx_pix_inside_rect(pos, &geom.up_through_rect);
+	inside_down = gfx_pix_inside_rect(pos, &geom.down_through_rect);
+
+	if (inside_up && !scrollbar->up_through_inside) {
+		scrollbar->up_through_inside = true;
+		(void) ui_scrollbar_paint(scrollbar);
+	} else if (!inside_up && scrollbar->up_through_inside) {
+		scrollbar->up_through_inside = false;
+		(void) ui_scrollbar_paint(scrollbar);
+	}
+
+	if (inside_down && !scrollbar->down_through_inside) {
+		scrollbar->down_through_inside = true;
+		(void) ui_scrollbar_paint(scrollbar);
+	} else if (!inside_down && scrollbar->down_through_inside) {
+		scrollbar->down_through_inside = false;
+		(void) ui_scrollbar_paint(scrollbar);
+	}
+}
+
 /** Pointer moved.
  *
@@ -744,4 +803,6 @@
 		ui_scrollbar_set_pos(scrollbar, spos);
 	}
+
+	ui_scrollbar_throughs_update(scrollbar, pos);
 }
 
@@ -845,4 +906,5 @@
 	case POS_UPDATE:
 		ui_scrollbar_update(scrollbar, &pos);
+		scrollbar->last_curs_pos = pos;
 		break;
 	case POS_DCLICK:
@@ -861,6 +923,23 @@
 {
 	ui_scrollbar_t *scrollbar = (ui_scrollbar_t *)arg;
-
-	ui_scrollbar_up(scrollbar);
+	ui_clickmatic_t *clickmatic = ui_get_clickmatic(scrollbar->ui);
+
+	ui_clickmatic_set_cb(clickmatic, &ui_scrollbar_clickmatic_up_cb,
+	    (void *) scrollbar);
+	ui_clickmatic_press(clickmatic);
+}
+
+/** Scrollbar up button released.
+ *
+ * @param pbutton Up button
+ * @param arg Argument (ui_scrollbar_t *)
+ */
+static void ui_scrollbar_up_btn_up(ui_pbutton_t *pbutton, void *arg)
+{
+	ui_scrollbar_t *scrollbar = (ui_scrollbar_t *)arg;
+	ui_clickmatic_t *clickmatic = ui_get_clickmatic(scrollbar->ui);
+
+	ui_clickmatic_release(clickmatic);
+	ui_clickmatic_set_cb(clickmatic, NULL, NULL);
 }
 
@@ -873,6 +952,23 @@
 {
 	ui_scrollbar_t *scrollbar = (ui_scrollbar_t *)arg;
-
-	ui_scrollbar_down(scrollbar);
+	ui_clickmatic_t *clickmatic = ui_get_clickmatic(scrollbar->ui);
+
+	ui_clickmatic_set_cb(clickmatic, &ui_scrollbar_clickmatic_down_cb,
+	    (void *) scrollbar);
+	ui_clickmatic_press(clickmatic);
+}
+
+/** Scrollbar down button released.
+ *
+ * @param pbutton Down button
+ * @param arg Argument (ui_scrollbar_t *)
+ */
+static void ui_scrollbar_down_btn_up(ui_pbutton_t *pbutton, void *arg)
+{
+	ui_scrollbar_t *scrollbar = (ui_scrollbar_t *)arg;
+	ui_clickmatic_t *clickmatic = ui_get_clickmatic(scrollbar->ui);
+
+	ui_clickmatic_release(clickmatic);
+	ui_clickmatic_set_cb(clickmatic, NULL, NULL);
 }
 
@@ -908,9 +1004,61 @@
 ui_evclaim_t ui_scrollbar_ctl_pos_event(void *arg, pos_event_t *event)
 {
-	ui_scrollbar_t *scrollbar = (ui_scrollbar_t *) arg;
+	ui_scrollbar_t *scrollbar = (ui_scrollbar_t *)arg;
 
 	return ui_scrollbar_pos_event(scrollbar, event);
 }
 
+/** Scrollbar clickmatic up button click event.
+ *
+ * @param clickmatic Clickmatic
+ * @param arg Argument (ui_scrollbar_t *)
+ */
+static void ui_scrollbar_cm_up(ui_clickmatic_t *clickmatic, void *arg)
+{
+	ui_scrollbar_t *scrollbar = (ui_scrollbar_t *)arg;
+
+	if (scrollbar->up_btn->inside)
+		ui_scrollbar_up(scrollbar);
+}
+
+/** Scrollbar clickmatic down button click event.
+ *
+ * @param clickmatic Clickmatic
+ * @param arg Argument (ui_scrollbar_t *)
+ */
+static void ui_scrollbar_cm_down(ui_clickmatic_t *clickmatic, void *arg)
+{
+	ui_scrollbar_t *scrollbar = (ui_scrollbar_t *)arg;
+
+	if (scrollbar->down_btn->inside)
+		ui_scrollbar_down(scrollbar);
+}
+
+/** Scrollbar clickmatic up through click event.
+ *
+ * @param clickmatic Clickmatic
+ * @param arg Argument (ui_scrollbar_t *)
+ */
+static void ui_scrollbar_cm_page_up(ui_clickmatic_t *clickmatic, void *arg)
+{
+	ui_scrollbar_t *scrollbar = (ui_scrollbar_t *)arg;
+
+	if (scrollbar->up_through_inside)
+		ui_scrollbar_page_up(scrollbar);
+}
+
+/** Scrollbar clickmatic down through click event.
+ *
+ * @param clickmatic Clickmatic
+ * @param arg Argument (ui_scrollbar_t *)
+ */
+static void ui_scrollbar_cm_page_down(ui_clickmatic_t *clickmatic, void *arg)
+{
+	ui_scrollbar_t *scrollbar = (ui_scrollbar_t *)arg;
+
+	if (scrollbar->down_through_inside)
+		ui_scrollbar_page_down(scrollbar);
+}
+
 /** @}
  */
Index: uspace/lib/ui/src/ui.c
===================================================================
--- uspace/lib/ui/src/ui.c	(revision d4ea1f67c45352df9a960c7a2a7b061780108b7c)
+++ uspace/lib/ui/src/ui.c	(revision 8965860ccae8af5b3d432226917c44dff1028ca8)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2021 Jiri Svoboda
+ * Copyright (c) 2022 Jiri Svoboda
  * All rights reserved.
  *
@@ -39,4 +39,5 @@
 #include <errno.h>
 #include <fibril.h>
+#include <fibril_synch.h>
 #include <gfx/color.h>
 #include <gfx/cursor.h>
@@ -47,4 +48,5 @@
 #include <str.h>
 #include <task.h>
+#include <ui/clickmatic.h>
 #include <ui/ui.h>
 #include <ui/wdecor.h>
@@ -198,4 +200,5 @@
 {
 	ui_t *ui;
+	errno_t rc;
 
 	ui = calloc(1, sizeof(ui_t));
@@ -203,6 +206,13 @@
 		return ENOMEM;
 
+	rc = ui_clickmatic_create(ui, &ui->clickmatic);
+	if (rc != EOK) {
+		free(ui);
+		return rc;
+	}
+
 	ui->console = console;
 	list_initialize(&ui->windows);
+	fibril_mutex_initialize(&ui->lock);
 	*rui = ui;
 	return EOK;
@@ -218,4 +228,5 @@
 {
 	ui_t *ui;
+	errno_t rc;
 
 	ui = calloc(1, sizeof(ui_t));
@@ -223,6 +234,13 @@
 		return ENOMEM;
 
+	rc = ui_clickmatic_create(ui, &ui->clickmatic);
+	if (rc != EOK) {
+		free(ui);
+		return rc;
+	}
+
 	ui->display = disp;
 	list_initialize(&ui->windows);
+	fibril_mutex_initialize(&ui->lock);
 	*rui = ui;
 	return EOK;
@@ -443,4 +461,30 @@
 }
 
+/** Lock UI.
+ *
+ * Block UI from calling window callbacks. @c ui_lock() and @c ui_unlock()
+ * must be used when accessing UI resources from a fibril (as opposed to
+ * from a window callback).
+ *
+ * @param ui UI
+ */
+void ui_lock(ui_t *ui)
+{
+	fibril_mutex_lock(&ui->lock);
+}
+
+/** Unlock UI.
+ *
+ * Allow UI to call window callbacks. @c ui_lock() and @c ui_unlock()
+ * must be used when accessing window resources from a fibril (as opposed to
+ * from a window callback).
+ *
+ * @param ui UI
+ */
+void ui_unlock(ui_t *ui)
+{
+	fibril_mutex_unlock(&ui->lock);
+}
+
 /** Terminate user interface.
  *
@@ -480,4 +524,14 @@
 }
 
+/** Get clickmatic from UI.
+ *
+ * @pararm ui UI
+ * @return Clickmatic
+ */
+ui_clickmatic_t *ui_get_clickmatic(ui_t *ui)
+{
+	return ui->clickmatic;
+}
+
 /** @}
  */
Index: uspace/lib/ui/src/window.c
===================================================================
--- uspace/lib/ui/src/window.c	(revision d4ea1f67c45352df9a960c7a2a7b061780108b7c)
+++ uspace/lib/ui/src/window.c	(revision 8965860ccae8af5b3d432226917c44dff1028ca8)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2021 Jiri Svoboda
+ * Copyright (c) 2022 Jiri Svoboda
  * All rights reserved.
  *
@@ -766,6 +766,9 @@
 {
 	ui_window_t *window = (ui_window_t *) arg;
-
+	ui_t *ui = window->ui;
+
+	ui_lock(ui);
 	ui_window_send_close(window);
+	ui_unlock(ui);
 }
 
@@ -774,4 +777,6 @@
 {
 	ui_window_t *window = (ui_window_t *) arg;
+
+	ui_lock(window->ui);
 
 	if (window->wdecor != NULL) {
@@ -781,4 +786,5 @@
 
 	ui_window_send_focus(window);
+	ui_unlock(window->ui);
 }
 
@@ -788,6 +794,7 @@
 	ui_window_t *window = (ui_window_t *) arg;
 
-	(void) window;
+	ui_lock(window->ui);
 	ui_window_send_kbd(window, kbd_event);
+	ui_unlock(window->ui);
 }
 
@@ -801,6 +808,8 @@
 		return;
 
+	ui_lock(window->ui);
 	ui_wdecor_pos_event(window->wdecor, event);
 	ui_window_send_pos(window, event);
+	ui_unlock(window->ui);
 }
 
@@ -817,6 +826,8 @@
 		return;
 
+	ui_lock(window->ui);
 	(void) ui_window_resize(window, rect);
 	(void) ui_window_paint(window);
+	ui_unlock(window->ui);
 }
 
@@ -825,4 +836,6 @@
 {
 	ui_window_t *window = (ui_window_t *) arg;
+
+	ui_lock(window->ui);
 
 	if (window->wdecor != NULL) {
@@ -832,4 +845,5 @@
 
 	ui_window_send_unfocus(window);
+	ui_unlock(window->ui);
 }
 
Index: uspace/lib/ui/test/clickmatic.c
===================================================================
--- uspace/lib/ui/test/clickmatic.c	(revision 8965860ccae8af5b3d432226917c44dff1028ca8)
+++ uspace/lib/ui/test/clickmatic.c	(revision 8965860ccae8af5b3d432226917c44dff1028ca8)
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2022 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <errno.h>
+#include <pcut/pcut.h>
+#include <ui/clickmatic.h>
+#include <ui/ui.h>
+#include "../private/clickmatic.h"
+
+PCUT_INIT;
+
+PCUT_TEST_SUITE(clickmatic);
+
+static void test_clicked(ui_clickmatic_t *, void *);
+
+static ui_clickmatic_cb_t test_cb = {
+	.clicked = test_clicked
+};
+
+typedef struct {
+	int clicked_cnt;
+} test_resp_t;
+
+/** Create and destroy clickmatic */
+PCUT_TEST(create_destroy)
+{
+	ui_t *ui = NULL;
+	ui_clickmatic_t *clickmatic = NULL;
+	errno_t rc;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ui_clickmatic_create(ui, &clickmatic);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(clickmatic);
+
+	ui_clickmatic_destroy(clickmatic);
+	ui_destroy(ui);
+}
+
+/** ui_clickmatic_destroy() can take NULL argument (no-op) */
+PCUT_TEST(destroy_null)
+{
+	ui_clickmatic_destroy(NULL);
+}
+
+/** ui_clickmatic_set_cb() sets internal fields */
+PCUT_TEST(set_cb)
+{
+	ui_t *ui = NULL;
+	ui_clickmatic_t *clickmatic = NULL;
+	test_resp_t test_resp;
+	errno_t rc;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ui_clickmatic_create(ui, &clickmatic);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(clickmatic);
+
+	ui_clickmatic_set_cb(clickmatic, &test_cb, (void *)&test_resp);
+	PCUT_ASSERT_EQUALS(&test_cb, clickmatic->cb);
+	PCUT_ASSERT_EQUALS((void *)&test_resp, clickmatic->arg);
+
+	ui_clickmatic_destroy(clickmatic);
+	ui_destroy(ui);
+}
+
+/** Pressing and releasing clickmatic generates one event */
+PCUT_TEST(press_release)
+{
+	ui_t *ui = NULL;
+	ui_clickmatic_t *clickmatic = NULL;
+	test_resp_t test_resp;
+	errno_t rc;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ui_clickmatic_create(ui, &clickmatic);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(clickmatic);
+
+	test_resp.clicked_cnt = 0;
+
+	ui_clickmatic_set_cb(clickmatic, &test_cb, (void *)&test_resp);
+	PCUT_ASSERT_EQUALS(&test_cb, clickmatic->cb);
+	PCUT_ASSERT_EQUALS((void *)&test_resp, clickmatic->arg);
+
+	PCUT_ASSERT_INT_EQUALS(0, test_resp.clicked_cnt);
+
+	ui_clickmatic_press(clickmatic);
+	ui_clickmatic_release(clickmatic);
+
+	PCUT_ASSERT_INT_EQUALS(1, test_resp.clicked_cnt);
+
+	ui_clickmatic_destroy(clickmatic);
+	ui_destroy(ui);
+}
+
+static void test_clicked(ui_clickmatic_t *clickmatic, void *arg)
+{
+	test_resp_t *test_resp = (test_resp_t *)arg;
+
+	++test_resp->clicked_cnt;
+}
+
+PCUT_EXPORT(clickmatic);
Index: uspace/lib/ui/test/main.c
===================================================================
--- uspace/lib/ui/test/main.c	(revision d4ea1f67c45352df9a960c7a2a7b061780108b7c)
+++ uspace/lib/ui/test/main.c	(revision 8965860ccae8af5b3d432226917c44dff1028ca8)
@@ -34,4 +34,5 @@
 PCUT_IMPORT(control);
 PCUT_IMPORT(checkbox);
+PCUT_IMPORT(clickmatic);
 PCUT_IMPORT(entry);
 PCUT_IMPORT(file_dialog);
Index: uspace/lib/ui/test/scrollbar.c
===================================================================
--- uspace/lib/ui/test/scrollbar.c	(revision d4ea1f67c45352df9a960c7a2a7b061780108b7c)
+++ uspace/lib/ui/test/scrollbar.c	(revision 8965860ccae8af5b3d432226917c44dff1028ca8)
@@ -35,4 +35,6 @@
 #include <ui/scrollbar.h>
 #include <ui/resource.h>
+#include <ui/ui.h>
+#include <ui/window.h>
 #include "../private/pbutton.h"
 #include "../private/scrollbar.h"
@@ -41,25 +43,4 @@
 
 PCUT_TEST_SUITE(scrollbar);
-
-static errno_t testgc_set_clip_rect(void *, gfx_rect_t *);
-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 errno_t testgc_bitmap_create(void *, gfx_bitmap_params_t *,
-    gfx_bitmap_alloc_t *, void **);
-static errno_t testgc_bitmap_destroy(void *);
-static errno_t testgc_bitmap_render(void *, gfx_rect_t *, gfx_coord2_t *);
-static errno_t testgc_bitmap_get_alloc(void *, gfx_bitmap_alloc_t *);
-
-static gfx_context_ops_t ops = {
-	.set_clip_rect = testgc_set_clip_rect,
-	.set_color = testgc_set_color,
-	.fill_rect = testgc_fill_rect,
-	.update = testgc_update,
-	.bitmap_create = testgc_bitmap_create,
-	.bitmap_destroy = testgc_bitmap_destroy,
-	.bitmap_render = testgc_bitmap_render,
-	.bitmap_get_alloc = testgc_bitmap_get_alloc
-};
 
 static void test_scrollbar_up(ui_scrollbar_t *, void *);
@@ -79,21 +60,4 @@
 static ui_scrollbar_cb_t dummy_scrollbar_cb = {
 };
-
-typedef struct {
-	bool bm_created;
-	bool bm_destroyed;
-	gfx_bitmap_params_t bm_params;
-	void *bm_pixels;
-	gfx_rect_t bm_srect;
-	gfx_coord2_t bm_offs;
-	bool bm_rendered;
-	bool bm_got_alloc;
-} test_gc_t;
-
-typedef struct {
-	test_gc_t *tgc;
-	gfx_bitmap_alloc_t alloc;
-	bool myalloc;
-} testgc_bitmap_t;
 
 typedef struct {
@@ -109,27 +73,27 @@
 PCUT_TEST(create_destroy)
 {
-	errno_t rc;
-	gfx_context_t *gc = NULL;
-	test_gc_t tgc;
-	ui_resource_t *resource = NULL;
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
 	ui_scrollbar_t *scrollbar = NULL;
-
-	memset(&tgc, 0, sizeof(tgc));
-	rc = gfx_context_new(&ops, &tgc, &gc);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	rc = ui_resource_create(gc, false, &resource);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-	PCUT_ASSERT_NOT_NULL(resource);
-
-	rc = ui_scrollbar_create(resource, &scrollbar);
+	errno_t rc;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_scrollbar_create(ui, window, &scrollbar);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 	PCUT_ASSERT_NOT_NULL(scrollbar);
 
 	ui_scrollbar_destroy(scrollbar);
-	ui_resource_destroy(resource);
-
-	rc = gfx_context_delete(gc);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	ui_window_destroy(window);
+	ui_destroy(ui);
 }
 
@@ -143,20 +107,22 @@
 PCUT_TEST(ctl)
 {
-	errno_t rc;
-	gfx_context_t *gc = NULL;
-	test_gc_t tgc;
-	ui_resource_t *resource = NULL;
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
 	ui_scrollbar_t *scrollbar = NULL;
 	ui_control_t *control;
-
-	memset(&tgc, 0, sizeof(tgc));
-	rc = gfx_context_new(&ops, &tgc, &gc);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	rc = ui_resource_create(gc, false, &resource);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-	PCUT_ASSERT_NOT_NULL(resource);
-
-	rc = ui_scrollbar_create(resource, &scrollbar);
+	errno_t rc;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_scrollbar_create(ui, window, &scrollbar);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 	PCUT_ASSERT_NOT_NULL(scrollbar);
@@ -166,8 +132,6 @@
 
 	ui_control_destroy(control);
-	ui_resource_destroy(resource);
-
-	rc = gfx_context_delete(gc);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	ui_window_destroy(window);
+	ui_destroy(ui);
 }
 
@@ -175,20 +139,22 @@
 PCUT_TEST(set_rect)
 {
-	errno_t rc;
-	gfx_context_t *gc = NULL;
-	test_gc_t tgc;
-	ui_resource_t *resource = NULL;
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
 	ui_scrollbar_t *scrollbar = NULL;
 	gfx_rect_t rect;
-
-	memset(&tgc, 0, sizeof(tgc));
-	rc = gfx_context_new(&ops, &tgc, &gc);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	rc = ui_resource_create(gc, false, &resource);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-	PCUT_ASSERT_NOT_NULL(resource);
-
-	rc = ui_scrollbar_create(resource, &scrollbar);
+	errno_t rc;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_scrollbar_create(ui, window, &scrollbar);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 	PCUT_ASSERT_NOT_NULL(scrollbar);
@@ -206,8 +172,6 @@
 
 	ui_scrollbar_destroy(scrollbar);
-	ui_resource_destroy(resource);
-
-	rc = gfx_context_delete(gc);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	ui_window_destroy(window);
+	ui_destroy(ui);
 }
 
@@ -215,19 +179,21 @@
 PCUT_TEST(paint_gfx)
 {
-	errno_t rc;
-	gfx_context_t *gc = NULL;
-	test_gc_t tgc;
-	ui_resource_t *resource = NULL;
-	ui_scrollbar_t *scrollbar;
-
-	memset(&tgc, 0, sizeof(tgc));
-	rc = gfx_context_new(&ops, &tgc, &gc);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	rc = ui_resource_create(gc, false, &resource);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-	PCUT_ASSERT_NOT_NULL(resource);
-
-	rc = ui_scrollbar_create(resource, &scrollbar);
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
+	ui_scrollbar_t *scrollbar;
+	errno_t rc;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_scrollbar_create(ui, window, &scrollbar);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -236,8 +202,6 @@
 
 	ui_scrollbar_destroy(scrollbar);
-	ui_resource_destroy(resource);
-
-	rc = gfx_context_delete(gc);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	ui_window_destroy(window);
+	ui_destroy(ui);
 }
 
@@ -245,20 +209,22 @@
 PCUT_TEST(paint_text)
 {
-	errno_t rc;
-	gfx_context_t *gc = NULL;
-	test_gc_t tgc;
-	ui_resource_t *resource = NULL;
-	ui_scrollbar_t *scrollbar;
-	gfx_rect_t rect;
-
-	memset(&tgc, 0, sizeof(tgc));
-	rc = gfx_context_new(&ops, &tgc, &gc);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	rc = ui_resource_create(gc, true, &resource);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-	PCUT_ASSERT_NOT_NULL(resource);
-
-	rc = ui_scrollbar_create(resource, &scrollbar);
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
+	ui_scrollbar_t *scrollbar;
+	gfx_rect_t rect;
+	errno_t rc;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_scrollbar_create(ui, window, &scrollbar);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -273,8 +239,47 @@
 
 	ui_scrollbar_destroy(scrollbar);
-	ui_resource_destroy(resource);
-
-	rc = gfx_context_delete(gc);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	ui_window_destroy(window);
+	ui_destroy(ui);
+}
+
+/** ui_scrollbar_get_geom() returns scrollbar geometry */
+PCUT_TEST(get_geom)
+{
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
+	ui_scrollbar_t *scrollbar;
+	ui_scrollbar_geom_t geom;
+	gfx_rect_t rect;
+	errno_t rc;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_scrollbar_create(ui, window, &scrollbar);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rect.p0.x = 10;
+	rect.p0.y = 10;
+	rect.p1.x = 100;
+	rect.p1.y = 30;
+	ui_scrollbar_set_rect(scrollbar, &rect);
+
+	ui_scrollbar_get_geom(scrollbar, &geom);
+	PCUT_ASSERT_INT_EQUALS(11, geom.up_btn_rect.p0.x);
+	PCUT_ASSERT_INT_EQUALS(11, geom.up_btn_rect.p0.y);
+	PCUT_ASSERT_INT_EQUALS(99, geom.down_btn_rect.p1.x);
+	PCUT_ASSERT_INT_EQUALS(29, geom.down_btn_rect.p1.y);
+
+	ui_scrollbar_destroy(scrollbar);
+	ui_window_destroy(window);
+	ui_destroy(ui);
 }
 
@@ -282,21 +287,23 @@
 PCUT_TEST(through_length)
 {
-	errno_t rc;
-	gfx_context_t *gc = NULL;
-	test_gc_t tgc;
-	ui_resource_t *resource = NULL;
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
 	ui_scrollbar_t *scrollbar;
 	gfx_coord_t length;
 	gfx_rect_t rect;
-
-	memset(&tgc, 0, sizeof(tgc));
-	rc = gfx_context_new(&ops, &tgc, &gc);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	rc = ui_resource_create(gc, false, &resource);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-	PCUT_ASSERT_NOT_NULL(resource);
-
-	rc = ui_scrollbar_create(resource, &scrollbar);
+	errno_t rc;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_scrollbar_create(ui, window, &scrollbar);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -315,8 +322,6 @@
 
 	ui_scrollbar_destroy(scrollbar);
-	ui_resource_destroy(resource);
-
-	rc = gfx_context_delete(gc);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	ui_window_destroy(window);
+	ui_destroy(ui);
 }
 
@@ -324,21 +329,23 @@
 PCUT_TEST(move_length)
 {
-	errno_t rc;
-	gfx_context_t *gc = NULL;
-	test_gc_t tgc;
-	ui_resource_t *resource = NULL;
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
 	ui_scrollbar_t *scrollbar;
 	gfx_coord_t length;
 	gfx_rect_t rect;
-
-	memset(&tgc, 0, sizeof(tgc));
-	rc = gfx_context_new(&ops, &tgc, &gc);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	rc = ui_resource_create(gc, false, &resource);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-	PCUT_ASSERT_NOT_NULL(resource);
-
-	rc = ui_scrollbar_create(resource, &scrollbar);
+	errno_t rc;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_scrollbar_create(ui, window, &scrollbar);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -357,8 +364,6 @@
 
 	ui_scrollbar_destroy(scrollbar);
-	ui_resource_destroy(resource);
-
-	rc = gfx_context_delete(gc);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	ui_window_destroy(window);
+	ui_destroy(ui);
 }
 
@@ -366,21 +371,23 @@
 PCUT_TEST(get_pos)
 {
-	errno_t rc;
-	gfx_context_t *gc = NULL;
-	test_gc_t tgc;
-	ui_resource_t *resource = NULL;
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
 	ui_scrollbar_t *scrollbar;
 	gfx_coord_t pos;
 	gfx_rect_t rect;
-
-	memset(&tgc, 0, sizeof(tgc));
-	rc = gfx_context_new(&ops, &tgc, &gc);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	rc = ui_resource_create(gc, false, &resource);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-	PCUT_ASSERT_NOT_NULL(resource);
-
-	rc = ui_scrollbar_create(resource, &scrollbar);
+	errno_t rc;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_scrollbar_create(ui, window, &scrollbar);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -398,8 +405,6 @@
 
 	ui_scrollbar_destroy(scrollbar);
-	ui_resource_destroy(resource);
-
-	rc = gfx_context_delete(gc);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	ui_window_destroy(window);
+	ui_destroy(ui);
 }
 
@@ -407,20 +412,22 @@
 PCUT_TEST(set_thumb_length)
 {
-	errno_t rc;
-	gfx_context_t *gc = NULL;
-	test_gc_t tgc;
-	ui_resource_t *resource = NULL;
-	ui_scrollbar_t *scrollbar;
-	gfx_rect_t rect;
-
-	memset(&tgc, 0, sizeof(tgc));
-	rc = gfx_context_new(&ops, &tgc, &gc);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	rc = ui_resource_create(gc, false, &resource);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-	PCUT_ASSERT_NOT_NULL(resource);
-
-	rc = ui_scrollbar_create(resource, &scrollbar);
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
+	ui_scrollbar_t *scrollbar;
+	gfx_rect_t rect;
+	errno_t rc;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_scrollbar_create(ui, window, &scrollbar);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -437,8 +444,6 @@
 
 	ui_scrollbar_destroy(scrollbar);
-	ui_resource_destroy(resource);
-
-	rc = gfx_context_delete(gc);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	ui_window_destroy(window);
+	ui_destroy(ui);
 }
 
@@ -446,21 +451,23 @@
 PCUT_TEST(set_pos)
 {
-	errno_t rc;
-	gfx_context_t *gc = NULL;
-	test_gc_t tgc;
-	ui_resource_t *resource = NULL;
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
 	ui_scrollbar_t *scrollbar;
 	gfx_coord_t pos;
 	gfx_rect_t rect;
-
-	memset(&tgc, 0, sizeof(tgc));
-	rc = gfx_context_new(&ops, &tgc, &gc);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	rc = ui_resource_create(gc, false, &resource);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-	PCUT_ASSERT_NOT_NULL(resource);
-
-	rc = ui_scrollbar_create(resource, &scrollbar);
+	errno_t rc;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_scrollbar_create(ui, window, &scrollbar);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -489,8 +496,6 @@
 
 	ui_scrollbar_destroy(scrollbar);
-	ui_resource_destroy(resource);
-
-	rc = gfx_context_delete(gc);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	ui_window_destroy(window);
+	ui_destroy(ui);
 }
 
@@ -498,22 +503,24 @@
 PCUT_TEST(thumb_press_release)
 {
-	errno_t rc;
-	gfx_context_t *gc = NULL;
-	test_gc_t tgc;
-	ui_resource_t *resource = NULL;
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
 	gfx_coord2_t pos;
 	gfx_rect_t rect;
 	ui_scrollbar_t *scrollbar;
 	test_cb_resp_t resp;
-
-	memset(&tgc, 0, sizeof(tgc));
-	rc = gfx_context_new(&ops, &tgc, &gc);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	rc = ui_resource_create(gc, false, &resource);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-	PCUT_ASSERT_NOT_NULL(resource);
-
-	rc = ui_scrollbar_create(resource, &scrollbar);
+	errno_t rc;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_scrollbar_create(ui, window, &scrollbar);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -545,8 +552,6 @@
 
 	ui_scrollbar_destroy(scrollbar);
-	ui_resource_destroy(resource);
-
-	rc = gfx_context_delete(gc);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	ui_window_destroy(window);
+	ui_destroy(ui);
 }
 
@@ -554,22 +559,24 @@
 PCUT_TEST(thumb_press_update_release)
 {
-	errno_t rc;
-	gfx_context_t *gc = NULL;
-	test_gc_t tgc;
-	ui_resource_t *resource = NULL;
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
 	gfx_coord2_t pos;
 	gfx_rect_t rect;
 	ui_scrollbar_t *scrollbar;
 	test_cb_resp_t resp;
-
-	memset(&tgc, 0, sizeof(tgc));
-	rc = gfx_context_new(&ops, &tgc, &gc);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	rc = ui_resource_create(gc, false, &resource);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-	PCUT_ASSERT_NOT_NULL(resource);
-
-	rc = ui_scrollbar_create(resource, &scrollbar);
+	errno_t rc;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_scrollbar_create(ui, window, &scrollbar);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -609,8 +616,6 @@
 
 	ui_scrollbar_destroy(scrollbar);
-	ui_resource_destroy(resource);
-
-	rc = gfx_context_delete(gc);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	ui_window_destroy(window);
+	ui_destroy(ui);
 }
 
@@ -618,22 +623,24 @@
 PCUT_TEST(up_through_press_release)
 {
-	errno_t rc;
-	gfx_context_t *gc = NULL;
-	test_gc_t tgc;
-	ui_resource_t *resource = NULL;
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
 	gfx_coord2_t pos;
 	gfx_rect_t rect;
 	ui_scrollbar_t *scrollbar;
 	test_cb_resp_t resp;
-
-	memset(&tgc, 0, sizeof(tgc));
-	rc = gfx_context_new(&ops, &tgc, &gc);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	rc = ui_resource_create(gc, false, &resource);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-	PCUT_ASSERT_NOT_NULL(resource);
-
-	rc = ui_scrollbar_create(resource, &scrollbar);
+	errno_t rc;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_scrollbar_create(ui, window, &scrollbar);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -661,8 +668,6 @@
 
 	ui_scrollbar_destroy(scrollbar);
-	ui_resource_destroy(resource);
-
-	rc = gfx_context_delete(gc);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	ui_window_destroy(window);
+	ui_destroy(ui);
 }
 
@@ -670,22 +675,24 @@
 PCUT_TEST(down_through_press_release)
 {
-	errno_t rc;
-	gfx_context_t *gc = NULL;
-	test_gc_t tgc;
-	ui_resource_t *resource = NULL;
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
 	gfx_coord2_t pos;
 	gfx_rect_t rect;
 	ui_scrollbar_t *scrollbar;
 	test_cb_resp_t resp;
-
-	memset(&tgc, 0, sizeof(tgc));
-	rc = gfx_context_new(&ops, &tgc, &gc);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	rc = ui_resource_create(gc, false, &resource);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-	PCUT_ASSERT_NOT_NULL(resource);
-
-	rc = ui_scrollbar_create(resource, &scrollbar);
+	errno_t rc;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_scrollbar_create(ui, window, &scrollbar);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -713,8 +720,49 @@
 
 	ui_scrollbar_destroy(scrollbar);
-	ui_resource_destroy(resource);
-
-	rc = gfx_context_delete(gc);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	ui_window_destroy(window);
+	ui_destroy(ui);
+}
+
+/** Updating state of throughs when cursor or thumb moves */
+PCUT_TEST(throughs_update)
+{
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
+	gfx_coord2_t pos;
+	gfx_rect_t rect;
+	ui_scrollbar_t *scrollbar;
+	errno_t rc;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_scrollbar_create(ui, window, &scrollbar);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rect.p0.x = 10;
+	rect.p0.y = 20;
+	rect.p1.x = 110;
+	rect.p1.y = 120;
+	ui_scrollbar_set_rect(scrollbar, &rect);
+
+	PCUT_ASSERT_FALSE(scrollbar->down_through_inside);
+
+	pos.x = 60;
+	pos.y = 22;
+
+	ui_scrollbar_throughs_update(scrollbar, &pos);
+	PCUT_ASSERT_TRUE(scrollbar->down_through_inside);
+
+	ui_scrollbar_destroy(scrollbar);
+	ui_window_destroy(window);
+	ui_destroy(ui);
 }
 
@@ -722,20 +770,22 @@
 PCUT_TEST(up)
 {
-	ui_scrollbar_t *scrollbar;
-	errno_t rc;
-	gfx_context_t *gc = NULL;
-	test_gc_t tgc;
-	ui_resource_t *resource = NULL;
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
+	ui_scrollbar_t *scrollbar;
 	test_cb_resp_t resp;
-
-	memset(&tgc, 0, sizeof(tgc));
-	rc = gfx_context_new(&ops, &tgc, &gc);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	rc = ui_resource_create(gc, false, &resource);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-	PCUT_ASSERT_NOT_NULL(resource);
-
-	rc = ui_scrollbar_create(resource, &scrollbar);
+	errno_t rc;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_scrollbar_create(ui, window, &scrollbar);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -754,8 +804,6 @@
 
 	ui_scrollbar_destroy(scrollbar);
-	ui_resource_destroy(resource);
-
-	rc = gfx_context_delete(gc);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	ui_window_destroy(window);
+	ui_destroy(ui);
 }
 
@@ -763,20 +811,22 @@
 PCUT_TEST(down)
 {
-	ui_scrollbar_t *scrollbar;
-	errno_t rc;
-	gfx_context_t *gc = NULL;
-	test_gc_t tgc;
-	ui_resource_t *resource = NULL;
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
+	ui_scrollbar_t *scrollbar;
 	test_cb_resp_t resp;
-
-	memset(&tgc, 0, sizeof(tgc));
-	rc = gfx_context_new(&ops, &tgc, &gc);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	rc = ui_resource_create(gc, false, &resource);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-	PCUT_ASSERT_NOT_NULL(resource);
-
-	rc = ui_scrollbar_create(resource, &scrollbar);
+	errno_t rc;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_scrollbar_create(ui, window, &scrollbar);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -795,8 +845,6 @@
 
 	ui_scrollbar_destroy(scrollbar);
-	ui_resource_destroy(resource);
-
-	rc = gfx_context_delete(gc);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	ui_window_destroy(window);
+	ui_destroy(ui);
 }
 
@@ -804,20 +852,22 @@
 PCUT_TEST(page_up)
 {
-	ui_scrollbar_t *scrollbar;
-	errno_t rc;
-	gfx_context_t *gc = NULL;
-	test_gc_t tgc;
-	ui_resource_t *resource = NULL;
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
+	ui_scrollbar_t *scrollbar;
 	test_cb_resp_t resp;
-
-	memset(&tgc, 0, sizeof(tgc));
-	rc = gfx_context_new(&ops, &tgc, &gc);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	rc = ui_resource_create(gc, false, &resource);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-	PCUT_ASSERT_NOT_NULL(resource);
-
-	rc = ui_scrollbar_create(resource, &scrollbar);
+	errno_t rc;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_scrollbar_create(ui, window, &scrollbar);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -836,8 +886,6 @@
 
 	ui_scrollbar_destroy(scrollbar);
-	ui_resource_destroy(resource);
-
-	rc = gfx_context_delete(gc);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	ui_window_destroy(window);
+	ui_destroy(ui);
 }
 
@@ -845,20 +893,22 @@
 PCUT_TEST(page_down)
 {
-	ui_scrollbar_t *scrollbar;
-	errno_t rc;
-	gfx_context_t *gc = NULL;
-	test_gc_t tgc;
-	ui_resource_t *resource = NULL;
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
+	ui_scrollbar_t *scrollbar;
 	test_cb_resp_t resp;
-
-	memset(&tgc, 0, sizeof(tgc));
-	rc = gfx_context_new(&ops, &tgc, &gc);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	rc = ui_resource_create(gc, false, &resource);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-	PCUT_ASSERT_NOT_NULL(resource);
-
-	rc = ui_scrollbar_create(resource, &scrollbar);
+	errno_t rc;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_scrollbar_create(ui, window, &scrollbar);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -877,8 +927,6 @@
 
 	ui_scrollbar_destroy(scrollbar);
-	ui_resource_destroy(resource);
-
-	rc = gfx_context_delete(gc);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	ui_window_destroy(window);
+	ui_destroy(ui);
 }
 
@@ -886,20 +934,22 @@
 PCUT_TEST(moved)
 {
-	ui_scrollbar_t *scrollbar;
-	errno_t rc;
-	gfx_context_t *gc = NULL;
-	test_gc_t tgc;
-	ui_resource_t *resource = NULL;
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
+	ui_scrollbar_t *scrollbar;
 	test_cb_resp_t resp;
-
-	memset(&tgc, 0, sizeof(tgc));
-	rc = gfx_context_new(&ops, &tgc, &gc);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	rc = ui_resource_create(gc, false, &resource);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-	PCUT_ASSERT_NOT_NULL(resource);
-
-	rc = ui_scrollbar_create(resource, &scrollbar);
+	errno_t rc;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_scrollbar_create(ui, window, &scrollbar);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -920,8 +970,6 @@
 
 	ui_scrollbar_destroy(scrollbar);
-	ui_resource_destroy(resource);
-
-	rc = gfx_context_delete(gc);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	ui_window_destroy(window);
+	ui_destroy(ui);
 }
 
@@ -929,22 +977,24 @@
 PCUT_TEST(pos_event_press_release_thumb)
 {
-	errno_t rc;
-	gfx_context_t *gc = NULL;
-	test_gc_t tgc;
-	ui_resource_t *resource = NULL;
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
 	ui_scrollbar_t *scrollbar;
 	ui_evclaim_t claim;
 	pos_event_t event;
 	gfx_rect_t rect;
-
-	memset(&tgc, 0, sizeof(tgc));
-	rc = gfx_context_new(&ops, &tgc, &gc);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	rc = ui_resource_create(gc, false, &resource);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-	PCUT_ASSERT_NOT_NULL(resource);
-
-	rc = ui_scrollbar_create(resource, &scrollbar);
+	errno_t rc;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_scrollbar_create(ui, window, &scrollbar);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -982,8 +1032,6 @@
 
 	ui_scrollbar_destroy(scrollbar);
-	ui_resource_destroy(resource);
-
-	rc = gfx_context_delete(gc);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	ui_window_destroy(window);
+	ui_destroy(ui);
 }
 
@@ -991,22 +1039,24 @@
 PCUT_TEST(pos_event_press_release_up_btn)
 {
-	errno_t rc;
-	gfx_context_t *gc = NULL;
-	test_gc_t tgc;
-	ui_resource_t *resource = NULL;
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
 	ui_scrollbar_t *scrollbar;
 	ui_evclaim_t claim;
 	pos_event_t event;
 	gfx_rect_t rect;
-
-	memset(&tgc, 0, sizeof(tgc));
-	rc = gfx_context_new(&ops, &tgc, &gc);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	rc = ui_resource_create(gc, false, &resource);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-	PCUT_ASSERT_NOT_NULL(resource);
-
-	rc = ui_scrollbar_create(resource, &scrollbar);
+	errno_t rc;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_scrollbar_create(ui, window, &scrollbar);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -1028,8 +1078,6 @@
 
 	ui_scrollbar_destroy(scrollbar);
-	ui_resource_destroy(resource);
-
-	rc = gfx_context_delete(gc);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	ui_window_destroy(window);
+	ui_destroy(ui);
 }
 
@@ -1037,22 +1085,24 @@
 PCUT_TEST(pos_event_press_release_up_through)
 {
-	errno_t rc;
-	gfx_context_t *gc = NULL;
-	test_gc_t tgc;
-	ui_resource_t *resource = NULL;
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
 	ui_scrollbar_t *scrollbar;
 	ui_evclaim_t claim;
 	pos_event_t event;
 	gfx_rect_t rect;
-
-	memset(&tgc, 0, sizeof(tgc));
-	rc = gfx_context_new(&ops, &tgc, &gc);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	rc = ui_resource_create(gc, false, &resource);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-	PCUT_ASSERT_NOT_NULL(resource);
-
-	rc = ui_scrollbar_create(resource, &scrollbar);
+	errno_t rc;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_scrollbar_create(ui, window, &scrollbar);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -1085,8 +1135,6 @@
 
 	ui_scrollbar_destroy(scrollbar);
-	ui_resource_destroy(resource);
-
-	rc = gfx_context_delete(gc);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	ui_window_destroy(window);
+	ui_destroy(ui);
 }
 
@@ -1094,22 +1142,24 @@
 PCUT_TEST(pos_event_press_release_down_through)
 {
-	errno_t rc;
-	gfx_context_t *gc = NULL;
-	test_gc_t tgc;
-	ui_resource_t *resource = NULL;
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
 	ui_scrollbar_t *scrollbar;
 	ui_evclaim_t claim;
 	pos_event_t event;
 	gfx_rect_t rect;
-
-	memset(&tgc, 0, sizeof(tgc));
-	rc = gfx_context_new(&ops, &tgc, &gc);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	rc = ui_resource_create(gc, false, &resource);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-	PCUT_ASSERT_NOT_NULL(resource);
-
-	rc = ui_scrollbar_create(resource, &scrollbar);
+	errno_t rc;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_scrollbar_create(ui, window, &scrollbar);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -1139,8 +1189,6 @@
 
 	ui_scrollbar_destroy(scrollbar);
-	ui_resource_destroy(resource);
-
-	rc = gfx_context_delete(gc);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	ui_window_destroy(window);
+	ui_destroy(ui);
 }
 
@@ -1148,22 +1196,24 @@
 PCUT_TEST(pos_event_press_relese_down_btn)
 {
-	errno_t rc;
-	gfx_context_t *gc = NULL;
-	test_gc_t tgc;
-	ui_resource_t *resource = NULL;
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
 	ui_scrollbar_t *scrollbar;
 	ui_evclaim_t claim;
 	pos_event_t event;
 	gfx_rect_t rect;
-
-	memset(&tgc, 0, sizeof(tgc));
-	rc = gfx_context_new(&ops, &tgc, &gc);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	rc = ui_resource_create(gc, false, &resource);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-	PCUT_ASSERT_NOT_NULL(resource);
-
-	rc = ui_scrollbar_create(resource, &scrollbar);
+	errno_t rc;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_scrollbar_create(ui, window, &scrollbar);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -1185,100 +1235,6 @@
 
 	ui_scrollbar_destroy(scrollbar);
-	ui_resource_destroy(resource);
-
-	rc = gfx_context_delete(gc);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-}
-
-static errno_t testgc_set_clip_rect(void *arg, gfx_rect_t *rect)
-{
-	(void) arg;
-	(void) rect;
-	return EOK;
-}
-
-static errno_t testgc_set_color(void *arg, gfx_color_t *color)
-{
-	(void) arg;
-	(void) color;
-	return EOK;
-}
-
-static errno_t testgc_fill_rect(void *arg, gfx_rect_t *rect)
-{
-	(void) arg;
-	(void) rect;
-	return EOK;
-}
-
-static errno_t testgc_update(void *arg)
-{
-	(void) arg;
-	return EOK;
-}
-
-static errno_t testgc_bitmap_create(void *arg, gfx_bitmap_params_t *params,
-    gfx_bitmap_alloc_t *alloc, void **rbm)
-{
-	test_gc_t *tgc = (test_gc_t *) arg;
-	testgc_bitmap_t *tbm;
-
-	tbm = calloc(1, sizeof(testgc_bitmap_t));
-	if (tbm == NULL)
-		return ENOMEM;
-
-	if (alloc == NULL) {
-		tbm->alloc.pitch = (params->rect.p1.x - params->rect.p0.x) *
-		    sizeof(uint32_t);
-		tbm->alloc.off0 = 0;
-		tbm->alloc.pixels = calloc(sizeof(uint32_t),
-		    (params->rect.p1.x - params->rect.p0.x) *
-		    (params->rect.p1.y - params->rect.p0.y));
-		tbm->myalloc = true;
-		if (tbm->alloc.pixels == NULL) {
-			free(tbm);
-			return ENOMEM;
-		}
-	} else {
-		tbm->alloc = *alloc;
-	}
-
-	tbm->tgc = tgc;
-	tgc->bm_created = true;
-	tgc->bm_params = *params;
-	tgc->bm_pixels = tbm->alloc.pixels;
-	*rbm = (void *)tbm;
-	return EOK;
-}
-
-static errno_t testgc_bitmap_destroy(void *bm)
-{
-	testgc_bitmap_t *tbm = (testgc_bitmap_t *)bm;
-	if (tbm->myalloc)
-		free(tbm->alloc.pixels);
-	tbm->tgc->bm_destroyed = true;
-	free(tbm);
-	return EOK;
-}
-
-static errno_t testgc_bitmap_render(void *bm, gfx_rect_t *srect,
-    gfx_coord2_t *offs)
-{
-	testgc_bitmap_t *tbm = (testgc_bitmap_t *)bm;
-
-	tbm->tgc->bm_rendered = true;
-	if (srect != NULL)
-		tbm->tgc->bm_srect = *srect;
-	if (offs != NULL)
-		tbm->tgc->bm_offs = *offs;
-	return EOK;
-}
-
-static errno_t testgc_bitmap_get_alloc(void *bm, gfx_bitmap_alloc_t *alloc)
-{
-	testgc_bitmap_t *tbm = (testgc_bitmap_t *)bm;
-	*alloc = tbm->alloc;
-	tbm->tgc->bm_got_alloc = true;
-	return EOK;
+	ui_window_destroy(window);
+	ui_destroy(ui);
 }
 
Index: uspace/lib/ui/test/ui.c
===================================================================
--- uspace/lib/ui/test/ui.c	(revision d4ea1f67c45352df9a960c7a2a7b061780108b7c)
+++ uspace/lib/ui/test/ui.c	(revision 8965860ccae8af5b3d432226917c44dff1028ca8)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2022 Jiri Svoboda
  * All rights reserved.
  *
@@ -169,3 +169,19 @@
 }
 
+/** ui_lock(), ui_unlock() */
+PCUT_TEST(lock_unlock)
+{
+	ui_t *ui = NULL;
+	errno_t rc;
+
+	rc = ui_create_disp((display_t *)(-1), &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(ui);
+
+	ui_lock(ui);
+	ui_unlock(ui);
+
+	ui_destroy(ui);
+}
+
 PCUT_EXPORT(ui);
