'Why is ANSI printing the code the first time the function is called and printing colours the second time around?
I am trying to change the colour output of my printf statements in a connect 4 game. I have made a function to set the print colour and reset it. It works for most of my code but doesn't work on the first function that is called the first time it is called, but works from there out. The function in question is the first function called in the main program.
I have tried different positioning of the function, positioning it either side of where the function is called and inputting the colour codes directly into the printf function but it always fails the first (and only the first time).
#include <stdio.h>
void sprint_green();
void sprint_red();
int main_menu ( void ){
int a = 0;
char opt [20];
sprint_red();
printf("============================\n");
printf(" Welcome to Connect4!\n");
printf("============================\n");
sprint_reset();
// Continue asking for an option until a valid option (n/l/q) is entered
while (a == 0)
{
sprint_green();
printf("(N)ew game\n(L)oad game\n(Q)uit");
sprint_reset();
fgets(opt, 20, stdin);
// if 'n', return 1
if(opt[0] == 'n' || opt[0] == 'N'){
a = 1;
}
// if 'l', return 2
else if(opt[0] == 'l' || opt[0] == 'L'){
a = 2;
}
// if 'q', return -1
else if(opt[0] == 'q' || opt[0] == 'Q'){
a = -1;
printf("\nGoodbye!\n");
}
// if anything else, give error message and ask again..
else
{
printf("Invalid option\n");
}
}
system("cls");
return(a);
}
int main (void)
{
int i;
for(i = 0; i < 5; i++)
{
main_menu();
}
}
void sprint_green()
{
printf("\033[1;32m");
}
void sprint_red()
{
printf("\033[1;31m");
}
void sprint_reset()
{
printf("\033[0m");
}
First three printf statements are expected to be printed in red and the next one is to be printed in green. However the first time it is called the print the ANSI colour codes.
[1;31m============================
Welcome to Connect4!
============================
[0m[1;32m(N)ew game
(L)oad game
(Q)uit[0m
However, after running the game once and starting again (without closing the terminal) these functions work as expected.
Solution 1:[1]
The windows command shell does not start vt100 emulation by default. As @Bodo pointed out in his answer, this can be triggered by running the shell command cls
. However, technically the shell command need not be cls
or even a valid command at all. You can trigger it with just an empty system(" ")
call! This is also portable because it does nothing except start a shell instance momentarily and kill it. So it should work equally fine in Windows or Linux environments.
Code with the fix:
#include <stdio.h>
#include <stdlib.h> // for system()
void sprint_green();
void sprint_red();
int main_menu ( void ){
int a = 0;
char opt [20];
system(" "); // Trigger ANSI emulation
sprint_red();
printf("============================\n");
printf(" Welcome to Connect4!\n");
printf("============================\n");
sprint_reset();
// Continue asking for an option until a valid option (n/l/q) is entered
while (a == 0)
{
sprint_green();
printf("(N)ew game\n(L)oad game\n(Q)uit");
sprint_reset();
fgets(opt, 20, stdin);
// if 'n', return 1
if(opt[0] == 'n' || opt[0] == 'N'){
a = 1;
}
// if 'l', return 2
else if(opt[0] == 'l' || opt[0] == 'L'){
a = 2;
}
// if 'q', return -1
else if(opt[0] == 'q' || opt[0] == 'Q'){
a = -1;
printf("\nGoodbye!\n");
}
// if anything else, give error message and ask again..
else
{
printf("Invalid option\n");
}
}
system("cls");
return(a);
}
int main (void)
{
int i;
for(i = 0; i < 5; i++)
{
main_menu();
}
}
void sprint_green()
{
printf("\033[1;32m");
}
void sprint_red()
{
printf("\033[1;31m");
}
void sprint_reset()
{
printf("\033[0m");
}
Solution 2:[2]
system("cls");
works, but it clears the screen.
But if you call it only once at startup it seems to fix it for the entire application
Solution 3:[3]
According to
https://solarianprogrammer.com/2019/04/08/c-programming-ansi-escape-codes-windows-macos-linux-terminals/
and
https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences?redirectedfrom=MSDN
the Windows console must be put into an ANSI escape mode to be able to process escape sequences instead of printing them.
Apparently executing the cls
command seems to do this.
You can set the terminal mode programmatically using GetConsoleMode()
and SetConsoleMode()
by setting the flag ENABLE_VIRTUAL_TERMINAL_PROCESSING
Code example copied from the referenced Microsoft page:
#include <stdio.h>
#include <wchar.h>
#include <windows.h>
int main()
{
// Set output mode to handle virtual terminal sequences
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
if (hOut == INVALID_HANDLE_VALUE)
{
return GetLastError();
}
DWORD dwMode = 0;
if (!GetConsoleMode(hOut, &dwMode))
{
return GetLastError();
}
dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
if (!SetConsoleMode(hOut, dwMode))
{
return GetLastError();
}
// Try some Set Graphics Rendition (SGR) terminal escape sequences
wprintf(L"\x1b[31mThis text has a red foreground using SGR.31.\r\n");
wprintf(L"\x1b[1mThis text has a bright (bold) red foreground using SGR.1 to affect the previous color setting.\r\n");
/* ... more examples removed */
return 0;
}
Obviously this solution is for Windows only, but running system("cls")
is also non-portable.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 | Obsidian |
Solution 3 | Bodo |