diff --git a/README.md b/README.md
index b056a6e..918e6a5 100644
--- a/README.md
+++ b/README.md
@@ -79,7 +79,7 @@ of small previews is displayed, making it easy to choose an image to open.
     -i           Read file list from stdin
     -n NUM       Start at picture NUM
     -N NAME      Set X window resource name to NAME
-    -o           Write file list to stdout when quitting
+    -o           Write list of marked/all files to stdout when quitting
     -p           Pixelize, i.e. turn off image anti-aliasing
     -q           Be quiet, disable warnings
     -r           Search given directories recursively for images
@@ -108,6 +108,10 @@ of small previews is displayed, making it easy to choose an image to open.
     R            Reload all thumbnails
     D            Remove image from file list and go to next image
 
+    m            Mark/unmark current image
+    N            Go [count] marked images forward
+    P            Go [count] marked images backward
+
 *Thumbnail mode:*
 
     h,j,k,l      Move selection left/down/up/right [count] times
diff --git a/commands.c b/commands.c
index 36da023..c1792a5 100644
--- a/commands.c
+++ b/commands.c
@@ -48,6 +48,7 @@ extern win_t win;
 
 extern fileinfo_t *files;
 extern int filecnt, fileidx;
+extern int markcnt;
 extern int alternate;
 
 extern int prefix;
@@ -61,8 +62,10 @@ bool it_quit(arg_t a)
 	unsigned int i;
 
 	if (options->to_stdout) {
-		for (i = 0; i < filecnt; i++)
-			printf("%s\n", files[i].name);
+		for (i = 0; i < filecnt; i++) {
+			if (!markcnt || files[i].marked)
+				printf("%s\n", files[i].name);
+		}
 	}
 	cleanup();
 	exit(EXIT_SUCCESS);
@@ -237,6 +240,46 @@ bool i_toggle_animation(arg_t a)
 	return true;
 }
 
