Skip to content

Instantly share code, notes, and snippets.

@MCJack123
Created May 14, 2018 02:48
Show Gist options
  • Save MCJack123/cb8c047f8ba3b8ca40499483bc0bc47a to your computer and use it in GitHub Desktop.
Save MCJack123/cb8c047f8ba3b8ca40499483bc0bc47a to your computer and use it in GitHub Desktop.
Two-line menu system supporting HD44780 LCDs with ncurses simulation
// Copyright Pololu Corporation. For more information, see http://www.pololu.com/
#ifdef __arm__
#include "PololuHD44780.h"
#define LCD_CLEAR 0x01
#define LCD_SHOW_BLINK 0x0F
#define LCD_SHOW_SOLID 0x0E
#define LCD_HIDE 0x0C
#define LCD_CURSOR_L 0x10
#define LCD_CURSOR_R 0x14
#define LCD_SHIFT_L 0x18
#define LCD_SHIFT_R 0x1C
PololuHD44780Base::PololuHD44780Base()
{
initialized = false;
}
void PololuHD44780Base::init2()
{
// The startup procedure comes from Figure 24 of the HD44780 datasheet. The
// delay times in the later part of this function come from Table 6.
initPins();
// We need to wait at least 15 ms after VCC reaches 4.5 V.
//
// Assumption: The AVR's power-on reset is already configured to wait for
// tens of milliseconds, so no delay is needed here.
sendCommand4Bit(3); // Function set
_delay_us(4200); // Needs to be at least 4.1 ms.
sendCommand4Bit(3); // Function set
_delay_us(150); // Needs to be at least 100 us.
sendCommand4Bit(3); // Function set
sendCommand4Bit(0b0010); // 4-bit interface
sendCommand(0b00101000); // 4-bit, 2 line, 5x8 dots font
setDisplayControl(0b000); // display off, cursor off, blinking off
clear();
setEntryMode(0b10); // cursor shifts right, no auto-scrolling
setDisplayControl(0b100); // display on, cursor off, blinking off
}
void PololuHD44780Base::sendAndDelay(uint8_t data, bool rsValue, bool only4bit)
{
init();
send(data, rsValue, only4bit);
// Every data transfer or command takes at least 37 us to complete, and most
// of them only take that long according to the HD44780 datasheet. We delay
// for 37 us here so we don't have to do it in lots of other places.
//
// NOTE: If we add support for configurations where the R/W line is
// connected, then this delay and others like it should be disabled, and we
// should instead wait for the busy flag before sending the next command.
_delay_us(37);
}
size_t PololuHD44780Base::write(uint8_t data)
{
sendData(data);
return 1;
}
size_t PololuHD44780Base::write(const uint8_t * buffer, size_t length)
{
size_t n = length;
while (n--)
{
sendData(*buffer++);
}
return length;
}
void PololuHD44780Base::clear()
{
sendCommand(LCD_CLEAR);
// It's not clear how long this command takes because it doesn't say in
// Table 6 of the HD44780 datasheet. A good guess is that it takes 1.52 ms,
// since the Return Home command does.
_delay_us(2000);
}
void PololuHD44780Base::gotoXY(uint8_t x, uint8_t y)
{
// Each entry is the RAM address of a line, with its most
// significant bit set for convenience.
const uint8_t line_mem[] = {0x80, 0xC0, 0x94, 0xD4};
// Avoid out-of-bounds array access.
if (y > 3) { y = 3; }
sendCommand(line_mem[y] + x);
// This could take up to 37 us according to Table 6 of the HD44780 datasheet.
_delay_us(37);
}
void PololuHD44780Base::loadCustomCharacter(const uint8_t * picture, uint8_t number)
{
uint8_t address = number * 8;
for(uint8_t i = 0; i < 8; i++)
{
// Set CG RAM address.
sendCommand(0b01000000 | (address + i));
// Write character data.
sendData(pgm_read_byte(picture + i));
}
}
void PololuHD44780Base::loadCustomCharacterFromRam(const uint8_t * picture, uint8_t number)
{
uint8_t address = number * 8;
for(uint8_t i = 0; i < 8; i++)
{
// Set CG RAM address.
sendCommand(0b01000000 | (address + i));
// Write character data.
sendData(picture[i]);
}
}
void PololuHD44780Base::setDisplayControl(uint8_t displayControl)
{
sendCommand(0b00001000 | displayControl);
this->displayControl = displayControl;
}
void PololuHD44780Base::cursorSolid()
{
setDisplayControl((displayControl | 0b010) & ~0b001);
}
void PololuHD44780Base::cursorBlinking()
{
setDisplayControl((displayControl | 0b001) & ~0b010);
}
void PololuHD44780Base::hideCursor()
{
setDisplayControl(displayControl & ~0b011);
}
void PololuHD44780Base::noDisplay()
{
setDisplayControl(displayControl & ~0b100);
}
void PololuHD44780Base::display()
{
setDisplayControl(displayControl | 0b100);
}
void PololuHD44780Base::noCursor()
{
setDisplayControl(displayControl & ~0b010);
}
void PololuHD44780Base::cursor()
{
setDisplayControl(displayControl | 0b010);
}
void PololuHD44780Base::noBlink()
{
setDisplayControl(displayControl & ~0b001);
}
void PololuHD44780Base::blink()
{
setDisplayControl(displayControl | 0b001);
}
void PololuHD44780Base::scrollDisplayLeft()
{
sendCommand(0b00011000);
}
void PololuHD44780Base::scrollDisplayRight()
{
sendCommand(0b00011100);
}
void PololuHD44780Base::home()
{
sendCommand(0b00000010);
_delay_us(1600); // needs to be at least 1.52 ms
}
void PololuHD44780Base::setEntryMode(uint8_t entryMode)
{
sendCommand(0b00000100 | entryMode);
this->entryMode = entryMode;
}
void PololuHD44780Base::leftToRight()
{
setEntryMode(entryMode | 0b10);
}
void PololuHD44780Base::rightToLeft()
{
setEntryMode(entryMode & ~0b10);
}
void PololuHD44780Base::autoscroll()
{
setEntryMode(entryMode | 0b01);
}
void PololuHD44780Base::noAutoscroll()
{
setEntryMode(entryMode & ~0b01);
}
#endif
// Copyright Pololu Corporation. For more information, see http://www.pololu.com/
/*! \file PololuHD44780.h
*
* This is the main header file for the %PololuHD44780 library.
*
* For an overview of the library's features, see
* https://github.com/pololu/pololu-hd44780-arduino. That is the main
* repository for the library, though copies of the library may exist in other
* repositories. */
#pragma once
#define SCREEN
#include <util/delay.h>
#include <wiringPi.h>
/*! \brief General class for handling the HD44780 protocol.
*
* This is an abstract class that knows about the HD44780 LCD commands but
* does not directly read or write from the actual LCD. To make a usable class,
* you need to define a subclass of PololuHD44780Base and implement the
* initPins() and send() functions.
*
* The subclass will inherit all the functions from PololuHD44780Base which are
* documented here. It will also inherit all of the functions from the Arduino `Print` class.
* For more information about what the `Print` class provides, see the [Arduino print() documentation](http://arduino.cc/en/Serial/Print) or look at [Print.h in the Arduino IDE source code](https://github.com/arduino/Arduino/blob/master/hardware/arduino/cores/arduino/Print.h).
*
* Most users of this library will not need to directly use this class and
* should use PololuHD44780 or some other subclass of PololuHD44780Base defined
* in a different library.
*
* ## LCD scrolling ##
*
* The PololuHD44780Base class provides several functions related to scrolling:
*
* * scrollDisplayLeft() scrolls everything on the screen one position to the left.
* * scrollDisplayRight() scrolls everything on the screen one position to the right.
* * autoscroll() and noAutoscroll() control whether auto-scrolling is enabled.
* * home() and clear() both reset the scroll position
*
* The HD44780 actually stores 40 columns internally. By default, the left-most
* internal columns are the ones that are actually displayed on the screen, but
* the scrolling features allow that correspondence to change. The scrolling
* wraps around, so it is possible to display some of the right-most columns on
* the screen at the same time as some of the left-most columns.
*
* For the gotoXY() function, the x coordinate actually corresponds to the
* internal column index. The left-most internal column has an x coordinate of
* 0, and the right-most has an x coordinate of 39.
*
* For example, if you are controlling a 2&times;8 character LCD and you call
* scrollDisplayLeft() 35 times (or call scrollDisplayRight() 5 times), then the
* X coordinates of the columns displayed, from left to right, will be 35, 36,
* 37, 38, 39, 0, 1, and 2.
*
*/
class PololuHD44780Base
{
public:
PololuHD44780Base();
/*! Initializes the pins so that the send() function can be called
* successfully. This is the first step of initializing the LCD. */
virtual void initPins() = 0;
/*! Initialize the LCD if it has not already been initialized. */
void init()
{
if (!initialized)
{
initialized = true;
init2();
}
}
/*! Reinitialize the LCD. This performs the same initialization that is
* done automatically the first time any function is called that writes to
* the LCD. This is useful if you want to get it back to a totally clean
* state. */
void reinitialize()
{
initialized = true;
init2();
}
/*! Sends data or commands to the LCD.
*
* The initPins() function will always be called before the first time this
* function is called. This function does not need to worry about the
* delays necessary to make sure the previous command has finished; that is
* taken care of by PololuHD44780Base.
*
* This function, along with initPins(), comprise the hardware abstraction
* layer for the LCD, and must be defined in a subclass of
* PololuHD44780Base. All other functions use these two functions to
* communicate with the LCD.
*
* @param data The data to send to the LCD.
* @param rsValue True to drive the RS pin high, false to drive it low.
* @param only4bits: If true, and the LCD is using a 4-bit interface, only sends
* the lower 4 bits of the data. */
virtual void send(uint8_t data, bool rsValue, bool only4bits) = 0;
private:
void sendAndDelay(uint8_t data, bool rsValue, bool only4bit);
/*! Sends an 8-bit command to the LCD. */
void sendCommand(uint8_t cmd)
{
sendAndDelay(cmd, false, false);
}
/*! Sends a 4-bit command to the LCD. */
void sendCommand4Bit(uint8_t cmd)
{
sendAndDelay(cmd, false, true);
}
/*! Sends 8 bits of a data to the LCD. */
void sendData(uint8_t data)
{
sendAndDelay(data, true, false);
}
public:
/*! Clear the contents of the LCDs, resets the cursor position to the upper
* left, and resets the scroll position. */
void clear();
/*! Defines a custom character.
* @param picture A pointer to the character dot pattern, in program space.
* @param number A number between 0 and 7. */
void loadCustomCharacter(const uint8_t * picture, uint8_t number);
/*! Defines a custom character from RAM.
* @param picture A pointer to the character dot pattern, in RAM.
* @param number A number between 0 and 7. */
void loadCustomCharacterFromRam(const uint8_t * picture, uint8_t number);
/*! This overload of loadCustomCharacter is only provided for compatibility
* with OrangutanLCD; a lot of Orangutan code defines an array of chars for
* custom character pictures. */
void loadCustomCharacter(const char * picture, uint8_t number)
{
loadCustomCharacter((const uint8_t *)picture, number);
}
/*! Defines a custom character.
* This is provided for compatibility with the LiquidCrystal library. */
void createChar(uint8_t number, uint8_t picture[])
{
loadCustomCharacterFromRam(picture, number);
}
/*! Change the location of the cursor. The cursor (whether visible or invisible),
* is the place where the next character written to the LCD will be displayed.
*
* Note that the scrolling features of the LCD change the correspondence
* between the `x` parameter and the physical column that the data is
* displayed on. See the "LCD scrolling" section above for more information.
*
* @param x The number of the column to go to, with 0 being the leftmost column.
* @param y The number of the row to go to, with 0 being the top row. */
void gotoXY(uint8_t x, uint8_t y);
/*! Changes the location of the cursor. This is just a wrapper around
* gotoXY provided for compaitibility with the LiquidCrystal library. */
void setCursor(uint8_t col, uint8_t row)
{
gotoXY(col, row);
}
/*! Turns off the display while preserving its state.
*
* You can turn the display on again by calling display(). */
void noDisplay();
/*! Turns the display on. This should only be needed if noDisplay() was
* previously called. */
void display();
/*! Hides the solid cursor.
*
* This function clears the LCD's "C" configuration bit without changing
* the other bits.
*
* If the "B" bit is set to 1, a blinking cursor will still be displayed
* even after calling this function. For that reason, it is usually better
* to call hideCursor() instead. This function is only provided for
* compatibility with the LiquidCrystal library. */
void noCursor();
/*! Shows the solid cursor.
*
* This function sets the LCD's "C" configuration bit without changing the
* other bits.
*
* The cursor will normally be a solid line in the bottom row, but there
* could be a blinking rectangle superimposed on it if previous commands
* have enabled the blinking cursor. For this reason, it is usually better
* to call cursorSolid() or cursorBlinking() instead. This function is only
* provided for compatibility with the LiquidCrystal library. */
void cursor();
/*! Hides the blinking cursor.
*
* This functions clears the LCD's "B" configuration bit without changing
* the other bits.
*
* Calling this function does not enable or disable the solid cursor (a
* solid line in the bottom row) so it is usually better to call
* hideCursor() or cursorSolid() instead. This function is only provided
* for compatibilty with the LiquidCrystal library. */
void noBlink();
/*! Shows the blinking cursor.
*
* This function sets the LCD's "B" configuration bit without changing the
* other bits.
*
* The cursor will normally be a blinking rectangle, but there could also be
* a row of solid black pixels at the bottom if previous commands have
* enabled the solid cursor. For this reason, it is usually better to call
* cursorSolid() or cursorBlinking() instead. This function is only
* provided for compatibilty with the LiquidCrystal library. */
void blink();
/*! Enables a cursor that appears as a solid line in the bottom row.
*
* This sets the LCD's "C" configuration bit and clears its "B" bit.
*
* Note that the cursor will not be shown if the display is currently off
* (due to a call to noDisplay()), or if the cursor position is not within
* the bounds of the screen. */
void cursorSolid();
/*! Enables a cursor that appears as a blinking black rectangle.
*
* This sets the LCD's "C" and "B" configuration bits.
*
* Note that the cursor will not be shown if the display is currently off
* (due to a call to noDisplay()), or if the cursor position is not within
* the bounds of the screen. */
void cursorBlinking();
/*! Hides the solid and blinking cursors.
*
* This clears the LCD's "C" and "B" configuration bits. */
void hideCursor();
/*! Scrolls everything on the screen one position to the left.
*
* This command takes about 37 microseconds. */
void scrollDisplayLeft();
/*! Scrolls everything on the screen one position to the right.
*
* This command takes about 37 microseconds. */
void scrollDisplayRight();
/*! Resets the screen scrolling position back to the default and moves the
* cursor to the upper left corner of the screen.
*
* This command takes about 1600 microseconds, so it would be faster to
* instead call scrollDisplayLeft() or scrollDisplayRight() the appropriate
* number of times and then call gotoXY(0, 0). */
void home();
/*! Puts the LCD into left-to-right mode: the cursor will shift to the right
* after any character is written. This is the default behavior. */
void leftToRight();
/*! Puts the LCD into right-to-left mode: the cursor will shift to the left
* after any character is written. */
void rightToLeft();
/*! Turns on auto-scrolling.
*
* When auto-scrolling is enabled, every time a character is written, the
* screen will automatically scroll by one column in the appropriate
* direction. */
void autoscroll();
/*! Turns off auto-scrolling. Auto-scrolling is off by default. */
void noAutoscroll();
//void initPrintf();
//void initPrintf(uint8_t lcdWidth, uint8_t lcdHeight);
/*! Send an arbitrary command to the LCD. This is here for compatibility
* with the LiquidCrystal library. */
void command(uint8_t cmd)
{
sendCommand(cmd);
}
/*! Writes a single character to the LCD. */
virtual size_t write(uint8_t c);
/*! Writes multiple characters to the LCD.
*
* @param buffer Pointer to a string of characters in RAM, not
* necessarily null-terminated.
* @param size The number of characters to write to the LCD, excluding any
* null termination character. */
virtual size_t write(const uint8_t * buffer, size_t size);
// This allows us to easily call overrides of write that are
// defined in Print.
//using Print::write;
private:
bool initialized;
/* The lower three bits of this store the arguments to the
* last "Display on/off control" HD44780 command that we sent.
* bit 2: D: Whether the display is on.
* bit 1: C: Whether the cursor is shown.
* bit 0: B: Whether the cursor is blinking. */
uint8_t displayControl;
/* The lower two bits of this variable store the arguments to the
* last "Entry mode set" HD44780 command that we sent.
* bit 1: I/D: 0 for moving the cursor to the left after data is written,
* 1 for moving the cursor to the right.
* bit 0: 1 for autoscrolling. */
uint8_t entryMode;
void setEntryMode(uint8_t entryMode);
void setDisplayControl(uint8_t displayControl);
void init2();
};
class PololuHD44780 : public PololuHD44780Base
{
public:
/*! Creates a new instance of PololuHD44780.
*
* @param rs The pin number for the microcontroller pin that is
* connected to the RS pin of the LCD.
* @param e The pin number for the microcontroller pin that is
* connected to the E pin of the LCD.
* @param db4 The pin number for the microcontroller pin that is
* connected to the DB4 pin of the LCD.
* @param db5 The pin number for the microcontroller pin that is
* connected to the DB5 pin of the LCD.
* @param db6 The pin number for the microcontroller pin that is
* connected to the DB6 pin of the LCD.
* @param db7 The pin number for the microcontroller pin that is
* connected to the DB7 pin of the LCD.
*/
PololuHD44780(uint8_t rs, uint8_t e, uint8_t db4, uint8_t db5,
uint8_t db6, uint8_t db7)
{
this->rs = rs;
this->e = e;
this->db4 = db4;
this->db5 = db5;
this->db6 = db6;
this->db7 = db7;
}
virtual void initPins()
{
digitalWrite(e, LOW);
pinMode(e, OUTPUT);
}
virtual void send(uint8_t data, bool rsValue, bool only4bits)
{
digitalWrite(rs, rsValue);
pinMode(rs, OUTPUT);
pinMode(db4, OUTPUT);
pinMode(db5, OUTPUT);
pinMode(db6, OUTPUT);
pinMode(db7, OUTPUT);
if (!only4bits) { sendNibble(data >> 4); }
sendNibble(data & 0x0F);
}
private:
void sendNibble(uint8_t data)
{
digitalWrite(db4, data >> 0 & 1);
digitalWrite(db5, data >> 1 & 1);
digitalWrite(db6, data >> 2 & 1);
digitalWrite(db7, data >> 3 & 1);
digitalWrite(e, HIGH);
_delay_us(1); // Must be at least 450 ns.
digitalWrite(e, LOW);
_delay_us(1); // Must be at least 550 ns.
}
uint8_t rs, e, db4, db5, db6, db7;
};
//
// screenio.cpp
// cplusplus
//
// Created by Jack on 5/10/18.
// Copyright © 2018 JackMacWindows. All rights reserved.
//
#include <algorithm>
#include "screenio.hpp"
#define qd(a, b) ((a - (a % b)) / b)
#ifdef SCREEN
#define write(x, y, text) screen.gotoXY(x, y); screen.write((const uint8_t *)text.c_str(), text.length())
#else
#define write(x, y, text) mvaddstr(y, x, text.c_str()); move(1, SCREEN_WIDTH - 1); refresh()
#endif
#define incWithMax(num, max) if (num >= max) num = 0; else num++
#define decWithMax(num, max) if (num == 0) num = max - 1; else num--
int scrollpoint = 0;
int scrollspeed = 125;
std::string currentItem = "*";
std::string currentTitle = "*";
std::string currentText = "";
std::chrono::system_clock::time_point lastScroll = std::chrono::system_clock::now();
//WINDOW * win;
void initialize() {
#ifdef SCREEN
screen.initPins();
screen.hideCursor();
#else
initscr();
noecho();
cbreak();
keypad(stdscr, true);
nodelay(stdscr, true);
//win = newwin(2, SCREEN_WIDTH, 0, 0);
//wrefresh(win);
#endif
}
void deinitialize() {
#ifndef SCREEN
//delwin(win);
echo();
nocbreak();
keypad(stdscr, false);
nodelay(stdscr, false);
endwin();
#endif
}
void setTitle(std::string title) {
if (title.length() > SCREEN_WIDTH) title = title.substr(SCREEN_WIDTH - 3) + "...";
//if (title == currentTitle) return;
int spaces = qd((SCREEN_WIDTH - (int)title.length()), 2);
if (spaces < 0) spaces = 0;
if (title.length() % 2 > 0 && spaces > 0) title.append(" ");
currentTitle = std::string(spaces, ' ') + title + std::string(spaces, ' ');
assert(currentTitle.length() == SCREEN_WIDTH);
write(0, 0, currentTitle);
}
void setText(std::string text) {
int spaces = qd((SCREEN_WIDTH - (int)text.length()), 2);
if (spaces < 0) spaces = 0;
std::string newText = text.substr(0, SCREEN_WIDTH);
if (text.length() % 2 > 0 && spaces > 0) newText.append(" ");
newText = std::string(spaces, ' ') + newText + std::string(spaces, ' ');
currentText = text;
scrollpoint = 0;
lastScroll = std::chrono::system_clock::now() + std::chrono::seconds(1);
//std::cout << "\"" << newText << "\"=" << newText.length() << "\n";
assert(newText.length() == SCREEN_WIDTH);
write(0, 1, newText);
}
int getButton() {
while (true) {
if (std::chrono::system_clock::now() - lastScroll >= std::chrono::milliseconds(scrollspeed) && currentText.length() > SCREEN_WIDTH) {
if (++scrollpoint > currentText.size() + 4) {
scrollpoint = 0;
lastScroll = std::chrono::system_clock::now() + std::chrono::seconds(1);
} else lastScroll = std::chrono::system_clock::now();
write(0, 1, extractLine(currentText, scrollpoint));
}
#ifdef SCREEN
if (digitalRead(PIN_LEFT) == HIGH) return BUTTON_LEFT;
if (digitalRead(PIN_RIGHT) == HIGH) return BUTTON_RIGHT;
if (digitalRead(PIN_OK) == HIGH) return BUTTON_OK;
if (digitalRead(PIN_BACK) == HIGH) return BUTTON_BACK;
#else
int ch = getch();
if (ch == KEY_LEFT) return BUTTON_LEFT;
if (ch == KEY_RIGHT) return BUTTON_RIGHT;
if (ch == KEY_ENTER || ch == 10) return BUTTON_OK;
if (ch == KEY_BACKSPACE || ch == 127) return BUTTON_BACK;
#endif
}
}
std::string extractLine(std::string text, int offset) {
std::string shortened = "";
std::replace(text.begin(), text.end(), '\n', ' ');
if (offset < text.size()) {
shortened = text.substr(offset, SCREEN_WIDTH);
if (shortened.length() < SCREEN_WIDTH) shortened += std::string((SCREEN_WIDTH - shortened.length() > 4 ? 4 : SCREEN_WIDTH - shortened.length()), ' ');
if (shortened.length() < SCREEN_WIDTH) shortened += text.substr(0, SCREEN_WIDTH - shortened.length());
} else {
shortened += std::string(4 - (offset - text.length()), ' ');
if (shortened.length() < SCREEN_WIDTH) shortened += text.substr(0, SCREEN_WIDTH - shortened.length());
}
assert(shortened.length() == SCREEN_WIDTH);
return shortened;
}
int showMenu(std::string title, list items) {
setTitle(title);
int itemnum = 0;
while (true) {
currentItem = items[itemnum];
setText(items[itemnum]);
int button = getButton();
if (button == BUTTON_LEFT) {decWithMax(itemnum, (int)items.size());}
else if (button == BUTTON_RIGHT) {incWithMax(itemnum, (int)items.size() - 1);}
else if (button == BUTTON_BACK) return -1;
else if (button == BUTTON_OK) return itemnum;
}
}
//
// screenio.hpp
// cplusplus
//
// Created by Jack on 5/10/18.
// Copyright © 2018 JackMacWindows. All rights reserved.
#ifndef screenio_hpp
#define screenio_hpp
#include <string>
#include <chrono>
#include <vector>
#define SCREEN_WIDTH 16
#ifdef __arm__
#include "PoluluHD44780.h"
// To be filled in
PoluluHD44710 screen(0, 0, 0, 0, 0, 0);
#define PIN_LEFT 0
#define PIN_RIGHT 0
#define PIN_OK 0
#define PIN_BACK 0
#else
#include <ncurses.h>
#include <unistd.h>
//extern WINDOW * win;
#endif
typedef std::vector<std::string> list;
enum button_presses {
BUTTON_NONE,
BUTTON_LEFT,
BUTTON_RIGHT,
BUTTON_BACK,
BUTTON_OK
};
extern int scrollpoint;
extern int scrollspeed;
extern std::string currentItem;
extern std::string currentTitle;
extern std::string currentText;
extern std::chrono::system_clock::time_point lastScroll;
extern void initialize();
extern void deinitialize();
extern void setTitle(std::string title);
extern void setText(std::string text);
extern int getButton();
extern std::string extractLine(std::string text, int offset);
extern int showMenu(std::string title, list items);
#endif /* screenio_hpp */
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment