banner



How To Get Color Attributes In Ncurses

Jim demonstrates color manipulation with curses by calculation colors to his final chance game.

In parts one and ii of my article series about programming with the ncurses library, I introduced a few curses functions to draw text on the screen, query characters from the screen and read from the keyboard. To demonstrate several of these functions, I created a simple adventure game in curses that drew a game map and player character using simple characters. In this follow-up article, I evidence how to add color to a curses program.

Drawing on the screen is all very well and proficient, just if information technology'due south all white-on-blackness text, your program might seem dull. Colors tin help convey more information—for example, if your program needs to point success or failure. In such a instance, you could display text in greenish or cherry to aid emphasize the outcome. Or, maybe you but want to use colors to "snazz" up your program to brand it expect prettier.

In this article, I use a uncomplicated example to demonstrate color manipulation via the curses functions. In my previous article, I wrote a basic risk-style game that lets you motion a player character around a crudely drawn map. Still, the map was entirely blackness and white text, relying on shapes to advise water (~) or mountains (^), so let'southward update the game to use colors.

Color Essentials

Before you tin can use colors, your program needs to know if it can rely on the concluding to display the colors correctly. On mod systems, this e'er should be truthful. But in the classic days of computing, some terminals were monochromatic, such as the venerable VT52 and VT100 terminals, normally providing white-on-blackness or dark-green-on-black text.

To query the terminal capability for colors, employ the has_colors() function. This volition return a true value if the last tin brandish colour, and a false value if not. It is usually used to start an if cake, like this:

                      if (has_colors() == FALSE) {     endwin();     printf("Your terminal does not support color\n");     exit(ane); }                  

Having determined that the terminal can brandish colour, you lot then tin can ready curses to utilise colors with the start_color() function. At present you're prepare to define the colors your program will utilise.

In curses, you define colors in pairs: a foreground color on a background color. This allows curses to ready both color attributes at one time, which frequently is what you want to do. To found a color pair, utilise init_pair() to define a foreground and background color, and associate information technology to an alphabetize number. The general syntax is:

                      init_pair(index,            foreground,            background);                  

Consoles support only viii bones colors: black, reddish, greenish, yellow, bluish, magenta, cyan and white. These colors are defined for yous with the following names:

  • COLOR_BLACK
  • COLOR_RED
  • COLOR_GREEN
  • COLOR_YELLOW
  • COLOR_BLUE
  • COLOR_MAGENTA
  • COLOR_CYAN
  • COLOR_WHITE

Applying the Colors

In my risk game, I'd like the grassy areas to exist dark-green and the player's "trail" to exist a subtle yellow-on-green dotted path. Water should be blue, with the tildes in the similar cyan color. I'd like mountains to be grey, but black text on a white background should brand for a reasonable compromise. To make the histrion'southward character more than visible, I'd like to utilize a garish carmine-on-magenta scheme. I can define these color pairs like so:

                      start_color(); init_pair(1, COLOR_YELLOW, COLOR_GREEN); init_pair(2, COLOR_CYAN, COLOR_BLUE); init_pair(3, COLOR_BLACK, COLOR_WHITE); init_pair(4, COLOR_RED, COLOR_MAGENTA);                  

To make my color pairs easy to remember, my programme defines a few symbolic constants:

                      #define GRASS_PAIR     1 #ascertain EMPTY_PAIR     1 #ascertain WATER_PAIR     ii #define MOUNTAIN_PAIR  3 #ascertain PLAYER_PAIR    iv                  

With these constants, my color definitions become:

                      start_color(); init_pair(GRASS_PAIR, COLOR_YELLOW, COLOR_GREEN); init_pair(WATER_PAIR, COLOR_CYAN, COLOR_BLUE); init_pair(MOUNTAIN_PAIR, COLOR_BLACK, COLOR_WHITE); init_pair(PLAYER_PAIR, COLOR_RED, COLOR_MAGENTA);                  

Whenever you want to display text using a color, y'all simply need to tell curses to fix that color aspect. For good programming practice, you also should tell curses to undo the colour combination when you're done using the colors. To set up the color, use attron() before calling functions like mvaddch(), and so turn off the color attributes with attroff() afterward. For case, when I draw the player's character, I might do this:

                      attron(COLOR_PAIR(PLAYER_PAIR)); mvaddch(y, x, Thespian); attroff(COLOR_PAIR(PLAYER_PAIR));                  

