1 module dlangui.platforms.console.consoleapp; 2 3 public import dlangui.core.config; 4 static if (BACKEND_CONSOLE): 5 6 import dlangui.core.logger; 7 import dlangui.platforms.common.platform; 8 import dlangui.graphics.drawbuf; 9 import dlangui.graphics.fonts; 10 import dlangui.widgets.styles; 11 import dlangui.widgets.widget; 12 import dlangui.platforms.console.consolefont; 13 private import dlangui.platforms.console.dconsole; 14 15 class ConsoleWindow : Window { 16 ConsolePlatform _platform; 17 ConsoleWindow _parent; 18 this(ConsolePlatform platform, dstring caption, Window parent, uint flags) { 19 super(); 20 _platform = platform; 21 _parent = cast(ConsoleWindow)parent; 22 _dx = _platform.console.width; 23 _dy = _platform.console.height; 24 } 25 /// show window 26 override void show() { 27 if (!_mainWidget) { 28 Log.e("Window is shown without main widget"); 29 _mainWidget = new Widget(); 30 } 31 _visible = true; 32 } 33 private dstring _windowCaption; 34 /// returns window caption 35 override @property dstring windowCaption() { 36 return _windowCaption; 37 } 38 /// sets window caption 39 override @property void windowCaption(dstring caption) { 40 _windowCaption = caption; 41 } 42 /// sets window icon 43 override @property void windowIcon(DrawBufRef icon) { 44 // ignore 45 } 46 /// request window redraw 47 override void invalidate() { 48 _platform.update(); 49 } 50 /// close window 51 override void close() { 52 Log.d("ConsoleWindow.close()"); 53 _platform.closeWindow(this); 54 } 55 protected bool _visible; 56 /// returns true if window is shown 57 @property bool visible() { 58 return _visible; 59 } 60 } 61 62 class ConsolePlatform : Platform { 63 protected Console _console; 64 65 @property Console console() { return _console; } 66 67 protected ConsoleDrawBuf _drawBuf; 68 this() { 69 _console = new Console(); 70 _console.batchMode = true; 71 _console.keyEvent = &onConsoleKey; 72 _console.mouseEvent = &onConsoleMouse; 73 _console.resizeEvent = &onConsoleResize; 74 _console.inputIdleEvent = &onInputIdle; 75 _console.init(); 76 _console.setCursorType(ConsoleCursorType.Invisible); 77 _uiDialogDisplayMode = DialogDisplayMode.allTypesOfDialogsInPopup; 78 _drawBuf = new ConsoleDrawBuf(_console); 79 } 80 ~this() { 81 destroy(_drawBuf); 82 } 83 84 ConsoleWindow[] _windowList; 85 86 /** 87 * create window 88 * Args: 89 * windowCaption = window caption text 90 * parent = parent Window, or null if no parent 91 * flags = WindowFlag bit set, combination of Resizable, Modal, Fullscreen 92 * width = window width 93 * height = window height 94 * 95 * Window w/o Resizable nor Fullscreen will be created with size based on measurement of its content widget 96 */ 97 override Window createWindow(dstring windowCaption, Window parent, uint flags = WindowFlag.Resizable, uint width = 0, uint height = 0) { 98 ConsoleWindow res = new ConsoleWindow(this, windowCaption, parent, flags); 99 _windowList ~= res; 100 return res; 101 } 102 103 104 ConsoleWindow activeWindow() { 105 if (!_windowList.length) 106 return null; 107 return _windowList[$ - 1]; 108 } 109 110 @property DrawBuf drawBuf() { return _drawBuf; } 111 protected bool onConsoleKey(KeyEvent event) { 112 auto w = activeWindow; 113 if (!w) 114 return false; 115 if (w.dispatchKeyEvent(event)) { 116 _needRedraw = true; 117 return true; 118 } 119 return false; 120 } 121 122 protected bool onConsoleMouse(MouseEvent event) { 123 auto w = activeWindow; 124 if (!w) 125 return false; 126 if (w.dispatchMouseEvent(event)) { 127 _needRedraw = true; 128 return true; 129 } 130 return false; 131 } 132 133 protected bool onConsoleResize(int width, int height) { 134 drawBuf.resize(width, height); 135 foreach(w; _windowList) { 136 w.onResize(width, height); 137 } 138 _needRedraw = true; 139 return false; 140 } 141 142 protected bool _needRedraw = true; 143 void update() { 144 _needRedraw = true; 145 } 146 147 protected void redraw() { 148 if (!_needRedraw) 149 return; 150 foreach(w; _windowList) { 151 if (w.visible) { 152 _drawBuf.fillRect(Rect(0, 0, w.width, w.height), w.backgroundColor); 153 w.onDraw(_drawBuf); 154 auto caretRect = w.caretRect; 155 if ((w is activeWindow)) { 156 if (!caretRect.empty) { 157 _drawBuf.console.setCursor(caretRect.left, caretRect.top); 158 _drawBuf.console.setCursorType(w.caretReplace ? ConsoleCursorType.Replace : ConsoleCursorType.Insert); 159 } else { 160 _drawBuf.console.setCursorType(ConsoleCursorType.Invisible); 161 } 162 _drawBuf.console.setWindowCaption(w.windowCaption); 163 } 164 } 165 } 166 _needRedraw = false; 167 } 168 169 protected bool onInputIdle() { 170 checkClosedWindows(); 171 foreach(w; _windowList) { 172 w.pollTimers(); 173 w.handlePostedEvents(); 174 } 175 checkClosedWindows(); 176 redraw(); 177 _console.flush(); 178 return false; 179 } 180 181 protected Window[] _windowsToClose; 182 protected void handleCloseWindow(Window w) { 183 for (int i = 0; i < _windowList.length; i++) { 184 if (_windowList[i] is w) { 185 for (int j = i; j + 1 < _windowList.length; j++) 186 _windowList[j] = _windowList[j + 1]; 187 _windowList[$ - 1] = null; 188 _windowList.length--; 189 destroy(w); 190 return; 191 } 192 } 193 } 194 195 protected void checkClosedWindows() { 196 for (int i = 0; i < _windowsToClose.length; i++) { 197 handleCloseWindow(_windowsToClose[i]); 198 } 199 _windowsToClose.length = 0; 200 } 201 /** 202 * close window 203 * 204 * Closes window earlier created with createWindow() 205 */ 206 override void closeWindow(Window w) { 207 _windowsToClose ~= w; 208 } 209 /** 210 * Starts application message loop. 211 * 212 * When returned from this method, application is shutting down. 213 */ 214 override int enterMessageLoop() { 215 while (_console.pollInput()) { 216 if (_windowList.length == 0) 217 break; 218 } 219 // TODO 220 return 0; 221 } 222 private dstring _clipboardText; 223 /// retrieves text from clipboard (when mouseBuffer == true, use mouse selection clipboard - under linux) 224 override dstring getClipboardText(bool mouseBuffer = false) { 225 return _clipboardText; 226 } 227 /// sets text to clipboard (when mouseBuffer == true, use mouse selection clipboard - under linux) 228 override void setClipboardText(dstring text, bool mouseBuffer = false) { 229 _clipboardText = text; 230 } 231 232 /// calls request layout for all windows 233 override void requestLayout() { 234 // TODO 235 } 236 } 237 238 /// drawing buffer - image container which allows to perform some drawing operations 239 class ConsoleDrawBuf : DrawBuf { 240 241 protected Console _console; 242 @property Console console() { return _console; } 243 244 this(Console console) { 245 _console = console; 246 resetClipping(); 247 } 248 249 ~this() { 250 Log.d("Calling console.uninit"); 251 _console.uninit(); 252 } 253 254 /// returns current width 255 override @property int width() { return _console.width; } 256 /// returns current height 257 override @property int height() { return _console.height; } 258 259 /// reserved for hardware-accelerated drawing - begins drawing batch 260 override void beforeDrawing() { 261 // TODO? 262 } 263 /// reserved for hardware-accelerated drawing - ends drawing batch 264 override void afterDrawing() { 265 // TODO? 266 } 267 /// returns buffer bits per pixel 268 override @property int bpp() { return 4; } 269 // returns pointer to ARGB scanline, null if y is out of range or buffer doesn't provide access to its memory 270 //uint * scanLine(int y) { return null; } 271 /// resize buffer 272 override void resize(int width, int height) { 273 // IGNORE 274 resetClipping(); 275 } 276 277 //======================================================== 278 // Drawing methods. 279 280 /// fill the whole buffer with solid color (no clipping applied) 281 override void fill(uint color) { 282 // TODO 283 fillRect(Rect(0, 0, width, height), color); 284 } 285 286 private struct RGB { 287 int r; 288 int g; 289 int b; 290 int match(int rr, int gg, int bb) immutable { 291 int dr = rr - r; 292 int dg = gg - g; 293 int db = bb - b; 294 if (dr < 0) dr = -dr; 295 if (dg < 0) dg = -dg; 296 if (db < 0) db = -db; 297 return dr + dg + db; 298 } 299 } 300 version(Windows) { 301 // windows color table 302 static immutable RGB[16] CONSOLE_COLORS_RGB = [ 303 RGB(0,0,0), 304 RGB(0,0,128), 305 RGB(0,128,0), 306 RGB(0,128,128), 307 RGB(128,0,0), 308 RGB(128,0,128), 309 RGB(128,128,0), 310 RGB(192,192,192), 311 RGB(0x7c,0x7c,0x7c), // ligth gray 312 RGB(0,0,255), 313 RGB(0,255,0), 314 RGB(0,255,255), 315 RGB(255,0,0), 316 RGB(255,0,255), 317 RGB(255,255,0), 318 RGB(255,255,255), 319 ]; 320 } else { 321 // linux color table 322 static immutable RGB[16] CONSOLE_COLORS_RGB = [ 323 RGB(0,0,0), 324 RGB(128,0,0), 325 RGB(0,128,0), 326 RGB(128,128,0), 327 RGB(0,0,128), 328 RGB(128,0,128), 329 RGB(0,128,128), 330 RGB(192,192,192), 331 RGB(0x7c,0x7c,0x7c), // ligth gray 332 RGB(255,0,0), 333 RGB(0,255,0), 334 RGB(255,255,0), 335 RGB(0,0,255), 336 RGB(255,0,255), 337 RGB(0,255,255), 338 RGB(255,255,255), 339 ]; 340 } 341 342 static ubyte toConsoleColor(uint color, bool forBackground = false) { 343 if (forBackground && ((color >> 24) & 0xFF) >= 0x80) 344 return CONSOLE_TRANSPARENT_BACKGROUND; 345 int r = (color >> 16) & 0xFF; 346 int g = (color >> 8) & 0xFF; 347 int b = (color >> 0) & 0xFF; 348 int bestMatch = CONSOLE_COLORS_RGB[0].match(r,g,b); 349 int bestMatchIndex = 0; 350 for (int i = 1; i < 16; i++) { 351 int m = CONSOLE_COLORS_RGB[i].match(r,g,b); 352 if (m < bestMatch) { 353 bestMatch = m; 354 bestMatchIndex = i; 355 } 356 } 357 return cast(ubyte)bestMatchIndex; 358 } 359 360 361 static immutable dstring SPACE_STRING = 362 " " 363 ~ " " 364 ~ " " 365 ~ " " 366 ~ " "; 367 368 /// fill rectangle with solid color (clipping is applied) 369 override void fillRect(Rect rc, uint color) { 370 uint alpha = color >> 24; 371 if (alpha >= 128) 372 return; // transparent 373 _console.backgroundColor = toConsoleColor(color); 374 if (applyClipping(rc)) { 375 int w = rc.width; 376 foreach(y; rc.top .. rc.bottom) { 377 _console.setCursor(rc.left, y); 378 _console.writeText(SPACE_STRING[0 .. w]); 379 } 380 } 381 } 382 383 /// fill rectangle with solid color and pattern (clipping is applied) 0=solid fill, 1 = dotted 384 override void fillRectPattern(Rect rc, uint color, int pattern) { 385 // default implementation: does not support patterns 386 fillRect(rc, color); 387 } 388 389 /// draw pixel at (x, y) with specified color 390 override void drawPixel(int x, int y, uint color) { 391 // TODO 392 } 393 394 void drawChar(int x, int y, dchar ch, uint color, uint bgcolor) { 395 if (x < _clipRect.left || x >= _clipRect.right || y < _clipRect.top || y >= _clipRect.bottom) 396 return; 397 ubyte tc = toConsoleColor(color, false); 398 ubyte bc = toConsoleColor(bgcolor, true); 399 dchar[1] text; 400 text[0] = ch; 401 _console.textColor = tc; 402 _console.backgroundColor = bc; 403 _console.setCursor(x, y); 404 _console.writeText(cast(dstring)text); 405 } 406 407 /// draw 8bit alpha image - usually font glyph using specified color (clipping is applied) 408 override void drawGlyph(int x, int y, Glyph * glyph, uint color) { 409 // TODO 410 } 411 412 /// draw source buffer rectangle contents to destination buffer 413 override void drawFragment(int x, int y, DrawBuf src, Rect srcrect) { 414 // not supported 415 } 416 417 /// draw source buffer rectangle contents to destination buffer rectangle applying rescaling 418 override void drawRescaled(Rect dstrect, DrawBuf src, Rect srcrect) { 419 // not supported 420 } 421 422 override void clear() { 423 resetClipping(); 424 } 425 } 426 427 //version (none): 428 // entry point for console app 429 extern(C) int DLANGUImain(string[] args) { 430 initLogs(); 431 SCREEN_DPI = 10; 432 Platform.setInstance(new ConsolePlatform()); 433 FontManager.instance = new ConsoleFontManager(); 434 initResourceManagers(); 435 436 version (Windows) { 437 import core.sys.windows.winuser; 438 DOUBLE_CLICK_THRESHOLD_MS = GetDoubleClickTime(); 439 } 440 441 currentTheme = createDefaultTheme(); 442 Platform.instance.uiTheme = "theme_default"; 443 444 Log.i("Entering UIAppMain: ", args); 445 int result = -1; 446 try { 447 result = UIAppMain(args); 448 Log.i("UIAppMain returned ", result); 449 } catch (Exception e) { 450 Log.e("Abnormal UIAppMain termination"); 451 Log.e("UIAppMain exception: ", e); 452 } 453 454 Platform.setInstance(null); 455 456 releaseResourcesOnAppExit(); 457 458 Log.d("Exiting main"); 459 APP_IS_SHUTTING_DOWN = true; 460 461 return result; 462 }