Index: uspace/app/uidemo/uidemo.c
===================================================================
--- uspace/app/uidemo/uidemo.c	(revision fa01c0532bcb8f0b3291b5a02e32606e0515492b)
+++ uspace/app/uidemo/uidemo.c	(revision b71c0fcb07738ec42b4e693c19b1a5c1e2f329a6)
@@ -34,5 +34,4 @@
 
 #include <gfx/coord.h>
-#include <io/pos_event.h>
 #include <stdio.h>
 #include <str.h>
@@ -43,16 +42,11 @@
 #include <ui/resource.h>
 #include <ui/ui.h>
-#include <ui/wdecor.h>
 #include <ui/window.h>
 #include "uidemo.h"
 
 static void wnd_close(ui_window_t *, void *);
-static errno_t wnd_paint(ui_window_t *, void *);
-static void wnd_pos(ui_window_t *, void *, pos_event_t *pos);
 
 static ui_window_cb_t window_cb = {
-	.close = wnd_close,
-	.paint = wnd_paint,
-	.pos = wnd_pos
+	.close = wnd_close
 };
 
@@ -73,39 +67,4 @@
 
 	ui_quit(demo->ui);
-}
-
-/** Window paint request.
- *
- * @param window Window
- * @param arg Argument (demo)
- * @return EOK on success or an error code
- */
-static errno_t wnd_paint(ui_window_t *window, void *arg)
-{
-	ui_demo_t *demo = (ui_demo_t *) arg;
-	errno_t rc;
-
-	/* Let window paint its background */
-	rc = ui_window_def_paint(window);
-	if (rc != EOK)
-		return rc;
-
-	return ui_fixed_paint(demo->fixed);
-}
-
-/** Window position event.
- *
- * @param window Window
- * @param arg Argument (demo)
- */
-static void wnd_pos(ui_window_t *window, void *arg, pos_event_t *event)
-{
-	ui_demo_t *demo = (ui_demo_t *) arg;
-
-	/* Make sure we don't process events until fully initialized */
-	if (demo->fixed == NULL)
-		return;
-
-	ui_fixed_pos_event(demo->fixed, event);
 }
 
@@ -240,4 +199,6 @@
 	}
 
