text: /**
   * @file Arduboy2.h
   * \brief
   * The Arduboy2Base and Arduboy2 classes and support objects and definitions.
   */
  
  #ifndef ARDUBOY2_H
  #define ARDUBOY2_H
  
  // this is for my two ps2 controller setups, you can ignore it
  //#define HARTMANNS_PS2Controller
  
  #if defined(HARTMANNS_PS2Controller)
  #define COUNT_LAST_FRAMES
  #if defined(ESP8266)
  #define DISPLAY_SSD1306
  #elif defined(ESP32)
  #define DISPLAY_TFT_eSPI
  #endif
  
  #else
  
  // please choose the display you wanna use, it will be loaded in Arduboy2Config.h
  //#define DISPLAY_Default
  //#define DISPLAY_Adafruit
  #define DISPLAY_TFT_eSPI
  //#define DISPLAY_SSD1306
  
  #endif
  
  #include <Arduino.h>
  #include <EEPROM.h>
  #include "Arduboy2Core.h"
  #include "Arduboy2Beep.h"
  #include "Sprites.h"
  #include "SpritesB.h"
  #include <Print.h>
  #include <limits.h>
  
  /** \brief
   * Library version
   *
   * \details
   * For a version number in the form of x.y.z the value of the define will be
   * ((x * 10000) + (y * 100) + (z)) as a decimal number.
   * So, it will read as xxxyyzz, with no leading zeros on x.
   *
   * A user program can test this value to conditionally compile based on the
   * library version. For example:
   *
   * \code{.cpp}
   * // If the library is version 2.1.0 or higher
   * #if ARDUBOY_LIB_VER >= 20100
   *   // ... code that make use of a new feature added to V2.1.0
   * #endif
   * \endcode
   */
  #define ARDUBOY_LIB_VER 50100
  
  // EEPROM settings
  #define ARDUBOY_UNIT_NAME_LEN 6 /**< The maximum length of the unit name string. */
  
  #define EEPROM_VERSION 0
  #define EEPROM_SYS_FLAGS 1
  #define EEPROM_AUDIO_ON_OFF 2
  #define EEPROM_UNIT_ID 8      
  #define EEPROM_UNIT_NAME 10   
                                // A uint16_t binary unit ID
                                // An up to 6 character unit name. Cannot contain 
                                // 0x00 or 0xFF. Lengths less than 6 are padded 
                                // with 0x00
  
  // EEPROM_SYS_FLAGS values
  #define SYS_FLAG_UNAME 0 // Display the unit name on the logo screen
  #define SYS_FLAG_UNAME_MASK _BV(SYS_FLAG_UNAME)
  #define SYS_FLAG_SHOW_LOGO 1 // Show the logo sequence during boot up
  #define SYS_FLAG_SHOW_LOGO_MASK _BV(SYS_FLAG_SHOW_LOGO)
  #define SYS_FLAG_SHOW_LOGO_LEDS 2 // Flash the RGB led during the boot logo
  #define SYS_FLAG_SHOW_LOGO_LEDS_MASK _BV(SYS_FLAG_SHOW_LOGO_LEDS)
  
  /** \brief
   * Start of EEPROM storage space for sketches.
   *
   * \details
   * An area at the start of EEPROM is reserved for system use.
   * This define specifies the first EEPROM location past the system area.
   * Sketches can use locations from here to the end of EEPROM space.
   */
  #define EEPROM_STORAGE_SPACE_START 16
  
  // eeprom settings above are neded for audio
  #include "Arduboy2Audio.h"
  
  // If defined, it is safe to draw outside of the screen boundaries.
  // Pixels that would exceed the display limits will be ignored.
  #define PIXEL_SAFE_MODE
  
  // pixel colors
  #define BLACK 0 /**< Color value for an unlit pixel for draw functions. */
  #define WHITE 1 /**< Color value for a lit pixel for draw functions. */
  /** \brief
   * Color value to indicate pixels are to be inverted.
   *
   * \details
   * BLACK pixels will become WHITE and WHITE will become BLACK.
   *
   * \note
   * Only function Arduboy2Base::drawBitmap() currently supports this value.
   */
  #define INVERT 2
  #define INVERSE INVERT // sometimes INVERSE is used instead
  
  #define CLEAR_BUFFER true /**< Value to be passed to `display()` to clear the screen buffer. */
  
  /** \brief
   * A rectangle object for collision functions.
   *
   * \details
   * The X and Y coordinates specify the top left corner of a rectangle with the
   * given width and height.
   *
   * \see Arduboy2Base::collide(Point, Rect) Arduboy2Base::collide(Rect, Rect)
   */
  struct Rect
  {
    int16_t x;      /**< The X coordinate of the top left corner */
    int16_t y;      /**< The Y coordinate of the top left corner */
    uint8_t width;  /**< The width of the rectangle */
    uint8_t height; /**< The height of the rectangle */
  };
  
  /** \brief
   * An object to define a single point for collision functions.
   *
   * \details
   * The location of the point is given by X and Y coordinates.
   *
   * \see Arduboy2Base::collide(Point, Rect)
   */
  struct Point
  {
    int16_t x; /**< The X coordinate of the point */
    int16_t y; /**< The Y coordinate of the point */
  };
  
  //==================================
  //========== Arduboy2Base ==========
  //==================================
  
  /** \brief
   * The main functions provided for writing sketches for the Arduboy,
   * _minus_ text output.
   *
   * \details
   * This class in inherited by Arduboy2, so if text output functions are
   * required Arduboy2 should be used instead.
   *
   * \note
   * \parblock
   * An Arduboy2Audio class object named `audio` will be created by the
   * Arduboy2Base class, so there is no need for a sketch itself to create an
   * Arduboy2Audio object. Arduboy2Audio functions can be called using the
   * Arduboy2 or Arduboy2Base `audio` object.
   *
   * Example:
   *
   * \code{.cpp}
   * #include <Arduboy2.h>
   *
   * Arduboy2 arduboy;
   *
   * // Arduboy2Audio functions can be called as follows:
   *   arduboy.audio.on();
   *   arduboy.audio.off();
   * \endcode
   * \endparblock
   *
   * \note
   * \parblock
   * A friend class named _Arduboy2Ex_ is declared by this class. The intention
   * is to allow a sketch to create an _Arduboy2Ex_ class which would have access
   * to the private and protected members of the Arduboy2Base class. It is hoped
   * that this may eliminate the need to create an entire local copy of the
   * library, in order to extend the functionality, in most circumstances.
   * \endparblock
   *
   * \see Arduboy2
   */
  class Arduboy2Base : public Arduboy2Core
  {
    friend class Arduboy2Ex;
  
  public:
    Arduboy2Base();
  
    /** \brief
     * An object created to provide audio control functions within this class.
     *
     * \details
     * This object is created to eliminate the need for a sketch to create an
     * Arduboy2Audio class object itself.
     *
     * \see Arduboy2Audio
     */
    Arduboy2Audio audio;
  
    /** \brief
     * Initialize the hardware, display the boot logo, provide boot utilities, etc.
     *
     * \details
     * This function should be called once near the start of the sketch,
     * usually in `setup()`, before using any other functions in this class.
     * It initializes the display, displays the boot logo, provides "flashlight"
     * and system control features and initializes audio control.
     *
     * \note
     * To free up some code space for use by the sketch, `boot()` can be used
     * instead of `begin()` to allow the elimination of some of the things that
     * aren't really required, such as displaying the boot logo.
     *
     * \see boot()
     */
    void begin();
  
    /** \brief
     * Turn the RGB LED and display fully on to act as a small flashlight/torch.
     *
     * \details
     * Checks if the UP button is pressed and if so turns the RGB LED and all
     * display pixels fully on. If the UP button is detected, this function
     * does not exit. The Arduboy must be restarted after flashlight mode is used.
     *
     * This function is called by `begin()` and can be called by a sketch
     * after `boot()`.
     *
     * \note
     * \parblock
     * This function also contains code to address a problem with uploading a new
     * sketch, for sketches that interfere with the bootloader "magic number".
     * This problem occurs with certain sketches that use large amounts of RAM.
     * Being in flashlight mode when uploading a new sketch can fix this problem.
     *
     * Therefore, for sketches that potentially could cause this problem, and use
     * `boot()` instead of `begin()`, it is recommended that a call to
     * `flashlight()` be included after calling `boot()`. If program space is
     * limited, `safeMode()` can be used instead of `flashlight()`.
     * \endparblock
     *
     * \see begin() boot() safeMode()
     */
    void flashlight();
  
    /** \brief
     * Handle buttons held on startup for system control.
     *
     * \details
     * This function is called by `begin()` and can be called by a sketch
     * after `boot()`.
     *
     * Hold the B button when booting to enter system control mode.
     * The B button must be held continuously to remain in this mode.
     * Then, pressing other buttons will perform system control functions:
     *
     * - UP: Set "sound enabled" in EEPROM
     * - DOWN: Set "sound disabled" (mute) in EEPROM
     *
     * \see begin() boot()
     */
    void systemButtons();
  
    /** \brief
     * Display the boot logo sequence using `drawBitmap()`.
     *
     * \details
     * This function is called by `begin()` and can be called by a sketch
     * after `boot()`.
     *
     * The Arduboy logo scrolls down from the top of the screen to the center
     * while the RGB LEDs light in sequence.
     *
     * The `bootLogoShell()` helper function is used to perform the actual
     * sequence. The documentation for `bootLogoShell()` provides details on how
     * it operates.
     *
     * \see begin() boot() bootLogoShell() Arduboy2::bootLogoText()
     */
    void bootLogo();
  
    /** \brief
     * Display the boot logo sequence using `drawCompressed()`.
     *
     * \details
     * This function can be called by a sketch after `boot()` as an alternative to
     * `bootLogo()`. This may reduce code size if the sketch itself uses
     * `drawCompressed()`.
     *
     * \see bootLogo() begin() boot()
     */
    void bootLogoCompressed();
  
    /** \brief
     * Display the boot logo sequence using `Sprites::drawSelfMasked()`.
     *
     * \details
     * This function can be called by a sketch after `boot()` as an alternative to
     * `bootLogo()`. This may reduce code size if the sketch itself uses
     * `Sprites` class functions.
     *
     * \see bootLogo() begin() boot() Sprites
     */
    void bootLogoSpritesSelfMasked();
  
    /** \brief
     * Display the boot logo sequence using `Sprites::drawOverwrite()`.
     *
     * \details
     * This function can be called by a sketch after `boot()` as an alternative to
     * `bootLogo()`. This may reduce code size if the sketch itself uses
     * `Sprites` class functions.
     *
     * \see bootLogo() begin() boot() Sprites
     */
    void bootLogoSpritesOverwrite();
  
    /** \brief
     * Display the boot logo sequence using `SpritesB::drawSelfMasked()`.
     *
     * \details
     * This function can be called by a sketch after `boot()` as an alternative to
     * `bootLogo()`. This may reduce code size if the sketch itself uses
     * `SpritesB` class functions.
     *
     * \see bootLogo() begin() boot() SpritesB
     */
    void bootLogoSpritesBSelfMasked();
  
    /** \brief
     * Display the boot logo sequence using `SpritesB::drawOverwrite()`.
     *
     * \details
     * This function can be called by a sketch after `boot()` as an alternative to
     * `bootLogo()`. This may reduce code size if the sketch itself uses
     * `SpritesB` class functions.
     *
     * \see bootLogo() begin() boot() SpritesB
     */
    void bootLogoSpritesBOverwrite();
  
    /** \brief
     * Display the boot logo sequence using the provided function
     *
     * \param drawLogo A reference to a function which will draw the boot logo
     * at the given Y position.
     *
     * \details
     * This common function executes the sequence to display the boot logo.
     * It is called by `bootLogo()` and other similar functions which provide it
     * with a reference to a function which will do the actual drawing of the
     * logo.
     *
     * This function calls `bootLogoExtra()` after the logo stops scrolling down,
     * which derived classes can implement to add additional information to the
     * logo screen. The `Arduboy2` class uses this to display the unit name.
     *
     * If the RIGHT button is pressed while the logo is scrolling down,
     * the boot logo sequence will be aborted. This can be useful for
     * developers who wish to quickly start testing, or anyone else who is
     * impatient and wants to go straight to the actual sketch.
     *
     * If the SYS_FLAG_SHOW_LOGO_LEDS flag in system EEPROM is cleared,
     * the RGB LEDs will not be flashed during the logo display sequence.
     *
     * If the SYS_FLAG_SHOW_LOGO flag in system EEPROM is cleared, this function
     * will return without executing the logo display sequence.
     *
     * The prototype for the function provided to draw the logo is:
     *
     * \code{.cpp}
     * void drawLogo(int16_t y);
     * \endcode
     *
     * The y parameter is the Y offset for the top of the logo. It is expected
     * that the logo will be 16 pixels high and centered horizontally. This will
     * result in the logo stopping in the middle of the screen at the end of the
     * sequence. If the logo height is not 16 pixels, the Y value can be adjusted
     * to compensate.
     *
     * \see bootLogo() boot() Arduboy2::bootLogoExtra()
     */
    void bootLogoShell(void (*drawLogo)(int16_t));
  
    // Called by bootLogoShell() to allow derived classes to display additional
    // information after the logo stops scrolling down.
    virtual void bootLogoExtra();
  
    /** \brief
     * Wait until all buttons have been released.
     *
     * \details
     * This function is called by `begin()` and can be called by a sketch
     * after `boot()`.
     *
     * It won't return unless no buttons are being pressed. A short delay is
     * performed each time before testing the state of the buttons to do a
     * simple button debounce.
     *
     * This function is called at the end of `begin()` to make sure no buttons
     * used to perform system start up actions are still being pressed, to
     * prevent them from erroneously being detected by the sketch code itself.
     *
     * \see begin() boot()
     */
    void waitNoButtons();
  
    /** \brief
     * Clear the display buffer.
     *
     * \details
     * The entire contents of the screen buffer are cleared to BLACK.
     *
     * \see display(bool)
     */
    void clear();
  
    /** \brief
     * Copy the contents of the display buffer to the display.
     *
     * \details
     * The contents of the display buffer in RAM are copied to the display and
     * will appear on the screen.
     *
     * \see display(bool)
     */
    void display();
  
    /** \brief
     * Copy the contents of the display buffer to the display. The display buffer
     * can optionally be cleared.
     *
     * \param clear If `true` the display buffer will be cleared to zero.
     * The defined value `CLEAR_BUFFER` should be used instead of `true` to make
     * it more meaningful.
     *
     * \details
     * Operation is the same as calling `display()` without parameters except
     * additionally the display buffer will be cleared if the parameter evaluates
     * to `true`. (The defined value `CLEAR_BUFFER` can be used for this)
     *
     * Using `display(CLEAR_BUFFER)` is faster and produces less code than
     * calling `display()` followed by `clear()`.
     *
     * \see display() clear()
     */
    void display(bool clear);
  
    /** \brief
     * Set a single pixel in the display buffer to the specified color.
     *
     * \param x The X coordinate of the pixel.
     * \param y The Y coordinate of the pixel.
     * \param color The color of the pixel (optional; defaults to WHITE).
     *
     * \details
     * The single pixel specified location in the display buffer is set to the
     * specified color. The values WHITE or BLACK can be used for the color.
     * If the `color` parameter isn't included, the pixel will be set to WHITE.
     */
    void drawPixel(int16_t x, int16_t y, uint8_t color = WHITE);
  
    /** \brief
     * Returns the state of the given pixel in the screen buffer.
     *
     * \param x The X coordinate of the pixel.
     * \param y The Y coordinate of the pixel.
     *
     * \return WHITE if the pixel is on or BLACK if the pixel is off.
     */
    uint8_t getPixel(uint8_t x, uint8_t y);
  
    /** \brief
     * Draw a circle of a given radius.
     *
     * \param x0 The X coordinate of the circle's center.
     * \param y0 The Y coordinate of the circle's center.
     * \param r The radius of the circle in pixels.
     * \param color The circle's color (optional; defaults to WHITE).
     */
    void drawCircle(int16_t x0, int16_t y0, uint8_t r, uint8_t color = WHITE);
  
    // Draw one or more "corners" of a circle.
    // (Not officially part of the API)
    void drawCircleHelper(int16_t x0, int16_t y0, uint8_t r, uint8_t corners, uint8_t color = WHITE);
  
    /** \brief
     * Draw a filled-in circle of a given radius.
     *
     * \param x0 The X coordinate of the circle's center.
     * \param y0 The Y coordinate of the circle's center.
     * \param r The radius of the circle in pixels.
     * \param color The circle's color (optional; defaults to WHITE).
     */
    void fillCircle(int16_t x0, int16_t y0, uint8_t r, uint8_t color = WHITE);
  
    // Draw one or both vertical halves of a filled-in circle or
    // rounded rectangle edge.
    // (Not officially part of the API)
    void fillCircleHelper(int16_t x0, int16_t y0, uint8_t r, uint8_t sides, int16_t delta, uint8_t color = WHITE);
  
    /** \brief
     * Draw a line between two specified points.
     *
     * \param x0,x1 The X coordinates of the line ends.
     * \param y0,y1 The Y coordinates of the line ends.
     * \param color The line's color (optional; defaults to WHITE).
     *
     * \details
     * Draw a line from the start point to the end point using
     * Bresenham's algorithm.
     * The start and end points can be at any location with respect to the other.
     */
    void drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint8_t color = WHITE);
  
    /** \brief
     * Draw a rectangle of a specified width and height.
     *
     * \param x The X coordinate of the upper left corner.
     * \param y The Y coordinate of the upper left corner.
     * \param w The width of the rectangle.
     * \param h The height of the rectangle.
     * \param color The color of the pixel (optional; defaults to WHITE).
     */
    void drawRect(int16_t x, int16_t y, uint8_t w, uint8_t h, uint8_t color = WHITE);
  
    /** \brief
     * Draw a vertical line.
     *
     * \param x The X coordinate of the upper start point.
     * \param y The Y coordinate of the upper start point.
     * \param h The height of the line.
     * \param color The color of the line (optional; defaults to WHITE).
     */
    void drawFastVLine(int16_t x, int16_t y, uint8_t h, uint8_t color = WHITE);
  
    /** \brief
     * Draw a horizontal line.
     *
     * \param x The X coordinate of the left start point.
     * \param y The Y coordinate of the left start point.
     * \param w The width of the line.
     * \param color The color of the line (optional; defaults to WHITE).
     */
    void drawFastHLine(int16_t x, int16_t y, uint8_t w, uint8_t color = WHITE);
  
    /** \brief
     * Draw a filled-in rectangle of a specified width and height.
     *
     * \param x The X coordinate of the upper left corner.
     * \param y The Y coordinate of the upper left corner.
     * \param w The width of the rectangle.
     * \param h The height of the rectangle.
     * \param color The color of the pixel (optional; defaults to WHITE).
     */
    void fillRect(int16_t x, int16_t y, uint8_t w, uint8_t h, uint8_t color = WHITE);
  
    /** \brief
     * Fill the screen buffer with the specified color.
     *
     * \param color The fill color (optional; defaults to WHITE).
     */
    void fillScreen(uint8_t color = WHITE);
  
    /** \brief
     * Draw a rectangle with rounded corners.
     *
     * \param x The X coordinate of the left edge.
     * \param y The Y coordinate of the top edge.
     * \param w The width of the rectangle.
     * \param h The height of the rectangle.
     * \param r The radius of the semicircles forming the corners.
     * \param color The color of the rectangle (optional; defaults to WHITE).
     */
    void drawRoundRect(int16_t x, int16_t y, uint8_t w, uint8_t h, uint8_t r, uint8_t color = WHITE);
  
    /** \brief
     * Draw a filled-in rectangle with rounded corners.
     *
     * \param x The X coordinate of the left edge.
     * \param y The Y coordinate of the top edge.
     * \param w The width of the rectangle.
     * \param h The height of the rectangle.
     * \param r The radius of the semicircles forming the corners.
     * \param color The color of the rectangle (optional; defaults to WHITE).
     */
    void fillRoundRect(int16_t x, int16_t y, uint8_t w, uint8_t h, uint8_t r, uint8_t color = WHITE);
  
    /** \brief
     * Draw a triangle given the coordinates of each corner.
     *
     * \param x0,x1,x2 The X coordinates of the corners.
     * \param y0,y1,y2 The Y coordinates of the corners.
     * \param color The triangle's color (optional; defaults to WHITE).
     *
     * \details
     * A triangle is drawn by specifying each of the three corner locations.
     * The corners can be at any position with respect to the others.
     */
    void drawTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint8_t color = WHITE);
  
    /** \brief
     * Draw a filled-in triangle given the coordinates of each corner.
     *
     * \param x0,x1,x2 The X coordinates of the corners.
     * \param y0,y1,y2 The Y coordinates of the corners.
     * \param color The triangle's color (optional; defaults to WHITE).
     *
     * \details
     * A triangle is drawn by specifying each of the three corner locations.
     * The corners can be at any position with respect to the others.
     */
    void fillTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint8_t color = WHITE);
  
    /** \brief
     * Draw a bitmap from an array in program memory.
     *
     * \param x The X coordinate of the top left pixel affected by the bitmap.
     * \param y The Y coordinate of the top left pixel affected by the bitmap.
     * \param bitmap A pointer to the bitmap array in program memory.
     * \param w The width of the bitmap in pixels.
     * \param h The height of the bitmap in pixels.
     * \param color The color of pixels for bits set to 1 in the bitmap.
     *              If the value is INVERT, bits set to 1 will invert the
     *              corresponding pixel. (optional; defaults to WHITE).
     *
     * \details
     * Bits set to 1 in the provided bitmap array will have their corresponding
     * pixel set to the specified color. For bits set to 0 in the array, the
     * corresponding pixel will be left unchanged.
     *
     * Each byte in the array specifies a vertical column of 8 pixels, with the
     * least significant bit at the top.
     *
     * The array must be located in program memory by using the PROGMEM modifier.
     */
    static void drawBitmap(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t w, uint8_t h, uint8_t color = WHITE);
  
    /** \brief
     * Draw a bitmap from a horizontally oriented array in program memory.
     *
     * \param x The X coordinate of the top left pixel affected by the bitmap.
     * \param y The Y coordinate of the top left pixel affected by the bitmap.
     * \param bitmap A pointer to the bitmap array in program memory.
     * \param w The width of the bitmap in pixels.
     * \param h The height of the bitmap in pixels.
     * \param color The color of pixels for bits set to 1 in the bitmap.
     *              (optional; defaults to WHITE).
     *
     * \details
     * Bits set to 1 in the provided bitmap array will have their corresponding
     * pixel set to the specified color. For bits set to 0 in the array, the
     * corresponding pixel will be left unchanged.
     *
     * Each byte in the array specifies a horizontal row of 8 pixels, with the
     * most significant bit at the left end of the row.
     *
     * The array must be located in program memory by using the PROGMEM modifier.
     *
     * \note
     * This function requires a lot of additional CPU power and will draw images
     * slower than `drawBitmap()`, which uses bitmaps that are stored in a format
     * that allows them to be directly written to the screen. It is recommended
     * you use `drawBitmap()` when possible.
     */
    void drawSlowXYBitmap(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t w, uint8_t h, uint8_t color = WHITE);
  
    /** \brief
     * Draw a bitmap from an array of compressed data.
     *
     * \param sx The X coordinate of the top left pixel affected by the bitmap.
     * \param sy The Y coordinate of the top left pixel affected by the bitmap.
     * \param bitmap A pointer to the compressed bitmap array in program memory.
     * \param color The color of pixels for bits set to 1 in the bitmap.
     *              (optional; defaults to WHITE).
     *
     * \details
     * Draw a bitmap starting at the given coordinates from an array that has
     * been compressed using an algorthm implemented by Team A.R.G.
     * For more information see:
     * https://github.com/TEAMarg/drawCompressed
     * https://github.com/TEAMarg/Cabi
     *
     * Bits set to 1 in the provided bitmap array will have their corresponding
     * pixel set to the specified color. For bits set to 0 in the array, the
     * corresponding pixel will be left unchanged.
     *
     * The array must be located in program memory by using the PROGMEM modifier.
     */
    static void drawCompressed(int16_t sx, int16_t sy, const uint8_t *bitmap, uint8_t color = WHITE);
  
    /** \brief
     * Get a pointer to the display buffer in RAM.
     *
     * \return A pointer to the display buffer array in RAM.
     *
     * \details
     * The location of the display buffer in RAM, which is displayed using
     * `display()`, can be gotten using this function. The buffer can then be
     *  read and directly manipulated.
     *
     * \note
     * The display buffer array, `sBuffer`, is public. A sketch can access it
     * directly. Doing so may be more efficient than accessing it via the
     * pointer returned by `getBuffer()`.
     *
     * \see sBuffer
     */
    uint8_t *getBuffer();
  
    /** \brief
     * Seed the random number generator with a random value.
     *
     * \details
     * The Arduino random number generator is seeded with a random value
     * derived from entropy from an ADC reading of a floating pin combined with
     * the microseconds since boot.
     *
     * This method is still most effective when called after a semi-random time,
     * such as after a user hits a button to start a game or other semi-random
     * event.
     */
    void initRandomSeed();
  
    // Swap the values of two int16_t variables passed by reference.
    void swap(int16_t &a, int16_t &b);
  
    /** \brief
     * Set the frame rate used by the frame control functions.
     *
     * \param rate The desired frame rate in frames per second.
     *
     * \details
     * Set the frame rate, in frames per second, used by `nextFrame()` to update
     * frames at a given rate. If this function or `setFrameDuration()`
     * isn't used, the default rate will be 60 (actually 62.5, see note below).
     *
     * Normally, the frame rate would be set to the desired value once, at the
     * start of the game, but it can be changed at any time to alter the frame
     * update rate.
     *
     * \note
     * \parblock
     * The given rate is internally converted to a frame duration in milliseconds,
     * rounded down to the nearest integer. Therefore, the actual rate will be
     * equal to or higher than the rate given.
  
     * For example, 60 FPS would be 16.67ms per frame. This will be rounded down
     * to 16ms, giving an actual frame rate of 62.5 FPS.
     * \endparblock
     *
     * \see nextFrame() setFrameDuration()
     */
    void setFrameRate(uint8_t rate);
  
    /** \brief
     * Set the frame rate, used by the frame control functions, by giving
     * the duration of each frame.
     *
     * \param duration The desired duration of each frame in milliseconds.
     *
     * \details
     * Set the frame rate by specifying the duration of each frame in
     * milliseconds. This is used by `nextFrame()` to update frames at a
     * given rate. If this function or `setFrameRate()` isn't used,
     * the default will be 16ms per frame.
     *
     * Normally, the frame rate would be set to the desired value once, at the
     * start of the game, but it can be changed at any time to alter the frame
     * update rate.
     *
     * \see nextFrame() setFrameRate()
     */
    void setFrameDuration(uint8_t duration);
  
    /** \brief
     * Indicate that it's time to render the next frame.
     *
     * \return `true` if it's time for the next frame.
     *
     * \details
     * When this function returns `true`, the amount of time has elapsed to
     * display the next frame, as specified by `setFrameRate()`.
     *
     * This function will normally be called at the start of the rendering loop
     * which would wait for `true` to be returned before rendering and
     * displaying the next frame.
     *
     * example:
     * \code{.cpp}
     * void loop() {
     *   if (!arduboy.nextFrame()) {
     *     return; // go back to the start of the loop
     *   }
     *   // render and display the next frame
     * }
     * \endcode
     *
     * \see setFrameRate() setFrameDuration() nextFrameDEV()
     */
    bool nextFrame();
  
    /** \brief
     * Indicate that it's time to render the next frame, and visually indicate
     * if the code is running slower than the desired frame rate.
     * **FOR USE DURING DEVELOPMENT**
     *
     * \return `true` if it's time for the next frame.
     *
     * \details
     * This function is intended to be used in place of `nextFrame()` during the
     * development of a sketch. It does the same thing as `nextFrame()` but
     * additionally will light the yellow TX LED (at the bottom, to the left
     * of the USB connector) whenever a frame takes longer to generate than the
     * time allotted per frame, as determined by the `setFrameRate()` function.
     *
     * Therefore, whenever the TX LED comes on (while not communicating over
     * USB), it indicates that the sketch is running slower than the desired
     * rate set by `setFrameRate()`. In this case the developer may wish to set
     * a slower frame rate, or reduce or optimize the code for such frames.
     *
     * \note
     * Once a sketch is ready for release, it would be expected that
     * `nextFrameDEV()` calls be restored to `nextFrame()`.
     *
     * \see nextFrame() cpuLoad() setFrameRate()
     */
    bool nextFrameDEV();
  
    /** \brief
     * Indicate if the specified number of frames has elapsed.
     *
     * \param frames The desired number of elapsed frames.
     *
     * \return `true` if the specified number of frames has elapsed.
     *
     * \details
     * This function should be called with the same value each time for a given
     * event. It will return `true` if the given number of frames has elapsed
     * since the previous frame in which it returned `true`.
     *
     * For example, if you wanted to fire a shot every 5 frames while the A button
     * is being held down:
     *
     * \code{.cpp}
     * if (arduboy.everyXFrames(5)) {
     *   if arduboy.pressed(A_BUTTON) {
     *     fireShot();
     *   }
     * }
     * \endcode
     *
     * \see setFrameRate() nextFrame()
     */
    bool everyXFrames(uint8_t frames);
  
    /** \brief
     * Return the load on the CPU as a percentage.
     *
     * \return The load on the CPU as a percentage of the total frame time.
     *
     * \details
     * The returned value gives the time spent processing a frame as a percentage
     * the total time allotted for a frame, as determined by the frame rate.
     *
     * This function normally wouldn't be used in the final program. It is
     * intended for use during program development as an aid in helping with
     * frame timing.
     *
     * \note
     * The percentage returned can be higher than 100 if more time is spent
     * processing a frame than the time allotted per frame. This would indicate
     * that the frame rate should be made slower or the frame processing code
     * should be optimized to run faster.
     *
     * \see setFrameRate() nextFrame()
     */
    int cpuLoad();
  
    /** \brief
     * Test if the specified buttons are pressed.
     *
     * \param buttons A bit mask indicating which buttons to test.
     * (Can be a single button)
     *
     * \return `true` if *all* buttons in the provided mask are currently pressed.
     *
     * \details
     * Read the state of the buttons and return `true` if all the buttons in the
     * specified mask are being pressed.
     *
     * Example: `if (pressed(LEFT_BUTTON + A_BUTTON))`
     *
     * \note
     * This function does not perform any button debouncing.
     */
    bool pressed(uint8_t buttons);
  
    /** \brief
     * Test if the specified buttons are not pressed.
     *
     * \param buttons A bit mask indicating which buttons to test.
     * (Can be a single button)
     *
     * \return `true` if *all* buttons in the provided mask are currently
     * released.
     *
     * \details
     * Read the state of the buttons and return `true` if all the buttons in the
     * specified mask are currently released.
     *
     * Example: `if (notPressed(UP_BUTTON))`
     *
     * \note
     * This function does not perform any button debouncing.
     */
    bool notPressed(uint8_t buttons);
  
    /** \brief
     * Poll the buttons and track their state over time.
     *
     * \details
     * Read and save the current state of the buttons and also keep track of the
     * button state when this function was previouly called. These states are
     * used by the `justPressed()` and `justReleased()` functions to determine
     * if a button has changed state between now and the previous call to
     * `pollButtons()`.
     *
     * This function should be called once at the start of each new frame.
     *
     * The `justPressed()` and `justReleased()` functions rely on this function.
     *
     * example:
     * \code{.cpp}
     * void loop() {
     *   if (!arduboy.nextFrame()) {
     *     return;
     *   }
     *   arduboy.pollButtons();
     *
     *   // use justPressed() as necessary to determine if a button was just pressed
     * \endcode
     *
     * \note
     * As long as the elapsed time between calls to this function is long
     * enough, buttons will be naturally debounced. Calling it once per frame at
     * a frame rate of 60 or lower (or possibly somewhat higher), should be
     * sufficient.
     *
     * \see justPressed() justReleased()
     */
    void pollButtons();
  
    /** \brief
     * Check if a button has just been pressed.
     *
     * \param button The button to test for. Only one button should be specified.
     *
     * \return `true` if the specified button has just been pressed.
     *
     * \details
     * Return `true` if the given button was pressed between the latest
     * call to `pollButtons()` and previous call to `pollButtons()`.
     * If the button has been held down over multiple polls, this function will
     * return `false`.
     *
     * There is no need to check for the release of the button since it must have
     * been released for this function to return `true` when pressed again.
     *
     * This function should only be used to test a single button.
     *
     * \see pollButtons() justReleased()
     */
    bool justPressed(uint8_t button);
  
    /** \brief
     * Check if a button has just been released.
     *
     * \param button The button to test for. Only one button should be specified.
     *
     * \return `true` if the specified button has just been released.
     *
     * \details
     * Return `true` if the given button, having previously been pressed,
     * was released between the latest call to `pollButtons()` and previous call
     * to `pollButtons()`. If the button has remained released over multiple
     * polls, this function will return `false`.
     *
     * There is no need to check for the button having been pressed since it must
     * have been previously pressed for this function to return `true` upon
     * release.
     *
     * This function should only be used to test a single button.
     *
     * \note
     * There aren't many cases where this function would be needed. Wanting to
     * know if a button has been released, without knowing when it was pressed,
     * is uncommon.
     *
     * \see pollButtons() justPressed()
     */
    bool justReleased(uint8_t button);
  
    /** \brief
     * Test if a point falls within a rectangle.
     *
     * \param point A structure describing the location of the point.
     * \param rect A structure describing the location and size of the rectangle.
     *
     * \return `true` if the specified point is within the specified rectangle.
     *
     * \details
     * This function is intended to detemine if an object, whose boundaries are
     * are defined by the given rectangle, is in contact with the given point.
     *
     * \see Point Rect
     */
    bool collide(Point point, Rect rect);
  
    /** \brief
     * Test if a rectangle is intersecting with another rectangle.
     *
     * \param rect1,rect2 Structures describing the size and locations of the
     * rectangles.
     *
     * \return `true` if the first rectangle is intersecting the second.
     *
     * \details
     * This function is intended to detemine if an object, whose boundaries are
     * are defined by the given rectangle, is in contact with another rectangular
     * object.
     *
     * \see Rect
     */
    bool collide(Rect rect1, Rect rect2);
  
    /** \brief
     * Read the unit ID from system EEPROM.
     *
     * \return The value of the unit ID stored in system EEPROM.
     *
     * \details
     * This function reads the unit ID that has been set in system EEPROM.
     * The ID can be any value. It is intended to allow different units to be
     * uniquely identified.
     *
     * \see writeUnitID() readUnitName()
     */
    uint16_t readUnitID();
  
    /** \brief
     * Write a unit ID to system EEPROM.
     *
     * \param id The value of the unit ID to be stored in system EEPROM.
     *
     * \details
     * This function writes a unit ID to a reserved location in system EEPROM.
     * The ID can be any value. It is intended to allow different units to be
     * uniquely identified.
     *
     * \see readUnitID() writeUnitName()
     */
    void writeUnitID(uint16_t id);
  
    /** \brief
     * Read the unit name from system EEPROM.
     *
     * \param name A pointer to a string array variable where the unit name will
     * be placed. The string will be up to 6 characters and terminated with a
     * null (0x00) character, so the provided array must be at least 7 bytes long.
     *
     * \return The length of the string (0-6).
     *
     * \details
     * This function reads the unit name that has been set in system EEPROM. The
     * name is in ASCII and can contain any values except 0xFF and the
     * null (0x00) terminator value.
     *
     * The name can be used for any purpose. It could identify the owner or
     * give the unit itself a nickname. A sketch could use it to automatically
     * fill in a name or initials in a high score table, or display it as the
     * "player" when the opponent is the computer.
     *
     * \note
     * Sketches can use the defined value `ARDUBOY_UNIT_NAME_LEN` instead of
     * hard coding a 6 when working with the unit name. For example, to allocate
     * a buffer and read the unit name into it:
     * \code{.cpp}
     * // Buffer for maximum name length plus the terminator
     * char unitName[ARDUBOY_UNIT_NAME_LEN + 1];
     *
     * // The actual name length
     * byte unitNameLength;
     *
     * unitNameLength = arduboy.readUnitName(unitName);
     * \endcode
     *
     * \see writeUnitName() readUnitID() Arduboy2::bootLogoExtra()
     */
    uint8_t readUnitName(char *name);
  
    /** \brief
     * Write a unit name to system EEPROM.
     *
     * \param name A pointer to a string array variable containing the unit name
     * to be saved. The string can be up to 6 characters and must be terminated
     * with a null (0x00) character. It can contain any values except 0xFF.
     *
     * \details
     * This function writes a unit name to a reserved area in system EEPROM.
     * The name is in ASCII and can contain any values except 0xFF and the
     * null (0x00) terminator value. The newline character (LF, \\n, 0x0A) and
     * carriage return character (CR, \\r, 0x0D) should also be avoided.
     *
     * The name can be used for any purpose. It could identify the owner or
     * give the unit itself a nickname. A sketch could use it to automatically
     * fill in a name or initials in a high score table, or display it as the
     * "player" when the opponent is the computer.
     *
     * \note
     * Sketches can use the defined value `ARDUBOY_UNIT_NAME_LEN` instead of
     * hard coding a 6 when working with the unit name.
     *
     * \see readUnitName() writeUnitID() Arduboy2::bootLogoExtra()
     */
    void writeUnitName(char *name);
  
    /** \brief
     * Read the "Show Boot Logo" flag in system EEPROM.
     *
     * \return `true` if the flag is set to indicate that the boot logo sequence
     * should be displayed. `false` if the flag is set to not display the
     * boot logo sequence.
     *
     * \details
     * The "Show Boot Logo" flag is used to determine whether the system
     * boot logo sequence is to be displayed when the system boots up.
     * This function returns the value of this flag.
     *
     * \see writeShowBootLogoFlag() bootLogo()
     */
    bool readShowBootLogoFlag();
  
    /** \brief
     * Write the "Show Boot Logo" flag in system EEPROM.
     *
     * \param val If `true` the flag is set to indicate that the boot logo
     * sequence should be displayed. If `false` the flag is set to not display
     * the boot logo sequence.
     *
     * \details
     * The "Show Boot Logo" flag is used to determine whether the system
     * boot logo sequence is to be displayed when the system boots up.
     * This function allows the flag to be saved with the desired value.
     *
     * \see readShowBootLogoFlag() bootLogo()
     */
    void writeShowBootLogoFlag(bool val);
  
    /** \brief
     * Read the "Show Unit Name" flag in system EEPROM.
     *
     * \return `true` if the flag is set to indicate that the unit name should
     * be displayed. `false` if the flag is set to not display the unit name.
     *
     * \details
     * The "Show Unit Name" flag is used to determine whether the system
     * unit name is to be displayed at the end of the boot logo sequence.
     * This function returns the value of this flag.
     *
     * \see writeShowUnitNameFlag() writeUnitName() readUnitName()
     * Arduboy2::bootLogoExtra()
     */
    bool readShowUnitNameFlag();
  
    /** \brief
     * Write the "Show Unit Name" flag in system EEPROM.
     *
     * \param val If `true` the flag is set to indicate that the unit name should
     * be displayed. If `false` the flag is set to not display the unit name.
     *
     * \details
     * The "Show Unit Name" flag is used to determine whether the system
     * unit name is to be displayed at the end of the boot logo sequence.
     * This function allows the flag to be saved with the desired value.
     *
     * \see readShowUnitNameFlag() writeUnitName() readUnitName()
     * Arduboy2::bootLogoExtra()
     */
    void writeShowUnitNameFlag(bool val);
  
    /** \brief
     * Read the "Show LEDs with boot logo" flag in system EEPROM.
     *
     * \return `true` if the flag is set to indicate that the RGB LEDs should be
     * flashed. `false` if the flag is set to leave the LEDs off.
     *
     * \details
     * The "Show LEDs with boot logo" flag is used to determine whether the
     * RGB LEDs should be flashed in sequence while the boot logo is being
     * displayed. This function returns the value of this flag.
     *
     * \see writeShowBootLogoLEDsFlag()
     */
    bool readShowBootLogoLEDsFlag();
  
    /** \brief
     * Write the "Show LEDs with boot logo" flag in system EEPROM.
     *
     * \param val If `true` the flag is set to indicate that the RGB LEDs should
     * be flashed. If `false` the flag is set to leave the LEDs off.
     *
     * \details
     * The "Show LEDs with boot logo" flag is used to determine whether the
     * RGB LEDs should be flashed in sequence while the boot logo is being
     * displayed. This function allows the flag to be saved with the desired
     * value.
     *
     * \see readShowBootLogoLEDsFlag()
     */
    void writeShowBootLogoLEDsFlag(bool val);
  
    /** \brief
     * A counter which is incremented once per frame.
     *
     * \details
     * This counter is incremented once per frame when using the `nextFrame()`
     * function. It will wrap to zero when it reaches its maximum value.
     *
     * It could be used to have an event occur for a given number of frames, or
     * a given number of frames later, in a way that wouldn't be quantized the
     * way that using `everyXFrames()` might.
     *
     * example:
     * \code{.cpp}
     * // move for 10 frames when right button is pressed, if not already moving
     * if (!moving) {
     *   if (arduboy.justPressed(RIGHT_BUTTON)) {
     *     endMoving = arduboy.frameCount + 10;
     *     moving = true;
     *   }
     * } else {
     *   movePlayer();
     *   if (arduboy.frameCount == endMoving) {
     *     moving = false;
     *   }
     * }
     * \endcode
     *
     * This counter could also be used to determine the number of frames that
     * have elapsed between events but the possibility of the counter wrapping
     * would have to be accounted for.
     *
     * \see nextFrame() everyXFrames()
     */
    uint16_t frameCount;
  
    /** \brief
     * The display buffer array in RAM.
     *
     * \details
     * The display buffer (also known as the screen buffer) contains an
     * image bitmap of the desired contents of the display, which is written
     * to the display using the `display()` function. The drawing functions of
     * this library manipulate the contents of the display buffer. A sketch can
     * also access the display buffer directly.
     *
     * \see getBuffer()
     */
  
    /** \brief
       * Get the current state of all buttons as a bitmask.
       *
       * \return A bitmask of the state of all the buttons.
       *
       * \details
       * The returned mask contains a bit for each button. For any pressed button,
       * its bit will be 1. For released buttons their associated bits will be 0.
       *
       * The following defined mask values should be used for the buttons:
       *
       * LEFT_BUTTON, RIGHT_BUTTON, UP_BUTTON, DOWN_BUTTON, A_BUTTON, B_BUTTON
       */
    uint8_t buttonsState();
  
    // redraws the full screen
    void redraw();
  
    // sets the global uin8_t externalButtons that stores the current Button States
    void setExternalButtons(uint8_t but);
  
    // sets a void function that is called whenever buttonState is called, can be used with the function above
    void setExternalButtonsHandler(void (*function)());
  
    // sets a uint8_t function that is called whenever buttonState is called and sets the externalButtons
    void setExternalButtonsFunction(uint8_t (*function)());
  
    uint32_t nextButtonsRead;
    uint8_t externalButtons;
    void (*externalButtonsHandler)();
    uint8_t (*externalButtonsFunction)();
  
    // moved form ArduboyCore, because it is now done in Display_xxxx class
    void paintScreen(const uint8_t *image);
    void paintScreen(uint8_t image[], bool clear);
  
    // just a pointer set in arduboy.begin(), the array is implemented in Display_xxxx class
    static uint8_t *sBuffer;
  
  protected:
    // helper function for sound enable/disable system control
    void sysCtrlSound(uint8_t buttons, uint8_t led, uint8_t eeVal);
  
    // functions passed to bootLogoShell() to draw the logo
    static void drawLogoBitmap(int16_t y);
    static void drawLogoCompressed(int16_t y);
    static void drawLogoSpritesSelfMasked(int16_t y);
    static void drawLogoSpritesOverwrite(int16_t y);
    static void drawLogoSpritesBSelfMasked(int16_t y);
    static void drawLogoSpritesBOverwrite(int16_t y);
  
    // For button handling
    uint8_t currentButtonState;
    uint8_t previousButtonState;
  
    // For frame funcions
    uint8_t eachFrameMillis;
    uint8_t thisFrameStart;
    bool justRendered;
    uint8_t lastFrameDurationMs;
  };
  
  //==============================
  //========== Arduboy2 ==========
  //==============================
  
  /** \brief
   * The main functions provided for writing sketches for the Arduboy,
   * _including_ text output.
   *
   * \details
   * This class is derived from Arduboy2Base. It provides text output functions
   * in addition to all the functions inherited from Arduboy2Base.
   *
   * \note
   * A friend class named _Arduboy2Ex_ is declared by this class. The intention
   * is to allow a sketch to create an _Arduboy2Ex_ class which would have access
   * to the private and protected members of the Arduboy2 class. It is hoped
   * that this may eliminate the need to create an entire local copy of the
   * library, in order to extend the functionality, in most circumstances.
   *
   * \see Arduboy2Base
   */
  class Arduboy2 : public Print, public Arduboy2Base
  {
    friend class Arduboy2Ex;
  
  public:
    Arduboy2();
  
    /** \class Print
     * \brief
     * The Arduino `Print` class is available for writing text to the screen
     * buffer.
     *
     * \details
     * For an `Arduboy2` class object, functions provided by the Arduino `Print`
     * class can be used to write text to the screen buffer, in the same manner
     * as the Arduino `Serial.print()`, etc., functions.
     *
     * Print will use the `write()` function to actually draw each character
     * in the screen buffer.
     *
     * See:
     * https://www.arduino.cc/en/Serial/Print
     *
     * Example:
     * \code{.cpp}
     * int value = 42;
     *
     * arduboy.println("Hello World"); // Prints "Hello World" and then moves the
     *                                 // text cursor to the start of the next line
     * arduboy.print(value);  // Prints "42"
     * arduboy.print('\n');   // Moves the text cursor to the start of the next line
     * arduboy.print(78, HEX) // Prints "4E" (78 in hexadecimal)
     * \endcode
     *
     * \see Arduboy2::write()
     */
  
    /** \brief
     * Display the boot logo sequence using printed text instead of a bitmap.
     *
     * \details
     * This function can be called by a sketch after `boot()` as an alternative
     * to `bootLogo()`.
     *
     * The Arduboy logo scrolls down from the top of the screen to the center
     * while the RGB LEDs light in sequence.
     *
     * This function is the same as `bootLogo()` except the logo is printed as
     * text instead of being rendered as a bitmap. It can be used to save some
     * code space in a case where the sketch is using the Print class functions
     * to display text. However, the logo will not look as good when printed as
     * text as it does with the bitmap used by `bootLogo()`.
     *
     * If the RIGHT button is pressed while the logo is scrolling down,
     * the boot logo sequence will be aborted. This can be useful for
     * developers who wish to quickly start testing, or anyone else who is
     * impatient and wants to go straight to the actual sketch.
     *
     * If the SYS_FLAG_SHOW_LOGO_LEDS flag in system EEPROM is cleared,
     * the RGB LEDs will not be flashed during the logo display sequence.
     *
     * If the SYS_FLAG_SHOW_LOGO flag in system EEPROM is cleared, this function
     * will return without executing the logo display sequence.
     *
     * \see bootLogo() boot() Arduboy2::bootLogoExtra()
     */
    void bootLogoText();
  
    /** \brief
     * Show the unit name at the bottom of the boot logo screen.
     *
     * \details
     * This function is called by `bootLogoShell()` and `bootLogoText()`.
     *
     * If a unit name has been saved in system EEPROM, it will be displayed at
     * the bottom of the screen. This function pauses for a short time to allow
     * the name to be seen.
     *
     * If the SYS_FLAG_UNAME flag in system EEPROM is cleared, this function
     * will return without showing the unit name or pausing.
     *
     * \note
     * This function would not normally be called directly from within a sketch
     * itself.
     *
     * \see readUnitName() writeUnitName() bootLogo() bootLogoShell()
     * bootLogoText() writeShowUnitNameFlag() begin()
     */
    virtual void bootLogoExtra();
  
    /** \brief
     * Write a single ASCII character at the current text cursor location.
     *
     * \param c The ASCII value of the character to be written.
     *
     * \return The number of characters written (will always be 1).
     *
     * \details
     * This is the Arduboy implemetation of the Arduino virtual `write()`
     * function. The single ASCII character specified is written to the
     * the screen buffer at the current text cursor. The text cursor is then
     * moved to the next character position in the screen buffer. This new cursor
     * position will depend on the current text size and possibly the current
     * wrap mode.
     *
     * Two special characters are handled:
     *
     * - The newline character `\n`. This will move the text cursor to the start
     *   of the next line based on the current text size.
     * - The carriage return character `\r`. This character will be ignored.
     *
     * \note
     * This function is rather low level and, although it's available as a public
     * function, it wouldn't normally be used. In most cases the Arduino Print
     * class should be used for writing text.
     *
     * \see Print setTextSize() setTextWrap()
     */
    virtual size_t write(uint8_t);
  
    /** \brief
     * Draw a single ASCII character at the specified location in the screen
     * buffer.
     *
     * \param x The X coordinate, in pixels, for where to draw the character.
     * \param y The Y coordinate, in pixels, for where to draw the character.
     * \param c The ASCII value of the character to be drawn.
     * \param color the forground color of the character.
     * \param bg the background color of the character.
     * \param size The size of the character to draw.
     *
     * \details
     * The specified ASCII character is drawn starting at the provided
     * coordinate. The point specified by the X and Y coordinates will be the
     * top left corner of the character.
     *
     * \note
     * This is a low level function used by the `write()` function to draw a
     * character. Although it's available as a public function, it wouldn't
     * normally be used. In most cases the Arduino Print class should be used for
     * writing text.
     *
     * \see Print write() setTextColor() setTextBackground() setTextSize()
     */
    void drawChar(int16_t x, int16_t y, unsigned char c, uint8_t color, uint8_t bg, uint8_t size);
  
    /** \brief
     * Set the location of the text cursor.
     *
     * \param x The X coordinate, in pixels, for the new location of the text cursor.
     * \param y The Y coordinate, in pixels, for the new location of the text cursor.
     *
     * \details
     * The location of the text cursor is set the the specified coordinates.
     * The coordinates are in pixels. Since the coordinates can specify any pixel
     * location, the text does not have to be placed on specific rows.
     * As with all drawing functions, location 0, 0 is the top left corner of
     * the display. The cursor location will be the top left corner of the next
     * character written.
     *
     * \see getCursorX() getCursorY()
     */
    void setCursor(int16_t x, int16_t y);
  
    /** \brief
     * Get the X coordinate of the current text cursor position.
     *
     * \return The X coordinate of the current text cursor position.
     *
     * \details
     * The X coordinate returned is a pixel location with 0 indicating the
     * leftmost column.
     *
     * \see getCursorY() setCursor()
     */
    int16_t getCursorX();
  
    /** \brief
     * Get the Y coordinate of the current text cursor position.
     *
     * \return The Y coordinate of the current text cursor position.
     *
     * \details
     * The Y coordinate returned is a pixel location with 0 indicating the
     * topmost row.
     *
     * \see getCursorX() setCursor()
     */
    int16_t getCursorY();
  
    /** \brief
     * Set the text foreground color.
     *
     * \param color The color to be used for following text.
     *
     * \see setTextBackground() getTextColor()
     */
    void setTextColor(uint8_t color);
  
    /** \brief
     * Get the currently set text foreground color.
     *
     * \return The color that will be used to display any following text.
     *
     * \see setTextColor()
     */
    uint8_t getTextColor();
  
    /** \brief
     * Set the text background color.
     *
     * \param bg The background color to be used for following text.
     *
     * \see setTextColor() getTextBackground()
     */
    void setTextBackground(uint8_t bg);
  
    /** \brief
     * Get the currently set text background color.
     *
     * \return The background color that will be used to display any following text.
     *
     * \see setTextBackground()
     */
    uint8_t getTextBackground();
  
    /** \brief
     * Set the text character size.
     *
     * \param s The text size multiplier. Must be 1 or higher.
     *
     * \details
     * Setting a text size of 1 will result in standard size characters which
     * occupy 6x8 pixels (the result of 5x7 characters with spacing on the
     * right and bottom edges).
     *
     * The value specified is a multiplier. A value of 2 will double the
     * size so they will occupy 12x16 pixels. A value of 3 will result in
     * 18x24, etc.
     *
     * \see getTextSize()
     */
    void setTextSize(uint8_t s);
  
    /** \brief
     * Get the currently set text size.
     *
     * \return The size that will be used for any following text.
     *
     * \see setTextSize()
     */
    uint8_t getTextSize();
  
    /** \brief
     * Set or disable text wrap mode.
     *
     * \param w `true` enables text wrap mode. `false` disables it.
     *
     * \details
     * Text wrap mode is enabled by specifying `true`. In wrap mode, the text
     * cursor will be moved to the start of the next line (based on the current
     * text size) if the following character wouldn't fit entirely at the end of
     * the current line.
  
     * If wrap mode is disabled, characters will continue to be written to the
     * same line. A character at the right edge of the screen may only be
     * partially displayed and additional characters will be off screen.
     *
     * \see getTextWrap()
     */
    void setTextWrap(bool w);
  
    /** \brief
     * Get the currently set text wrap mode.
     *
     * \return `true` if text wrapping is on, `false` if wrapping is off.
     *
     * \see setTextWrap()
     */
    bool getTextWrap();
  
    /** \brief
     * Clear the display buffer and set the text cursor to location 0, 0
     */
    void clear();
  
  #if defined(COUNT_LAST_FRAMES)
    uint16_t getFramesPerSecond();
    uint16_t framesNow = 0;
    uint16_t framesCurrent = 0;
    uint16_t secondsNow = 0;
    uint16_t secondsCurrent = 255;
  
    void printFramesPerSecond();
  #endif
  
  protected:
    int16_t cursor_x;
    int16_t cursor_y;
    uint8_t textColor;
    uint8_t textBackground;
    uint8_t textSize;
    bool textWrap;
  };
  
  #endif
  