Index: uspace/app/nav/nav.c
===================================================================
--- uspace/app/nav/nav.c	(revision 805a149bb760f7dc7a16bc1442f6c3e5b0186395)
+++ uspace/app/nav/nav.c	(revision 8edec5347eb33f22b1815db3609ff69c0f618d63)
@@ -130,4 +130,6 @@
 	ui_wnd_params_t params;
 	gfx_rect_t rect;
+	gfx_rect_t arect;
+	gfx_coord_t pw;
 	unsigned i;
 	errno_t rc;
@@ -155,4 +157,5 @@
 
 	ui_window_set_cb(navigator->window, &window_cb, (void *) navigator);
+	ui_window_get_app_rect(navigator->window, &arect);
 
 	rc = ui_fixed_create(&navigator->fixed);
@@ -176,4 +179,7 @@
 		return rc;
 	}
+
+	/* Panel width */
+	pw = (arect.p1.x - arect.p0.x) / 2;
 
 	for (i = 0; i < 2; i++) {
@@ -183,8 +189,8 @@
 			goto error;
 
-		rect.p0.x = 40 * i;
-		rect.p0.y = 1;
-		rect.p1.x = 40 * (i + 1);
-		rect.p1.y = 24;
+		rect.p0.x = arect.p0.x + pw * i;
+		rect.p0.y = arect.p0.y + 1;
+		rect.p1.x = arect.p0.x + pw * (i + 1);
+		rect.p1.y = arect.p1.y - 1;
 		panel_set_rect(navigator->panel[i], &rect);
 
Index: uspace/app/nav/panel.c
===================================================================
--- uspace/app/nav/panel.c	(revision 805a149bb760f7dc7a16bc1442f6c3e5b0186395)
+++ uspace/app/nav/panel.c	(revision 8edec5347eb33f22b1815db3609ff69c0f618d63)
@@ -351,10 +351,10 @@
 		panel_activate_req(panel);
 
-	if (event->type == POS_PRESS) {
-		irect.p0.x = panel->rect.p0.x + 1;
-		irect.p0.y = panel->rect.p0.y + 1;
-		irect.p1.x = panel->rect.p1.x - 1;
-		irect.p1.y = panel->rect.p1.y - 1;
-
+	irect.p0.x = panel->rect.p0.x + 1;
+	irect.p0.y = panel->rect.p0.y + 1;
+	irect.p1.x = panel->rect.p1.x - 1;
+	irect.p1.y = panel->rect.p1.y - 1;
+
+	if (event->type == POS_PRESS || event->type == POS_DCLICK) {
 		/* Did we click on one of the entries? */
 		if (gfx_pix_inside_rect(&pos, &irect)) {
@@ -365,12 +365,20 @@
 			entry = panel_page_nth_entry(panel, n, &entry_idx);
 
-			/* Move to the entry found */
-			panel_cursor_move(panel, entry, entry_idx);
+			if (event->type == POS_PRESS) {
+				/* Move to the entry found */
+				panel_cursor_move(panel, entry, entry_idx);
+			} else {
+				/* event->type == POS_DCLICK */
+				panel_open(panel, entry);
+			}
 		} else {
-			/* It's in the border. Top or bottom half? */
-			if (pos.y >= (irect.p0.y + irect.p1.y) / 2)
-				panel_page_down(panel);
-			else
-				panel_page_up(panel);
+			/* It's in the border. */
+			if (event->type == POS_PRESS) {
+				/* Top or bottom half? */
+				if (pos.y >= (irect.p0.y + irect.p1.y) / 2)
+					panel_page_down(panel);
+				else
+					panel_page_up(panel);
+			}
 		}
 	}
Index: uspace/app/nav/panel.h
===================================================================
--- uspace/app/nav/panel.h	(revision 805a149bb760f7dc7a16bc1442f6c3e5b0186395)
+++ uspace/app/nav/panel.h	(revision 8edec5347eb33f22b1815db3609ff69c0f618d63)
@@ -83,5 +83,4 @@
 extern void panel_activate_req(panel_t *);
 
-
 #endif
 
Index: uspace/app/terminal/terminal.c
===================================================================
--- uspace/app/terminal/terminal.c	(revision 805a149bb760f7dc7a16bc1442f6c3e5b0186395)
+++ uspace/app/terminal/terminal.c	(revision 8edec5347eb33f22b1815db3609ff69c0f618d63)
@@ -842,5 +842,6 @@
 	sysarg_t sy = -term->off.y;
 
-	if (event->type == POS_PRESS || event->type == POS_RELEASE) {
+	if (event->type == POS_PRESS || event->type == POS_RELEASE ||
+	    event->type == POS_DCLICK) {
 		cevent.type = CEV_POS;
 		cevent.ev.pos.type = event->type;
Index: uspace/lib/c/generic/io/input.c
===================================================================
--- uspace/lib/c/generic/io/input.c	(revision 805a149bb760f7dc7a16bc1442f6c3e5b0186395)
+++ uspace/lib/c/generic/io/input.c	(revision 8edec5347eb33f22b1815db3609ff69c0f618d63)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2012 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -164,4 +164,15 @@
 }
 
+static void input_ev_dclick(input_t *input, ipc_call_t *call)
+{
+	int bnum;
+	errno_t rc;
+
+	bnum = ipc_get_arg1(call);
+
+	rc = input->ev_ops->dclick(input, bnum);
+	async_answer_0(call, rc);
+}
+
 static void input_cb_conn(ipc_call_t *icall, void *arg)
 {
@@ -196,4 +207,7 @@
 			input_ev_button(input, &call);
 			break;
+		case INPUT_EVENT_DCLICK:
+			input_ev_dclick(input, &call);
+			break;
 		default:
 			async_answer_0(&call, ENOTSUP);
Index: uspace/lib/c/include/io/input.h
===================================================================
--- uspace/lib/c/include/io/input.h	(revision 805a149bb760f7dc7a16bc1442f6c3e5b0186395)
+++ uspace/lib/c/include/io/input.h	(revision 8edec5347eb33f22b1815db3609ff69c0f618d63)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2012 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -54,4 +54,5 @@
 	errno_t (*abs_move)(input_t *, unsigned, unsigned, unsigned, unsigned);
 	errno_t (*button)(input_t *, int, int);
+	errno_t (*dclick)(input_t *, int);
 } input_ev_ops_t;
 
Index: uspace/lib/c/include/io/pos_event.h
===================================================================
--- uspace/lib/c/include/io/pos_event.h	(revision 805a149bb760f7dc7a16bc1442f6c3e5b0186395)
+++ uspace/lib/c/include/io/pos_event.h	(revision 8edec5347eb33f22b1815db3609ff69c0f618d63)
@@ -1,5 +1,5 @@
 /*
+ * Copyright (c) 2021 Jiri Svoboda
  * Copyright (c) 2012 Petr Koupy
- * Copyright (c) 2013 Jiri Svoboda
  * All rights reserved.
  *
@@ -40,7 +40,12 @@
 
 typedef enum {
+	/** Position update */
 	POS_UPDATE,
+	/** Button press */
 	POS_PRESS,
-	POS_RELEASE
+	/** Button release */
+	POS_RELEASE,
+	/** Double click */
+	POS_DCLICK
 } pos_event_type_t;
 
Index: uspace/lib/c/include/ipc/input.h
===================================================================
--- uspace/lib/c/include/ipc/input.h	(revision 805a149bb760f7dc7a16bc1442f6c3e5b0186395)
+++ uspace/lib/c/include/ipc/input.h	(revision 8edec5347eb33f22b1815db3609ff69c0f618d63)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2011 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -48,5 +48,6 @@
 	INPUT_EVENT_MOVE,
 	INPUT_EVENT_ABS_MOVE,
-	INPUT_EVENT_BUTTON
+	INPUT_EVENT_BUTTON,
+	INPUT_EVENT_DCLICK
 } input_notif_t;
 
Index: uspace/lib/ui/src/checkbox.c
===================================================================
--- uspace/lib/ui/src/checkbox.c	(revision 805a149bb760f7dc7a16bc1442f6c3e5b0186395)
+++ uspace/lib/ui/src/checkbox.c	(revision 8edec5347eb33f22b1815db3609ff69c0f618d63)
@@ -397,4 +397,6 @@
 		}
 		break;