+	ui_window_add(window, ui_fixed_ctl(demo.fixed));
+
 	rc = ui_window_paint(window);
 	if (rc != EOK) {
@@ -248,5 +209,4 @@
 	ui_run(ui);
 
-	ui_fixed_destroy(demo.fixed);
 	ui_window_destroy(window);
 	ui_destroy(ui);
Index: uspace/lib/ui/include/ui/fixed.h
===================================================================
--- uspace/lib/ui/include/ui/fixed.h	(revision fa01c0532bcb8f0b3291b5a02e32606e0515492b)
+++ uspace/lib/ui/include/ui/fixed.h	(revision b71c0fcb07738ec42b4e693c19b1a5c1e2f329a6)
@@ -45,4 +45,5 @@
 extern errno_t ui_fixed_create(ui_fixed_t **);
 extern void ui_fixed_destroy(ui_fixed_t *);
+extern ui_control_t *ui_fixed_ctl(ui_fixed_t *);
 extern errno_t ui_fixed_add(ui_fixed_t *, ui_control_t *);
 extern void ui_fixed_remove(ui_fixed_t *, ui_control_t *);
Index: uspace/lib/ui/include/ui/window.h
===================================================================
--- uspace/lib/ui/include/ui/window.h	(revision fa01c0532bcb8f0b3291b5a02e32606e0515492b)
+++ uspace/lib/ui/include/ui/window.h	(revision b71c0fcb07738ec42b4e693c19b1a5c1e2f329a6)
@@ -40,4 +40,6 @@
 #include <gfx/context.h>
 #include <gfx/coord.h>
+#include <io/pos_event.h>
+#include <types/ui/control.h>
 #include <types/ui/ui.h>
 #include <types/ui/resource.h>
@@ -49,4 +51,6 @@
 extern void ui_window_set_cb(ui_window_t *, ui_window_cb_t *, void *);
 extern void ui_window_destroy(ui_window_t *);
+extern void ui_window_add(ui_window_t *, ui_control_t *);
+extern void ui_window_remove(ui_window_t *, ui_control_t *);
 extern ui_resource_t *ui_window_get_res(ui_window_t *);
 extern gfx_context_t *ui_window_get_gc(ui_window_t *);
@@ -54,4 +58,5 @@
 extern errno_t ui_window_paint(ui_window_t *);
 extern errno_t ui_window_def_paint(ui_window_t *);
+extern void ui_window_def_pos(ui_window_t *, pos_event_t *);
 
 #endif
Index: uspace/lib/ui/private/fixed.h
===================================================================
--- uspace/lib/ui/private/fixed.h	(revision fa01c0532bcb8f0b3291b5a02e32606e0515492b)
+++ uspace/lib/ui/private/fixed.h	(revision b71c0fcb07738ec42b4e693c19b1a5c1e2f329a6)
@@ -48,4 +48,6 @@
  */
 struct ui_fixed {
+	/** Base control object */
+	struct ui_control *control;
 	/** Layout elements (ui_fixed_elem_t) */
 	list_t elem;
Index: uspace/lib/ui/private/window.h
===================================================================
--- uspace/lib/ui/private/window.h	(revision fa01c0532bcb8f0b3291b5a02e32606e0515492b)
+++ uspace/lib/ui/private/window.h	(revision b71c0fcb07738ec42b4e693c19b1a5c1e2f329a6)
@@ -63,4 +63,6 @@
 	/** Window decoration */
 	struct ui_wdecor *wdecor;
+	/** Top-level control in the application area */
+	struct ui_control *control;
 };
 
Index: uspace/lib/ui/src/control.c
===================================================================
--- uspace/lib/ui/src/control.c	(revision fa01c0532bcb8f0b3291b5a02e32606e0515492b)
+++ uspace/lib/ui/src/control.c	(revision b71c0fcb07738ec42b4e693c19b1a5c1e2f329a6)
@@ -81,8 +81,11 @@
  * extended data).
  *
- * @param control Control
+ * @param control Control or @c NULL
  */
 void ui_control_destroy(ui_control_t *control)
 {
+	if (control == NULL)
+		return;
+
 	return control->ops->destroy(control->ext);
 }
Index: uspace/lib/ui/src/fixed.c
===================================================================
--- uspace/lib/ui/src/fixed.c	(revision fa01c0532bcb8f0b3291b5a02e32606e0515492b)
+++ uspace/lib/ui/src/fixed.c	(revision b71c0fcb07738ec42b4e693c19b1a5c1e2f329a6)
@@ -44,4 +44,15 @@
 #include "../private/fixed.h"
 
+static void ui_fixed_ctl_destroy(void *);
+static errno_t ui_fixed_ctl_paint(void *);
+static ui_evclaim_t ui_fixed_ctl_pos_event(void *, pos_event_t *);
+
+/** Push button control ops */
+ui_control_ops_t ui_fixed_ops = {
+	.destroy = ui_fixed_ctl_destroy,
+	.paint = ui_fixed_ctl_paint,
+	.pos_event = ui_fixed_ctl_pos_event
+};
+
 /** Create new fixed layout.
  *
@@ -52,8 +63,15 @@
 {
 	ui_fixed_t *fixed;
+	errno_t rc;
 
 	fixed = calloc(1, sizeof(ui_fixed_t));
 	if (fixed == NULL)
 		return ENOMEM;
+
+	rc = ui_control_new(&ui_fixed_ops, (void *) fixed, &fixed->control);
+	if (rc != EOK) {
+		free(fixed);
+		return rc;
+	}
 
 	list_initialize(&fixed->elem);
@@ -83,5 +101,16 @@
 	}
 
+	ui_control_delete(fixed->control);
 	free(fixed);
+}
+
+/** Get base control from fixed layout.
+ *
+ * @param fixed Fixed layout
+ * @return Control
+ */
+ui_control_t *ui_fixed_ctl(ui_fixed_t *fixed)
+{
+	return fixed->control;
 }
 
@@ -203,4 +232,40 @@
 }
 
