From b967d39b8253eb4fda1a5507af6199ae76540d45 Mon Sep 17 00:00:00 2001
From: Jiri Kalvoda <jirikalvoda@kam.mff.cuni.cz>
Date: Mon, 1 Feb 2021 23:48:01 +0100
Subject: [PATCH] Add module support by osdd_abstract -- names and module
 creators

---
 display.c  | 163 ++++++++++++++++++++++++++++++++++++++++++++--
 display.h  |   7 +-
 osdd-set.c |  76 +++++++++++++++++++++-
 osdd-set.h |  37 ++++++++++-
 osdd.c     | 186 +++++++++++++++++++++++++++++++++++++----------------
 5 files changed, 405 insertions(+), 64 deletions(-)

diff --git a/display.c b/display.c
index ed8c57c..0912671 100644
--- a/display.c
+++ b/display.c
@@ -18,6 +18,7 @@
 #include <X11/extensions/shape.h>
 #include <X11/extensions/render.h>
 #include <X11/Xft/Xft.h>
+#include <getopt.h>
 
 #define DEBUG
 #include "display.h"
@@ -25,8 +26,6 @@
 
 #define SLIDERS_WITH_BRACKETS
 
-static char *font_name = "times-64:bold";
-static double line_spacing = 0.2;
 
 
 
@@ -124,7 +123,164 @@ stay_on_top(struct display_state *display)
   DBG("stay_on_top: WM does not support any known protocol\n");
 }
 