+	case POS_DCLICK:
+		break;
 	}
 
Index: uspace/lib/ui/src/menuentry.c
===================================================================
--- uspace/lib/ui/src/menuentry.c	(revision 805a149bb760f7dc7a16bc1442f6c3e5b0186395)
+++ uspace/lib/ui/src/menuentry.c	(revision 8edec5347eb33f22b1815db3609ff69c0f618d63)
@@ -497,4 +497,6 @@
 		}
 		break;
+	case POS_DCLICK:
+		break;
 	}
 
Index: uspace/lib/ui/src/pbutton.c
===================================================================
--- uspace/lib/ui/src/pbutton.c	(revision 805a149bb760f7dc7a16bc1442f6c3e5b0186395)
+++ uspace/lib/ui/src/pbutton.c	(revision 8edec5347eb33f22b1815db3609ff69c0f618d63)
@@ -521,4 +521,6 @@
 		}
 		break;
+	case POS_DCLICK:
+		break;
 	}
 
Index: uspace/lib/ui/src/rbutton.c
===================================================================
--- uspace/lib/ui/src/rbutton.c	(revision 805a149bb760f7dc7a16bc1442f6c3e5b0186395)
+++ uspace/lib/ui/src/rbutton.c	(revision 8edec5347eb33f22b1815db3609ff69c0f618d63)
@@ -490,4 +490,6 @@
 		}
 		break;
+	case POS_DCLICK:
+		break;
 	}
 
Index: uspace/lib/ui/src/slider.c
===================================================================
--- uspace/lib/ui/src/slider.c	(revision 805a149bb760f7dc7a16bc1442f6c3e5b0186395)
+++ uspace/lib/ui/src/slider.c	(revision 8edec5347eb33f22b1815db3609ff69c0f618d63)
@@ -522,4 +522,6 @@
 		ui_slider_update(slider, &pos);
 		break;
+	case POS_DCLICK:
+		break;
 	}
 
Index: uspace/srv/hid/console/console.c
===================================================================
--- uspace/srv/hid/console/console.c	(revision 805a149bb760f7dc7a16bc1442f6c3e5b0186395)
+++ uspace/srv/hid/console/console.c	(revision 8edec5347eb33f22b1815db3609ff69c0f618d63)
@@ -124,4 +124,5 @@
 static errno_t input_ev_abs_move(input_t *, unsigned, unsigned, unsigned, unsigned);
 static errno_t input_ev_button(input_t *, int, int);
+static errno_t input_ev_dclick(input_t *, int);
 
 static input_ev_ops_t input_ev_ops = {
@@ -131,5 +132,6 @@
 	.move = input_ev_move,
 	.abs_move = input_ev_abs_move,
-	.button = input_ev_button
+	.button = input_ev_button,
+	.dclick = input_ev_dclick
 };
 
@@ -440,4 +442,18 @@
 }
 
+static errno_t input_ev_dclick(input_t *input, int bnum)
+{
+	cons_event_t event;
+
+	event.type = CEV_POS;
+	event.ev.pos.type = POS_DCLICK;
+	event.ev.pos.btn_num = bnum;
+	event.ev.pos.hpos = pointer_x / mouse_scale_x;
+	event.ev.pos.vpos = pointer_y / mouse_scale_y;
+
+	console_queue_cons_event(active_console, &event);
+	return EOK;
+}
+
 /** Process a character from the client (TTY emulation). */
 static void cons_write_char(console_t *cons, char32_t ch)