+/** Destroy fixed layout control.
+ *
+ * @param arg Argument (ui_fixed_t *)
+ */
+void ui_fixed_ctl_destroy(void *arg)
+{
+	ui_fixed_t *fixed = (ui_fixed_t *) arg;
+
+	ui_fixed_destroy(fixed);
+}
+
+/** Paint fixed layout control.
+ *
+ * @param arg Argument (ui_fixed_t *)
+ * @return EOK on success or an error code
+ */
+errno_t ui_fixed_ctl_paint(void *arg)
+{
+	ui_fixed_t *fixed = (ui_fixed_t *) arg;
+
+	return ui_fixed_paint(fixed);
+}
+
+/** Handle fixed layout control position event.
+ *
+ * @param arg Argument (ui_fixed_t *)
+ * @param pos_event Position event
+ * @return @c ui_claimed iff the event is claimed
+ */
+ui_evclaim_t ui_fixed_ctl_pos_event(void *arg, pos_event_t *event)
+{
+	ui_fixed_t *fixed = (ui_fixed_t *) arg;
+
+	return ui_fixed_pos_event(fixed, event);
+}
+
 /** @}
  */
Index: uspace/lib/ui/src/window.c
===================================================================
--- uspace/lib/ui/src/window.c	(revision fa01c0532bcb8f0b3291b5a02e32606e0515492b)
+++ uspace/lib/ui/src/window.c	(revision b71c0fcb07738ec42b4e693c19b1a5c1e2f329a6)
@@ -42,7 +42,9 @@
 #include <mem.h>
 #include <stdlib.h>
+#include <ui/control.h>
 #include <ui/resource.h>
 #include <ui/wdecor.h>
 #include <ui/window.h>
+#include "../private/control.h"
 #include "../private/dummygc.h"
 #include "../private/resource.h"
@@ -170,4 +172,5 @@
 		return;
 
+	ui_control_destroy(window->control);
 	ui_wdecor_destroy(window->wdecor);
 	ui_resource_destroy(window->res);
@@ -177,4 +180,35 @@
 }
 
+/** Add control to window.
+ *
+ * Only one control can be added to a window. If more than one control
+ * is added, the results are undefined.
+ *
+ * @param fixed Fixed layout
+ * @param control Control
+ * @return EOK on success, ENOMEM if out of memory
+ */
+void ui_window_add(ui_window_t *window, ui_control_t *control)
+{
+	assert(window->control == NULL);
+
+	window->control = control;
+	control->elemp = (void *) window;
+}
+
+/** Remove control from window.
+ *
+ * @param window Window
+ * @param control Control
+ */
+void ui_window_remove(ui_window_t *window, ui_control_t *control)
+{
+	assert(window->control == control);
+	assert((ui_window_t *) control->elemp == window);
+
+	window->control = NULL;
+	control->elemp = NULL;
+}
+
 /** Set window callbacks.
  *
@@ -343,4 +377,6 @@
 	if (window->cb != NULL && window->cb->pos != NULL)
 		window->cb->pos(window, window->arg, pos);
+	else
+		ui_window_def_pos(window, pos);
 }
 
@@ -375,7 +411,21 @@
 		return rc;
 
+	if (window->control != NULL)
+		return ui_control_paint(window->control);
+
 	return EOK;
 }
 
+/** Default window position event routine.
+ *
+ * @param window Window
+ * @return EOK on success or an error code
+ */
+void ui_window_def_pos(ui_window_t *window, pos_event_t *pos)
+{
+	if (window->control != NULL)
+		ui_control_pos_event(window->control, pos);
+}
+
 /** @}
  */
Index: uspace/lib/ui/test/fixed.c
===================================================================
--- uspace/lib/ui/test/fixed.c	(revision fa01c0532bcb8f0b3291b5a02e32606e0515492b)
+++ uspace/lib/ui/test/fixed.c	(revision b71c0fcb07738ec42b4e693c19b1a5c1e2f329a6)
@@ -83,4 +83,20 @@
 }
 