Annotation that applying colors to your programs adds a subtle change to how yous query the screen. Normally, the value returned by mvinch() is of blazon chtype. Without color attributes, this is basically an integer and can be used as such. But, colors add extra attributes to the characters on the screen, so chtype carries extra color information in an extended flake pattern. If you use mvinch(), the returned value volition comprise this extra color value. To excerpt simply the "text" value, such as in the is_move_okay() function, you demand to apply a bitwise & with the A_CHARTEXT bit mask:

                      int is_move_okay(int y, int ten) {     int testch;      /* return truthful if the space is okay to move into */      testch = mvinch(y, x);     render (((testch & A_CHARTEXT) == GRASS)             || ((testch & A_CHARTEXT) == EMPTY)); }                  

With these changes, I tin update the adventure game to utilize colors:

                      /* quest.c */  #include <curses.h> #include <stdlib.h>  #define GRASS     ' ' #define EMPTY     '.' #ascertain H2o     '~' #define Mountain  '^' #define Player    '*'  #ascertain GRASS_PAIR     ane #ascertain EMPTY_PAIR     1 #define WATER_PAIR     two #define MOUNTAIN_PAIR  iii #define PLAYER_PAIR    four  int is_move_okay(int y, int x); void draw_map(void);  int chief(void) {     int y, x;     int ch;      /* initialize curses */      initscr();     keypad(stdscr, True);     cbreak();     noecho();      /* initialize colors */      if (has_colors() == FALSE) {         endwin();         printf("Your terminal does not back up color\n");         exit(1);     }      start_color();     init_pair(GRASS_PAIR, COLOR_YELLOW, COLOR_GREEN);     init_pair(WATER_PAIR, COLOR_CYAN, COLOR_BLUE);     init_pair(MOUNTAIN_PAIR, COLOR_BLACK, COLOR_WHITE);     init_pair(PLAYER_PAIR, COLOR_RED, COLOR_MAGENTA);      articulate();      /* initialize the quest map */      draw_map();      /* outset player at lower-left */      y = LINES - 1;     x = 0;      do {                                      /* past default, you get a blinking cursor - employ information technology to            indicate thespian * */          attron(COLOR_PAIR(PLAYER_PAIR));         mvaddch(y, ten, Histrion);         attroff(COLOR_PAIR(PLAYER_PAIR));         motility(y, 10);         refresh();          ch = getch();          /* examination inputted key and determine direction */          switch (ch) {         instance KEY_UP:         example 'w':         case 'W':             if ((y > 0) && is_move_okay(y - 1, ten)) {                 attron(COLOR_PAIR(EMPTY_PAIR));                 mvaddch(y, x, EMPTY);                 attroff(COLOR_PAIR(EMPTY_PAIR));                 y = y - one;             }             break;         instance KEY_DOWN:         case 's':         example 'Due south':             if ((y < LINES - 1) && is_move_okay(y + one, x)) {                 attron(COLOR_PAIR(EMPTY_PAIR));                 mvaddch(y, ten, EMPTY);                 attroff(COLOR_PAIR(EMPTY_PAIR));                 y = y + one;             }             break;         case KEY_LEFT:         case 'a':         example 'A':             if ((x > 0) && is_move_okay(y, x - 1)) {                 attron(COLOR_PAIR(EMPTY_PAIR));                 mvaddch(y, 10, EMPTY);                 attroff(COLOR_PAIR(EMPTY_PAIR));                 10 = x - one;             }             pause;         case KEY_RIGHT:         case 'd':         case 'D':             if ((x < COLS - i) && is_move_okay(y, x + 1)) {                 attron(COLOR_PAIR(EMPTY_PAIR));                 mvaddch(y, x, EMPTY);                 attroff(COLOR_PAIR(EMPTY_PAIR));                 x = x + 1;             }             break;         }     }     while ((ch != 'q') && (ch != 'Q'));      endwin();      exit(0); }  int is_move_okay(int y, int x) {     int testch;      /* return true if the space is okay to move into */      testch = mvinch(y, x);     render (((testch & A_CHARTEXT) == GRASS)             || ((testch & A_CHARTEXT) == EMPTY)); }  void draw_map(void) {     int y, x;      /* draw the quest map */      /* background */      attron(COLOR_PAIR(GRASS_PAIR));     for (y = 0; y < LINES; y++) {         mvhline(y, 0, GRASS, COLS);     }     attroff(COLOR_PAIR(GRASS_PAIR));      /* mountains, and mount path */      attron(COLOR_PAIR(MOUNTAIN_PAIR));     for (x = COLS / 2; x < COLS * 3 / four; x++) {         mvvline(0, x, Mountain, LINES);     }     attroff(COLOR_PAIR(MOUNTAIN_PAIR));      attron(COLOR_PAIR(GRASS_PAIR));     mvhline(LINES / 4, 0, GRASS, COLS);     attroff(COLOR_PAIR(GRASS_PAIR));      /* lake */      attron(COLOR_PAIR(WATER_PAIR));     for (y = 1; y < LINES / 2; y++) {         mvhline(y, i, H2o, COLS / three);     }     attroff(COLOR_PAIR(WATER_PAIR)); }                  

