//////////////////////////////////////////////////////////////////////////////// // / // 2012-2020 (c) Baical / // / // This library is free software; you can redistribute it and/or / // modify it under the terms of the GNU Lesser General Public / // License as published by the Free Software Foundation; either / // version 3.0 of the License, or (at your option) any later version. / // / // This library 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 / // Lesser General Public License for more details. / // / // You should have received a copy of the GNU Lesser General Public / // License along with this library. / // / //////////////////////////////////////////////////////////////////////////////// #ifndef PCONSOLE_H #define PCONSOLE_H #include #include #include #include #include #include "IConsole.h" #include "RBTree.h" #include "AList.h" //////////////////////////////////////////////////////////////////////////////// //Is_Key_Hit static __attribute__ ((unused)) tBOOL Is_Key_Hit(void) { struct termios oldt, newt; int ch; int oldf; tcgetattr(STDIN_FILENO, &oldt); newt = oldt; newt.c_lflag &= ~(ICANON | ECHO); tcsetattr(STDIN_FILENO, TCSANOW, &newt); oldf = fcntl(STDIN_FILENO, F_GETFL, 0); fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK); ch = getchar(); tcsetattr(STDIN_FILENO, TCSANOW, &oldt); fcntl(STDIN_FILENO, F_SETFL, oldf); if(ch != EOF) { ungetc(ch, stdin); return TRUE; } return FALSE; }//Is_Key_Hit //////////////////////////////////////////////////////////////////////////////// //Get_Char static __attribute__ ((unused)) tXCHAR Get_Char() { return getchar(); }//Get_Char //////////////////////////////////////////////////////////////////////////////// //SetEchoOff static __attribute__ ((unused)) void Set_EchoOff(tBOOL i_bOff) { struct termios termInfo; if (-1 == tcgetattr(0,&termInfo)) { return; } if (i_bOff) { termInfo.c_lflag &= ~ECHO; //turn off ECHO } else { termInfo.c_lflag |= ECHO; //turn on ECHO } tcsetattr(0, 0, &termInfo); }//Get_Char //////////////////////////////////////////////////////////////////////////////// struct stKeyMap { tUINT16 wKey; tUINT64 qwHash; stKeyMap(tUINT16 i_wKey, tUINT8 i_pv7, tUINT8 i_pv6, tUINT8 i_pv5, tUINT8 i_pv4, tUINT8 i_pv3, tUINT8 i_pv2, tUINT8 i_pv1, tUINT8 i_pv0 ) : wKey(i_wKey), qwHash(0) { qwHash = ((tUINT64)i_pv7 << 56) | ((tUINT64)i_pv6 << 48) | ((tUINT64)i_pv5 << 40) | ((tUINT64)i_pv4 << 32) | ((tUINT64)i_pv3 << 24) | ((tUINT64)i_pv2 << 16) | ((tUINT64)i_pv1 << 8) | (tUINT64)i_pv0; } stKeyMap(tUINT16 i_wKey, tUINT8 i_pv4, tUINT8 i_pv3, tUINT8 i_pv2, tUINT8 i_pv1, tUINT8 i_pv0 ) : wKey(i_wKey), qwHash(0) { qwHash = ((tUINT64)i_pv4 << 32) | ((tUINT64)i_pv3 << 24) | ((tUINT64)i_pv2 << 16) | ((tUINT64)i_pv1 << 8) | (tUINT64)i_pv0; } stKeyMap(tUINT16 i_wKey, tUINT8 i_pv3, tUINT8 i_pv2, tUINT8 i_pv1, tUINT8 i_pv0 ) : wKey(i_wKey), qwHash(0) { qwHash = ((tUINT64)i_pv3 << 24) | ((tUINT64)i_pv2 << 16) | ((tUINT64)i_pv1 << 8) | (tUINT64)i_pv0; } stKeyMap(tUINT16 i_wKey, tUINT8 i_pv2, tUINT8 i_pv1, tUINT8 i_pv0 ) : wKey(i_wKey), qwHash(0) { qwHash = ((tUINT64)i_pv2 << 16) | ((tUINT64)i_pv1 << 8) | (tUINT64)i_pv0; } stKeyMap(tUINT16 i_wKey, tUINT8 i_pv1, tUINT8 i_pv0 ) : wKey(i_wKey), qwHash(0) { qwHash = ((tUINT64)i_pv1 << 8) | (tUINT64)i_pv0; } }; //////////////////////////////////////////////////////////////////////////////// //CKeysTree class CKeysList : public CListPool { protected: virtual tBOOL Data_Release(char i_pData) { UNUSED_ARG(i_pData); return TRUE; } }; //////////////////////////////////////////////////////////////////////////////// //CKeysTree class CKeysTree: public CRBTree { public: //////////////////////////////////////////////////////////////////////////// //~CKeysTree virtual ~CKeysTree() { Clear(); }//~CRBTree protected: //////////////////////////////////////////////////////////////////////////// virtual tBOOL Data_Release(stKeyMap* i_pData) { delete i_pData; return TRUE; //memory used from pool, not necessary to return }// Data_Release //////////////////////////////////////////////////////////////////////////// //Return //TRUE - if (i_pKey < i_pData::key) //FALSE - otherwise tBOOL Is_Key_Less(tUINT64 i_pKey, stKeyMap *i_pData) { return (i_pKey < i_pData->qwHash); } //////////////////////////////////////////////////////////////////////////// //Return //TRUE - if (i_pKey == i_pData::key) //FALSE - otherwise tBOOL Is_Qual(tUINT64 i_pKey, stKeyMap *i_pData) { return (i_pKey == i_pData->qwHash); } };//CDesc_Tree //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// class CConsole : public IConsole { tUINT32 m_pCl[IConsole::eColorsMax]; size_t m_szText; tXCHAR *m_pText; tXCHAR *m_pEmpty; size_t m_szEmpty; CKeysTree m_pKeys; CKeysList m_cKeysList; public: CConsole() { m_szText = 32768; m_pText = (tXCHAR*)malloc(m_szText * sizeof(tXCHAR)); m_szEmpty = 0; m_pEmpty = NULL; AllocEmptyString(1024); Set_EchoOff(TRUE); //https://en.wikipedia.org/wiki/ANSI_escape_code m_pCl[IConsole::eBlack] = 0; m_pCl[IConsole::eBlue] = 12; m_pCl[IConsole::eGreen] = 40; m_pCl[IConsole::eCyan] = 51; m_pCl[IConsole::eRed] = 124; m_pCl[IConsole::eMagenta] = 129; m_pCl[IConsole::eBrown] = 94; m_pCl[IConsole::eLightGray] = 252; m_pCl[IConsole::eDarkGray] = 244; m_pCl[IConsole::eLightBlue] = 33; m_pCl[IConsole::eLightGreen] = 48; m_pCl[IConsole::eLightCyan] = 195; m_pCl[IConsole::eLightRed] = 196; m_pCl[IConsole::eLightMagenta] = 201; m_pCl[IConsole::eYellow] = 11; m_pCl[IConsole::eWhite] = 15; stKeyMap *l_pKey = new stKeyMap(eKeyArrowLeft, 27,91,68); m_pKeys.Push(l_pKey, l_pKey->qwHash); l_pKey = new stKeyMap(eKeyArrowRight, 27,91,67); m_pKeys.Push(l_pKey, l_pKey->qwHash); l_pKey = new stKeyMap(eKeyArrowDown, 27,91,66); m_pKeys.Push(l_pKey, l_pKey->qwHash); l_pKey = new stKeyMap(eKeyArrowUp, 27,91,65); m_pKeys.Push(l_pKey, l_pKey->qwHash); l_pKey = new stKeyMap(eKeyPageUp, 27,91,53,126); m_pKeys.Push(l_pKey, l_pKey->qwHash); l_pKey = new stKeyMap(eKeyPageDown, 27,91,54,126); m_pKeys.Push(l_pKey, l_pKey->qwHash); l_pKey = new stKeyMap(eKeyHome, 27,79,72); m_pKeys.Push(l_pKey, l_pKey->qwHash); l_pKey = new stKeyMap(eKeyEnd, 27,79,70); m_pKeys.Push(l_pKey, l_pKey->qwHash); l_pKey = new stKeyMap(eKeyDel, 27,91,51,126); m_pKeys.Push(l_pKey, l_pKey->qwHash); l_pKey = new stKeyMap(eKeyInsert, 27,91,50,126); m_pKeys.Push(l_pKey, l_pKey->qwHash); l_pKey = new stKeyMap(eKeyF1, 27,79,80); m_pKeys.Push(l_pKey, l_pKey->qwHash); l_pKey = new stKeyMap(eKeyF2, 27,79,81); m_pKeys.Push(l_pKey, l_pKey->qwHash); l_pKey = new stKeyMap(eKeyF3, 27,79,82); m_pKeys.Push(l_pKey, l_pKey->qwHash); l_pKey = new stKeyMap(eKeyF4, 27,79,83); m_pKeys.Push(l_pKey, l_pKey->qwHash); l_pKey = new stKeyMap(eKeyF5, 27,91,49,53,126); m_pKeys.Push(l_pKey, l_pKey->qwHash); l_pKey = new stKeyMap(eKeyF6, 27,91,49,55,126); m_pKeys.Push(l_pKey, l_pKey->qwHash); l_pKey = new stKeyMap(eKeyF7, 27,91,49,56,126); m_pKeys.Push(l_pKey, l_pKey->qwHash); l_pKey = new stKeyMap(eKeyF8, 27,91,49,57,126); m_pKeys.Push(l_pKey, l_pKey->qwHash); l_pKey = new stKeyMap(eKeyF9, 27,91,50,48,126); m_pKeys.Push(l_pKey, l_pKey->qwHash); l_pKey = new stKeyMap(eKeyF10, 27,91,50,49,126); m_pKeys.Push(l_pKey, l_pKey->qwHash); l_pKey = new stKeyMap(eKeyF11, 27,91,50,51,126); m_pKeys.Push(l_pKey, l_pKey->qwHash); l_pKey = new stKeyMap(eKeyF12, 27,91,50,52,126); m_pKeys.Push(l_pKey, l_pKey->qwHash); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// virtual ~CConsole() { if (m_pText) { free(m_pText); m_pText = NULL; } if (m_pEmpty) { free(m_pEmpty); m_pEmpty = NULL; } m_pKeys.Clear(); m_cKeysList.Clear(TRUE); Set_EchoOff(FALSE); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// tBOOL SetCursor(tUINT32 i_uX, tUINT32 i_uY) { printf("\033[%d;%dH", i_uY+1, i_uX+1); return TRUE; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// tUINT32 GetCursorX() { return 0; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// tBOOL GetInput(IConsole::sInput &o_rInput) { while (Is_Key_Hit()) { m_cKeysList.Add_After(m_cKeysList.Get_Last(), Get_Char()); } if (!m_cKeysList.Count()) { return FALSE; } char l_cVal = m_cKeysList.Get_Data(m_cKeysList.Get_First()); tBOOL l_bReturn = FALSE; o_rInput.eEvent = IConsole::eKey; o_rInput.stKey.bDown = TRUE; o_rInput.stKey.uControls = 0; o_rInput.stKey.wCount = 1; o_rInput.stKey.wKey = l_cVal; if ( (27 == l_cVal) //escape sequence && (m_cKeysList.Count()) ) { tUINT64 l_qwHash = 0; pAList_Cell l_pEl = m_cKeysList.Get_First(); stKeyMap *l_pKey = NULL; tUINT32 l_uiCount = 0; while ( (l_pEl) && (8 > l_uiCount) ) { l_uiCount++; l_qwHash = (l_qwHash << 8) | m_cKeysList.Get_Data(l_pEl); l_pKey = m_pKeys.Find(l_qwHash); if (l_pKey) { o_rInput.stKey.wKey = l_pKey->wKey; l_bReturn = TRUE; while (l_pEl) //clear esc sequence used to identefy key { pAList_Cell l_pPrevEl = m_cKeysList.Get_Prev(l_pEl); m_cKeysList.Del(l_pEl, TRUE); l_pEl = l_pPrevEl; } break; } else { l_pEl = m_cKeysList.Get_Next(l_pEl); } } } if (!l_bReturn) //if it is not esc sequence - get first char and return it { if (10 == l_cVal) { o_rInput.stKey.wKey = (tUINT16)IConsole::eKeyEnter; } else if (9 == l_cVal) { o_rInput.stKey.wKey = (tUINT16)IConsole::eKeyTab; } else if (8 == l_cVal) { o_rInput.stKey.wKey = (tUINT16)IConsole::eKeyBackSpace; } l_bReturn = TRUE; m_cKeysList.Del(m_cKeysList.Get_First(), TRUE); } return l_bReturn; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// tUINT32 GetCursorY() { return 0; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// tUINT32 GetWidth() { struct winsize w; ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); return w.ws_col; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// tUINT32 GetHeight() { struct winsize w; ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); return w.ws_row; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// tBOOL SetTextAttr(IConsole::eColor i_eFg, IConsole::eColor i_eBg) { if ( (i_eFg < 0) || (i_eFg >= IConsole::eColorsMax) || (i_eBg < 0) || (i_eBg >= IConsole::eColorsMax) ) { return FALSE; } printf("\x1b[38;5;%dm\x1b[48;5;%dm", m_pCl[i_eFg], m_pCl[i_eBg]); return TRUE; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// tBOOL Clear() { printf("\033[H\033[J"); return TRUE; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// tBOOL Clear(tUINT32 i_uX, tUINT32 i_uY, tUINT32 i_uW, tUINT32 i_uH, IConsole::eColor i_eBg) { tUINT32 l_uiW = GetWidth(); tUINT32 l_uiH = GetHeight(); if ( (i_uX > l_uiW) || (i_uY > l_uiH) || (!i_uW) || (!i_uH) || (i_uW >= m_szEmpty) ) { return FALSE; } if ((i_uX + i_uW) > l_uiW) { i_uW = l_uiW - i_uX; } if ((i_uY + i_uH) > l_uiH) { i_uH = l_uiH - i_uY; } if (i_uW > m_szEmpty) { if (!AllocEmptyString((size_t)i_uW)) { return FALSE; } } if (!SetTextAttr(IConsole::eBlack, i_eBg)) { return FALSE; } SetCursor(i_uX, i_uY); tUINT32 l_uiXOld = i_uX; tUINT32 l_uiYOld = i_uY; while (i_uH--) { SetCursor(i_uX, i_uY++); m_pEmpty[i_uW] = 0; WriteTextB(m_pEmpty, i_uW); m_pEmpty[i_uW] = ' '; } SetCursor(l_uiXOld, l_uiYOld); return TRUE; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// size_t WriteTextB(const tXCHAR *i_pText, size_t i_szText) { UNUSED_ARG(i_szText); return (size_t)printf("%s", i_pText); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// size_t WriteTextB(IConsole::eColor i_eFg, IConsole::eColor i_eBg, size_t i_szLenMin, size_t i_szLenMax, size_t i_szOffset, tXCHAR *i_pText, size_t i_szText, size_t i_szTextMax ) { if ( (i_szLenMin > i_szLenMax) || (!i_pText) || (8 >= i_szTextMax) || (i_szText >= i_szTextMax) ) { return 0; } if (!SetTextAttr(i_eFg, i_eBg)) { return 0; } tXCHAR *l_pText = i_pText; if (!i_szText) { i_szText = PStrLen(i_pText); } //if there is offset, recalculate all inputs if (i_szOffset) { if (i_szOffset < i_szText) //set sext offset and recalculate all { l_pText = l_pText + i_szOffset; i_szText -= i_szOffset; i_szTextMax -= i_szOffset; i_szOffset = 0; *l_pText = TM('<'); } else //if (i_szOffset >= i_szText) { l_pText = m_pText; *l_pText = TM('<'); i_szText = 1; i_szTextMax= m_szText; } } if (i_szText > i_szLenMax) { i_szText = i_szLenMax; l_pText[i_szText-1] = TM('>'); } else if (i_szText < i_szLenMin) { if (i_szTextMax < (i_szLenMin + 1)) { PStrNCpy(m_pText, m_szText, l_pText, i_szText); l_pText = m_pText; i_szTextMax = m_szText; } tXCHAR *l_pIter = l_pText + i_szText; while (i_szText < i_szLenMin) { *l_pIter = TM(' '); l_pIter ++; i_szText++; } } if (i_szText < i_szTextMax) { l_pText[i_szText] = 0; } else { l_pText[i_szText-1] = 0; } return (size_t)printf("%s", l_pText); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// size_t WriteTextF(IConsole::eColor i_eFg, IConsole::eColor i_eBg, size_t i_szLenMin, size_t i_szLenMax, size_t i_szOffset, const tXCHAR *i_pFormat, va_list i_pVA ) { int l_iReturn = PVsnprintf(m_pText, m_szText, i_pFormat, i_pVA); if (0 > l_iReturn) { return 0; } return WriteTextB(i_eFg, i_eBg, i_szLenMin, i_szLenMax, i_szOffset, m_pText, (size_t)l_iReturn, m_szText); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// tBOOL Present() { return TRUE; } protected: //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// tBOOL AllocEmptyString(size_t i_szEmpty) { if (m_pEmpty) { free(m_pEmpty); } if (i_szEmpty > 4096) { i_szEmpty = 4096; } m_szEmpty = i_szEmpty; m_pEmpty = (tXCHAR*)malloc(m_szEmpty * sizeof(tXCHAR)); if (!m_pEmpty) { return FALSE; } tXCHAR *l_pEmpty = m_pEmpty; size_t l_szIterator = m_szEmpty; while (l_szIterator--) { *l_pEmpty = TM(' '); l_pEmpty++; } return TRUE; } }; #endif //PCONSOLE_H