curses and colors

Pages: 12
Don't use A_BLINK and A_BOLD. They are not necessary to get the bright colors. In this system just add 8 to the color name to get the bright color. Here's my final version. It has global default colors BACK_COLOR and FORE_COLOR which are set on entry and before exit to/from conout (wsconout, actually). An extra letter option, D (or d), after & or ^ sets both the fore color and back color back to the defaults in the middle of a string.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#include <curses.h>
#include <stdio.h> // vs(n)printf(), va_list, etc.
#include <ctype.h> // toupper(), isupper()

const short COLOR_BRIGHT = 8;

const short BACK_COLOR = COLOR_WHITE + COLOR_BRIGHT;
const short FORE_COLOR = COLOR_GREEN;

const int   MAX_STRING = 512;
const char  SET_BACK   = '&';
const char  SET_FORE   = '^';

inline chtype COLORPAIR( short x, short y ) {
    return COLOR_PAIR( x * COLORS + y );
}

void wsconout( WINDOW* win, char* strIn ) {

    short fore = FORE_COLOR;
    short back = BACK_COLOR;
    
    for( char* str = strIn; /*empty*/; ++str )
    {
        switch( *str ) {

          case '\0': // end-of-string, return
            goto ResetDefaultsAndReturn;

          default: // Output all unrecognized chars literally
            waddch( win, *str );
            continue;

          case SET_BACK:
          case SET_FORE:
          {
            char mode = *str++;
            short newColor;

            switch( toupper( *str )) {

              case '\0': // end-of-string, output lone mode char and return
                waddch( win, mode );
                goto ResetDefaultsAndReturn;

              default: // Print something we don't recognize as is
                waddch( win, mode );
                // Just print one mode char for two in a row
                if( *str != mode ) waddch( win, *str );
                continue;

              case 'D': // Reset default colors (both fore and back)
                back = BACK_COLOR;
                fore = FORE_COLOR;
                wattrset( win, COLORPAIR( fore, back ));
                continue;

              case 'K': newColor = COLOR_BLACK;   break;
              case 'W': newColor = COLOR_WHITE;   break;
              case 'R': newColor = COLOR_RED;     break;
              case 'G': newColor = COLOR_GREEN;   break;
              case 'B': newColor = COLOR_BLUE;    break;
              case 'C': newColor = COLOR_CYAN;    break;
              case 'Y': newColor = COLOR_YELLOW;  break;
              case 'P': newColor = COLOR_MAGENTA; break;
            }

            newColor += isupper( *str ) ? COLOR_BRIGHT : 0; // Set brightness

            if( mode == SET_BACK ) back = newColor;
            else                   fore = newColor;

            wattrset( win, COLORPAIR( fore, back ));
          }
          continue;
        }
    }
ResetDefaultsAndReturn:
    wattrset( win, COLORPAIR( FORE_COLOR, BACK_COLOR ));
}

void wvconout( WINDOW* win, char* fmt, va_list args ) {
    char str[ MAX_STRING ];
    _vsnprintf( str, MAX_STRING, fmt, args ); // protects from overrun
//    vsprintf( str, fmt, args ); // doesn't protect from overrun
    wsconout( win, str );
}

void wconout( WINDOW* win, char* fmt, ... ) {
    va_list args;
    va_start( args, fmt );
    wvconout( win, fmt, args );
    va_end( args );
}

void conout( char *fmt, ... ) {
    va_list args;
    va_start( args, fmt );
    wvconout( stdscr, fmt, args );
    va_end( args );
}

void init_color_pairs() {
    short f, b;
    for( f = 0; f < COLORS; ++f )
        for( b = 0; b < COLORS; ++b )
            init_pair( f * COLORS + b, f, b );
}

int startwin() {
    initscr();
    start_color();
    init_color_pairs();
    bkgd( COLORPAIR( FORE_COLOR, BACK_COLOR ));
}

int main( int argc, char *argv[] ) {
    startwin();

    conout( "Default color\n" );
    conout( "^G&KBright Green On Bright Black\n" );
    conout( "^g&kDark Green on Dark Black\n" );
    conout( "Back to default color\n" );
    conout( "^C&bCyan on blue^D back to defaults\n" );
    conout( "^cW^Ch^ya^Yt'^gs ^G^^^bu^rp^p^^^R?\n" );
    conout( "Back to default fore && back\n" );

    getch();
    endwin();
}