Index: uspace/srv/hid/display/input.c
===================================================================
--- uspace/srv/hid/display/input.c	(revision 805a149bb760f7dc7a16bc1442f6c3e5b0186395)
+++ uspace/srv/hid/display/input.c	(revision 8edec5347eb33f22b1815db3609ff69c0f618d63)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2019 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -49,4 +49,5 @@
 static errno_t ds_input_ev_abs_move(input_t *, unsigned, unsigned, unsigned, unsigned);
 static errno_t ds_input_ev_button(input_t *, int, int);
+static errno_t ds_input_ev_dclick(input_t *, int);
 
 static input_ev_ops_t ds_input_ev_ops = {
@@ -56,5 +57,6 @@
 	.move = ds_input_ev_move,
 	.abs_move = ds_input_ev_abs_move,
-	.button = ds_input_ev_button
+	.button = ds_input_ev_button,
+	.dclick = ds_input_ev_dclick
 };
 
@@ -131,4 +133,21 @@
 
 	event.type = bpress ? PTD_PRESS : PTD_RELEASE;
+	event.btn_num = bnum;
+	event.dmove.x = 0;
+	event.dmove.y = 0;
+
+	ds_display_lock(disp);
+	rc = ds_display_post_ptd_event(disp, &event);
+	ds_display_unlock(disp);
+	return rc;
+}
+
+static errno_t ds_input_ev_dclick(input_t *input, int bnum)
+{
+	ds_display_t *disp = (ds_display_t *) input->user;
+	ptd_event_t event;
+	errno_t rc;
+
+	event.type = PTD_DCLICK;
 	event.btn_num = bnum;
 	event.dmove.x = 0;
Index: uspace/srv/hid/display/seat.c
===================================================================
--- uspace/srv/hid/display/seat.c	(revision 805a149bb760f7dc7a16bc1442f6c3e5b0186395)
+++ uspace/srv/hid/display/seat.c	(revision 8edec5347eb33f22b1815db3609ff69c0f618d63)
@@ -362,8 +362,21 @@
 	}
 
-	if (event->type == PTD_PRESS || event->type == PTD_RELEASE) {
+	if (event->type == PTD_PRESS || event->type == PTD_RELEASE ||
+	    event->type == PTD_DCLICK) {
 		pevent.pos_id = 0;
-		pevent.type = (event->type == PTD_PRESS) ?
-		    POS_PRESS : POS_RELEASE;
+		switch (event->type) {
+		case PTD_PRESS:
+			pevent.type = POS_PRESS;
+			break;
+		case PTD_RELEASE:
+			pevent.type = POS_RELEASE;
+			break;
+		case PTD_DCLICK:
+			pevent.type = POS_DCLICK;
+			break;
+		default:
+			assert(false);
+		}
+
 		pevent.btn_num = event->btn_num;
 		pevent.hpos = seat->pntpos.x;
Index: uspace/srv/hid/display/types/display/ptd_event.h
===================================================================
--- uspace/srv/hid/display/types/display/ptd_event.h	(revision 805a149bb760f7dc7a16bc1442f6c3e5b0186395)
+++ uspace/srv/hid/display/types/display/ptd_event.h	(revision 8edec5347eb33f22b1815db3609ff69c0f618d63)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2019 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -42,5 +42,6 @@
 	PTD_ABS_MOVE,
 	PTD_PRESS,
-	PTD_RELEASE
+	PTD_RELEASE,
+	PTD_DCLICK
 } ptd_event_type_t;
 
Index: uspace/srv/hid/input/input.c
===================================================================
--- uspace/srv/hid/input/input.c	(revision 805a149bb760f7dc7a16bc1442f6c3e5b0186395)
+++ uspace/srv/hid/input/input.c	(revision 8edec5347eb33f22b1815db3609ff69c0f618d63)
@@ -1,5 +1,5 @@
 /*
+ * Copyright (c) 2021 Jiri Svoboda
  * Copyright (c) 2006 Josef Cejka
- * Copyright (c) 2011 Jiri Svoboda
  * All rights reserved.
  *
@@ -311,5 +311,17 @@
 }
 
-/** Arbitrate client actiovation */
+/** Mouse button has been double-clicked. */
+void mouse_push_event_dclick(mouse_dev_t *mdev, int bnum)
+{
+	list_foreach(clients, link, client_t, client) {
+		if (client->active) {
+			async_exch_t *exch = async_exchange_begin(client->sess);
+			async_msg_1(exch, INPUT_EVENT_DCLICK, bnum);
+			async_exchange_end(exch);
+		}
+	}
+}
+
+/** Arbitrate client activation */
 static void client_arbitration(void)
 {
Index: uspace/srv/hid/input/mouse.h
===================================================================
--- uspace/srv/hid/input/mouse.h	(revision 805a149bb760f7dc7a16bc1442f6c3e5b0186395)
+++ uspace/srv/hid/input/mouse.h	(revision 8edec5347eb33f22b1815db3609ff69c0f618d63)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2011 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -66,4 +66,5 @@
     unsigned int, unsigned int);
 extern void mouse_push_event_button(mouse_dev_t *, int, int);
+extern void mouse_push_event_dclick(mouse_dev_t *, int);
 
 #endif
Index: uspace/srv/hid/input/proto/mousedev.c
===================================================================
--- uspace/srv/hid/input/proto/mousedev.c	(revision 805a149bb760f7dc7a16bc1442f6c3e5b0186395)
+++ uspace/srv/hid/input/proto/mousedev.c	(revision 8edec5347eb33f22b1815db3609ff69c0f618d63)
@@ -1,3 +1,4 @@
 /*
+ * Copyright (c) 2021 Jiri Svoboda
  * Copyright (c) 2011 Martin Decky
  * All rights reserved.
@@ -43,4 +44,5 @@
 #include <loc.h>
 #include <stdlib.h>
+#include <time.h>
 #include "../mouse.h"
 #include "../mouse_port.h"
@@ -48,8 +50,17 @@
 #include "../input.h"
 
+enum {
+	/** Default double-click speed in milliseconds */
+	dclick_delay_ms = 500
+};
+
 /** Mousedev softstate */
 typedef struct {
 	/** Link to generic mouse device */
 	mouse_dev_t *mouse_dev;
+	/** Button number of last button pressed (or -1 if none) */
+	int press_bnum;
+	/** Time at which button was last pressed */
+	struct timespec press_time;
 } mousedev_t;
 
@@ -61,4 +72,5 @@
 
 	mousedev->mouse_dev = mdev;
+	mousedev->press_bnum = -1;
 
 	return mousedev;
@@ -68,4 +80,28 @@
 {
 	free(mousedev);
+}
+
+static void mousedev_press(mousedev_t *mousedev, int bnum)
+{
+	struct timespec now;
+	nsec_t ms_delay;
+
+	getuptime(&now);
+
+	/* Same button was pressed previously */
+	if (mousedev->press_bnum == bnum) {
+		/* Compute milliseconds since previous press */
+		ms_delay = ts_sub_diff(&now, &mousedev->press_time) / 1000000;
+
+		if (ms_delay <= dclick_delay_ms) {
+			mouse_push_event_dclick(mousedev->mouse_dev, bnum);
+			mousedev->press_bnum = -1;
+			return;
+		}
+	}
+
+	/* Record which button was last pressed and at what time */
+	mousedev->press_bnum = bnum;
+	mousedev->press_time = now;
 }
 
@@ -103,4 +139,6 @@
 			mouse_push_event_button(mousedev->mouse_dev,
 			    ipc_get_arg1(&call), ipc_get_arg2(&call));
+			if (ipc_get_arg2(&call) != 0)
+				mousedev_press(mousedev, ipc_get_arg1(&call));
 			retval = EOK;
 			break;
