From 21169eca91b0b2fb67349c038ca7bbbd392a9a0b Mon Sep 17 00:00:00 2001 From: Federico Terraneo <fede.tft@miosix.org> Date: Wed, 8 Dec 2021 23:41:33 +0100 Subject: [PATCH] Added er_oledm015 driver --- .../display_er_oledm015.cpp | 394 ++++++++++++++++++ .../display_er_oledm015/display_er_oledm015.h | 303 ++++++++++++++ _examples/display_er_oledm015/main.cpp | 53 +++ drivers/display_st7735.h | 6 +- line.h | 5 - 5 files changed, 752 insertions(+), 9 deletions(-) create mode 100644 _examples/display_er_oledm015/display_er_oledm015.cpp create mode 100644 _examples/display_er_oledm015/display_er_oledm015.h create mode 100644 _examples/display_er_oledm015/main.cpp diff --git a/_examples/display_er_oledm015/display_er_oledm015.cpp b/_examples/display_er_oledm015/display_er_oledm015.cpp new file mode 100644 index 0000000..edbc4cc --- /dev/null +++ b/_examples/display_er_oledm015/display_er_oledm015.cpp @@ -0,0 +1,394 @@ +/*************************************************************************** + * Copyright (C) 2021 by Terraneo Federico * + * * + * This program 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 2 of the License, or * + * (at your option) any later version. * + * * + * This program 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. * + * * + * As a special exception, if other files instantiate templates or use * + * macros or inline functions from this file, or you compile this file * + * and link it with other works to produce a work based on this file, * + * this file does not by itself cause the resulting work to be covered * + * by the GNU General Public License. However the source code for this * + * file must still be made available in accordance with the GNU General * + * Public License. This exception does not invalidate any other reasons * + * why a work based on this file might be covered by the GNU General * + * Public License. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, see <http://www.gnu.org/licenses/> * + ***************************************************************************/ + +#include "display_er_oledm015.h" +#include <miosix.h> +#include <algorithm> +#include <line.h> + +using namespace std; +using namespace miosix; +using namespace mxgui; + +#ifndef _BOARD_STM32F411CE_BLACKPILL +#warning "The SPI driver has only been tested on an STM32F411CE_BLACKPILL" +#endif + +//Display connection + +using cs = Gpio<GPIOB_BASE,7>; +using sck = Gpio<GPIOB_BASE,3>; //Used as HW SPI +using mosi = Gpio<GPIOB_BASE,5>; //Used as HW SPI +using dc = Gpio<GPIOB_BASE,6>; +using res = Gpio<GPIOB_BASE,4>; + +/** + * Send and receive a byte, thus returning only after transmission is complete + * \param x byte to send + * \return the received byte + */ +static unsigned char spi1sendRecv(unsigned char x=0) +{ + SPI1->DR=x; + while((SPI1->SR & SPI_SR_RXNE)==0) ; + return SPI1->DR; +} + +/** + * Send a byte only. + * NOTE: this function requires special care to use as + * - it returns before the byte has been transmitted, and if this is the last + * byte, you have to wait with spi1waitCompletion() before deasserting cs + * - as the received byte is ignored, the overrun flag gets set and it must be + * cleared (spi1waitCompletion() does that as well) + */ +static void spi1sendOnly(unsigned char x) +{ + //NOTE: data is sent after the function returns, watch out! + while((SPI1->SR & SPI_SR_TXE)==0) ; + SPI1->DR=x; +} + +/** + * Must be called after using spi1sendOnly(), complete the last byte transmission + */ +static void spi1waitCompletion() +{ + while(SPI1->SR & SPI_SR_BSY) ; + //Reading DR and then SR clears overrun flag + [[gnu::unused]] volatile int unused; + unused=SPI1->DR; + unused=SPI1->SR; +} + +/** + * Send a command to the display + * \param c command + */ +static void cmd(unsigned char c) +{ + dc::low(); + cs::low(); + spi1sendRecv(c); + cs::high(); + delayUs(1); +} + +/** + * Send data to the display + * \param d data + */ +static void dat(unsigned char d) +{ + dc::high(); + cs::low(); + spi1sendRecv(d); + cs::high(); + delayUs(1); +} + +static const int width=128, height=128; ///< Display size + +/** + * Set cursor to desired location + * \param point where to set cursor (0<=x<128, 0<=y<128) + */ +static void setCursor(Point p) +{ + #ifdef MXGUI_ORIENTATION_VERTICAL + cmd(0x15); dat(p.x()); dat(0x7f); // Set column address + cmd(0x75); dat(p.y()); dat(0x7f); // Set row address + #else //MXGUI_ORIENTATION_HORIZONTAL + cmd(0x15); dat(p.y()); dat(0x7f); // Set column address + cmd(0x75); dat(p.x()); dat(0x7f); // Set row address + #endif //Hardware doesn't seem to support mirroring +} + +/** + * Set a hardware window on the screen, optimized for writing text. + * The GRAM increment will be set to up-to-down first, then left-to-right which + * is the correct increment to draw fonts + * \param p1 upper left corner of the window + * \param p2 lower right corner of the window + */ +static void textWindow(Point p1, Point p2) +{ + #ifdef MXGUI_ORIENTATION_VERTICAL + cmd(0x15); dat(p1.x()); dat(p2.x()); // Set column address + cmd(0x75); dat(p1.y()); dat(p2.y()); // Set row address + cmd(0xa0); dat(0x67); + #else //MXGUI_ORIENTATION_HORIZONTAL + cmd(0x15); dat(p1.y()); dat(p2.y()); // Set column address + cmd(0x75); dat(p1.x()); dat(p2.x()); // Set row address + cmd(0xa0); dat(0x64); + #endif //Hardware doesn't seem to support mirroring +} + +/** + * Set a hardware window on the screen, optimized for drawing images. + * The GRAM increment will be set to left-to-right first, then up-to-down which + * is the correct increment to draw images + * \param p1 upper left corner of the window + * \param p2 lower right corner of the window + */ +static inline void imageWindow(Point p1, Point p2) +{ + #ifdef MXGUI_ORIENTATION_VERTICAL + cmd(0x15); dat(p1.x()); dat(p2.x()); // Set column address + cmd(0x75); dat(p1.y()); dat(p2.y()); // Set row address + cmd(0xa0); dat(0x66); + #else //MXGUI_ORIENTATION_HORIZONTAL + cmd(0x15); dat(p1.y()); dat(p2.y()); // Set column address + cmd(0x75); dat(p1.x()); dat(p2.x()); // Set row address + cmd(0xa0); dat(0x65); + #endif //Hardware doesn't seem to support mirroring +} + +// +// class DisplayErOledm015 +// + +namespace mxgui { + +DisplayErOledm015::DisplayErOledm015() : buffer(nullptr) +{ + { + FastInterruptDisableLock dLock; + cs::mode(Mode::OUTPUT); cs::high(); + sck::mode(Mode::ALTERNATE); sck::alternateFunction(5); + mosi::mode(Mode::ALTERNATE); mosi::alternateFunction(5); + dc::mode(Mode::OUTPUT); + res::mode(Mode::OUTPUT); + + RCC->APB2ENR |= RCC_APB2ENR_SPI1EN; + RCC_SYNC(); + } + + SPI1->CR1=SPI_CR1_SSM //No HW cs + | SPI_CR1_SSI + | SPI_CR1_SPE //SPI enabled + | SPI_CR1_BR_0 //SPI clock 50/4=12.5 MHz (Fmax=20MHz) + | SPI_CR1_MSTR;//Master mode + + res::high(); + Thread::sleep(1); + res::low(); + delayUs(100); + res::high(); + delayUs(100); + + static const unsigned char grayTable[]= + { + 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 21, 23, 25, 27, 29, 31, 33, 35, 37, 39, 42, 45, 48, + 51, 54, 57, 60, 63, 66, 69, 72, 76, 80, 84, 88, 92, 96,100,104, + 108,112,116,120,125,130,135,140,145,150,155,160,165,170,175,180 + }; + + cmd(0xfd); dat(0x12); // Disable command lock + cmd(0xfd); dat(0xb1); // Enable all commands + cmd(0xae); // Display OFF + cmd(0xa1); dat(0x00); // Set display start line + cmd(0xa2); dat(0x00); // Set display offset + cmd(0xa6); // Normal display mode + cmd(0xab); dat(0x01); // 8bit iface, 2.5V regulator ON + cmd(0xb1); dat(0x32); // Precharge phase2=3 phase1=5 + cmd(0xb3); dat(0xf1); // Oscillator 0xf divide by 2 + cmd(0xb4); dat(0xa0); dat(0xb5); dat(0x55); // External VSL + cmd(0xb6); dat(0x01); // Second precharge 1 DCLKS + cmd(0xb8); // Set gray table + for(unsigned int i=0;i<sizeof(grayTable);i++) dat(grayTable[i]); + cmd(0xbb); dat(0x17); // Precharge voltage ~0.5VCC + cmd(0xbe); dat(0x05); // VCOMH + cmd(0xc1); dat(0x88); dat(0x70); dat(0x88); // A B C brightness + cmd(0xc7); dat(0x0f); // Master brightness + cmd(0xca); dat(0x7f); // Duty 1:128 + clear(0); + cmd(0xaf); // Display ON + + setTextColor(make_pair(Color(0xffff),Color(0x0))); +} + +void DisplayErOledm015::doTurnOn() +{ + cmd(0xaf); +} + +void DisplayErOledm015::doTurnOff() +{ + cmd(0xae); +} + +void DisplayErOledm015::doSetBrightness(int brt) +{ + cmd(0xc7); dat(max(0,min(15,brt/6))); +} + +pair<short int, short int> DisplayErOledm015::doGetSize() const +{ + return make_pair(height,width); +} + +void DisplayErOledm015::write(Point p, const char *text) +{ + font.draw(*this,textColor,p,text); +} + +void DisplayErOledm015::clippedWrite(Point p, Point a, Point b, const char *text) +{ + font.clippedDraw(*this,textColor,p,a,b,text); +} + +void DisplayErOledm015::clear(Color color) +{ + clear(Point(0,0),Point(width-1,height-1),color); +} + +void DisplayErOledm015::clear(Point p1, Point p2, Color color) +{ + imageWindow(p1,p2); + doBeginPixelWrite(); + int numPixels=(p2.x()-p1.x()+1)*(p2.y()-p1.y()+1); + for(int i=0;i<numPixels;i++) doWritePixel(color); + doEndPixelWrite(); +} + +void DisplayErOledm015::beginPixel() {} + +void DisplayErOledm015::setPixel(Point p, Color color) +{ + //Can't move boilerplate to beginPixel, as can't do setCursor in between + setCursor(p); + doBeginPixelWrite(); + doWritePixel(color); + doEndPixelWrite(); +} + +void DisplayErOledm015::line(Point a, Point b, Color color) +{ + //Horizontal line speed optimization + if(a.y()==b.y()) + { + imageWindow(Point(min(a.x(),b.x()),a.y()), + Point(max(a.x(),b.x()),a.y())); + doBeginPixelWrite(); + int numPixels=abs(a.x()-b.x()); + for(int i=0;i<=numPixels;i++) doWritePixel(color); + doEndPixelWrite(); + return; + } + //Vertical line speed optimization + if(a.x()==b.x()) + { + textWindow(Point(a.x(),min(a.y(),b.y())), + Point(a.x(),max(a.y(),b.y()))); + doBeginPixelWrite(); + int numPixels=abs(a.y()-b.y()); + for(int i=0;i<=numPixels;i++) doWritePixel(color); + doEndPixelWrite(); + return; + } + //General case + Line::draw(*this,a,b,color); +} + +void DisplayErOledm015::scanLine(Point p, const Color *colors, unsigned short length) +{ + imageWindow(p,Point(width-1,p.y())); + doBeginPixelWrite(); + for(int i=0;i<length;i++) doWritePixel(colors[i]); + doEndPixelWrite(); +} + +Color *DisplayErOledm015::getScanLineBuffer() +{ + if(buffer==nullptr) buffer=new Color[getWidth()]; + return buffer; +} + +void DisplayErOledm015::scanLineBuffer(Point p, unsigned short length) +{ + scanLine(p,buffer,length); +} + +void DisplayErOledm015::drawImage(Point p, const ImageBase& img) +{ + img.draw(*this,p); +} + +void DisplayErOledm015::clippedDrawImage(Point p, Point a, Point b, const ImageBase& img) +{ + img.clippedDraw(*this,p,a,b); +} + +void DisplayErOledm015::drawRectangle(Point a, Point b, Color c) +{ + line(a,Point(b.x(),a.y()),c); + line(Point(b.x(),a.y()),b,c); + line(b,Point(a.x(),b.y()),c); + line(Point(a.x(),b.y()),a,c); +} + +DisplayErOledm015::pixel_iterator DisplayErOledm015::begin(Point p1, Point p2, + IteratorDirection d) +{ + if(p1.x()<0 || p1.y()<0 || p2.x()<0 || p2.y()<0) return pixel_iterator(); + if(p1.x()>=width || p1.y()>=height || p2.x()>=width || p2.y()>=height) + return pixel_iterator(); + if(p2.x()<p1.x() || p2.y()<p1.y()) return pixel_iterator(); + + if(d==DR) textWindow(p1,p2); + else imageWindow(p1,p2); + doBeginPixelWrite(); + + unsigned int numPixels=(p2.x()-p1.x()+1)*(p2.y()-p1.y()+1); + return pixel_iterator(numPixels); +} + +DisplayErOledm015::~DisplayErOledm015() {} + +void DisplayErOledm015::doBeginPixelWrite() +{ + cmd(0x5c); + dc::high(); + cs::low(); +} + +void DisplayErOledm015::doWritePixel(Color c) +{ + spi1sendOnly(c>>8); + spi1sendOnly(c); +} + +void DisplayErOledm015::doEndPixelWrite() +{ + spi1waitCompletion(); + cs::high(); + delayUs(1); +} + +} //namespace mxgui diff --git a/_examples/display_er_oledm015/display_er_oledm015.h b/_examples/display_er_oledm015/display_er_oledm015.h new file mode 100644 index 0000000..3380825 --- /dev/null +++ b/_examples/display_er_oledm015/display_er_oledm015.h @@ -0,0 +1,303 @@ +/*************************************************************************** + * Copyright (C) 2021 by Terraneo Federico * + * * + * This program 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 2 of the License, or * + * (at your option) any later version. * + * * + * This program 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. * + * * + * As a special exception, if other files instantiate templates or use * + * macros or inline functions from this file, or you compile this file * + * and link it with other works to produce a work based on this file, * + * this file does not by itself cause the resulting work to be covered * + * by the GNU General Public License. However the source code for this * + * file must still be made available in accordance with the GNU General * + * Public License. This exception does not invalidate any other reasons * + * why a work based on this file might be covered by the GNU General * + * Public License. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, see <http://www.gnu.org/licenses/> * + ***************************************************************************/ + +#pragma once + +#include <config/mxgui_settings.h> +#include "display.h" +#include "point.h" +#include "color.h" +#include "iterator_direction.h" +#include <cstdio> +#include <cstring> +#include <algorithm> + +namespace mxgui { + +//This display is 16 bit per pixel, check that the color depth is properly +//configured +#ifndef MXGUI_COLOR_DEPTH_16_BIT +#error The SSD1351 driver requires a color depth of 16bit per pixel +#endif + +class DisplayErOledm015 : public Display +{ +public: + /** + * Constructor. + */ + DisplayErOledm015(); + + /** + * Turn the display On after it has been turned Off. + * Display initial state is On. + */ + void doTurnOn() override; + + /** + * Turn the display Off. It can be later turned back On. + */ + void doTurnOff() override; + + /** + * Set display brightness. Depending on the underlying driver, + * may do nothing. + * \param brt from 0 to 100 + */ + void doSetBrightness(int brt) override; + + /** + * \return a pair with the display height and width + */ + std::pair<short int, short int> doGetSize() const override; + + /** + * Write text to the display. If text is too long it will be truncated + * \param p point where the upper left corner of the text will be printed + * \param text, text to print. + */ + void write(Point p, const char *text) override; + + /** + * Write part of text to the display + * \param p point of the upper left corner where the text will be drawn. + * Negative coordinates are allowed, as long as the clipped view has + * positive or zero coordinates + * \param a Upper left corner of clipping rectangle + * \param b Lower right corner of clipping rectangle + * \param text text to write + */ + void clippedWrite(Point p, Point a, Point b, const char *text) override; + + /** + * Clear the Display. The screen will be filled with the desired color + * \param color fill color + */ + void clear(Color color) override; + + /** + * Clear an area of the screen + * \param p1 upper left corner of area to clear + * \param p2 lower right corner of area to clear + * \param color fill color + */ + void clear(Point p1, Point p2, Color color) override; + + /** + * This backend does not require it, so it is a blank. + */ + void beginPixel() override; + + /** + * Draw a pixel with desired color. You have to call beginPixel() once + * before calling setPixel() + * \param p point where to draw pixel + * \param color pixel color + */ + void setPixel(Point p, Color color) override; + + /** + * Draw a line between point a and point b, with color c + * \param a first point + * \param b second point + * \param c line color + */ + void line(Point a, Point b, Color color) override; + + /** + * Draw an horizontal line on screen. + * Instead of line(), this member function takes an array of colors to be + * able to individually set pixel colors of a line. + * \param p starting point of the line + * \param colors an array of pixel colors whoase size must be b.x()-a.x()+1 + * \param length length of colors array. + * p.x()+length must be <= display.width() + */ + void scanLine(Point p, const Color *colors, unsigned short length) override; + + /** + * \return a buffer of length equal to this->getWidth() that can be used to + * render a scanline. + */ + Color *getScanLineBuffer() override; + + /** + * Draw the content of the last getScanLineBuffer() on an horizontal line + * on the screen. + * \param p starting point of the line + * \param length length of colors array. + * p.x()+length must be <= display.width() + */ + void scanLineBuffer(Point p, unsigned short length) override; + + /** + * Draw an image on the screen + * \param p point of the upper left corner where the image will be drawn + * \param i image to draw + */ + void drawImage(Point p, const ImageBase& img) override; + + /** + * Draw part of an image on the screen + * \param p point of the upper left corner where the image will be drawn. + * Negative coordinates are allowed, as long as the clipped view has + * positive or zero coordinates + * \param a Upper left corner of clipping rectangle + * \param b Lower right corner of clipping rectangle + * \param i Image to draw + */ + void clippedDrawImage(Point p, Point a, Point b, const ImageBase& img) override; + + /** + * Draw a rectangle (not filled) with the desired color + * \param a upper left corner of the rectangle + * \param b lower right corner of the rectangle + * \param c color of the line + */ + void drawRectangle(Point a, Point b, Color c) override; + + /** + * Pixel iterator. A pixel iterator is an output iterator that allows to + * define a window on the display and write to its pixels. + */ + class pixel_iterator + { + public: + /** + * Default constructor, results in an invalid iterator. + */ + pixel_iterator(): pixelLeft(0) {} + + /** + * Set a pixel and move the pointer to the next one + * \param color color to set the current pixel + * \return a reference to this + */ + pixel_iterator& operator= (Color color) + { + pixelLeft--; + DisplayErOledm015::doWritePixel(color); + if(pixelLeft==0) DisplayErOledm015::doEndPixelWrite(); + return *this; + } + + /** + * Compare two pixel_iterators for equality. + * They are equal if they point to the same location. + */ + bool operator== (const pixel_iterator& itr) + { + return this->pixelLeft==itr.pixelLeft; + } + + /** + * Compare two pixel_iterators for inequality. + * They different if they point to different locations. + */ + bool operator!= (const pixel_iterator& itr) + { + return this->pixelLeft!=itr.pixelLeft; + } + + /** + * \return a reference to this. + */ + pixel_iterator& operator* () { return *this; } + + /** + * \return a reference to this. Does not increment pixel pointer. + */ + pixel_iterator& operator++ () { return *this; } + + /** + * \return a reference to this. Does not increment pixel pointer. + */ + pixel_iterator& operator++ (int) { return *this; } + + /** + * Must be called if not all pixels of the required window are going + * to be written. + */ + void invalidate() + { + DisplayErOledm015::doEndPixelWrite(); + } + + private: + /** + * Constructor + * \param start Upper left corner of window + * \param end Lower right corner of window + * \param direction Iterator direction + * \param disp Display we're associated + */ + pixel_iterator(unsigned int pixelLeft): pixelLeft(pixelLeft) {} + + unsigned int pixelLeft; ///< How many pixels are left to draw + + friend class DisplayErOledm015; //Needs access to ctor + }; + + /** + * Specify a window on screen and return an object that allows to write + * its pixels. + * Note: a call to begin() will invalidate any previous iterator. + * \param p1 upper left corner of window + * \param p2 lower right corner (included) + * \param d increment direction + * \return a pixel iterator + */ + pixel_iterator begin(Point p1, Point p2, IteratorDirection d); + + /** + * \return an iterator which is one past the last pixel in the pixel + * specified by begin. Behaviour is undefined if called before calling + * begin() + */ + pixel_iterator end() const + { + //Default ctor: pixelLeft is zero. + return pixel_iterator(); + } + + /** + * Destructor + */ + ~DisplayErOledm015(); + +private: + + static void doBeginPixelWrite(); + + static void doWritePixel(Color c); + + static void doEndPixelWrite(); + + Color *buffer; ///< For scanLineBuffer +}; + +} //namespace mxgui diff --git a/_examples/display_er_oledm015/main.cpp b/_examples/display_er_oledm015/main.cpp new file mode 100644 index 0000000..33806c9 --- /dev/null +++ b/_examples/display_er_oledm015/main.cpp @@ -0,0 +1,53 @@ + +#include <cstdio> +#include "miosix.h" +#include "mxgui/display.h" +#include "mxgui/misc_inst.h" +#include "display_er_oledm015.h" + +using namespace std; +using namespace miosix; +using namespace mxgui; + +//NOTE: to try this example, you have to connect the display to the board as +//written in display_er_oledm015.cpp + +/* + * On boards which do not have a built-in display, MXGUI requires you to + * implement the registerDisplayHook callback to tell MXGUI which display to + * use. If you want to adapt this example for a board that already has a + * display, you can register a secondary display in the main with the following + * line + * \code + * DisplayManager::instance().registerDisplay(new DisplayLy091wg14<sda,scl,reset>); + * \endcode + * And then get the display with DisplayManager::instance().getDisplay(1). + * Note that 0 is the default display, 1 would be the secondary one. + */ +namespace mxgui { +void registerDisplayHook(DisplayManager& dm) +{ + dm.registerDisplay(new DisplayErOledm015); +} +} //namespace mxgui + +int main() +{ + auto& display=DisplayManager::instance().getDisplay(); + { + DrawingContext dc(display); + dc.setFont(tahoma); + dc.write(Point(0,0),"Miosix OS"); + dc.write(Point(0,tahoma.getHeight()),"MXGUI graphics library"); + } + for(int i=0;;i++) + { + { + DrawingContext dc(display); + char s[16]; + sniprintf(s,15,"%02d:%02d",i/60,i%60); + dc.write(Point(0,2*tahoma.getHeight()),s); + } + Thread::sleep(1000); + } +} diff --git a/drivers/display_st7735.h b/drivers/display_st7735.h index d248460..02a8f37 100644 --- a/drivers/display_st7735.h +++ b/drivers/display_st7735.h @@ -329,14 +329,12 @@ protected: private: - #if defined MXGUI_ORIENTATION_VERTICAL + #ifdef MXGUI_ORIENTATION_VERTICAL static const short int width = 128; static const short int height = 160; - #elif defined MXGUI_ORIENTATION_HORIZONTAL + #else //MXGUI_ORIENTATION_HORIZONTAL static const short int width = 160; static const short int height = 128; - #else - #error Orientation not defined #endif /** diff --git a/line.h b/line.h index 035fa42..ca9a225 100644 --- a/line.h +++ b/line.h @@ -25,11 +25,6 @@ * along with this program; if not, see <http://www.gnu.org/licenses/> * ***************************************************************************/ -#ifndef MXGUI_LIBRARY -#error "This is header is private, it can be used only within mxgui." -#error "If your code depends on a private header, it IS broken." -#endif //MXGUI_LIBRARY - #include "point.h" #include "color.h" -- GitLab