diff --git a/Makefile b/Makefile
index 530473cc3a49041e2cf39b44af8124fb7c82d233..eb8ad586da99dd7927d883877244dfec4345ffab 100644
--- a/Makefile
+++ b/Makefile
@@ -1,17 +1,22 @@
 VERSION=1.1
 ARCHIVE=osdd-$(VERSION).tar.gz
 
-CFLAGS=-g3 -Wall -W -Wno-parentheses -Wstrict-prototypes -Wmissing-prototypes -Wundef -Wredundant-decls -std=gnu99 -fsanitize=address 
+CFLAGS=-g3 -Wall -W -Wno-parentheses -Wstrict-prototypes -Wmissing-prototypes -Wundef -Wredundant-decls -std=gnu99 -Wformat-truncation=0
+
+#-fsanitize=address 
 
 all: osdd osdc osd-batt osd-alsa
 
-osdd: osdd.o util.o display.o osdd-set.o
+osdd: osdd.o util.o osdd-set.o
 osdc: osdc.o util.o client.o
 osd-batt: osd-batt.o util.o client.o
 osd-alsa: osd-alsa.o util.o client.o
 
+# OSDD MODULES
+osdd: display.o osdd-file.o
+
 osdd: LDLIBS+=$(shell pkg-config --libs xft) -lXext -lX11 
-LDLIBS += -fsanitize=address
+#LDLIBS += -fsanitize=address
 
 osdc: LDLIBS+=-lX11
 osd-batt: LDLIBS+=-lX11
@@ -19,7 +24,7 @@ osd-batt: LDLIBS+=-lX11
 osd-alsa.o: CFLAGS+=$(shell pkg-config --cflags alsa)
 osd-alsa: LDLIBS+=$(shell pkg-config --libs alsa) -lX11
 
-display.o: CFLAGS+=$(shell pkg-config --cflags xft)
+display.o osdd-file.o: CFLAGS+=$(shell pkg-config --cflags xft)
 
 clean:
 	rm -f *~ *.o TAGS core osdd osdc osd-batt osd-alsa
diff --git a/README b/README
index 2641754339f05c75954ebac2cfc6a8c7d0ad24fd..4829d2cf485451ed12f527b1f0a32d37269fd7b7 100644
--- a/README
+++ b/README
@@ -54,9 +54,8 @@ You can also add attributes to the message (written as command-line options):
 
   --to=stdout		Write message to stdout instead of display.
 
-  --log=mask		Write this message to log file. Bitmask.
-	Last bit:       Write to ~/.osdd_last in machine readable form.
-	Second bit:     Append to ~/.osdd_log in human readable form with RGB color.
+  --output=name		
+  --output-no=name		
 
 
 The default values of most of these attributes can be given by command-line
diff --git a/display.c b/display.c
index 6bd4f329e9a1723540d110dc1c2c336b41a2163d..ecbecd754cfaa1f5333b2a6a936ce61a3155b99b 100644
--- a/display.c
+++ b/display.c
@@ -27,6 +27,31 @@
 #define SLIDERS_WITH_BRACKETS
 
 
+struct display_state;
+
+struct display_line {
+  struct osd_line * line;
+  // Used internally
+  int width;
+  int height;
+  int x_pos;
+  int y_pos;
+  int slider_unit;
+  int slider_space;
+  int slider_units;
+};
+
+struct osd_abstract display_state_new_arg(int argc, char ** argv,Display * dpy);
+struct osd_abstract display_state_new(Display *dpy,int width,int height,int x,int y, char * font_name, double line_spacing);
+void display_free(struct display_state *display);
+void display_set_font(struct display_state *display, char *font_name, double line_spacing);
+struct display_line *display_add_line(struct display_state *display, struct osd_line * line);
+void display_show(struct display_state *display, struct osd_line * lines, int num_lines);
+void display_hide(struct display_state *display);
+void display_clear(struct display_state *display);
+bool display_handle_event(struct display_state *display, XEvent *ev);
+
+
 
 
 struct display_state {
@@ -130,6 +155,10 @@ void display_state_new_arg_help(FILE * f)
 	fprintf(f,"Module DISPLAY help:\n\
 -f, --font=<f>\t\tFont to use for the OSD\n\
 -s, --line-spacing=<n>\tSet line spacing factor (decimal fraction, default=0.2)\n\
+-x, --x=<px>\t\tLeft up corner positing (move right, default=0)\n\
+-y, --y=<px>\t\tLeft up corner positing (move down, default=0)\n\
+-w, --width=<px>\tDisplay width (default screen size)\n\
+-h, --height=<px>\tDisplay height (default screen size)\n\
 \n");
 }
 