+bool it_toggle_image_mark(arg_t a)
+{
+	int sel = mode == MODE_IMAGE ? fileidx : tns.sel;
+
+	files[sel].marked = !files[sel].marked;
+	markcnt += files[sel].marked ? 1 : -1;
+	return true;
+}
+
+bool it_navigate_marked(arg_t a)
+{
+	long n = (long) a;
+	int d, i, cnt, sel, new;
+	
+	if (mode == MODE_IMAGE)
+		cnt = filecnt, sel = new = fileidx;
+	else
+		cnt = tns.cnt, sel = new = tns.sel;
+	if (prefix > 0)
+		n *= prefix;
+	d = n > 0 ? 1 : -1;
+	for (i = sel + d; n != 0 && i >= 0 && i < cnt; i += d) {
+		if (files[i].marked) {
+			n -= d;
+			new = i;
+		}
+	}
+	if (new != sel) {
+		if (mode == MODE_IMAGE) {
+			load_image(new);
+		} else {
+			tns.sel = new;
+			tns.dirty = true;
+		}
+		return true;
+	} else {
+		return false;
+	}
+}
+
 bool it_scroll_move(arg_t a)
 {
 	direction_t dir = (direction_t) a;
diff --git a/commands.h b/commands.h
index 7a6752f..118c89a 100644
--- a/commands.h
+++ b/commands.h
@@ -54,6 +54,8 @@ bool it_first(arg_t);
 bool it_n_or_last(arg_t);
 bool i_navigate_frame(arg_t);
 bool i_toggle_animation(arg_t);
+bool it_toggle_image_mark(arg_t);
+bool it_navigate_marked(arg_t);
 bool it_scroll_move(arg_t);
 bool it_scroll_screen(arg_t);
 bool i_scroll_to_edge(arg_t);
diff --git a/config.def.h b/config.def.h
index e0e1460..d24723d 100644
--- a/config.def.h
+++ b/config.def.h
@@ -81,6 +81,10 @@ static const keymap_t keys[] = {
 	{ true,   XK_p,             i_navigate_frame,     (arg_t) -1 },
 	{ true,   XK_space,         i_toggle_animation,   (arg_t) None },
 
+	{ false,  XK_m,             it_toggle_image_mark, (arg_t) None },
+	{ false,  XK_N,             it_navigate_marked,   (arg_t) +1 },
+	{ false,  XK_P,             it_navigate_marked,   (arg_t) -1 },
+
 	{ false,  XK_h,             it_scroll_move,       (arg_t) DIR_LEFT },
 	{ false,  XK_Left,          it_scroll_move,       (arg_t) DIR_LEFT },
 	{ false,  XK_j,             it_scroll_move,       (arg_t) DIR_DOWN },
diff --git a/main.c b/main.c
index 4962782..fb8b8b6 100644
--- a/main.c
+++ b/main.c
@@ -65,6 +65,7 @@ win_t win;
 
 fileinfo_t *files;
 int filecnt, fileidx;
+int markcnt;
 int alternate;
 
 int prefix;
@@ -312,6 +313,7 @@ void update_info(void)
 	unsigned int i, fn, fw, n;
 	unsigned int llen = sizeof(win.bar.l), rlen = sizeof(win.bar.r);
 	char *lt = win.bar.l, *rt = win.bar.r, title[TITLE_LEN];
+	const char * mark;
 	bool ow_info;
 
 	for (fw = 0, i = filecnt; i > 0; fw++, i /= 10);
@@ -328,9 +330,10 @@ void update_info(void)
 	/* update bar contents */
 	if (win.bar.h == 0)
 		return;
+	mark = files[sel].marked ? "* " : "";
 	if (mode == MODE_THUMB) {
 		if (tns.cnt == filecnt) {
-			n = snprintf(rt, rlen, "%0*d/%d", fw, sel + 1, filecnt);
+			n = snprintf(rt, rlen, "%s%0*d/%d", mark, fw, sel + 1, filecnt);
 			ow_info = true;
 		} else {
 			snprintf(lt, llen, "Loading... %0*d/%d", fw, tns.cnt, filecnt);
@@ -338,7 +341,7 @@ void update_info(void)
 			ow_info = false;
 		}
 	} else {
-		n = snprintf(rt, rlen, "%3d%% | ", (int) (img.zoom * 100.0));
+		n = snprintf(rt, rlen, "%s%3d%% | ", mark, (int) (img.zoom * 100.0));
 		if (img.multi.cnt > 0) {
 			for (fn = 0, i = img.multi.cnt; i > 0; fn++, i /= 10);
 			n += snprintf(rt + n, rlen - n, "%0*d/%d | ",
diff --git a/sxiv.1 b/sxiv.1
index 7d06004..02593b1 100644
--- a/sxiv.1
+++ b/sxiv.1
@@ -62,7 +62,8 @@ Print brief usage information to standard output and exit.
 Read names of files to open from standard input.
 .TP
 .B \-o
-Write list of opened files to standard output when quitting. If combined with
+Write list of all marked files, or all opened files if no files are marked, to
+standard output when quitting. If combined with
 .IR \-i ,
 then sxiv acts as a visual filter/pipe.
 .TP
@@ -128,6 +129,19 @@ Reload all thumbnails.
 .TP
 .B D
 Remove current image from file list and go to next image.
+.TP
+.B m
+Mark/unmark the current image.
+.TP
+.B N
+Go
+.I count
+marked images forward.
+.TP
+.B P
+Go
+.I count
+marked images backward.
 .SH THUMBNAIL KEYBOARD COMMANDS
 The following keyboard commands are only available in thumbnail mode:
 .TP
diff --git a/types.h b/types.h
index 49aa0fd..2cd305e 100644
--- a/types.h
+++ b/types.h
@@ -69,6 +69,7 @@ typedef struct {
 	const char *path; /* always absolute */
 	const char *base;
 	bool loaded;
+	bool marked;
 } fileinfo_t;
 
 /* timeouts in milliseconds: */