-struct osd_abstract display_state_new(Display *dpy,int width,int height,int x,int y)
+// ****************************************************************************************
+static
+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\
+\n");
+}
+
+struct osd_abstract display_state_new_arg(int argc, char ** argv,Display *dpy)
+{
+static const char short_opts[] = "f:s:x:y:h:w:";
+static const struct option long_opts[] = {
+  { "font",		required_argument,	NULL,	'f' },
+  { "line-spacing",	required_argument,	NULL,	's' },
+  { "x",	required_argument,	NULL,	'x' },
+  { "y",	required_argument,	NULL,	'y' },
+  { "width",	required_argument,	NULL,	'h' },
+  { "height",	required_argument,	NULL,	'w' },
+  { NULL,		0,			NULL,	0   },
+};
+	fprintf(stderr,"NEW DISPLAY:\n");
+	for(int i=0;i<argc;i++) fprintf(stderr,"\t%s\n",argv[i]);
+	char *font_name = "times-64:bold";
+	double line_spacing = 0.2;
+	int opt;
+	int x=0,y=0;
+	int width = XDisplayWidth(dpy, XDefaultScreen(dpy));
+	int height = XDisplayHeight(dpy, XDefaultScreen(dpy));
+	optind = 0;
+	while (argv && argc>0 && (opt = getopt_long(argc+1, argv-1, short_opts, long_opts, NULL)) >= 0)
+	{
+		switch (opt)
+		{
+			case 'f':
+				font_name = optarg;
+				fprintf(stderr,"FONT SET\n");
+				break;
+			case 's':
+				line_spacing = atof(optarg);
+				break;
+			case 'x':
+				x = atoi(optarg);
+				break;
+			case 'y':
+				y = atoi(optarg);
+				break;
+			case 'w':
+				width = atoi(optarg);
+				break;
+			case 'h':
+				height = atoi(optarg);
+				break;
+			default:
+				display_state_new_arg_help(stderr);
+				exit(0);
+		}
+	}
+	return display_state_new(dpy,width,height,x,y,font_name,line_spacing);
+}
+
+struct osd_creator_abstract display_creator_new(void)
+{
+  struct osd_creator_abstract r;
+  memset(&r, 0, sizeof(r));
+  r.name = "DISPLAY";
+  r.help = display_state_new_arg_help;
+  r.new.add_one_osd = display_state_new_arg;
+  return r;
+}
+
+
+// ****************************************************************************************
+
+
+static
+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");
+}
+
+static
+void display_state_new_by_outputs(struct osd_set *set, int argc, char ** argv,Display *dpy,int names_len, char ** names)
+{
+static const char short_opts[] = "f:s:";
+static const struct option long_opts[] = {
+  { "font",		required_argument,	NULL,	'f' },
+  { "line-spacing",	required_argument,	NULL,	's' },
+  { "name",	required_argument,	NULL,	'n' },
+  { NULL,		0,			NULL,	0   },
+};
+	fprintf(stderr,"NEW DISPLAY:\n");
+	for(int i=0;i<argc;i++) fprintf(stderr,"\t%s\n",argv[i]);
+	char  * expanding_name = "display%d";
+	char *font_name = "times-64:bold";
+	double line_spacing = 0.2;
+	int opt;
+	optind = 0;
+	while (argv && argc>0 && (opt = getopt_long(argc+1, argv-1, short_opts, long_opts, NULL)) >= 0)
+	{
+		switch (opt)
+		{
+			case 'f':
+				font_name = optarg;
+				fprintf(stderr,"FONT SET\n");
+				break;
+			case 's':
+				line_spacing = atof(optarg);
+				break;
+			default:
+				display_state_new_by_outputs_help(stderr);
+				exit(0);
+		}
+	}
+  FILE * f = popen("xrandr | grep \\ connected","r");
+  char line[1001];
+  char ** new_names = xmalloc(sizeof(char *)*(names_len+1));
+  for(int i=0;i<names_len;i++) new_names[i]=names[i];
+  new_names[names_len] = xmalloc(sizeof(char)*(strlen(expanding_name)+100));
+  int i=0;
+  while(fscanf(f," %1000[^\n]",line)==1)
+  {
+	    int width,height,x,y;
+            char *l = line;
+            while(*l && !(*l==' ')) l++;
+            while(*l && !('0'<=*l&&*l<='9')) l++;
+            if(sscanf(l,"%dx%d+%d+%d",&width,&height,&x,&y)==4)
+			{
+
+			    DBG("ADD screen %dx%d on %d,%d\n",width,height,x,y);
+				sprintf(new_names[names_len],expanding_name,i,i,i,i,i,i);
+				osd_set_add(set,display_state_new(dpy,width,height,x,y,font_name,line_spacing),names_len+(bool)expanding_name[0],new_names);
+				i++;
+			}
+  }
+  free(new_names[names_len]);
+  free(new_names);
+}
+
+struct osd_creator_abstract display_by_outputs_creator_new(void)
+{
+  struct osd_creator_abstract r;
+  memset(&r, 0, sizeof(r));
+  r.name = "DISPLAY_BY_OUTPUTS";
+  r.new.add_multiple_osd = display_state_new_by_outputs;
+  r.help = display_state_new_by_outputs_help;
+  r.t = OSD_CEATE_TYPE_ADD_MULTIPLE_OSD;
+  return r;
+}
+
+// ****************************************************************************************
+
+struct osd_abstract display_state_new(Display *dpy,int width,int height,int x,int y, char * font_name, double line_spacing)
 {
   struct display_state *display = xmalloc(sizeof(*display));
   memset(display, 0, sizeof(*display));
@@ -202,7 +358,6 @@ struct display_line *display_add_line(struct display_state *display, struct osd_
     }
 
   struct display_line *l = &display->lines[display->num_lines++];
-  fprintf(stderr,"EZD %lld\n",(long long)display);
   l->line = line;
 
   return l;
diff --git a/display.h b/display.h
index 550f1c4..788ee28 100644
--- a/display.h
+++ b/display.h
@@ -26,7 +26,12 @@ struct display_line {
   int slider_units;
 };
 
-struct osd_abstract display_state_new(Display *dpy,int width,int height,int x,int y);
+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);
diff --git a/osdd-set.c b/osdd-set.c
index 629228e..cdf45c9 100644
--- a/osdd-set.c
+++ b/osdd-set.c
@@ -6,10 +6,53 @@
  */
 
 #include <stdio.h>
