1 module tagion.utils.Term;
2 
3 import std.format;
4 
5 import std.meta : AliasSeq, staticSort;
6 
7 //import std.algorithm : sort;
8 
9 enum {
10     BLACK = Color.Black.code,
11     RED = Color.Red.code,
12     GREEN = Color.Green.code,
13     YELLOW = Color.Yellow.code,
14     BLUE = Color.Blue.code,
15     MAGENTA = Color.Magenta.code,
16     CYAN = Color.Cyan.code,
17     WHITE = Color.White.code,
18 
19     BOLD = Mode.Bold.code,
20 
21     BACKGOUND_BLACK = Color.Black.code(true),
22     BACKGOUND_RED = Color.Red.code(true),
23     BACKGOUND_GREEN = Color.Green.code(true),
24     BACKGOUND_YELLOW = Color.Yellow.code(true),
25     BACKGOUND_BLUE = Color.Blue.code(true),
26     BACKGOUND_MAGENTA = Color.Magenta.code(true),
27     BACKGOUND_CYAN = Color.Cyan.code(true),
28     BACKGOUND_WHITE = Color.White.code(true),
29     BACKGOUND_RESET = Color.Reset.code(true),
30 
31     RESET = Color.Reset.code,
32     CLEARSCREEN = Cursor.ClearScreen.code(2),
33     CLEARDOWN = Cursor.ClearScreen.code(0),
34     CLEARUP = Cursor.ClearScreen.code(1),
35     CLEARLINE = Cursor.ClearLine.code(2),
36     CLEAREOL = Cursor.ClearLine.code(0),
37 
38     CR = "\x13",
39     NEXTLINE = Cursor.NextLine.code(0),
40     HOME = "\u001b[f",
41 }
42 
43 enum Color {
44     Black,
45     Red,
46     Green,
47     Yellow,
48     Blue,
49     Magenta,
50     Cyan,
51     White,
52     Reset,
53 }
54 
55 string code(
56         immutable Color c,
57         immutable bool background = false,
58         immutable bool bright = false) {
59     if (c is Color.Reset) {
60         return "\u001b[0m";
61     }
62     else {
63         immutable background_color = (background) ? "4" : "3";
64         immutable bright_color = (bright) ? "m" : ";1m";
65         return format("\u001b[%s%d%s", background_color, c, bright_color);
66     }
67     assert(0);
68 }
69 
70 enum Cursor : char {
71     Up = 'A', /// Moves cursor up by n
72     Down = 'B', /// Moves cursor down by n
73     Right = 'C', /// Moves cursor right by n
74     Left = 'D', /// Moves cursor left by n
75     NextLine = 'E', /// Moves cursor to beginning of line n lines down
76     PrevLine = 'F', /// Moves cursor to beginning of line n lines down
77     SetColumn = 'G', /// Moves cursor to column n
78     ClearScreen = 'J', /// clears the screen
79     ClearLine = 'K' /// clears the current line
80 }
81 
82 string code(immutable Cursor c, immutable uint n = 1) {
83     return format("\u001b[%d%s", n, char(c));
84 }
85 
86 string linePos(const size_t pos) {
87     return format("\r\x1B[%dC", pos);
88 }
89 
90 enum Mode {
91     None = 0, /// All attributes off
92     Bold = 1, /// Bold on
93     Underscore = 4, /// Underscore (on monochrome display adapter only)
94     Blink = 5, /// Blink on
95     Reverse = 7, /// Reverse video on
96     Concealed = 8, /// Concealed on
97 }
98 
99 string code(immutable Mode m) {
100     return format("\u001b[%dm", m);
101 }
102 
103 string setCursor(immutable uint row, immutable uint column) {
104     return format("\u001b[%d;%dH", row, column);
105 }
106 
107 enum saveCursorPos = "\u001b[s"; /// Saves the current cursor position
108 enum restoreCursorPos = "\u001b[u"; /// Restores the cursor to the last saved position
109 
110 version (MOBILE) {
111 }
112 else {
113     private import core.stdc.stdio;
114     private import core.sys.posix.termios;
115 
116     extern (C) void cfmakeraw(termios* termios_p);
117 
118     struct KeyStroke {
119 
120         termios ostate; /* saved tty state */
121         termios nstate; /* values for editor mode */
122 
123         int get()
124         out (ret) {
125             import std.stdio;
126 
127             writef("%d ", ret);
128 
129         }
130         do {
131             // Open stdin in raw mode
132             // Adjust output channel
133             tcgetattr(1, &ostate); // save old state
134             tcgetattr(1, &nstate); // get base of new state
135             cfmakeraw(&nstate);
136             tcsetattr(1, TCSADRAIN, &nstate); // set mode
137             scope (exit) {
138                 tcsetattr(1, TCSADRAIN, &ostate); // return to original mode
139             }
140             return fgetc(stdin);
141         }
142 
143         enum KeyCode {
144             NONE,
145             UP,
146             DOWN,
147             LEFT,
148             RIGHT,
149             HOME,
150             END,
151             PAGEUP,
152             PAGEDOWN,
153             ENTER,
154             DELETE,
155             BACKSPACE,
156             F1,
157             F2,
158             F3,
159             F4,
160             F5,
161             F6,
162             F7,
163             F8,
164             F9,
165             F10,
166             F11,
167             F12,
168             CTRL_A,
169             CTRL_B,
170             CTRL_C,
171             CTRL_D,
172         }
173 
174         struct KeyStrain {
175             KeyCode code;
176             int[] branch;
177             int opComp(const KeyStrain b) const {
178                 return branch < b.branch;
179             }
180         }
181 
182         enum strain = [
183             KeyStrain(KeyCode.UP, [27, 91, 65]),
184             KeyStrain(KeyCode.DOWN, [27, 91, 66]),
185             KeyStrain(KeyCode.RIGHT, [27, 91, 67]),
186             KeyStrain(KeyCode.LEFT, [27, 91, 68]),
187             KeyStrain(KeyCode.HOME, [27, 91, 49, 59, 50, 72]),
188             KeyStrain(KeyCode.END, [27, 91, 49, 59, 50, 70]),
189             KeyStrain(KeyCode.PAGEDOWN, [27, 91, 54, 126]),
190             KeyStrain(KeyCode.PAGEUP, [27, 91, 53, 126]),
191             KeyStrain(KeyCode.DELETE, [27, 91, 51, 126]),
192             KeyStrain(KeyCode.F1, [27, 79, 80]),
193             KeyStrain(KeyCode.F2, [27, 79, 81]),
194             KeyStrain(KeyCode.F3, [27, 79, 82]),
195             KeyStrain(KeyCode.F4, [27, 79, 83]),
196             KeyStrain(KeyCode.F5, [27, 91, 49, 53, 126]),
197             KeyStrain(KeyCode.F6, [27, 91, 49, 55, 126]),
198             KeyStrain(KeyCode.F7, [27, 91, 49, 56, 126]),
199             KeyStrain(KeyCode.F8, [27, 91, 49, 57, 126]),
200             KeyStrain(KeyCode.F9, [27, 91, 50, 48, 126]),
201             KeyStrain(KeyCode.F10, [27, 91, 50, 49, 126]),
202             KeyStrain(KeyCode.F11, [27, 91, 50, 51, 126]),
203             KeyStrain(KeyCode.F12, [27, 91, 50, 52, 126]),
204             KeyStrain(KeyCode.CTRL_A, [1]),
205             KeyStrain(KeyCode.CTRL_B, [2]),
206             KeyStrain(KeyCode.CTRL_C, [3]),
207             KeyStrain(KeyCode.CTRL_D, [4]),
208             KeyStrain(KeyCode.ENTER, [13]),
209             KeyStrain(KeyCode.BACKSPACE, [127]),
210         ];
211 
212         KeyCode getKey(ref int ch) {
213             import std.algorithm;
214             import std.array;
215 
216             enum StaticComp(KeyStrain a, KeyStrain b) = a.branch < b.branch;
217 
218             enum sorted_strain = strain.array.sort!((a, b) => a.branch < b.branch); //staticSort!(StaticComp, strain);
219             KeyCode select(uint index = 0, uint pos = 0)(ref int ch) {
220                 static if (index < sorted_strain.length) {
221                     static if (pos < sorted_strain[index].branch.length) {
222                         if (ch == sorted_strain[index].branch[pos]) {
223                             static if (pos + 1 is sorted_strain[index].branch.length) {
224                                 return sorted_strain[index].code;
225                             }
226                             else {
227                                 ch = get;
228                                 return select!(index, pos + 1)(ch);
229                             }
230                         }
231                         else if (ch > sorted_strain[index].branch[pos]) {
232                             return select!(index + 1, pos)(ch);
233                         }
234                         else {
235                             return KeyCode.NONE;
236                         }
237                     }
238                     else {
239                         return select!(index + 1, pos)(ch);
240                     }
241                 }
242                 else {
243                     return KeyCode.NONE;
244                 }
245             }
246 
247             ch = get;
248             return select(ch);
249         }
250     }
251 }