Index: uspace/app/uidemo/uidemo.c
===================================================================
--- uspace/app/uidemo/uidemo.c	(revision ba09d06aebf790cc076fec896c12c86cdc473960)
+++ uspace/app/uidemo/uidemo.c	(revision 20d2c6c347e0268ccd70ae81f107a1b4dd219ebb)
@@ -66,12 +66,12 @@
 };
 
+static void wd_close(ui_wdecor_t *, void *);
 static void wd_move(ui_wdecor_t *, void *, gfx_coord2_t *);
 
 static ui_wdecor_cb_t wdecor_cb = {
+	.close = wd_close,
 	.move = wd_move
 };
 
-static bool quit = false;
-
 /** Print syntax. */
 static void print_syntax(void)
@@ -83,6 +83,8 @@
 static void wnd_close_event(void *arg)
 {
+	ui_demo_t *demo = (ui_demo_t *) arg;
+
 	printf("Close event\n");
-	quit = true;
+	demo->quit = true;
 }
 
@@ -101,7 +103,8 @@
 static void wnd_kbd_event(void *arg, kbd_event_t *event)
 {
+	ui_demo_t *demo = (ui_demo_t *) arg;
+
+	(void) demo;
 	printf("Keyboard event type=%d key=%d\n", event->type, event->key);
-	if (event->type == KEY_PRESS)
-		quit = true;
 }
 
@@ -156,4 +159,17 @@
 }
 
+/** Window decoration requested window closure.
+ *
+ * @param wdecor Window decoration
+ * @param arg Argument (demo)
+ */
+static void wd_close(ui_wdecor_t *wdecor, void *arg)
+{
+	ui_demo_t *demo = (ui_demo_t *) arg;
+
+	printf("Close window requested\n");
+	demo->quit = true;
+}
+
 /** Window decoration requested window move.
  *
@@ -205,4 +221,5 @@
 	}
 
+	demo.quit = false;
 	demo.dwindow = window;
 
@@ -318,5 +335,5 @@
 	}
 
-	while (!quit) {
+	while (!demo.quit) {
 		fibril_usleep(100 * 1000);
 	}
Index: uspace/app/uidemo/uidemo.h
===================================================================
--- uspace/app/uidemo/uidemo.h	(revision ba09d06aebf790cc076fec896c12c86cdc473960)
+++ uspace/app/uidemo/uidemo.h	(revision 20d2c6c347e0268ccd70ae81f107a1b4dd219ebb)
@@ -49,4 +49,5 @@
 	ui_pbutton_t *pb1;
 	ui_pbutton_t *pb2;
+	bool quit;
 } ui_demo_t;
 
Index: uspace/lib/ui/private/wdecor.h
===================================================================
--- uspace/lib/ui/private/wdecor.h	(revision ba09d06aebf790cc076fec896c12c86cdc473960)
+++ uspace/lib/ui/private/wdecor.h	(revision 20d2c6c347e0268ccd70ae81f107a1b4dd219ebb)
@@ -56,9 +56,12 @@
 	gfx_rect_t rect;
 	/** Caption */
-	const char *caption;
+	char *caption;
 	/** Window is active */
 	bool active;
+	/** Close button */
+	struct ui_pbutton *btn_close;
 };
 
+extern void ui_wdecor_close(ui_wdecor_t *);
 extern void ui_wdecor_move(ui_wdecor_t *, gfx_coord2_t *);
 
Index: uspace/lib/ui/src/wdecor.c
===================================================================
--- uspace/lib/ui/src/wdecor.c	(revision ba09d06aebf790cc076fec896c12c86cdc473960)
+++ uspace/lib/ui/src/wdecor.c	(revision 20d2c6c347e0268ccd70ae81f107a1b4dd219ebb)
@@ -43,7 +43,14 @@
 #include <str.h>
 #include <ui/paint.h>
+#include <ui/pbutton.h>
 #include <ui/wdecor.h>
 #include "../private/resource.h"
 #include "../private/wdecor.h"
+
+static void ui_wdecor_btn_clicked(ui_pbutton_t *, void *);
+
+static ui_pbutton_cb_t ui_wdecor_btn_close_cb = {
+	.clicked = ui_wdecor_btn_clicked
+};
 
 /** Create new window decoration.
@@ -58,4 +65,5 @@
 {
 	ui_wdecor_t *wdecor;
+	errno_t rc;
 
 	wdecor = calloc(1, sizeof(ui_wdecor_t));
@@ -69,4 +77,14 @@
 	}
 
+	rc = ui_pbutton_create(resource, "X", &wdecor->btn_close);
+	if (rc != EOK) {
+		free(wdecor->caption);
+		free(wdecor);
+		return rc;
+	}
+
+	ui_pbutton_set_cb(wdecor->btn_close, &ui_wdecor_btn_close_cb,
+	    (void *)wdecor);
+
 	wdecor->res = resource;
 	wdecor->active = true;
@@ -84,4 +102,6 @@
 		return;
 
+	ui_pbutton_destroy(wdecor->btn_close);
+	free(wdecor->caption);
 	free(wdecor);
 }
@@ -106,5 +126,13 @@
 void ui_wdecor_set_rect(ui_wdecor_t *wdecor, gfx_rect_t *rect)
 {
+	gfx_rect_t crect;
+
 	wdecor->rect = *rect;
+	crect.p0.x = rect->p1.x - 5 - 20;
+	crect.p0.y = rect->p0.y + 5;
+	crect.p1.x = rect->p1.x - 5;
+	crect.p1.y = rect->p0.y + 5 + 20;
+
+	ui_pbutton_set_rect(wdecor->btn_close, &crect);
 }
 
@@ -191,5 +219,20 @@
 		return rc;
 
+	rc = ui_pbutton_paint(wdecor->btn_close);
+	if (rc != EOK)
+		return rc;
+
 	return EOK;
+}
+
+/** Send decoration close event.
+ *
+ * @param wdecor Window decoration
+ * @param pos Position where the title bar was pressed
+ */
+void ui_wdecor_close(ui_wdecor_t *wdecor)
+{
+	if (wdecor->cb != NULL && wdecor->cb->close != NULL)
+		wdecor->cb->close(wdecor, wdecor->arg);
 }
 