+#include <string.h>
 
 #include "osdd-set.h"
 #include "util.h"
 
+int osd_set_trie_char_to_index(char in)
+{
+	if('0'<=in && in<='9') return in-'0';
+	if(in == '_') return 10;
+	if('a'<=in && in<='z') return in-'a'+11;
+	return -1;
+}
+
+struct osd_set_trie * osd_set_trie_new(void)
+{
+	struct osd_set_trie * r = xmalloc(sizeof(r[0]));
+	memset(r, 0, sizeof(r[0]));
+	return r;
+}
+
+struct osd_set_trie * osd_set_trie_find(struct osd_set_trie * root, char * str, bool create)
+{
+	while(*str)
+	{
+		int i = osd_set_trie_char_to_index(*str);
+		if(i<0)
+		{
+			if(create)
+			{
+				die("osd_set_trie char %c not supported\n",*str);
+			}
+			else return 0;
+		}
+		if(!root->next[i])
+		{
+			if(create)
+				root->next[i] = osd_set_trie_new();
+			else return 0;
+		}
+		root = root->next[i];
+		str++;
+	}
+	return root;
+}
+
+
+// ***************************************************************************************************************
 
 struct osd_line *osd_set_add_line(struct osd_set *set, enum osd_line_type type)
 {
@@ -45,7 +88,7 @@ void osd_set_show(struct osd_set *set)
 {
 	for(int i=0;i<set->len;i++)
 	{
-		set->elements[i].show(set->elements[i].context,set->lines,set->num_lines);
+		if(set->is_active[i]) set->elements[i].show(set->elements[i].context,set->lines,set->num_lines);
 	}
 }
 
@@ -56,6 +99,16 @@ void osd_set_clear(struct osd_set *set)
 		set->elements[i].clear(set->elements[i].context);
 	}
 	set->num_lines = 0;
+	memset(set->is_active,0,sizeof(bool)*set->max);
+}
+
+void osd_set_active_outputs(struct osd_set *set, char * name, bool val)
+{
+	struct osd_set_trie *trie = osd_set_trie_find(&set->trie,name,0);
+	if(trie)
+	{
+		for(int i=0;i<trie->num_vals;i++) set->is_active[trie->vals[i]]=val;
+	}
 }
 
 bool osd_set_handle_event(struct osd_set *set, XEvent *ev)
@@ -71,6 +124,7 @@ bool osd_set_handle_event(struct osd_set *set, XEvent *ev)
 struct osd_set osd_set_new(void)
 {
   struct osd_set set;
+  memset(&set, 0, sizeof(set));
   set.len=0;
 
   set.num_lines=0;
@@ -79,3 +133,23 @@ struct osd_set osd_set_new(void)
   return set;
 }
 
+void osd_set_add(struct osd_set *set, struct osd_abstract abs, int names_len, char ** names)
+{
+	for(int i=0;i<names_len;i++)
+	{
+		printf("NAME |%s|\n",names[i]);
+		struct osd_set_trie *trie = osd_set_trie_find(&set->trie,names[i],1);
+		trie->vals = xrealloc(trie->vals,sizeof(trie->vals[0])*(trie->num_vals+1));
+		trie->vals[trie->num_vals++] = set->len;
+	}
+	if(set->len>=set->max)
+	{
+		set->max *= 2;
+		if(set->max < 4) set->max=4;
+		set->elements = xrealloc(set->elements,sizeof(set->elements[0])*set->max);
+		set->is_active = xrealloc(set->is_active,sizeof(set->is_active[0])*set->max);
+		memset(set->is_active,0,sizeof(bool)*set->max);
+	}
+	set->elements[set->len++] = abs;
+}
+
diff --git a/osdd-set.h b/osdd-set.h
index 348ff1a..a8b691c 100644
--- a/osdd-set.h
+++ b/osdd-set.h
@@ -14,9 +14,20 @@
 struct display_state;
 
 #define OSD_MAX_LINE_LEN 256
