Merge remote-tracking branch 'origin/master' into for-steve
[sxemacs] / contrib / tty-colors.c
1 /* Visually check 256 color capability of terminal
2  *
3  * (C) 2008 Nelson Ferreira
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under a BSD-like licence.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions are met:
10  * Redistributions of source code must retain the above copyright notice, this
11  * list of conditions and the following disclaimer.
12  * Redistributions in binary form must reproduce the above copyright notice,
13  * this list of conditions and the following disclaimer in the documentation
14  * and/or other materials provided with the distribution.
15  * Neither the name of the Technical University of Berlin nor the names of its
16  * contributors may be used to endorse or promote products derived from this
17  * software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 #include <stdlib.h>
32 #include <stdio.h>
33 #include <fcntl.h>
34 #include <curses.h>
35 #include <term.h>
36 #include <string.h>
37 #include <termios.h>
38 #include <unistd.h>
39
40 /*
41  * For the terminal info
42  */
43 static char term_buffer[8192];
44
45 /*
46  * For the capabilities
47  */
48 char    *setfore = NULL,
49         *setback = NULL,
50         *resetcolor = NULL,
51         *standstr = NULL,
52         *exit_stand = NULL,
53         *inicolor_str = NULL,
54         *clrnames  = NULL,
55         *bold = NULL,
56         *dim = NULL,
57         *reverse = NULL,
58         *blinking = NULL,
59         *exit_attr = NULL,
60         *opair = NULL;
61
62 int      maxcolors = 0,
63         maxpairs  = 0,
64         do_force_16  = 0;
65
66
67
68
69
70 /******************
71  * Initialize the terminal info
72  */
73 void init_terminal_data ()
74 {
75   char *termtype = getenv ("TERM");
76   int success;
77
78   if (termtype == 0) {
79     fprintf(stderr,"Specify a terminal type with `setenv TERM <yourtype>'.\n");
80     exit(1);
81   }
82
83   success = tgetent (term_buffer, termtype);
84   if (success < 0) {
85     fprintf(stderr,"Could not access the termcap data base.\n");
86     exit(1);
87   }
88   if (success == 0) {
89     fprintf(stderr,"Terminal type `%s' is not defined.\n", termtype);
90     exit(1);
91   }
92 }
93
94 /*******************
95  * Print a band with background of all available colors
96  */
97 void colormap( int do_boldstand )
98 {
99         int back;
100         for( back = 0; back < maxcolors; back++ ) {
101                 char *strB;
102                 strB = strdup(tparm(setback,back));
103                 printf("%s        %s",strB,resetcolor);
104                 if( back%8 == 7 )
105                         printf("\n");
106         }
107         if (do_boldstand) {
108                 for( back = 0; back < maxcolors; back++ ) {
109                         char *strB;
110                         strB = strdup(tparm(setfore,back));
111                         printf("%s%s%s        %s%s%s",bold,standstr,strB,resetcolor,
112                                exit_stand,exit_attr);
113                         if( back%8 == 7 )
114                                 printf("\n");
115                 }
116         }
117         for ( back = 0; back < maxcolors; back++ ) {
118                 char *strB;
119                 strB = strdup(tparm(setfore,back));
120                 printf("%sXXXXXXXX%s",strB,resetcolor);
121                 if( back%8 == 7 )
122                         printf("\n");
123         }
124         if ( bold ) {
125                 for ( back = 0; back < maxcolors; back++ ) {
126                         char *strB;
127                         strB = strdup(tparm(setfore,back));
128                         printf("%s%sXXXXXXXX%s%s",bold,strB,resetcolor,exit_attr);
129                         if( back%8 == 7 )
130                                 printf("\n");
131                 }
132         }
133 }
134
135
136 /*********************
137  * Print a table with all the fore and background
138  * color combinations, with bold and optionally standout
139  */
140 void combinations_of_colors(int do_standout, int do_boldstand)
141 {
142         int fore, back;
143         for( fore = 0; fore < maxcolors; fore ++ )
144                 for( back = 0; back < maxcolors; back ++ ) {
145                         char *strF, *strB;
146                         char *sstrF, *sstrB;
147                         strB = strdup(tparm(setback,back));
148                         strF = strdup(tparm(setfore,fore));
149                         printf( "%s%s%s [%3d|%3d]%s%s", "",strF,strB,fore,back,resetcolor,"");
150                         /* Bold */
151                         printf( "%s%s%sB[%3d|%3d]%s%s", bold,strF,strB,fore,back,resetcolor,exit_attr);
152                         /* Standout usually does not work good... do only if asked for */
153                         /* Since standout usually reverses, we'll reverse the fore and back too */
154                         sstrB = strdup(tparm(setback,fore));
155                         sstrF = strdup(tparm(setfore,back));
156                         if (do_standout)
157                                 printf( "%s%s%sS[%3d|%3d]%s%s",standstr,sstrF,sstrB,fore,back,resetcolor,exit_stand);
158                         if (do_boldstand)
159                                 printf( "%s%s%s%sS[%3d|%3d]%s%s%s",bold,standstr,sstrF,sstrB,fore,back,resetcolor,exit_stand,exit_attr);
160                         printf( "\n" );
161                 }
162         printf( "\n" );
163
164 }
165
166
167 /** Query and assign a capability
168  */
169 void capability(char**buf,char*code,char**var,char*message)
170 {
171         char *val = tgetstr(code,buf);
172         if ( ! val ) {
173                 *var=NULL;
174                 printf("%s\n",message);
175         } else {
176                 *var=strdup(val);
177         }
178 }
179
180
181 /****************************
182  * Retrieve the capabilities
183  */
184 void get_capabilities()
185 {
186         char buffer[2500]="", *buf=buffer;
187
188         /*
189          * Get the capabilities
190          */
191         maxcolors = tgetnum("Co");
192         if ( maxcolors <= 0 ) {
193                 printf("I am sorry, your terminal does not show color in the capabilities\n");
194         }
195         maxpairs = tgetnum("pa");
196         if (maxpairs <= 0 ) {
197                 printf("I am sorry, your terminal does not have the max color pairs information\n");
198         }
199
200         capability(&buf,"AF",&setfore,
201                    "I am sorry, your terminal does not have the set foreground color string in the capabilities");
202         capability(&buf,"AB",&setback,
203                    "I am sorry, your terminal does not have the set background color string in the capabilities");
204         capability(&buf,"op",&resetcolor,
205                    "I am sorry, your terminal does not have the reset current color string in the capabilities");
206         capability(&buf,"md",&bold,
207                    "I am sorry, your terminal does not have the bold string in the capabilities");
208         capability(&buf,"mh",&dim,
209                    "I am sorry, your terminal does not have the dim string in the capabilities");
210         capability(&buf,"mr",&reverse,
211                    "I am sorry, your terminal does not have the reverse string in the capabilities");
212         capability(&buf,"mb",&blinking,
213                    "I am sorry, your terminal does not have the blinking string in the capabilities");
214         capability(&buf,"so",&standstr,
215                    "I am sorry, your terminal does not have the standout string in the capabilities");
216         capability(&buf,"se",&exit_stand,
217                    "I am sorry, your terminal does not have the exit standout string in the capabilities");
218         capability(&buf,"me",&exit_attr,
219                    "I am sorry, your terminal does not have the exit attribute string in the capabilities");
220         capability(&buf,"Ic",&inicolor_str,
221                    "I am sorry, your terminal does not have the init color string in the capabilities");
222         capability(&buf,"Yw",&clrnames,
223                 "I am sorry, your terminal does not have the color names string in the capabilities");
224         capability(&buf,"op",&opair,
225                 "I am sorry, your terminal does not have the original pairs string in the capabilities");
226 }
227
228
229 /********************
230  * Query the RGB map
231  */
232 void query_rgb()
233 {
234         int i, fd=fileno(stdin);
235         long mode;
236         struct termios ts;
237
238         if ( ! isatty(fd) || ! isatty(fileno(stdout)) )
239                 printf("Sorry, but stdin and/or stdout is not the terminal tty so I cannot query it\n");
240         else {
241                 if ( ! inicolor_str )
242                         printf("According to your terminal info this terminal rgb query will hang. Press ^C to abort the query.\n");
243
244                 /*
245                  * We need to do the equivalent of cbreak()
246                  */
247                 tcgetattr(fd,&ts);
248                 mode=ts.c_lflag; /* Save mode so we can restore afterwards */
249                 ts.c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
250                 tcsetattr(fd, TCSANOW,&ts);
251                 ts.c_lflag = mode;
252
253                 /* Ask for the colors */
254                 for ( i = 0 ; 1 ; i ++ ) {
255                         char buffer[1000],
256                                 *buf=buffer,
257                                 *end=buffer+sizeof(buffer)-1;
258                         int interrupted = 0;
259
260                         /* This is a string retrieved from Noah
261                          * Friedmans xterm-frobs.el. I could not find
262                          * any termcap entry that verifies it
263                          */
264                         printf("\e]4;%d;?;\e\\",i);
265                         fflush(stdout);
266                         memset(buffer,0,sizeof(buffer));
267                         while( buf!=end ) {
268                                 fd_set fdsr, fdse,fdsw;
269                                 int fds;
270                                 struct timeval t;
271
272                                 t.tv_sec = 0;
273                                 t.tv_usec = 100000; /* 100ms */
274
275                                 FD_ZERO(&fdsr);
276                                 FD_ZERO(&fdse);
277                                 FD_ZERO(&fdsw);
278                                 FD_SET(fd, &fdsr);
279                                 FD_SET(fd, &fdse);
280                                 fds = select(fd+1,&fdsr,&fdsw,&fdse,&t);
281                                 if ( fds < 1) {
282                                         tcsetattr(fd, TCSANOW,&ts);
283                                         return;
284                                 }
285                                 if ( ! FD_ISSET(fd, &fdsr) ) {
286                                         tcsetattr(fd, TCSANOW,&ts);
287                                         return;
288                                 }
289                                 read(fd,buf,1);
290                                 if ( *buf == '\\') {
291                                         /* The response sequence ends with the only "\"
292                                            of the response
293                                         */
294                                         break;
295                                 }
296                                 if ( *buf == '\ 3' ) {
297                                         /*
298                                            If the terminal did not reply, chances are
299                                            this is not supported and the user used ^C
300                                         */
301                                         interrupted=1;
302                                         break;
303                                 }
304                                 *buf++;
305                         }
306                         if (interrupted)
307                                 break;
308
309                         /* Extract the rgb values */
310                         buf=strstr(buffer,"rgb:");
311                         if( buf ) {
312                                 buf+=4;
313                                 char *end = strstr(buf,"\e\\");
314                                 if(end)
315                                           *end='\0';
316                         } else {
317                                 buf="Did not find info";
318                         }
319                         printf("Color %d: %s\n",i,buf);
320                         fflush(stdout);
321                 }
322
323                 /* Restore terminal settings */
324                 tcsetattr(fd, TCSANOW,&ts);
325         }
326 }
327
328
329 /**************
330  * Fake the capabilities the ansi terminal info
331  * would return
332  */
333 void force_ansi()
334 {
335         if ( ! (maxcolors > 0) ) {
336                 maxcolors = 8;
337                 printf("WARNING: Forcing ANSI testing, 8 colors\n");
338         }
339         if ( maxcolors == 8 && do_force_16 ) {
340                 maxcolors = 16;
341                 printf("WARNING: Forcing 16 color testing\n");
342         }
343         if ( ! setfore ) {
344                 setfore="\e[3%p1%dm";
345                 printf("WARNING: Forcing ANSI set foreground: ^[%s\n", setfore+1);
346         }
347         if ( ! setback ) {
348                 setback="\e[4%p1%dm";
349                 printf("WARNING: Forcing ANSI set background: ^[%s\n", setback+1);
350         }
351         if ( ! resetcolor ) {
352                 resetcolor="\e[39;49m";
353                 printf("WARNING: Forcing ANSI reset colour  : ^[%s\n", resetcolor+1);
354         }
355 }
356
357
358
359 int
360 main( int argc, char *argv[] )
361 {
362   int     do_colormap = 0,
363           do_combinations = 0,
364           do_standout = 0,
365           do_boldstand = 0,
366           do_query = 0;
367
368   { /* Process arguments */
369           int i;
370
371           for( i = 1; i < argc; i++ ) {
372                   if( ! strcmp(argv[i],"-m") )
373                           do_colormap = 1;
374                   if( ! strcmp(argv[i],"-c") )
375                           do_combinations=1;
376                   if ( ! strcmp(argv[i], "-f" ) )
377                           do_force_16 = 1;
378                   if( ! strcmp(argv[i],"-s") )
379                           do_standout=1;
380                   if( ! strcmp(argv[i],"-bs") )
381                           do_boldstand=1;
382                   if( ! strcmp(argv[i],"-q") )
383                           do_query=1;
384           }
385   }
386
387   init_terminal_data();
388
389   get_capabilities();
390
391   /*
392    * Report the capabilities
393    */
394   printf("\n\nTerminal: %s\n",getenv("TERM"));
395   if ( maxcolors > 0 )
396     printf("You've got %d colours\n",maxcolors);
397   if ( maxpairs > 0 )
398     printf("You've got %d pairs of colors possible\n",maxpairs);
399
400   if ( setfore )    printf("To set foreground: ^[%s\n", setfore+1);
401   if ( setback )    printf("To set background: ^[%s\n", setback+1);
402   if ( resetcolor ) printf("To reset colours : ^[%s\n", resetcolor+1);
403   if ( standstr )   printf("To standout      : ^[%s\n", standstr+1);
404   if ( bold )       printf("To bold          : ^[%s\n", bold+1);
405   if ( dim )        printf("To dim           : ^[%s\n", dim+1);
406   if ( reverse )    printf("To reverse       : ^[%s\n", reverse+1);
407   if ( blinking )   printf("To blinking      : ^[%s\n", blinking+1);
408   if ( exit_stand ) printf("To exit standout : ^[%s\n", exit_stand+1);
409   if ( exit_attr )  printf("To exit attribute: ^[%s\n", exit_attr+1);
410   if ( inicolor_str ) printf("To init color    : ^[%s\n", inicolor_str+1);
411   if ( clrnames )   printf("To color names   : ^[%s\n", clrnames+1);
412   if ( opair )      printf("To original pairs: ^[%s\n",   opair+1);
413
414
415
416   /*
417    * Let's try the ANSI defaults
418    */
419   if ( do_colormap || do_combinations ) {
420           force_ansi();
421   }
422
423   if ( do_query ) {
424           printf("\nQuery the RGB from the terminal:\n");
425           query_rgb();
426   }
427
428   if ( do_colormap ) {
429           printf("\nColormap:\n");
430           colormap( do_boldstand );
431   }
432
433   if ( do_combinations ) {
434           printf("\nColor combinations:\n");
435           combinations_of_colors( do_standout,
436                                   do_boldstand);
437   }
438
439   return 0;
440 }