1
/**
2
 * Control the various text mode attributes, such as color, when writing text
3
 * to the console.
4
 *
5
 * Copyright:   Copyright (C) 1999-2020 by The D Language Foundation, All Rights Reserved
6
 * Authors:     $(LINK2 http://www.digitalmars.com, Walter Bright)
7
 * License:     $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
8
 * Source:      $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/console.d, _console.d)
9
 * Documentation:  https://dlang.org/phobos/dmd_console.html
10
 * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/console.d
11
 */
12

13
module dmd.console;
14

15
import core.stdc.stdio;
16
extern (C) int isatty(int) nothrow;
17

18

19
enum Color : int
20
{
21
    black         = 0,
22
    red           = 1,
23
    green         = 2,
24
    blue          = 4,
25
    yellow        = red | green,
26
    magenta       = red | blue,
27
    cyan          = green | blue,
28
    lightGray     = red | green | blue,
29
    bright        = 8,
30
    darkGray      = bright | black,
31
    brightRed     = bright | red,
32
    brightGreen   = bright | green,
33
    brightBlue    = bright | blue,
34
    brightYellow  = bright | yellow,
35
    brightMagenta = bright | magenta,
36
    brightCyan    = bright | cyan,
37
    white         = bright | lightGray,
38
}
39

40
struct Console
41
{
42
  nothrow:
43

44
    version (Windows)
45
    {
46
        import core.sys.windows.winbase;
47
        import core.sys.windows.wincon;
48
        import core.sys.windows.windef;
49

50
      private:
51
        CONSOLE_SCREEN_BUFFER_INFO sbi;
52
        HANDLE handle;
53
        FILE* _fp;
54

55
      public:
56

57
        @property FILE* fp() { return _fp; }
58

59
        /**
60
         Tries to detect whether DMD has been invoked from a terminal.
61
         Returns: `true` if a terminal has been detected, `false` otherwise
62
         */
63
        static bool detectTerminal()
64
        {
65
            auto h = GetStdHandle(STD_OUTPUT_HANDLE);
66
            CONSOLE_SCREEN_BUFFER_INFO sbi;
67
            if (GetConsoleScreenBufferInfo(h, &sbi) == 0) // get initial state of console
68
                return false; // no terminal detected
69

70
            version (CRuntime_DigitalMars)
71
            {
72
                return isatty(stdout._file) != 0;
73
            }
74
            else version (CRuntime_Microsoft)
75
            {
76
                return isatty(fileno(stdout)) != 0;
77
            }
78
            else
79
            {
80
                static assert(0, "Unsupported Windows runtime.");
81
            }
82
        }
83

84
        /*********************************
85
         * Create an instance of Console connected to stream fp.
86
         * Params:
87
         *      fp = io stream
88
         * Returns:
89
         *      pointer to created Console
90
         *      null if failed
91
         */
92
        static Console* create(FILE* fp)
93
        {
94
            /* Determine if stream fp is a console
95
             */
96
            version (CRuntime_DigitalMars)
97
            {
98
                if (!isatty(fp._file))
99
                    return null;
100
            }
101
            else version (CRuntime_Microsoft)
102
            {
103
                if (!isatty(fileno(fp)))
104
                    return null;
105
            }
106
            else
107
            {
108
                return null;
109
            }
110

111
            DWORD nStdHandle;
112
            if (fp == stdout)
113
                nStdHandle = STD_OUTPUT_HANDLE;
114
            else if (fp == stderr)
115
                nStdHandle = STD_ERROR_HANDLE;
116
            else
117
                return null;
118

119
            auto h = GetStdHandle(nStdHandle);
120
            CONSOLE_SCREEN_BUFFER_INFO sbi;
121
            if (GetConsoleScreenBufferInfo(h, &sbi) == 0) // get initial state of console
122
                return null;
123

124
            auto c = new Console();
125
            c._fp = fp;
126
            c.handle = h;
127
            c.sbi = sbi;
128
            return c;
129
        }
130

131
        /*******************
132
         * Turn on/off intensity.
133
         * Params:
134
         *      bright = turn it on
135
         */
136
        void setColorBright(bool bright)
137
        {
138
            SetConsoleTextAttribute(handle, sbi.wAttributes | (bright ? FOREGROUND_INTENSITY : 0));
139
        }
140

141
        /***************************
142
         * Set color and intensity.
143
         * Params:
144
         *      color = the color
145
         */
146
        void setColor(Color color)
147
        {
148
            enum FOREGROUND_WHITE = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
149
            WORD attr = sbi.wAttributes;
150
            attr = (attr & ~(FOREGROUND_WHITE | FOREGROUND_INTENSITY)) |
151
                   ((color & Color.red)    ? FOREGROUND_RED   : 0) |
152
                   ((color & Color.green)  ? FOREGROUND_GREEN : 0) |
153
                   ((color & Color.blue)   ? FOREGROUND_BLUE  : 0) |
154
                   ((color & Color.bright) ? FOREGROUND_INTENSITY : 0);
155
            SetConsoleTextAttribute(handle, attr);
156
        }
157

158
        /******************
159
         * Reset console attributes to what they were
160
         * when create() was called.
161
         */
162
        void resetColor()
163
        {
164
            SetConsoleTextAttribute(handle, sbi.wAttributes);
165
        }
166
    }
167
    else version (Posix)
168
    {
169
        /* The ANSI escape codes are used.
170
         * https://en.wikipedia.org/wiki/ANSI_escape_code
171
         * Foreground colors: 30..37
172
         * Background colors: 40..47
173
         * Attributes:
174
         *  0: reset all attributes
175
         *  1: high intensity
176
         *  2: low intensity
177
         *  3: italic
178
         *  4: single line underscore
179
         *  5: slow blink
180
         *  6: fast blink
181
         *  7: reverse video
182
         *  8: hidden
183
         */
184

185
        import core.sys.posix.unistd;
186

187
      private:
188
        FILE* _fp;
189

190
      public:
191

192 1
        @property FILE* fp() { return _fp; }
193
        /**
194
         Tries to detect whether DMD has been invoked from a terminal.
195
         Returns: `true` if a terminal has been detect, `false` otherwise
196
         */
197
        static bool detectTerminal()
198
        {
199
            import core.stdc.stdlib : getenv;
200 1
            const(char)* term = getenv("TERM");
201
            import core.stdc.string : strcmp;
202 1
            return isatty(STDERR_FILENO) && term && term[0] && strcmp(term, "dumb") != 0;
203
        }
204

205
        static Console* create(FILE* fp)
206
        {
207 1
            auto c = new Console();
208 1
            c._fp = fp;
209 1
            return c;
210
        }
211

212
        void setColorBright(bool bright)
213
        {
214 1
            fprintf(_fp, "\033[%dm", bright);
215
        }
216

217
        void setColor(Color color)
218
        {
219 1
            fprintf(_fp, "\033[%d;%dm", color & Color.bright ? 1 : 0, 30 + (color & ~Color.bright));
220
        }
221

222
        void resetColor()
223
        {
224 1
            fputs("\033[m", _fp);
225
        }
226
    }
227
    else
228
    {
229
        @property FILE* fp() { assert(0); }
230

231
        static Console* create(FILE* fp)
232
        {
233
            return null;
234
        }
235

236
        void setColorBright(bool bright)
237
        {
238
            assert(0);
239
        }
240

241
        void setColor(Color color)
242
        {
243
            assert(0);
244
        }
245

246
        void resetColor()
247
        {
248
            assert(0);
249
        }
250
    }
251

252
}

Read our documentation on viewing source code .

Loading