-#define OSD_MAX_SET_LEN 64
 
-#define OSD_ABSTRACT_NAME_LEN 64
+#define OSD_TRIE_LEN (26+10+1)
+
+struct osd_set_trie
+{
+	struct osd_set_trie *next[OSD_TRIE_LEN];
+	int num_vals;
+	int *vals;
+};
+
+int osd_set_trie_char_to_index(char in);
+struct osd_set_trie * osd_set_trie_new(void);
+struct osd_set_trie * osd_set_trie_find(struct osd_set_trie * root, char * str, bool create);
+
 
 enum osd_line_type {
   OSD_TYPE_TEXT,
@@ -45,18 +56,38 @@ struct osd_abstract {
 };
 
 struct osd_set {
-  struct osd_abstract elements[OSD_MAX_SET_LEN];
+  struct osd_abstract * elements;
+  bool * is_active;
   int len;
+  int max;
+  struct osd_set_trie trie;
 
   struct osd_line *lines;
   int num_lines;
   int max_lines;
 };
 
+struct osd_creator_abstract {
+	enum type {
+		OSD_CEATE_ADD_ONE_OSD=0,
+		OSD_CEATE_TYPE_ADD_MULTIPLE_OSD,
+	} t;
+	char *name;
+
+	void (*help)(FILE * out);
+	union {
+	struct osd_abstract (*add_one_osd)(int argc, char ** argv,Display *dpy);
+	void (*add_multiple_osd)(struct osd_set *set, int argc, char ** argv,Display *dpy,int names_len, char ** names);
+	} new;
+};
+
+
 struct osd_set osd_set_new(void);
+void osd_set_add(struct osd_set *set, struct osd_abstract abs, int names_len, char ** names);
 struct osd_line *osd_set_add_line(struct osd_set *set, enum osd_line_type type);
 void osd_set_show(struct osd_set *set);
 void osd_set_clear(struct osd_set *set);
 bool osd_set_handle_event(struct osd_set *set, XEvent *ev);
+void osd_set_active_outputs(struct osd_set *set, char * name, bool val);
 
 #endif
diff --git a/osdd.c b/osdd.c
index e9dc4af..ff4a795 100644
--- a/osdd.c
+++ b/osdd.c
@@ -22,27 +22,44 @@
 
 
 static struct osd_set osd;
+Display * dpy;
 #define FOREACHOSD for(int i=0;i<osd.len;i++)
 
 static timestamp_t now;
 
 /*** Options ***/
-
 static char *default_color = "green";
 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;
+static int debug_mode = 1;
 static int test_mode;
 
-static const char short_opts[] = "c:d:Df:m:o:O:s:";
 
 enum long_opt {
   OPT_TEST = 256,
 };
 
+
+static struct osd_creator_abstract * creator=NULL;
+int num_creator=0;
+int max_creator=0;
+
+static
+void add_creator(struct osd_creator_abstract in)
+{
+	if(num_creator <= max_creator)
+	{
+		max_creator *= 2;
+		if(max_creator < 4) max_creator=4;
+		creator = xrealloc(creator,sizeof(creator[0])*max_creator);
+	}
+	creator[num_creator++]=in;
+}
+
+static const char short_opts[] = "+c:d:Df:m:o:O:s:";
 static const struct option long_opts[] = {
   { "color",		required_argument,	NULL,	'c' },
   { "debug",		no_argument,		NULL,	'D' },
@@ -60,65 +77,121 @@ static const struct option long_opts[] = {
 static void NONRET
 usage(void)
 {
-  fprintf(stderr, "Usage: osdd <options>\n\n\
+  fprintf(stderr, "Usage: osdd <options> <modules>\n\n\
 Options:\n\
 -c, --color=<c>\t\tDefault color (#rgb, #rrggbb or a name from rgb.txt)\n\
 -D, --debug\t\tDebugging mode (do not detach from the terminal)\n\
 -d, --duration=<ms>\tDefault message duration in milliseconds\n\
--f, --font=<f>\t\tFont to use for the OSD\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\
--s, --line-spacing=<n>\tSet line spacing factor (decimal fraction, default=0.2)\n\
 -L, --log=<n>\t\tSet default log options (bitmask, default=255)\n\
+\n\
+Modules:\n\
+Each module id specify by:\n\
+\tMODULE_NAME user_name1 user_name2 ... [ args ]\n\
+\n\
+MODULE_NAME is name from following list\n\
+user_name is name of module instance, that could by used by osdc --output=\n\
+\t\tonly small letters numbers and _ is allowed\n\
+optional arg is really in '[' ']'. Backet should be separate arguments\n\
+\n\
 ");
+  for(int i=0;i<num_creator;i++) if(creator[i].help) creator[i].help(stderr);
   exit(1);
 }
 
+static bool 
+is_small_ch(char in)
+{
+	return 'a'<=in && in<='z';
+}
+
 static void
 parse_opts(int argc, char **argv)
 {
   int opt;
+  optind = 0;
   while ((opt = getopt_long(argc, argv, short_opts, long_opts, NULL)) >= 0)
-    switch (opt)
-      {
-      case 'c':
+  {
+	switch (opt)
+	  {
+	  case 'c':
 	default_color = optarg;
 	break;
-      case 'd':
+	  case 'd':
 	default_duration = atoi(optarg);
 	break;
-      case 'D':
+	  case 'D':
 	debug_mode = 1;
 	break;
-      case 'f':
-	//font_name = optarg;
-	break;
-      case 'l':
-	//line_spacing = atof(optarg);
-	break;
-      case 'm':
+	  case 'm':
 	default_min_duration = atoi(optarg);
 	break;
-      case 'o':
+	  case 'o':
 	default_outline_color = optarg;
 	break;
-      case 'O':
+	  case 'O':
 	default_outline_width = atoi(optarg);
 	break;
-      case 'L':
+	  case 'L':
 	default_log = atoi(optarg);
 	break;
-      case OPT_TEST:
+	  case OPT_TEST:
 	test_mode = 1;
 	debug_mode = 1;
 	break;
-      default:
+	  default:
+	fprintf(stderr,"Option %c not exist\n\n",opt);
 	usage();
-      }
+	  }
+  }
+  int ind = optind;
+  while(ind < argc)
+  {
+	    for(int i=0;i<num_creator;i++)
+	    {
+	        if(!strcmp(creator[i].name,argv[ind]))
+			{
+				char ** name_argv = argv + ++ind;
+				int name_argc=0;
+				char ** creator_argv = 0;
+				int creator_argc=0;
+				for(;ind<argc && is_small_ch(argv[ind][0]);ind++) name_argc++;
+				if(ind<argc && !strcmp(argv[ind],"["))
+				{
+					creator_argv = argv + ++ind;
+					int open_bracket = 1;
+					for(;ind<argc && open_bracket;ind++)
+					{
+						if(!strcmp(argv[ind],"[")) open_bracket++;
+						if(!strcmp(argv[ind],"]")) open_bracket--;
+						if(open_bracket) creator_argc++;
+					}
+					if(open_bracket)
+					{
+						fprintf(stderr,"Missing ']'\n\n");
+						usage();
+					}
+				}
+				char * default_str="default";
+				if(name_argc == 0)
+				{
+					name_argc=1;
+					name_argv=&default_str;
+				}
+				if(creator[i].t == OSD_CEATE_ADD_ONE_OSD)
+					osd_set_add(&osd, creator[i].new.add_one_osd(creator_argc,creator_argv,dpy), name_argc, name_argv);
+				else
+					creator[i].new.add_multiple_osd(&osd,creator_argc,creator_argv,dpy, name_argc, name_argv);
+				goto parse_opt_next;
+			}
+	    }
+		fprintf(stderr,"Module %s not exist\n\n",argv[ind]);
+		usage();
+parse_opt_next:;
+  }
 
-  if (optind < argc)
-    usage();
 }
 
 /*** Displaying of messages ***/
@@ -142,6 +215,7 @@ display_msg(struct msg *msg)
 
   char *line = msg->text;
   int num_l=0;
+  bool is_output_set=0;
   while (*line)
     {
       // The parser is destructive, but it does not do any harm, since we display each message only once.
@@ -190,6 +264,18 @@ display_msg(struct msg *msg)
 	outline_color = val;
       else if (!strcmp(key, "outline-width"))
 	outline_width = atoi(val);
+      else if (!strcmp(key, "output"))
+	  {
+		is_output_set=1;
+		osd_set_active_outputs(&osd,val,1);
+	  }
+      else if (!strcmp(key, "output-no"))
+	  {
+	    if(!is_output_set) 
+			osd_set_active_outputs(&osd,"default",1);
+		is_output_set=1;
+		osd_set_active_outputs(&osd,val,0);
+	  }
       if (l)
 	{
 	  num_l++;
@@ -202,6 +288,8 @@ display_msg(struct msg *msg)
       line = nl;
     }
 
+  if(!is_output_set) 
+    osd_set_active_outputs(&osd,"default",1);
   if(!num_l)
   {
 	  struct osd_line *l = osd_set_add_line(&osd,OSD_TYPE_TEXT);
@@ -364,11 +452,14 @@ do_test(void)
 int
 main(int argc, char **argv)
 {
-  parse_opts(argc, argv);
+  add_creator(display_creator_new());
+  add_creator(display_by_outputs_creator_new());
+
+
   setlocale(LC_CTYPE, "");
   XInitThreads();
 
-  Display *dpy = XOpenDisplay(NULL);
+  dpy = XOpenDisplay(NULL);
   if (!dpy)
     die("Cannot open display");
   Window win = DefaultRootWindow(dpy);
@@ -377,16 +468,6 @@ main(int argc, char **argv)
   if (!pty)
     die("Cannot intern OSD_QUEUE atom");
 
-  if (!debug_mode)
-    {
-      pid_t pid = fork();
-      if (pid < 0)
-	die("Cannot fork: %m");
-      if (pid > 0)
-        return 0;
-      setsid();
-    }
-
   if (test_mode)
     {
       do_test();
@@ -400,23 +481,18 @@ main(int argc, char **argv)
     }
 
   osd = osd_set_new();
-  {
-  FILE * f = popen("xrandr | grep \\ connected","r");
-  char line[1001];
-  while(fscanf(f," %1000[^\n]",line)==1)
-  {
-	    int width,height,x,y;
-            char *l = line;
-            while(*l && !(*l==' ')) l++;
-            while(*l && !('0'<=*l&&*l<='9')) l++;
-            if(sscanf(l,"%dx%d+%d+%d",&width,&height,&x,&y)==4)
-		    if(osd.len < OSD_MAX_SET_LEN)
-		    {
-			    DBG("ADD screen %dx%d on %d,%d\n",width,height,x,y);
-			    osd.elements[osd.len++] = display_state_new(dpy,width,height,x,y);
-		    }
-  }
-  }
+  parse_opts(argc, argv);
+
+  if (!debug_mode)
+    {
+      pid_t pid = fork();
+      if (pid < 0)
+	die("Cannot fork: %m");
+      if (pid > 0)
+        return 0;
+      setsid();
+    }
+
 
   struct pollfd pfd = {
     .fd = ConnectionNumber(dpy),
-- 
GitLab