Search code examples
c++colors

Displaying colored text in windows console by linux codes (C++)


I'm writing game for project in VS Community 2017 in c++. I can see that coloring text works well in windows terminal, but I'm not sure if it'll be working on every windows compiler? Is there any safer way to print colored text?

Example code:

#include <iostream>
#include <Windows.h>

using namespace std;

int main()
{
    cout<<"\033[34m"<<"Hello World in blue\n";
    cout<<"\033[31m"<<"Hello World in red\n";
    cout<<"\033[35m"<<"Hello World in purple\n";
    return 0;
}

Solution

  • The compiler has little (if anything) to do with this. Your code just sends data. It's up to the terminal program to do something with that data.

    There are some terminal programs that do, and others that don't interpret them well. Prior to Windows 10, the default Windows console didn't, so if you care about supporting older versions of Windows, it's not a great choice.

    So, it depends on what you about. If you want portability to (current) Windows and Linux, what you're doing is fine. If you want portability to older versions of Windows...not so much.

    If you need something that works for older Windows and don't care about Linux, you can Windows' console functions (e.g., WriteConsoleOutputAttribute or FillConsoleOutputAttribute).

    My own advice would be to use some manipulators, so you'd do something like:

    console << blue << "Hello world in blue\n";
    console << red << "Hello world in red\n";
    

    ...and when/if you need to move code to a different platform, you can rewrite just those manipulators. For Linux can current Windows you can send the ANSI escape sequences you already know about.

    Supporting manipulators like this on older versions of Windows isn't trivial, at least using actual cout. You need a Windows handle to the console. But it's pretty easy to do that, with an ostream, so writing to it works about like you'd normally expect.

    For example:

    // winbuf.hpp
    #pragma once
    #include <ios>
    #include <ostream>
    #include <windows.h>
    
    class WinBuf : public std::streambuf
    {
        HANDLE h;
    
    public:
        WinBuf(HANDLE h) : h(h) {}
        WinBuf(WinBuf const &) = delete;
        WinBuf &operator=(WinBuf const &) = delete;
    
        HANDLE handle() const { return h; }
    
    protected:
        virtual int_type overflow(int_type c) override
        {
            if (c != EOF)
            {
                DWORD written;
                WriteConsole(h, &c, 1, &written, nullptr);
            }
            return c;
        }
    
        virtual std::streamsize xsputn(char_type const *s, std::streamsize count) override
        {
            DWORD written;
            WriteConsole(h, s, DWORD(count), &written, nullptr);
            return written;
        }
    };
    
    class WinStream : public virtual std::ostream
    {
        WinBuf buf;
    
    public:
        WinStream(HANDLE dest = GetStdHandle(STD_OUTPUT_HANDLE))
            : buf(dest), std::ostream(&buf)
        {
        }
    
        WinBuf *rdbuf() { return &buf; }
    };
    
    class SetAttr
    {
        WORD attr;
    
    public:
        SetAttr(WORD attr) : attr(attr) {}
    
        friend WinStream &operator<<(WinStream &w, SetAttr const &c)
        {
            WinBuf *buf = w.rdbuf();
            auto h = buf->handle();
            SetConsoleTextAttribute(h, c.attr);
            return w;
        }
    
        SetAttr operator|(SetAttr const &r)
        {
            return SetAttr(attr | r.attr);
        }
    };
    
    class gotoxy
    {
        COORD coord;
    
    public:
        gotoxy(SHORT x, SHORT y) : coord{.X = x, .Y = y} {}
    
        friend WinStream &operator<<(WinStream &w, gotoxy const &pos)
        {
            WinBuf *buf = w.rdbuf();
            auto h = buf->handle();
            SetConsoleCursorPosition(h, pos.coord);
            return w;
        }
    };
    
    class cls
    {
        char ch;
    
    public:
        cls(char ch = ' ') : ch(ch) {}
    
        friend WinStream &operator<<(WinStream &os, cls const &c)
        {
            COORD tl = {0, 0};
            CONSOLE_SCREEN_BUFFER_INFO s;
            WinBuf *w = os.rdbuf();
            HANDLE console = w->handle();
    
            GetConsoleScreenBufferInfo(console, &s);
            DWORD written, cells = s.dwSize.X * s.dwSize.Y;
            FillConsoleOutputCharacter(console, c.ch, cells, tl, &written);
            FillConsoleOutputAttribute(console, s.wAttributes, cells, tl, &written);
            SetConsoleCursorPosition(console, tl);
            return os;
        }
    };
    
    extern SetAttr red;
    extern SetAttr green;
    extern SetAttr blue;
    extern SetAttr intense;
    
    extern SetAttr red_back;
    extern SetAttr blue_back;
    extern SetAttr green_back;
    extern SetAttr intense_back;
    

    Along with:

    // winbuf.cpp
    #include "winbuf.hpp"
    
    #define WIN32_LEAN_AND_MEAN
    #include <Windows.h>
    
    SetAttr red { FOREGROUND_RED };
    SetAttr green { FOREGROUND_GREEN };
    SetAttr blue { FOREGROUND_BLUE };
    SetAttr intense { FOREGROUND_INTENSITY };
    
    SetAttr red_back { BACKGROUND_RED };
    SetAttr blue_back { BACKGROUND_BLUE };
    SetAttr green_back { BACKGROUND_GREEN };
    SetAttr intense_back { BACKGROUND_INTENSITY };
    

    ...and a quick demo:

    #include "winbuf.hpp"
    
    int main() {
        WinStream w;
    
        auto attrib = intense | blue_back;
    
        w << attrib << cls() << "Some stuff";
        w << gotoxy(0, 4) << (green | blue_back) << "This should be green";
        w << gotoxy(0, 5) << attrib << "And this should be grey";
    }