-1

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;
}
olivvv
  • 3
  • 1
  • No windows works differently, there is no cross platform compatibility here. Solutions are easy to find on google. – Pepijn Kramer Jun 03 '23 at 19:26
  • Those are not linux codes, they are [ANSI escape sequences](https://en.wikipedia.org/wiki/ANSI_escape_code) How well they work on Windows depends on the console being used and the options that are set. In other words, they are not terribly reliable on Windows. – Retired Ninja Jun 03 '23 at 19:26
  • 1
    @RetiredNinja: how well they work on anything depends on what console is being used, and how well it supports ANSI escape sequences. – Jerry Coffin Jun 03 '23 at 19:28
  • https://stackoverflow.com/questions/2616906/how-do-i-output-coloured-text-to-a-linux-terminal no idea, why the question was reopened. – πάντα ῥεῖ Jun 03 '23 at 19:33
  • 2
    It was reopened because the nominated duplicate wasn't even close to being a duplicate. If you'd bothered to read the question, you'd know the OP is already well aware that these codes work on Linux. The question was about how reliable they are on Windows, not about Linux at all. – Jerry Coffin Jun 03 '23 at 19:36

1 Answers1

0

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";
}
Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111