1 /// contains the terminal object class 2 module ysge.ui.terminal; 3 4 import std.stdio; 5 import std.string; 6 import std.process; 7 import std.algorithm; 8 import core.stdc.stdlib; 9 import bindbc.sdl; 10 import ysge.project; 11 12 /// colours for the default pallette 13 enum Colour { 14 Black = 0, 15 Red = 1, 16 Green = 2, 17 Yellow = 3, 18 Blue = 4, 19 Purple = 5, 20 Cyan = 6, 21 White = 7, 22 Grey = 8, 23 BrightRed = 9, 24 BrightGreen = 10, 25 BrightYellow = 11, 26 BrightBlue = 12, 27 BrightPurple = 13, 28 BrightCyan = 14, 29 BrightWhite = 15 30 } 31 32 /// attributes for each character in the text buffer 33 struct Attributes { 34 ubyte fg = 7; /// foreground colour 35 ubyte bg = 0; /// background colour 36 } 37 38 /// cell structure (1 text buffer character) 39 struct Cell { 40 char ch = ' '; 41 Attributes attr; 42 43 /// creates a cell from a character with default attributes 44 static Cell FromChar(char ch) { 45 Cell ret; 46 Attributes attr; 47 48 ret.attr = attr; 49 ret.ch = ch; 50 51 return ret; 52 } 53 } 54 55 /// text screen object class 56 class TextScreen : UIElement { 57 Cell[][] cells; 58 Vec2!int cellSize; 59 SDL_Color[] palette; 60 SDL_Texture*[256] characters; 61 62 /// initialises text screen with the default palette 63 /// and also creates the characters 64 this(Project parent) { 65 // default pallette 66 palette = [ 67 // https://gogh-co.github.io/Gogh/ 68 // Pro colour scheme 69 70 /* 0 */ HexToColour(0x000000), 71 /* 1 */ HexToColour(0x990000), 72 /* 2 */ HexToColour(0x00A600), 73 /* 3 */ HexToColour(0x999900), 74 /* 4 */ HexToColour(0x2009DB), 75 /* 5 */ HexToColour(0xB200B2), 76 /* 6 */ HexToColour(0x00A6B2), 77 /* 7 */ HexToColour(0xBFBFBF), 78 /* 8 */ HexToColour(0x666666), 79 /* 9 */ HexToColour(0xE50000), 80 /* A */ HexToColour(0x00D900), 81 /* B */ HexToColour(0xE5E500), 82 /* C */ HexToColour(0x0000FF), 83 /* D */ HexToColour(0xE500E5), 84 /* E */ HexToColour(0x00E5E5), 85 /* F */ HexToColour(0xE5E5E5) 86 ]; 87 88 foreach (i, ref texture ; characters) { 89 size_t[] dontRender = [0, 173]; 90 if (dontRender.canFind(i)) { 91 texture = null; 92 continue; 93 } 94 95 auto colour = SDL_Colour(255, 255, 255, 255); 96 97 string str; 98 str ~= cast(char) i; 99 100 SDL_Surface* textSurface = TTF_RenderText_Solid( 101 parent.font, toStringz(str), colour 102 ); 103 104 if (textSurface is null) { 105 stderr.writefln( 106 "Failed to render text: %s", fromStringz(TTF_GetError()) 107 ); 108 exit(1); 109 } 110 111 texture = SDL_CreateTextureFromSurface(parent.renderer, textSurface); 112 if (texture is null) { 113 stderr.writefln( 114 "Failed to create texture: %s", fromStringz(TTF_GetError()) 115 ); 116 exit(1); 117 } 118 } 119 } 120 121 /// returns the size of the text buffer 122 Vec2!size_t GetSize() { 123 return Vec2!size_t(cells[0].length, cells.length); 124 } 125 126 /// sets the size of the text buffer 127 void SetSize(Vec2!size_t size) { 128 auto newCells = new Cell[][](size.y, size.x); 129 130 foreach (i, ref line ; cells) { 131 foreach (j, ref cell ; line) { 132 newCells[i][j] = cell; 133 } 134 } 135 136 cells = newCells; 137 } 138 139 /// sets a character in the text buffer 140 void SetCharacter( 141 Vec2!size_t pos, char ch, ubyte fg = Colour.White, ubyte bg = Colour.Black 142 ) { 143 try { 144 cells[pos.y][pos.x] = Cell(ch, Attributes(fg, bg)); 145 } 146 catch (Throwable) { 147 return; 148 } 149 } 150 151 /// adds characters starting from pos in the text buffer 152 void WriteString( 153 Vec2!size_t pos, string str, ubyte fg = Colour.White, ubyte bg = Colour.Black 154 ) { 155 foreach (i, ref ch ; str) { 156 SetCharacter(Vec2!size_t(pos.x + i, pos.y), ch, fg, bg); 157 } 158 } 159 160 /// writes a string centered horizontally 161 void WriteStringCentered( 162 size_t yPos, string str, ubyte fg = Colour.White, ubyte bg = Colour.Black 163 ) { 164 Vec2!size_t pos; 165 pos.y = yPos; 166 pos.x = (GetSize().x / 2) - (str.length / 2); 167 168 WriteString(pos, str, fg, bg); 169 } 170 171 /// writes multiple lines in the text buffer 172 void WriteStringLines( 173 Vec2!size_t pos, string[] strings, 174 ubyte fg = Colour.White, ubyte bg = Colour.Black 175 ) { 176 Vec2!size_t ipos = pos; 177 178 for (size_t i = 0; i < strings.length; ++ i, ++ ipos.y) { 179 WriteString(ipos, strings[i], fg, bg); 180 } 181 } 182 183 /// writes multiple horizontally centered lines 184 void WriteStringLinesCentered( 185 size_t yPos, string[] strings, ubyte fg = Colour.White, 186 ubyte bg = Colour.Black, 187 ) { 188 size_t iy = yPos; 189 190 for (size_t i = 0; i < strings.length; ++ i, ++ iy) { 191 WriteStringCentered(iy, strings[i], fg, bg); 192 } 193 } 194 195 /// draws a horizontal line in the text buffer 196 void HorizontalLine( 197 Vec2!size_t start, size_t length, char ch, 198 ubyte fg = Colour.White, ubyte bg = Colour.Black 199 ) { 200 for (size_t x = start.x; x < start.x + length; ++ x) { 201 SetCharacter(Vec2!size_t(x, start.y), ch, fg, bg); 202 } 203 } 204 205 /// draws a vertical line in the text buffer 206 void VerticalLine( 207 Vec2!size_t start, size_t length, char ch, 208 ubyte fg = Colour.White, ubyte bg = Colour.Black 209 ) { 210 for (size_t y = start.y; y < start.y + length; ++ y) { 211 SetCharacter(Vec2!size_t(start.x, y), ch, fg, bg); 212 } 213 } 214 215 /// sets a cell in the text buffer to the given cell 216 void SetCell(Vec2!size_t pos, Cell cell) { 217 try { 218 cells[pos.y][pos.x] = cell; 219 } 220 catch (Throwable) { 221 222 } 223 } 224 225 /// fills the text buffer with the given character 226 void Clear(char ch, ubyte fg = Colour.White, ubyte bg = Colour.Black) { 227 foreach (y, ref line ; cells) { 228 foreach (x, ref cell ; line) { 229 cell.ch = ch; 230 cell.attr.fg = fg; 231 cell.attr.bg = bg; 232 } 233 } 234 } 235 236 /// fills a rectangle 237 void FillRect( 238 SDL_Rect rect, char ch, ubyte fg = Colour.White, ubyte bg = Colour.Black 239 ) { 240 for (size_t i = rect.y; i < rect.y + rect.h; ++ i) { 241 for (size_t j = rect.x; j < rect.x + rect.w; ++ j) { 242 cells[i][j] = Cell(ch, Attributes(fg, bg)); 243 } 244 } 245 } 246 247 /// draws the outline of a rectangle 248 void DrawBox( 249 SDL_Rect rect, ubyte fg = Colour.White, ubyte bg = Colour.Black 250 ) { 251 for (size_t i = rect.y + 1; i < rect.y + rect.h - 1; ++ i) { 252 Cell cell = Cell(0xB3, Attributes(fg, bg)); 253 254 cells[i][rect.x] = cell; 255 cells[i][rect.x + rect.w - 1] = cell; 256 } 257 258 for (size_t i = rect.x + 1; i < rect.x + rect.w - 1; ++ i) { 259 Cell cell = Cell(0xC4, Attributes(fg, bg)); 260 261 cells[rect.y][i] = cell; 262 cells[rect.y + rect.h - 1][i] = cell; 263 } 264 265 cells[rect.y][rect.x] = Cell(0xDA, Attributes(fg, bg)); 266 cells[rect.y + rect.h - 1][rect.x] = Cell(0xC0, Attributes(fg, bg)); 267 cells[rect.y][rect.x + rect.w - 1] = Cell(0xBF, Attributes(fg, bg)); 268 269 cells[rect.y + rect.h - 1][rect.x + rect.w - 1] = Cell( 270 0xD9, Attributes(fg, bg) 271 ); 272 } 273 274 override bool HandleEvent(Project parent, SDL_Event e) { 275 return false; 276 } 277 278 override void Render(Project parent) { 279 foreach (i, ref line ; cells) { 280 foreach (j, ch ; line) { 281 auto rect = SDL_Rect( 282 cast(int) j * cellSize.x, 283 cast(int) i * cellSize.y, 284 cellSize.x, 285 cellSize.y 286 ); 287 288 SDL_Color fg = palette[ch.attr.fg]; 289 SDL_Color bg = palette[ch.attr.bg]; 290 291 SDL_SetRenderDrawColor(parent.renderer, bg.r, bg.g, bg.b, 255); 292 SDL_RenderFillRect(parent.renderer, &rect); 293 294 if (ch.ch != ' ') { 295 /*text.DrawCharacter( 296 video.renderer, ch.ch, Vec2!int(rect.x, rect.y), fg 297 );*/ 298 299 if (characters[ch.ch] is null) { 300 continue; 301 } 302 303 SDL_RenderCopy( 304 parent.renderer, characters[ch.ch], null, &rect 305 ); 306 } 307 } 308 } 309 } 310 } 311