Last edited on
Don't "just add 8 to the color name" -- that is non-portable and will fail with various curses implementations.

If you use wattrset() to adjust the color values that will also adjust the blink and bold attributes.

So use A_BLINK to set a bright background (but remember that your users have to have their terminals set up correctly!), then [w]attrset() the proper attribute when writing stuff.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <curses.h>

void init_EGA_colors()
  {
  int f, b;
  start_color();
  for (f = 0; f < 8; f++)
  for (b = 0; b < 8; b++)
    init_pair( (b *8) +f, f, b );
  }

int EGA_color_attribute( int f, int b )
  {
  return COLOR_PAIR( ((b %8) *8) +(f %8) )
       | ((f < 8) ? 0 : A_BOLD)
       | ((b < 8) ? 0 : A_BLINK);
  }

int main()
  {
  initscr();
  init_EGA_colors();

  bkgd( EGA_color_attribute( 0, 15 ) );  // black on white

  attrset( EGA_color_attribute( 14, 4 ) );  // yellow on red
  mvaddstr( 10, 32, " Press ENTER " );
  move( 24, 0 );
  refresh();

  getch();
  endwin();
  return 0;
  }

Hope this helps.
Hammurabi

Thanks to Hammurabi -- it's now solved. Although I agree w/ Duoas that it is not portable and will fail with various implementations -- Forunately I am designing this product to work on a select few computers which I can tailor any problems to and I think this is the proper way to go.

The final problem was in the code I kept adding an A_BLINK attribute to the bkgd() function.

1
2
init_pair( 1, COLOR_RED, COLOR_GREEN );
bkgd( COLOR_PAIR( 1 ) | A_BLINK );


Although the problem is that you cannot remove this attribute once it is set in the background without changing all of the background color. So the fix which is already there, but I did not realize

1
2
init_pair( 1, COLOR_RED, COLOR_GREEN + 8 );
bkgd( COLOR_PAIR(1) );


COLOR_GREEN + 8 will give the color bright green without having to add the attribute. Nice looking code as well Hammurabi. Here's a quick example of why this works now...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <curses.h>

int main( ) {
    initscr();
    start_color();
    init_pair( 1, COLOR_WHITE + 8, COLOR_BLUE + 8);
    init_pair( 2, COLOR_WHITE, COLOR_BLUE );
    init_pair( 3, COLOR_WHITE, COLOR_BLUE );

    bkgd(    COLOR_PAIR(3) | A_BLINK ); 
    printw( "This is the background, with A_BLINK attribute.\n" );
    attron( COLOR_PAIR(3) ); 
    printw( "This is the background, without A_BLINK attribute.\n\n" );
    attroff( COLOR_PAIR(3) );

    bkgd(    COLOR_PAIR(1) );  printw( "This is the background with (+8) for brightness.\n" );
    attrset( COLOR_PAIR(2) );  printw( "This is the new colors without (+8) for brightness.\n" );

    refresh();
    getch();
    endwin();
}


Thanks for everyone who assisted!
I still think you are making a mistake...

> Although the problem is that you cannot remove this attribute once it is set in the background without changing all of the background color.

I don't see why that is the case. When you [w]refresh(), it should update/draw the screen exactly as you specify it -- using the background colors everywhere you did not explicitly [w]addstr() with a different text attribute.

But that's my $0.02.
Yea that was the whole problem. I still am not sure why curses decides to execute this function in this way but that's the case. If you run the very last snippet I posted you'll see that even when you put the bkgd();, change the attributes around with ATTRON and ATTROFF, they do not get applied if you previously set them through bkgd();

Even when you refresh(); and send no A_BLINK attribute through addstr, it still will get tagged along if you set it through bkgd();

It's not the best way to execute this function, but it works for what I need it to. I've heard A_BLINK on linux consoles will actually make the background blink! Let's just hope this program won't ever have to be run on a linux! So much for this portability when concerning these types of color functions
Topic archived. No new replies allowed.
Pages: 12