@@ -205,7 +234,7 @@ void display_state_new_by_outputs_help(FILE * f)
 	fprintf(f,"Module DISPLAY_BY_OUTPUTS help:\n\
 -f, --font=<f>\t\tFont to use for the OSD\n\
 -s, --line-spacing=<n>\tSet line spacing factor (decimal fraction, default=0.2)\n\
--n, --name=<s> name with expansion %%d to display number (expanded by printf)\n\
+-n, --name=<s>\t\tname with expansion %%d to display number (expanded by printf)\n\
 \n");
 }
 
@@ -544,85 +573,6 @@ static void display_draw_line(struct display_state *display, int i)
   XftColorFree(display->dpy, display->visual, display->cmap, &display->mask_color);
 }
 
-static void repace_bad_char(char * in)
-{
-	for(;*in;in++)
-	{
-		if(' '== *in) continue;
-		if('a'<= *in&& *in<='z') continue;
-		if('A'<= *in&& *in<='Z') continue;
-		if('0'<= *in&& *in<='9') continue;
-		//if(128 <= (unsigned char)*in) continue;
-		*in='?';
-	}
-}
-
-static void display_log_to_file(struct display_state *display)
-{
-#define MAXIMUM_FILE_MANE_LEN 1024
-	int need_log=0;
-	for(int i=0;i<display->num_lines;i++) need_log|=display->lines[i].line->log;
-	if(need_log&1)
-	{
-		char name[MAXIMUM_FILE_MANE_LEN];
-		snprintf(name,MAXIMUM_FILE_MANE_LEN,"%s/.osdd_last",getenv("HOME"));
-		FILE * f = fopen(name,"w");
-		if(f)
-		{
-			fprintf(f,"%lld\n",(long long)time(0));
-			for(int i=0;i<display->num_lines;i++)
-			{
-				struct osd_line * line = display->lines[i].line;
-				if(!(line->log & 1)) continue;
-				if(line->type!=OSD_TYPE_TEXT) continue;
-				// Allocate colors
-				XftColor outline_color;
-				XRenderColor mask_rc = { .red = 0xffff, .green = 0xffff, .blue = 0xffff, .alpha = 0xffff };
-				if (!XftColorAllocName(display->dpy, display->visual, display->cmap, line->fg_color, &display->fg_color) ||
-						!XftColorAllocName(display->dpy, display->visual, display->cmap, line->outline_color, &outline_color) ||
-						!XftColorAllocValue(display->dpy, display->visual, display->cmap, &mask_rc, &display->mask_color))
-					die("Cannot allocate colors");
-				typeof(display->fg_color.color) c = display->fg_color.color;
-				//fprintf(stderr,"%02x%02x%02x %s\n",c.red/256,c.green/256,c.blue/256,display->lines[i].u.text);
-				char text[1024];
-				snprintf(text,1000,display->lines[i].line->u.text);
-				repace_bad_char(text);
-				fprintf(f,"%02x%02x%02x %s\n",c.red/256,c.green/256,c.blue/256,text);
-			}
-			fclose(f);
-		}
-	}
-	if(need_log&2)
-	{
-		char name[MAXIMUM_FILE_MANE_LEN];
-		snprintf(name,MAXIMUM_FILE_MANE_LEN,"%s/.osdd_log",getenv("HOME"));
-		FILE * f = fopen(name,"a");
-		if(f)
-		{
-			time_t t = time(NULL);
-			struct tm tm = *localtime(&t);
-			fprintf(f,"%04d-%02d-%02d %02d:%02d:%02d", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
-			for(int i=0;i<display->num_lines;i++)
-			{
-				struct osd_line * line = display->lines[i].line;
-				if(!(line->log & 2)) continue;
-				// Allocate colors
-				XftColor outline_color;
-				XRenderColor mask_rc = { .red = 0xffff, .green = 0xffff, .blue = 0xffff, .alpha = 0xffff };
-				if (!XftColorAllocName(display->dpy, display->visual, display->cmap, line->fg_color, &display->fg_color) ||
-						!XftColorAllocName(display->dpy, display->visual, display->cmap, line->outline_color, &outline_color) ||
-						!XftColorAllocValue(display->dpy, display->visual, display->cmap, &mask_rc, &display->mask_color))
-					die("Cannot allocate colors");
-				typeof(display->fg_color.color) c = display->fg_color.color;
-				//fprintf(stderr,"%02x%02x%02x %s\n",c.red/256,c.green/256,c.blue/256,display->lines[i].u.text);
-				fprintf(f," \x1b[38;2;%d;%d;%dm%s\e[0m",c.red/256,c.green/256,c.blue/256,display->lines[i].line->u.text);
-			}
-			fprintf(f,"\n");
-			fclose(f);
-		}
-	}
-}
-
 void display_show(struct display_state *display, struct osd_line * lines, int num_lines)
 {
   display_hide(display);
@@ -630,7 +580,6 @@ void display_show(struct display_state *display, struct osd_line * lines, int nu
   for(int i=0; i<num_lines; i++) display_add_line(display,lines+i);
 
   display_prepare(display);
-  display_log_to_file(display);
 
   // Create our window
   XSetWindowAttributes win_attr = {
diff --git a/display.h b/display.h
index 788ee280ea9337ef30fa63a8fe7aaca21cd82527..0e10d7f1787cdab8c9f7452b5f0a71ef7e91cc44 100644
--- a/display.h
+++ b/display.h
@@ -5,37 +5,10 @@
  *	(c) 2020--2021 Jiri Kalvoda <jirikalvoda@kam.mff.cuni.cz>
  */
 
-#include <stdbool.h>
-#include <X11/Xlib.h>
 
 #include "osdd-set.h"
 
-struct display_state;
-
-#define OSD_MAX_LINE_LEN 256
-
-struct display_line {
-  struct osd_line * line;
-  // Used internally
-  int width;
-  int height;
-  int x_pos;
-  int y_pos;
-  int slider_unit;
-  int slider_space;
-  int slider_units;
-};
 
 struct osd_creator_abstract display_by_outputs_creator_new(void);
 struct osd_creator_abstract display_creator_new(void);
 
-
-struct osd_abstract display_state_new_arg(int argc, char ** argv,Display * dpy);
-struct osd_abstract display_state_new(Display *dpy,int width,int height,int x,int y, char * font_name, double line_spacing);
-void display_free(struct display_state *display);
-void display_set_font(struct display_state *display, char *font_name, double line_spacing);
-struct display_line *display_add_line(struct display_state *display, struct osd_line * line);
-void display_show(struct display_state *display, struct osd_line * lines, int num_lines);
-void display_hide(struct display_state *display);
-void display_clear(struct display_state *display);
-bool display_handle_event(struct display_state *display, XEvent *ev);
diff --git a/osd-clock b/osd-clock
index 4d6d818a8d38b9e567522c5bbbc7178d8d95488d..d30835bc1ffc4c99444b9a5259c243ced15871b2 100755
--- a/osd-clock
+++ b/osd-clock
@@ -1,2 +1,2 @@
 #!/bin/sh
-date '+%Y-%m-%d  %H:%M:%S' | osdc --log=0 --min-duration=1 - 
+date '+%Y-%m-%d  %H:%M:%S' | osdc --output-no=log --min-duration=1 -
diff --git a/osdd-file.c b/osdd-file.c
new file mode 100644
index 0000000000000000000000000000000000000000..b35fd0142080a6a4bfa2d900481f6f06f26c679b
--- /dev/null
+++ b/osdd-file.c
@@ -0,0 +1,376 @@
+/*
+ *	On-screen Display
+ *
+ *	(c) 2020--2021 Jiri Kalvoda <jirikalvoda@kam.mff.cuni.cz>
+ */
+
+#include "osdd-file.h"
+#include "util.h"
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <getopt.h>
+#include <stdlib.h>
+
+#include <X11/Xlib.h>
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+#include <X11/extensions/shape.h>
+#include <X11/extensions/render.h>
+#include <X11/Xft/Xft.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <time.h>
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+#include <X11/extensions/shape.h>
+#include <X11/extensions/render.h>
+#include <X11/Xft/Xft.h>
+#include <getopt.h>
+
+
+static void replace_no_text(char * in)
+{
+	for(;*in;in++)
+	{
+		if(' '== *in) continue;
+		if('a'<= *in&& *in<='z') continue;
+		if('A'<= *in&& *in<='Z') continue;
+		if('0'<= *in&& *in<='9') continue;
+		//if(128 <= (unsigned char)*in) continue;
+		*in='?';
+	}
+}
+
+static
+char * expand_backslash(char * in)
+{
+	char * out = xmalloc(strlen(in)+1);
+	int i=0;
+	for(;*in;i++,in++)
+	{
+		if(in[0]=='\\')
+		{
+			switch(in[1])
+			{
+				case 'n':
+					out[i]='\n';
+					break;
+				case 't':
+					out[i]='\t';
+					break;
+				case '0':
+					out[i]='\0';
+					break;
+				default:
+					out[i]='?';
+					break;
+			}
+			if(in[1]) in++;
+		}
+		else
+			out[i]=*in;
+	}
+	out[i]=0;
+	return out;
+}
+
+static void
+colorToRGB(char * name, int *r, int *g, int *b)
+{
+
+	Display *d = XOpenDisplay(NULL);
+	int s = XDefaultScreen(d);
+  Visual *v = XDefaultVisual(d,s);
+  Colormap cmap = DefaultColormap(d,s);
+
+   XftColor color;
+	if (XftColorAllocName(d, v, cmap, name, &color))
+	{
+				typeof(color.color) c = color.color;
+				*r = c.red;
+				*g = c.green;
+				*b = c.blue;
+	}
+	else *r=*g=*b=-1;
+  XftColorFree(d, v, cmap, &color);
+	XCloseDisplay(d);
+}
+
+// *************************  STATE STRUCT **********
+
+struct file_state
+{
+	char * file_name;
+	char * open_mode;
+	bool clear;
+	char * line_separator;
+	char * msq_separator;
+	char * time_separator;
+
+	//tmp usage:
+	XftColor fg_color;
+	enum {
+		FILE_COLOR_NO,
+		FILE_COLOR_RGB_DEC,
+		FILE_COLOR_RGB_HEX,
+		FILE_COLOR_RGB_HASHTAG_HEX,
+		FILE_COLOR_TERM_RGB,
+		FILE_COLOR_ORIGINAL,
+	} color_mode;
+	enum {
+		FILE_TIME_NO,
+		FILE_TIME_UNIX,
+		FILE_TIME_ISO,
+	} time_mode;
+	bool replace_no_text;
+};
+
+// *************************  MAIN FUNCTIONS *******
+
+static
+void file_show(struct file_state *state, struct osd_line * lines, int num_lines)
+{
+		FILE * f = fopen(state->file_name,state->open_mode);
+		if(f)
+		{
+			switch(state->time_mode)
+			{
+				case FILE_TIME_NO:
+					break;
+				case FILE_TIME_UNIX:
+					fprintf(f,"%lld%s",(long long)time(0),state->time_separator);
+					break;
+				case FILE_TIME_ISO:
+					{
+						time_t t = time(NULL);
+						struct tm tm = *localtime(&t);
+						fprintf(f,"%04d-%02d-%02d %02d:%02d:%02d%s", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec,state->time_separator);
+					}
+					break;
+			}
+			for(int i=0;i<num_lines;i++)
+			{
+				struct osd_line * line = lines+i;
+				//if(!(line->log & 1)) continue;
+				if(line->type!=OSD_TYPE_TEXT) continue;
+				int r,g,b;
+				char text[1024];
+				char color[30];
+				color[0]=0;
+				char * end_color = "";
+
+				snprintf(text,1000,"%s",line->u.text);
+				if(state->replace_no_text) replace_no_text(text);
+				switch(state->color_mode)
+				{
+					case FILE_COLOR_RGB_DEC:
+					case FILE_COLOR_RGB_HEX:
+					case FILE_COLOR_RGB_HASHTAG_HEX:
+					case FILE_COLOR_TERM_RGB:
+						colorToRGB(line->fg_color,&r,&g,&b);
+						break;
+					default:
+						break;
+				}
+				switch(state->color_mode)
+				{
+					case FILE_COLOR_RGB_DEC:
+						sprintf(color,"%d %d %d ",r/256,g/256,b/256);
+						break;
+					case FILE_COLOR_RGB_HEX:
+						sprintf(color,"%02x%02x%02x ",r/256,g/256,b/256);
+						break;
+					case FILE_COLOR_RGB_HASHTAG_HEX:
+						sprintf(color,"#%02x%02x%02x ",r/256,g/256,b/256);
+						break;
+					case FILE_COLOR_TERM_RGB:
+						sprintf(color,"\e[38;2;%d;%d;%dm",r/256,g/256,b/256);
+						end_color = "\e[0m";
+						break;
+					case FILE_COLOR_ORIGINAL:
+						sprintf(color,"%s ",line->fg_color);
+						break;
+					case FILE_COLOR_NO:
+						break;
+				}
+				fprintf(f,"%s%s%s%s", color, text, end_color,state->line_separator);
+				//	fprintf(f,"%s\n",text);
+				//fprintf(stderr,"FO FILE: %d %d %d %s\n",r,g,b,text);
+
+			}
+			fprintf(f,"%s", state->msq_separator);
+			fclose(f);
+		}
+		/*
+		char name[MAXIMUM_FILE_MANE_LEN];
+		snprintf(name,MAXIMUM_FILE_MANE_LEN,"%s/.osdd_log",getenv("HOME"));
+		FILE * f = fopen(name,"a");
+		if(f)
+		{
+			time_t t = time(NULL);
+			struct tm tm = *localtime(&t);
+			fprintf(f,"%04d-%02d-%02d %02d:%02d:%02d", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
+			for(int i=0;i<display->num_lines;i++)
+			{
+				struct osd_line * line = display->lines[i].line;
+				if(!(line->log & 2)) continue;
+				// Allocate colors
+				XftColor outline_color;
+				XRenderColor mask_rc = { .red = 0xffff, .green = 0xffff, .blue = 0xffff, .alpha = 0xffff };
+				if (!XftColorAllocName(display->dpy, display->visual, display->cmap, line->fg_color, &display->fg_color) ||
+						!XftColorAllocName(display->dpy, display->visual, display->cmap, line->outline_color, &outline_color) ||
+						!XftColorAllocValue(display->dpy, display->visual, display->cmap, &mask_rc, &display->mask_color))
+					die("Cannot allocate colors");
+				typeof(display->fg_color.color) c = display->fg_color.color;
+				//fprintf(stderr,"%02x%02x%02x %s\n",c.red/256,c.green/256,c.blue/256,display->lines[i].u.text);
+				fprintf(f," \x1b[38;2;%d;%d;%dm%s\e[0m",c.red/256,c.green/256,c.blue/256,display->lines[i].line->u.text);
+			}
+			fprintf(f,"\n");
+			fclose(f);
+		}
+		*/
+}
+
+static
+void file_clear(struct file_state *state)
+{
+	if(state->clear)
+		file_show(state,0,0);
+}
+
+// *************************  NEW ******************
+
+static
+void file_new_help(FILE * f)
+{
+	fprintf(f,"\
+Module FILE help:\n\
+-a, --append \t\tAppend to file (do not overwrite)\n\
+-c, --color=<{}>\tSet how to print color of messages {no, rgbDEC, rgbHEX, rgbHEXHASHTAG, term, original}\n\
+-l, --line-separator=<s>Separato of each line.\n\
+-m, --msq-separator=<s>\tSeparato of each message.\n\
+-r, --replace\t\tReplace not alphabet and number in output to '?'\n\
+-t, --time=<{}>\t\tSet how to print actual time {no, unix, iso}\n\
+-T, --time-separator=<s>Separator of time and message text\n\
+\n\
+");
+}
+
+static 
+struct osd_abstract file_new(int argc, char ** argv, Display * nope)
+{
+	(void)nope;
+	argc++;argv--;
+	struct osd_abstract r;
+	static const char short_opts[] = "+ac:l:m:rt:T:";
+	static const struct option long_opts[] = {
+		{ "append",		no_argument,	NULL,	'a' },
+		{ "color",		required_argument,	NULL,	'c' },
+		{ "line-separator",		required_argument,	NULL,	'l' },
+		{ "msq-separator",		required_argument,	NULL,	'm' },
+		{ "replace",		no_argument,	NULL,	'r' },
+		{ "time",		required_argument,	NULL,	't' },
+		{ "time-separator",		required_argument,	NULL,	'T' },
+		{ NULL,		0,			NULL,	0   },
+	};
+	memset(&r,0,sizeof(r));
+	struct file_state *state = xmalloc(sizeof(*state));
+	memset(state,0,sizeof(state)[0]);
+
+	state->open_mode = "w";
+	state->line_separator = expand_backslash("\\n");
+	state->time_separator = expand_backslash("\\n");
+	state->msq_separator = expand_backslash("");
+	state->clear=0;
+
+	int opt;
+	optind = 0;
+	while ((opt = getopt_long(argc, argv, short_opts, long_opts, NULL)) >= 0)
+	{
+		switch (opt)
+		{
+			case 'a':
+				state->open_mode = "a";
+				break;
+			case 'c':
+				if(!strcmp(optarg,"no")) state->color_mode = FILE_COLOR_NO; else
+				if(!strcmp(optarg,"rgbDEC")) state->color_mode = FILE_COLOR_RGB_DEC; else
+				if(!strcmp(optarg,"rgbHEX")) state->color_mode = FILE_COLOR_RGB_HEX; else
+				if(!strcmp(optarg,"rgbHEXHASHTAG")) state->color_mode = FILE_COLOR_RGB_HASHTAG_HEX; else
+				if(!strcmp(optarg,"term")) state->color_mode = FILE_COLOR_TERM_RGB; else
+				if(!strcmp(optarg,"original")) state->color_mode = FILE_COLOR_ORIGINAL; else
+				{
+					fprintf(stderr,"Color mode %s not exist\n\n",optarg);
+					file_new_help(stderr);
+					exit(0);
+				}
+				break;
+			case 'l':
+				free(state->line_separator);
+				state->line_separator = expand_backslash(optarg);
+				break;
+			case 'm':
+				free(state->msq_separator);
+				state->msq_separator = expand_backslash(optarg);
+				break;
+			case 'r':
+				state->replace_no_text = 1;
+				break;
+			case 't':
+				if(!strcmp(optarg,"no")) state->time_mode = FILE_TIME_NO; else
+				if(!strcmp(optarg,"unix")) state->time_mode = FILE_TIME_UNIX; else
+				if(!strcmp(optarg,"iso")) state->time_mode = FILE_TIME_ISO; else
+				{
+					fprintf(stderr,"Time mode %s not exist\n\n",optarg);
+					file_new_help(stderr);
+					exit(0);
+				}
+				break;
+			case 'T':
+				free(state->time_separator);
+				state->time_separator = expand_backslash(optarg);
+				break;
+			default:
+				fprintf(stderr,"Option %c not exist\n\n",opt);
+				file_new_help(stderr);
+				exit(0);
+		}
+	}
+	int ind = optind;
+	if(ind >= argc)
+	{
+		fprintf(stderr,"Missing positional argument\n\n");
+		file_new_help(stderr);
+		exit(0);
+	}
+	state->file_name = argv[ind++];
+	if(ind != argc)
+	{
+		fprintf(stderr,"Too many arguments\n\n");
+		file_new_help(stderr);
+		exit(0);
+
+	}
+
+	r.context = state;
+	void (*show)(struct file_state*, struct osd_line*, int) = file_show;
+	r.show = (void (*)(void*, struct osd_line*, int)) show;
+	void (*clear)(struct file_state*) = file_clear;
+	r.clear = (void (*)(void*)) clear;
+	return r;
+}
+
+// *************************  CREATOR ****************************
+struct osd_creator_abstract file_creator_new(void)
+{
+  struct osd_creator_abstract r;
+  memset(&r, 0, sizeof(r));
+  r.name = "FILE";
+  r.help = file_new_help;
+  r.new.add_one_osd = file_new;
+  return r;
+}
diff --git a/osdd-file.h b/osdd-file.h
new file mode 100644
index 0000000000000000000000000000000000000000..c9af6177c83a15683f5d5699f95daa452ed96da1
--- /dev/null
+++ b/osdd-file.h
@@ -0,0 +1,9 @@
+/*
+ *	On-screen Display
+ *
+ *	(c) 2020--2021 Jiri Kalvoda <jirikalvoda@kam.mff.cuni.cz>
+ */
+
+#include "osdd-set.h"
+
+struct osd_creator_abstract file_creator_new(void);
diff --git a/osdd-run b/osdd-run
index cd84ca5ce43545c1c83886c86e705e4bda6cecb1..5a90f472b2cf920de6ef242b5fcfafc47b540885 100755
--- a/osdd-run
+++ b/osdd-run
@@ -6,5 +6,5 @@ pkill -f not2osd
 not2osd &
 tmp=$(mktemp)
 echo "Xft.render: False" > $tmp
-XENVIRONMENT=$tmp ./osdd -D DISPLAY_BY_OUTPUTS default d [ -n d%d ] | bash
+XENVIRONMENT=$tmp ./osdd -D DISPLAY_BY_OUTPUTS default nolog [ -n a%d ] FILE default log loglog [ -a -l' ' -m'\n' -T' ' -tiso -cterm ~/.osdd_log  ] FILE default log loglast [ -l'\n' -tunix -T'\n' -crgbHEX -r ~/.osdd_last ] | bash
 
diff --git a/osdd-set.c b/osdd-set.c
index 2645a062514cf89792a84dafc0fef8e829cc120b..6e0c35a0ec1ac3b90572f971c25d77d127b5899a 100644
--- a/osdd-set.c
+++ b/osdd-set.c
@@ -88,7 +88,7 @@ void osd_set_show(struct osd_set *set)
 {
 	for(int i=0;i<set->len;i++)
 	{
-		if(set->is_active[i]) set->elements[i].show(set->elements[i].context,set->lines,set->num_lines);
+		if(set->is_active[i]) if(set->elements[i].show) set->elements[i].show(set->elements[i].context,set->lines,set->num_lines);
 	}
 }
 
@@ -96,7 +96,7 @@ void osd_set_clear(struct osd_set *set)
 {
 	for(int i=0;i<set->len;i++)
 	{
-		set->elements[i].clear(set->elements[i].context);
+		if(set->elements[i].clear) set->elements[i].clear(set->elements[i].context);
 	}
 	set->num_lines = 0;
 	memset(set->is_active,0,sizeof(bool)*set->max);
diff --git a/osdd-set.h b/osdd-set.h
index a8b691c5659c4e1821ab07d431195aa44aadcc2e..de729c39c2f880c529bae2ba5bd900b666ab5be8 100644
--- a/osdd-set.h
+++ b/osdd-set.h
@@ -9,7 +9,9 @@
 #define OSDD_SET_H
 
 #include <stdbool.h>
-#include <X11/Xlib.h>
+typedef struct _XDisplay Display;
+typedef union _XEvent XEvent;
+typedef struct _IO_FILE FILE;
 
 struct display_state;
 
@@ -44,7 +46,6 @@ struct osd_line {
     char text[OSD_MAX_LINE_LEN];		// in UTF-8
     unsigned int percent;			// 0..100 for percentages and slider
   } u;
-  int log;
 };
 
 struct osd_abstract {
diff --git a/osdd.c b/osdd.c
index ff4a795a6bef4d32ddcadc04d843b65488680aaa..d1d93e79cbbe3124f5500a7971ef759a05f4b488 100644
--- a/osdd.c
+++ b/osdd.c
@@ -19,6 +19,7 @@
 #include "util.h"
 #include "osdd-set.h"
 #include "display.h"
+#include "osdd-file.h"
 
 
 static struct osd_set osd;
@@ -33,7 +34,6 @@ static char *default_outline_color = "black";
 static int default_outline_width = 2;
 static int default_duration = 1000;
 static int default_min_duration = 250;
-static int default_log = 255;
 static int debug_mode = 1;
 static int test_mode;
 
@@ -69,7 +69,6 @@ static const struct option long_opts[] = {
   { "outline-color",	required_argument,	NULL,	'o' },
   { "outline-width",	required_argument,	NULL,	'O' },
   { "line-spacing",	required_argument,	NULL,	's' },
-  { "log",		required_argument,	NULL,	'L' },
   { "test",		no_argument,		NULL,	OPT_TEST },	// Undocumented test mode
   { NULL,		0,			NULL,	0   },
 };
@@ -85,7 +84,6 @@ Options:\n\
 -m, --min-duration=<ms>\tDefault minimum message duration in milliseconds\n\
 -o, --outline-color=<c>\tDefault outline color\n\
 -O, --outline-width=<n>\tDefault outline width (default=2)\n\
--L, --log=<n>\t\tSet default log options (bitmask, default=255)\n\
 \n\
 Modules:\n\
 Each module id specify by:\n\
@@ -133,9 +131,6 @@ parse_opts(int argc, char **argv)
 	break;
 	  case 'O':
 	default_outline_width = atoi(optarg);
-	break;
-	  case 'L':
-	default_log = atoi(optarg);
 	break;
 	  case OPT_TEST:
 	test_mode = 1;
@@ -211,7 +206,6 @@ display_msg(struct msg *msg)
   char *fg_color = default_color;
   char *outline_color = default_outline_color;
   int outline_width = default_outline_width;
-  int log = default_log;
 
   char *line = msg->text;
   int num_l=0;
@@ -256,8 +250,6 @@ display_msg(struct msg *msg)
 	msg->max_light = now + atoi(val);
       else if (!strcmp(key, "min-duration"))
 	msg->min_light = now + atoi(val);
-      else if (!strcmp(key, "log"))
-	log = atoi(val);
       else if (!strcmp(key, "color"))
 	fg_color = val;
       else if (!strcmp(key, "outline-color"))
@@ -282,7 +274,6 @@ display_msg(struct msg *msg)
 	  l->fg_color = fg_color;
 	  l->outline_color = outline_color;
 	  l->outline_width = outline_width;
-	  l->log = log;
 	}
 
       line = nl;
@@ -297,7 +288,6 @@ display_msg(struct msg *msg)
 	  l->fg_color = fg_color;
 	  l->outline_color = outline_color;
 	  l->outline_width = outline_width;
-	  l->log = log;
   }
 
   if (msg->min_light > msg->max_light)
@@ -454,6 +444,7 @@ main(int argc, char **argv)
 {
   add_creator(display_creator_new());
   add_creator(display_by_outputs_creator_new());
+  add_creator(file_creator_new());
 
 
   setlocale(LC_CTYPE, "");