/* * © 2021, Chris Harlow, Neil McKechnie. All rights reserved. * * This file is part of CommandStation-EX * * This is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * It is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with CommandStation. If not, see . */ // CAUTION: the device dependent parts of this class are created in the .ini // using LCD_Implementation.h /* The strategy for drawing the screen is as follows. * 1) There are up to eight rows of text to be displayed. * 2) Blank rows of text are ignored. * 3) If there are more non-blank rows than screen lines, * then all of the rows are displayed, with the rest of the * screen being blank. * 4) If there are fewer non-blank rows than screen lines, * then a scrolling strategy is adopted so that, on each screen * refresh, a different subset of the rows is presented. * 5) On each entry into loop2(), a single operation is sent to the * screen; this may be a position command or a character for * display. This spreads the onerous work of updating the screen * and ensures that other loop() functions in the application are * not held up significantly. The exception to this is when * the loop2() function is called with force=true, where * a screen update is executed to completion. This is normally * only done during start-up. * The scroll mode is selected by defining SCROLLMODE as 0, 1 or 2 * in the config.h. * #define SCROLLMODE 0 is scroll continuous (fill screen if poss), * #define SCROLLMODE 1 is by page (alternate between pages), * #define SCROLLMODE 2 is by row (move up 1 row at a time). */ #include "Display.h" void Display::clear() { clearNative(); for (byte row = 0; row < MAX_LCD_ROWS; row++) rowBuffer[row][0] = '\0'; topRow = -1; // loop2 will fill from row 0 } void Display::setRow(byte line) { hotRow = line; hotCol = 0; } size_t Display::write(uint8_t b) { if (hotRow >= MAX_LCD_ROWS || hotCol >= MAX_LCD_COLS) return -1; rowBuffer[hotRow][hotCol] = b; hotCol++; rowBuffer[hotRow][hotCol] = 0; return 1; } void Display::loop() { if (!displayHandler) return; displayHandler->loop2(false); } Display *Display::loop2(bool force) { if (!displayHandler) return NULL; // If output device is busy, don't do anything on this loop // This avoids blocking while waiting for the device to complete. if (isBusy()) return NULL; unsigned long currentMillis = millis(); if (!force) { // See if we're in the time between updates if ((currentMillis - lastScrollTime) < LCD_SCROLL_TIME) return NULL; } else { // force full screen update from the beginning. rowFirst = -1; rowNext = 0; bufferPointer = 0; done = false; slot = 0; } do { if (bufferPointer == 0) { // Find a line of data to write to the screen. if (rowFirst < 0) rowFirst = rowNext; skipBlankRows(); if (!done) { // Non-blank line found, so copy it. for (uint8_t i = 0; i < sizeof(buffer); i++) buffer[i] = rowBuffer[rowNext][i]; } else buffer[0] = '\0'; // Empty line setRowNative(slot); // Set position for display charIndex = 0; bufferPointer = &buffer[0]; } else { // Write next character, or a space to erase current position. char ch = *bufferPointer; if (ch) { writeNative(ch); bufferPointer++; } else writeNative(' '); if (++charIndex >= MAX_LCD_COLS) { // Screen slot completed, move to next slot on screen slot++; bufferPointer = 0; if (!done) { moveToNextRow(); skipBlankRows(); } } if (slot >= lcdRows) { // Last slot finished, reset ready for next screen update. #if SCROLLMODE==2 if (!done) { // On next refresh, restart one row on from previous start. rowNext = rowFirst; moveToNextRow(); skipBlankRows(); } #endif done = false; slot = 0; rowFirst = -1; lastScrollTime = currentMillis; return NULL; } } } while (force); return NULL; } void Display::moveToNextRow() { rowNext = (rowNext + 1) % MAX_LCD_ROWS; #if SCROLLMODE == 1 // Finished if we've looped back to row 0 if (rowNext == 0) done = true; #else // Finished if we're back to the first one shown if (rowNext == rowFirst) done = true; #endif } void Display::skipBlankRows() { while (!done && rowBuffer[rowNext][0] == 0) moveToNextRow(); }