+/** ui_fixed_ctl() returns control that has a working virtual destructor */
+PCUT_TEST(ctl)
+{
+	ui_fixed_t *fixed;
+	ui_control_t *control;
+	errno_t rc;
+
+	rc = ui_fixed_create(&fixed);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	control = ui_fixed_ctl(fixed);
+	PCUT_ASSERT_NOT_NULL(control);
+
+	ui_control_destroy(control);
+}
+
 /** ui_fixed_add() / ui_fixed_remove() adds/removes control */
 PCUT_TEST(add_remove)
Index: uspace/lib/ui/test/label.c
===================================================================
--- uspace/lib/ui/test/label.c	(revision fa01c0532bcb8f0b3291b5a02e32606e0515492b)
+++ uspace/lib/ui/test/label.c	(revision b71c0fcb07738ec42b4e693c19b1a5c1e2f329a6)
@@ -32,4 +32,5 @@
 #include <pcut/pcut.h>
 #include <stdbool.h>
+#include <ui/control.h>
 #include <ui/label.h>
 #include <ui/resource.h>
@@ -95,4 +96,20 @@
 {
 	ui_label_destroy(NULL);
+}
+
+/** ui_label_ctl() returns control that has a working virtual destructor */
+PCUT_TEST(ctl)
+{
+	ui_label_t *label;
+	ui_control_t *control;
+	errno_t rc;
+
+	rc = ui_label_create(NULL, "Hello", &label);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	control = ui_label_ctl(label);
+	PCUT_ASSERT_NOT_NULL(control);
+
+	ui_control_destroy(control);
 }
 
Index: uspace/lib/ui/test/pbutton.c
===================================================================
--- uspace/lib/ui/test/pbutton.c	(revision fa01c0532bcb8f0b3291b5a02e32606e0515492b)
+++ uspace/lib/ui/test/pbutton.c	(revision b71c0fcb07738ec42b4e693c19b1a5c1e2f329a6)
@@ -32,4 +32,5 @@
 #include <pcut/pcut.h>
 #include <stdbool.h>
+#include <ui/control.h>
 #include <ui/pbutton.h>
 #include <ui/resource.h>
@@ -104,4 +105,20 @@
 {
 	ui_pbutton_destroy(NULL);
+}
+
+/** ui_pbutton_ctl() returns control that has a working virtual destructor */
+PCUT_TEST(ctl)
+{
+	ui_pbutton_t *pbutton;
+	ui_control_t *control;
+	errno_t rc;
+
+	rc = ui_pbutton_create(NULL, "Hello", &pbutton);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	control = ui_pbutton_ctl(pbutton);
+	PCUT_ASSERT_NOT_NULL(control);
+
+	ui_control_destroy(control);
 }
 
Index: uspace/lib/ui/test/window.c
===================================================================
--- uspace/lib/ui/test/window.c	(revision fa01c0532bcb8f0b3291b5a02e32606e0515492b)
+++ uspace/lib/ui/test/window.c	(revision b71c0fcb07738ec42b4e693c19b1a5c1e2f329a6)
@@ -34,4 +34,5 @@
 #include <pcut/pcut.h>
 #include <stdbool.h>
+#include <ui/control.h>
 #include <ui/resource.h>
 #include <ui/ui.h>
@@ -60,4 +61,12 @@
 
 static ui_window_cb_t dummy_window_cb = {
+};
+
+static errno_t test_ctl_paint(void *);
+static ui_evclaim_t test_ctl_pos_event(void *, pos_event_t *);
+
+static ui_control_ops_t test_ctl_ops = {
+	.paint = test_ctl_paint,
+	.pos_event = test_ctl_pos_event
 };
 
@@ -74,4 +83,12 @@
 } test_cb_resp_t;
 
+typedef struct {
+	errno_t rc;
+	ui_evclaim_t claim;
+	bool paint;
+	bool pos;
+	pos_event_t pos_event;
+} test_ctl_resp_t;
+
 /** Create and destroy window */
 PCUT_TEST(create_destroy)
@@ -100,4 +117,59 @@
 {
 	ui_window_destroy(NULL);
+}
+
+/** ui_window_add()/ui_window_remove() ... */
+PCUT_TEST(add_remove)
+{
+	errno_t rc;
+	ui_t *ui = NULL;
+	ui_wnd_params_t params;
+	ui_window_t *window = NULL;
+	ui_control_t *control = NULL;
+	test_ctl_resp_t resp;
+
+	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_control_new(&test_ctl_ops, &resp, &control);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	/* Control not called since it hasn't been added yet */
+	resp.rc = ENOMEM;
+	resp.paint = false;
+	rc = ui_window_def_paint(window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_FALSE(resp.paint);
+
+	ui_window_add(window, control);
+
+	/* Now paint request should be delivered to control */
+	resp.rc = EOK;
+	resp.paint = false;
+	rc = ui_window_def_paint(window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_TRUE(resp.paint);
+
+	ui_window_remove(window, control);
+
+	/*
+	 * After having removed the control the request should no longer
+	 * be delivered to it.
+	 */
+	resp.rc = ENOMEM;
+	resp.paint = false;
+	rc = ui_window_def_paint(window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_FALSE(resp.paint);
+
+	ui_window_destroy(window);
+	ui_destroy(ui);
 }
 
@@ -166,16 +238,88 @@
 	ui_wnd_params_t params;
 	ui_window_t *window = NULL;
-
-	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);
-
-	ui_window_def_paint(window);
+	ui_control_t *control = NULL;
+	test_ctl_resp_t resp;
+
+	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_control_new(&test_ctl_ops, &resp, &control);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_window_add(window, control);
+
+	resp.rc = EOK;
+	resp.paint = false;
+	rc = ui_window_def_paint(window);
+	PCUT_ASSERT_ERRNO_VAL(resp.rc, rc);
+	PCUT_ASSERT_TRUE(resp.paint);
+
+	resp.rc = ENOMEM;
+	resp.paint = false;
+	rc = ui_window_def_paint(window);
+	PCUT_ASSERT_ERRNO_VAL(resp.rc, rc);
+	PCUT_ASSERT_TRUE(resp.paint);
+
+	PCUT_ASSERT_TRUE(resp.paint);
+
+	/* Need to remove first because we didn't implement the destructor */
+	ui_window_remove(window, control);
+
+	ui_window_destroy(window);
+	ui_destroy(ui);
+}
+
+/** ui_window_def_pos() delivers position event to control in window */
+PCUT_TEST(def_pos)
+{
+	errno_t rc;
+	ui_t *ui = NULL;
+	ui_wnd_params_t params;
+	ui_window_t *window = NULL;
+	ui_control_t *control = NULL;
+	test_ctl_resp_t resp;
+	pos_event_t event;
+
+	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);
+
+	rc = ui_control_new(&test_ctl_ops, &resp, &control);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_window_add(window, control);
+
+	event.pos_id = 1;
+	event.type = POS_PRESS;
+	event.btn_num = 2;
+	event.hpos = 3;
+	event.vpos = 4;
+
+	resp.pos = false;
+	resp.claim = ui_claimed;
+
+	ui_window_def_pos(window, &event);
+
+	PCUT_ASSERT_TRUE(resp.pos);
+	PCUT_ASSERT_INT_EQUALS(event.pos_id, resp.pos_event.pos_id);
+	PCUT_ASSERT_INT_EQUALS(event.type, resp.pos_event.type);
+	PCUT_ASSERT_INT_EQUALS(event.btn_num, resp.pos_event.btn_num);
+	PCUT_ASSERT_INT_EQUALS(event.hpos, resp.pos_event.hpos);
+	PCUT_ASSERT_INT_EQUALS(event.vpos, resp.pos_event.vpos);
+
+	/* Need to remove first because we didn't implement the destructor */
+	ui_window_remove(window, control);
 
 	ui_window_destroy(window);
@@ -470,3 +614,21 @@
 }
 
+static errno_t test_ctl_paint(void *arg)
+{
+	test_ctl_resp_t *resp = (test_ctl_resp_t *) arg;
+
+	resp->paint = true;
+	return resp->rc;
+}
+
+static ui_evclaim_t test_ctl_pos_event(void *arg, pos_event_t *event)
+{
+	test_ctl_resp_t *resp = (test_ctl_resp_t *) arg;
+
+	resp->pos = true;
+	resp->pos_event = *event;
+
+	return resp->claim;
+}
+
 PCUT_EXPORT(window);
