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