@@ -213,4 +256,5 @@
 {
 	gfx_rect_t trect;
+	gfx_rect_t cbrect;
 	gfx_coord2_t pos;
 
@@ -220,11 +264,34 @@
 	trect.p1.y = trect.p0.y + 22;
 
+	cbrect.p0.x = wdecor->rect.p1.x - 5 - 20;
+	cbrect.p0.y = wdecor->rect.p0.y + 5;
+	cbrect.p1.x = wdecor->rect.p1.x - 5;
+	cbrect.p1.y = wdecor->rect.p0.y + 5 + 20;
+
 	pos.x = event->hpos;
 	pos.y = event->vpos;
 
+	if (gfx_pix_inside_rect(&pos, &cbrect)) {
+		ui_pbutton_pos_event(wdecor->btn_close, event);
+		return;
+	}
+
 	if (event->type == POS_PRESS && gfx_pix_inside_rect(&pos, &trect))
 		ui_wdecor_move(wdecor, &pos);
 }
 
+/** Window decoration close button was clicked.
+ *
+ * @param pbutton Close button
+ * @param arg Argument (ui_wdecor_t)
+ */
+static void ui_wdecor_btn_clicked(ui_pbutton_t *pbutton, void *arg)
+{
+	ui_wdecor_t *wdecor = (ui_wdecor_t *) arg;
+
+	(void) pbutton;
+	ui_wdecor_close(wdecor);
+}
+
 /** @}
  */
Index: uspace/lib/ui/test/wdecor.c
===================================================================
--- uspace/lib/ui/test/wdecor.c	(revision ba09d06aebf790cc076fec896c12c86cdc473960)
+++ uspace/lib/ui/test/wdecor.c	(revision 20d2c6c347e0268ccd70ae81f107a1b4dd219ebb)
@@ -32,4 +32,5 @@
 #include <pcut/pcut.h>
 #include <stdbool.h>
+#include <ui/pbutton.h>
 #include <ui/resource.h>
 #include <ui/wdecor.h>
@@ -57,7 +58,9 @@
 };
 
+static void test_wdecor_close(ui_wdecor_t *, void *);
 static void test_wdecor_move(ui_wdecor_t *, void *, gfx_coord2_t *);
 
 static ui_wdecor_cb_t test_wdecor_cb = {
+	.close = test_wdecor_close,
 	.move = test_wdecor_move
 };
@@ -84,4 +87,5 @@
 
 typedef struct {
+	bool close;
 	bool move;
 	gfx_coord2_t pos;
@@ -179,4 +183,30 @@
 	rc = gfx_context_delete(gc);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+/** Test ui_wdecor_close() */
+PCUT_TEST(close)
+{
+	errno_t rc;
+	ui_wdecor_t *wdecor;
+	test_cb_resp_t resp;
+
+	rc = ui_wdecor_create(NULL, "Hello", &wdecor);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	/* Close callback with no callbacks set */
+	ui_wdecor_close(wdecor);
+
+	/* Close callback with close callback not implemented */
+	ui_wdecor_set_cb(wdecor, &dummy_wdecor_cb, NULL);
+	ui_wdecor_close(wdecor);
+
+	/* Close callback with real callback set */
+	resp.close = false;
+	ui_wdecor_set_cb(wdecor, &test_wdecor_cb, &resp);
+	ui_wdecor_close(wdecor);
+	PCUT_ASSERT_TRUE(resp.close);
+
+	ui_wdecor_destroy(wdecor);
 }
 
@@ -211,4 +241,32 @@
 	PCUT_ASSERT_INT_EQUALS(pos.x, resp.pos.x);
 	PCUT_ASSERT_INT_EQUALS(pos.y, resp.pos.y);
+
+	ui_wdecor_destroy(wdecor);
+}
+
+/** Clicking the close button generates close callback */
+PCUT_TEST(close_btn_clicked)
+{
+	ui_wdecor_t *wdecor;
+	gfx_rect_t rect;
+	test_cb_resp_t resp;
+	errno_t rc;
+
+	rc = ui_wdecor_create(NULL, "Hello", &wdecor);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rect.p0.x = 10;
+	rect.p0.y = 20;
+	rect.p1.x = 100;
+	rect.p1.y = 200;
+
+	ui_wdecor_set_rect(wdecor, &rect);
+
+	ui_wdecor_set_cb(wdecor, &test_wdecor_cb, (void *) &resp);
+
+	resp.close = false;
+
+	ui_pbutton_clicked(wdecor->btn_close);
+	PCUT_ASSERT_TRUE(resp.close);
 
 	ui_wdecor_destroy(wdecor);
@@ -328,4 +386,11 @@
 }
 
+static void test_wdecor_close(ui_wdecor_t *wdecor, void *arg)
+{
+	test_cb_resp_t *resp = (test_cb_resp_t *) arg;
+
+	resp->close = true;
+}
+
 static void test_wdecor_move(ui_wdecor_t *wdecor, void *arg, gfx_coord2_t *pos)
 {