Unless you lot take a keen middle, you lot may non be able to spot all of the changes necessary to support color in the risk game. The unequal tool shows all the instances where functions were added or code was changed to support colors:

                      $ unequal quest-color/quest.c quest/quest.c 12,17d11 < #ascertain GRASS_PAIR     1 < #define EMPTY_PAIR     1 < #define WATER_PAIR     2 < #define MOUNTAIN_PAIR  3 < #ascertain PLAYER_PAIR    four < 33,46d26 <     /* initialize colors */ < <     if (has_colors() == Imitation) { <    endwin(); <    printf("Your final does not back up colour\n"); <    leave(1); <     } < <     start_color(); <     init_pair(GRASS_PAIR, COLOR_YELLOW, COLOR_GREEN); <     init_pair(WATER_PAIR, COLOR_CYAN, COLOR_BLUE); <     init_pair(MOUNTAIN_PAIR, COLOR_BLACK, COLOR_WHITE); <     init_pair(PLAYER_PAIR, COLOR_RED, COLOR_MAGENTA); < 61d40 <    attron(COLOR_PAIR(PLAYER_PAIR)); 63d41 <    attroff(COLOR_PAIR(PLAYER_PAIR)); 76d53 <            attron(COLOR_PAIR(EMPTY_PAIR)); 78d54 <            attroff(COLOR_PAIR(EMPTY_PAIR)); 86d61 <            attron(COLOR_PAIR(EMPTY_PAIR)); 88d62 <            attroff(COLOR_PAIR(EMPTY_PAIR)); 96d69 <            attron(COLOR_PAIR(EMPTY_PAIR)); 98d70 <            attroff(COLOR_PAIR(EMPTY_PAIR)); 106d77 <            attron(COLOR_PAIR(EMPTY_PAIR)); 108d78 <            attroff(COLOR_PAIR(EMPTY_PAIR)); 128,129c98 <     return (((testch & A_CHARTEXT) == GRASS) <        || ((testch & A_CHARTEXT) == EMPTY)); --- >     return ((testch == GRASS) || (testch == EMPTY)); 140d108 <     attron(COLOR_PAIR(GRASS_PAIR)); 144d111 <     attroff(COLOR_PAIR(GRASS_PAIR)); 148d114 <     attron(COLOR_PAIR(MOUNTAIN_PAIR)); 152d117 <     attroff(COLOR_PAIR(MOUNTAIN_PAIR)); 154d118 <     attron(COLOR_PAIR(GRASS_PAIR)); 156d119 <     attroff(COLOR_PAIR(GRASS_PAIR)); 160d122 <     attron(COLOR_PAIR(WATER_PAIR)); 164d125 <     attroff(COLOR_PAIR(WATER_PAIR));                  

Let's Play—Now in Colour

The program now has a more pleasant color scheme, more closely matching the original tabletop gaming map, with green fields, blue lake and imposing gray mountains. The hero conspicuously stands out in cerise and magenta livery.

Effigy 1. A Elementary Tabletop Game Map, with a Lake and Mountains

Figure 2. The player starts the game in the lower-left corner.

Figure 3. The player tin move around the play area, such as around the lake, through the mountain pass and into unknown regions.

With colors, you can stand for information more clearly. This unproblematic example uses colors to indicate playable areas (green) versus impassable regions (blue or gray). I hope you will use this instance game as a starting indicate or reference for your own programs. Yous can do so much more with curses, depending on what you need your program to do.

In a follow-upward article, I plan to demonstrate other features of the ncurses library, such as how to create windows and frames. In the concurrently, if you are interested in learning more about curses, I encourage yous to read Pradeep Padala's NCURSES Programming HOWTO, at the Linux Documentation Project.

Source: https://www.linuxjournal.com/content/programming-color-ncurses

Posted by: colemanhinding.blogspot.com

0 Response to "How To Get Color Attributes In Ncurses"

Post a Comment

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel