Index: uspace/lib/ui/src/entry.c
===================================================================
--- uspace/lib/ui/src/entry.c	(revision db3895d936f17c9389f3e8e2689bf28645fc3697)
+++ uspace/lib/ui/src/entry.c	(revision ec1683116db558cf06462cbc1eaf569a42e8bef9)
@@ -53,4 +53,5 @@
 static void ui_entry_ctl_destroy(void *);
 static errno_t ui_entry_ctl_paint(void *);
+static ui_evclaim_t ui_entry_ctl_kbd_event(void *, kbd_event_t *);
 static ui_evclaim_t ui_entry_ctl_pos_event(void *, pos_event_t *);
 
@@ -66,4 +67,5 @@
 	.destroy = ui_entry_ctl_destroy,
 	.paint = ui_entry_ctl_paint,
+	.kbd_event = ui_entry_ctl_kbd_event,
 	.pos_event = ui_entry_ctl_pos_event
 };
@@ -148,4 +150,14 @@
 }
 
+/** Set text entry read-only flag.
+ *
+ * @param entry Text entry
+ * @param read_only True iff entry is to be read-only.
+ */
+void ui_entry_set_read_only(ui_entry_t *entry, bool read_only)
+{
+	entry->read_only = read_only;
+}
+
 /** Set entry text.
  *
@@ -180,4 +192,5 @@
 	gfx_coord_t hpad;
 	gfx_coord_t vpad;
+	gfx_coord_t width;
 	gfx_rect_t inside;
 	errno_t rc;
@@ -212,4 +225,6 @@
 		goto error;
 
+	width = gfx_text_width(res->font, entry->text);
+
 	switch (entry->halign) {
 	case gfx_halign_left:
@@ -218,8 +233,8 @@
 		break;
 	case gfx_halign_center:
-		pos.x = (inside.p0.x + inside.p1.x) / 2;
+		pos.x = (inside.p0.x + inside.p1.x) / 2 - width / 2;
 		break;
 	case gfx_halign_right:
-		pos.x = inside.p1.x - hpad - 1;
+		pos.x = inside.p1.x - hpad - 1 - width;
 		break;
 	}
@@ -229,8 +244,29 @@
 	gfx_text_fmt_init(&fmt);
 	fmt.color = res->entry_fg_color;
-	fmt.halign = entry->halign;
+	fmt.halign = gfx_halign_left;
 	fmt.valign = gfx_valign_top;
 
+	rc = gfx_set_clip_rect(res->gc, &inside);
+	if (rc != EOK)
+		goto error;
+
 	rc = gfx_puttext(res->font, &pos, &fmt, entry->text);
+	if (rc != EOK) {
+		(void) gfx_set_clip_rect(res->gc, NULL);
+		goto error;
+	}
+
+	if (entry->active) {
+		/* Cursor */
+		pos.x += width;
+
+		rc = gfx_puttext(res->font, &pos, &fmt, "_");
+		if (rc != EOK) {
+			(void) gfx_set_clip_rect(res->gc, NULL);
+			goto error;
+		}
+	}
+
+	rc = gfx_set_clip_rect(res->gc, NULL);
 	if (rc != EOK)
 		goto error;
@@ -268,14 +304,107 @@
 }
 
-/** Handle text entry control position event.
- *
- * @param arg Argument (ui_entry_t *)
+/** Insert string at cursor position.
+ *
+ * @param entry Text entry
+ * @param str String
+ * @return EOK on success, ENOMEM if out of memory
+ */
+errno_t ui_entry_insert_str(ui_entry_t *entry, const char *str)
+{
+	char *newtext;
+	char *oldtext;
+	int rc;
+
+	rc = asprintf(&newtext, "%s%s", entry->text, str);
+	if (rc < 0)
+		return ENOMEM;
+
+	oldtext = entry->text;
+	entry->text = newtext;
+	free(oldtext);
+	ui_entry_paint(entry);
+
+	return EOK;
+}
+
+/** Delete character before cursor.
+ *
+ * @param entry Text entry
+ */
+void ui_entry_backspace(ui_entry_t *entry)
+{
+	size_t off;
+
+	off = str_size(entry->text);
+	(void) str_decode_reverse(entry->text, &off,
+	    str_size(entry->text));
+	entry->text[off] = '\0';
+	ui_entry_paint(entry);
+}
+
+/** Handle text entry key press without modifiers.
+ *
+ * @param entry Text entry
+ * @param kbd_event Keyboard event
+ * @return @c ui_claimed iff the event is claimed
+ */
+ui_evclaim_t ui_entry_key_press_unmod(ui_entry_t *entry, kbd_event_t *event)
+{
+	assert(event->type == KEY_PRESS);
+
+	if (event->key == KC_BACKSPACE)
+		ui_entry_backspace(entry);
+
+	if (event->key == KC_ESCAPE) {
+		entry->active = false;
+		(void) ui_entry_paint(entry);
+	}
+
+	return ui_claimed;
+}
+
+/** Handle text entry keyboard event.
+ *
+ * @param entry Text entry
+ * @param kbd_event Keyboard event
+ * @return @c ui_claimed iff the event is claimed
+ */
+ui_evclaim_t ui_entry_kbd_event(ui_entry_t *entry, kbd_event_t *event)
+{
+	char buf[STR_BOUNDS(1) + 1];
+	size_t off;
+	errno_t rc;
+
+	if (!entry->active)
+		return ui_unclaimed;
+
+	if (event->type == KEY_PRESS && event->c >= ' ') {
+		off = 0;
+		rc = chr_encode(event->c, buf, &off, sizeof(buf));
+		if (rc == EOK) {
+			buf[off] = '\0';
+			(void) ui_entry_insert_str(entry, buf);
+		}
+	}
+
+	if (event->type == KEY_PRESS &&
+	    (event->mods & (KM_CTRL | KM_ALT | KM_SHIFT)) == 0)
+		return ui_entry_key_press_unmod(entry, event);
+
+	return ui_claimed;
+}
+
+/** Handle text entry position event.
+ *
+ * @param entry Text entry
  * @param pos_event Position event
  * @return @c ui_claimed iff the event is claimed
  */
-ui_evclaim_t ui_entry_ctl_pos_event(void *arg, pos_event_t *event)
-{
-	ui_entry_t *entry = (ui_entry_t *) arg;
+ui_evclaim_t ui_entry_pos_event(ui_entry_t *entry, pos_event_t *event)
+{
 	gfx_coord2_t pos;
+
+	if (entry->read_only)
+		return ui_unclaimed;
 
 	if (event->type == POS_UPDATE) {
@@ -298,7 +427,52 @@
 	}
 
+	if (event->type == POS_PRESS) {
+		pos.x = event->hpos;
+		pos.y = event->vpos;
+
+		if (gfx_pix_inside_rect(&pos, &entry->rect)) {
+			if (!entry->active) {
+				entry->active = true;
+				(void) ui_entry_paint(entry);
+			}
+
+			return ui_claimed;
+		} else {
+			if (entry->active) {
+				entry->active = false;
+				(void) ui_entry_paint(entry);
+			}
+		}
+	}
+
 	return ui_unclaimed;
 }
 
+/** Handle text entry control keyboard event.
+ *
+ * @param arg Argument (ui_entry_t *)
+ * @param kbd_event Keyboard event
+ * @return @c ui_claimed iff the event is claimed
+ */
+static ui_evclaim_t ui_entry_ctl_kbd_event(void *arg, kbd_event_t *event)
+{
+	ui_entry_t *entry = (ui_entry_t *) arg;
+
+	return ui_entry_kbd_event(entry, event);
+}
+
+/** Handle text entry control position event.
+ *
+ * @param arg Argument (ui_entry_t *)
+ * @param pos_event Position event
+ * @return @c ui_claimed iff the event is claimed
+ */
+static ui_evclaim_t ui_entry_ctl_pos_event(void *arg, pos_event_t *event)
+{
+	ui_entry_t *entry = (ui_entry_t *) arg;
+
+	return ui_entry_pos_event(entry, event);
+}
+
 /** @}
  */
