<commit_before>/* -*- coding: utf-8; mode: c++; tab-width: 3; indent-tabs-mode: nil -*-

Copyright 2014
Raffaello D. Di Napoli

This file is part of Abaclade.

Abaclade is free software: you can redistribute it and/or modify it under the terms of the GNU
General Public License as published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.

Abaclade is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
Public License for more details.

You should have received a copy of the GNU General Public License along with Abaclade. If not, see
<http://www.gnu.org/licenses/>.
--------------------------------------------------------------------------------------------------*/

#include <abaclade.hxx>
#include <abaclade/text/ansi_escape_parser.hxx>

////////////////////////////////////////////////////////////////////////////////////////////////////
// abc::text::ansi_escape_parser

namespace abc {
namespace text {

ansi_escape_parser::ansi_escape_parser() :
   m_state(state::not_in_sequence) {
}

ansi_escape_parser::~ansi_escape_parser() {
}

bool ansi_escape_parser::got_one_argument(std::int16_t iDefault0) {
   ABC_TRACE_FUNC(this, iDefault0);

   if (m_viCmdArgs.size() == 0) {
      m_viCmdArgs.append(iDefault0);
   }
   return m_viCmdArgs.size() == 1;
}

bool ansi_escape_parser::got_two_arguments(std::int16_t iDefault0, std::int16_t iDefault1) {
   ABC_TRACE_FUNC(this, iDefault0, iDefault1);

   if (m_viCmdArgs.size() == 0) {
      m_viCmdArgs.append(iDefault0);
   }
   if (m_viCmdArgs.size() == 1) {
      m_viCmdArgs.append(iDefault1);
   }
   return m_viCmdArgs.size() == 2;
}

bool ansi_escape_parser::consume_sequence_char(char_t ch) {
   ABC_TRACE_FUNC(this, ch);

   switch (m_state.base()) {
      case state::escape:
         if (ch == '[' || ch == ']') {
            // Reinitialize the argument storage, preparing to parse the rest of the sequence.
            m_chSeqStart = ch;
            m_viCmdArgs.clear();
            m_sCmdArg.clear();
            m_state = state::bracket;
         } else if (ch == ')' || ch == '(') {
            m_state = state::ignore;
         } else if (ch == '\x1b') {
            // Multiple ESC characters are not counted; any other character ends the sequence.
            m_state = state::not_in_sequence;
            return false;
         }
         break;

      case state::bracket:
         if (ch >= '0' && ch <= '9') {
            m_viCmdArgs.append(static_cast<std::int16_t>(ch - '0'));
            m_state = state::numeric_arg;
         } else if (ch == ';') {
            m_viCmdArgs.append(0);
         } else if (ch == '?') {
            m_chSeqStart = ch;
         } else {
            run_sequence(ch);
            m_state = state::not_in_sequence;
            return false;
         }
         break;

      case state::numeric_arg:
         if (ch >= '0' && ch <= '9') {
            std::int16_t * piLastCmdArg = (m_viCmdArgs.end() - 1).base();
            *piLastCmdArg = static_cast<std::int16_t>(
               *piLastCmdArg * std::int16_t(10) + (ch - '0')
            );
         } else if (ch == ';') {
            m_viCmdArgs.append(0);
            if (m_chSeqStart == ']') {
               m_state = state::string_arg;
            }
         } else {
            run_sequence(ch);
            m_state = state::not_in_sequence;
            return false;
         }
         break;

      case state::string_arg:
         if (ch == '\b' || (ch == '\\' && m_sCmdArg && *(m_sCmdArg.cend() - 1) == '\x1b')) {
            run_sequence(ch);
            m_state = state::not_in_sequence;
         } else {
            m_sCmdArg += ch;
         }
         break;

      case state::ignore:
         m_state = state::not_in_sequence;
         break;

      default:
         break;
   }
   return true;
}

void ansi_escape_parser::run_erase_display_sequence(int iMode) {
   ABC_TRACE_FUNC(this, iMode);

   std::int16_t iRow, iCol, cRows, cCols;
   get_cursor_pos_and_display_size(&iRow, &iCol, &cRows, &cCols);
   if (iMode == 0) {
      // Erase from the cursor position to end of the display.
      clear_display_area(
         iRow, iCol,
         static_cast<std::size_t>(cCols - iCol) +
            static_cast<std::size_t>(cCols) * static_cast<std::size_t>(cRows - iRow - 1)
      );
   } else if (iMode == 1) {
      // Erase from row 0 up to the cursor position.
      clear_display_area(
         0, 0, static_cast<std::size_t>(cCols) * static_cast<std::size_t>(iRow + iCol)
      );
   } else if (iMode == 2) {
      // Erase the entire display.
      clear_display_area(0, 0, static_cast<std::size_t>(cCols) * static_cast<std::size_t>(cRows));
   }
}

void ansi_escape_parser::run_erase_row_sequence(int iMode) {
   ABC_TRACE_FUNC(this, iMode);

   std::int16_t iRow, iCol, cRows, cCols;
   get_cursor_pos_and_display_size(&iRow, &iCol, &cRows, &cCols);
   if (iMode == 0) {
      // Erase from the cursor position till the end of the row.
      clear_display_area(iRow, iCol, static_cast<std::size_t>(cCols - iCol));
   } else if (iMode == 1) {
      // Erase from column 0 of the cursor row up to the cursor position.
      clear_display_area(iRow, 0, static_cast<std::size_t>(iCol));
   } else if (iMode == 2) {
      // Erase current row.
      clear_display_area(iRow, 0, static_cast<std::size_t>(cCols));
   }
}

void ansi_escape_parser::run_sequence(char_t chCmd) {
   ABC_TRACE_FUNC(this, chCmd);

   if (m_chSeqStart == '[') {
      switch (chCmd) {
         case 'A': // Move cursor up N rows.
            if (got_one_argument(1)) {
               safe_set_cursor_pos(-m_viCmdArgs[0], 0);
            }
            break;
         case 'B': // Move cursor down N rows.
            if (got_one_argument(1)) {
               safe_set_cursor_pos(m_viCmdArgs[0], 0);
            }
            break;
         case 'C': // Move cursor right N columns.
            if (got_one_argument(1)) {
               safe_set_cursor_pos(0, m_viCmdArgs[0]);
            }
            break;
         case 'D': // Move cursor left N columns.
            if (got_one_argument(1)) {
               safe_set_cursor_pos(0, -m_viCmdArgs[0]);
            }
            break;
         case 'E': // Move cursor down N rows, column 1.
            if (got_one_argument(1)) {
               safe_set_cursor_pos(m_viCmdArgs[0], 0, false, true);
            }
            break;
         case 'F': // Move cursor up N rows, column 1.
            if (got_one_argument(1)) {
               safe_set_cursor_pos(-m_viCmdArgs[0], 0, false, true);
            }
            break;
         case 'G': // Move cursor to column N.
            if (got_one_argument(1)) {
               safe_set_cursor_pos(0, m_viCmdArgs[0] - 1, false, true);
            }
            break;
         case 'H': // Move cursor to row N, column M.
         case 'f': // Move cursor to row N, column M.
            if (got_two_arguments(1, 1)) {
               safe_set_cursor_pos(m_viCmdArgs[0] - 1, m_viCmdArgs[1] - 1, true, true);
            }
            break;
         case 'J': // Erase the display from the cursor down (N=0), up (N=1), or everything (N=2).
            if (got_one_argument(0)) {
               run_erase_display_sequence(m_viCmdArgs[0]);
            }
            break;
         case 'K': // Erase chars right (N=0), left (N=1) or both (N=2) sides of the current column.
            if (got_one_argument(0)) {
               run_erase_row_sequence(m_viCmdArgs[0]);
            }
            break;
         case 'S': // Scroll text up N rows.
            if (got_one_argument(1)) {
               scroll_text(static_cast<std::int16_t>(-m_viCmdArgs[0]), 0);
            }
            break;
         case 'T': // Scroll text down N rows.
            if (got_one_argument(1)) {
               scroll_text(m_viCmdArgs[0], 0);
            }
            break;
         case 'd': // Move cursor to row N.
            if (got_one_argument(1)) {
               safe_set_cursor_pos(m_viCmdArgs[0] - 1, 0, true, false);
            }
            break;
         case 'm': // Set character attributes.
            got_one_argument(0);
            run_set_char_attributes_sequence();
            break;
         case 's': // Save cursor position.
            if (!m_viCmdArgs) {
               get_cursor_pos_and_display_size(&m_iSavedRow, &m_iSavedCol, nullptr, nullptr);
            }
            break;
         case 'u': // Restore saved cursor position.
            if (!m_viCmdArgs) {
               safe_set_cursor_pos(m_iSavedRow, m_iSavedCol, true, true);
            }
            break;
      }
   } else if (m_chSeqStart == ']') {
      if (m_viCmdArgs.size() == 1 && m_viCmdArgs[0] == 0) {
         set_window_title(m_sCmdArg);
      }
   } else if (m_chSeqStart == '?') {
      if ((chCmd == 'h' || chCmd == 'l') && m_viCmdArgs.size() == 1 && m_viCmdArgs[0] == 25) {
         set_cursor_visibility(chCmd == 'h');
      }
   }
}

void ansi_escape_parser::run_set_char_attributes_sequence() {
   ABC_TRACE_FUNC(this);

   for (auto it(m_viCmdArgs.cbegin()); it != m_viCmdArgs.cend(); ++it) {
      std::int16_t iCmdArg = *it;
      switch (iCmdArg) {
         case 0:
         case 39:
         case 49:
            if (iCmdArg != 49) {
               m_chattrCurr.clrForeground = m_chattrDefault.clrForeground;
            }
            if (iCmdArg != 39) {
               m_chattrCurr.clrBackground = m_chattrDefault.clrBackground;
            }
            if (iCmdArg == 0) {
               m_chattrCurr.iBlinkSpeed   = m_chattrDefault.iBlinkSpeed;
               m_chattrCurr.bConcealed    = m_chattrDefault.bConcealed;
               m_chattrCurr.bCrossedOut   = m_chattrDefault.bCrossedOut;
               m_chattrCurr.iIntensity    = m_chattrDefault.iIntensity;
               m_chattrCurr.bItalic       = m_chattrDefault.bItalic;
               m_chattrCurr.bReverseVideo = m_chattrDefault.bReverseVideo;
               m_chattrCurr.iUnderline    = m_chattrDefault.iUnderline;
            }
            break;
         case  1: m_chattrCurr.iIntensity    = 2;     break;
         case  2: m_chattrCurr.iIntensity    = 0;     break;
         case  3: m_chattrCurr.bItalic       = true;  break;
         case  4: m_chattrCurr.iUnderline    = 1;     break;
         case  5: m_chattrCurr.iBlinkSpeed   = 1;     break;
         case  6: m_chattrCurr.iBlinkSpeed   = 2;     break;
         case  7: m_chattrCurr.bReverseVideo = true;  break;
         case  8: m_chattrCurr.bConcealed    = true;  break;
         case  9: m_chattrCurr.bCrossedOut   = true;  break;
         case 21: // This would set double underline on rare terminals, but bold off on others.
         case 22: m_chattrCurr.iIntensity    = 1;     break;
         case 23: m_chattrCurr.bItalic       = false; break;
         case 24: m_chattrCurr.iUnderline    = 0;     break;
         case 25: m_chattrCurr.iBlinkSpeed   = 0;     break;
         case 27: m_chattrCurr.bReverseVideo = false; break;
         case 28: m_chattrCurr.bConcealed    = false; break;
         case 29: m_chattrCurr.bCrossedOut   = false; break;

         case 30: case 31: case 32: case 33: case 34: case 35: case 36: case 37:
            m_chattrCurr.clrForeground = static_cast<ansi_terminal_color::enum_type>(iCmdArg - 30);
            break;
         case 40: case 41: case 42: case 43: case 44: case 45: case 46: case 47:
            m_chattrCurr.clrBackground = static_cast<ansi_terminal_color::enum_type>(iCmdArg - 40);
            break;
      }
   }
   set_char_attributes();
}

void ansi_escape_parser::safe_set_cursor_pos(
   int iRow, int iCol, bool bAbsoluteRow /*= false*/, bool bAbsoluteCol /*= false*/
) {
   ABC_TRACE_FUNC(this, iRow, iCol, bAbsoluteRow, bAbsoluteCol);

   std::int16_t iCurrRow, iCurrCol, cRows, cCols;
   get_cursor_pos_and_display_size(&iCurrRow, &iCurrCol, &cRows, &cCols);

   // If iRow is relative, make it absolute; then clip it to the display height.
   if (!bAbsoluteRow) {
      iRow += iCurrRow;
   }
   if (iRow < 0) {
      if (bAbsoluteRow) {
         // A negative value in absolute mode means use the current value.
         iRow = iCurrRow;
      } else {
         iRow = 0;
      }
   } else if (iRow >= cRows) {
      iRow = cRows - 1;
   }

   // If iCol is relative, make it absolute; then clip it to the display width.
   if (!bAbsoluteCol) {
      iCol += iCurrCol;
   }
   if (iCol < 0) {
      if (bAbsoluteCol) {
         // A negative value in absolute mode means use the current value.
         iCol = iCurrCol;
      } else {
         iCol = 0;
      }
   } else if (iCol >= cCols) {
      iCol = cCols - 1;
   }

   set_cursor_pos(static_cast<std::int16_t>(iRow), static_cast<std::int16_t>(iCol));
}

} //namespace text
} //namespace abc

////////////////////////////////////////////////////////////////////////////////////////////////////

<commit_msg>Do consume the last character of an ANSI escape sequence<commit_after>/* -*- coding: utf-8; mode: c++; tab-width: 3; indent-tabs-mode: nil -*-

Copyright 2014
Raffaello D. Di Napoli

This file is part of Abaclade.

Abaclade is free software: you can redistribute it and/or modify it under the terms of the GNU
General Public License as published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.

Abaclade is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
Public License for more details.

You should have received a copy of the GNU General Public License along with Abaclade. If not, see
<http://www.gnu.org/licenses/>.
--------------------------------------------------------------------------------------------------*/

#include <abaclade.hxx>
#include <abaclade/text/ansi_escape_parser.hxx>

////////////////////////////////////////////////////////////////////////////////////////////////////
// abc::text::ansi_escape_parser

namespace abc {
namespace text {

ansi_escape_parser::ansi_escape_parser() :
   m_state(state::not_in_sequence) {
}

ansi_escape_parser::~ansi_escape_parser() {
}

bool ansi_escape_parser::got_one_argument(std::int16_t iDefault0) {
   ABC_TRACE_FUNC(this, iDefault0);

   if (m_viCmdArgs.size() == 0) {
      m_viCmdArgs.append(iDefault0);
   }
   return m_viCmdArgs.size() == 1;
}

bool ansi_escape_parser::got_two_arguments(std::int16_t iDefault0, std::int16_t iDefault1) {
   ABC_TRACE_FUNC(this, iDefault0, iDefault1);

   if (m_viCmdArgs.size() == 0) {
      m_viCmdArgs.append(iDefault0);
   }
   if (m_viCmdArgs.size() == 1) {
      m_viCmdArgs.append(iDefault1);
   }
   return m_viCmdArgs.size() == 2;
}

bool ansi_escape_parser::consume_sequence_char(char_t ch) {
   ABC_TRACE_FUNC(this, ch);

   switch (m_state.base()) {
      case state::escape:
         if (ch == '[' || ch == ']') {
            // Reinitialize the argument storage, preparing to parse the rest of the sequence.
            m_chSeqStart = ch;
            m_viCmdArgs.clear();
            m_sCmdArg.clear();
            m_state = state::bracket;
         } else if (ch == ')' || ch == '(') {
            m_state = state::ignore;
         } else if (ch == '\x1b') {
            // Multiple ESC characters are not counted; any other character ends the sequence.
            m_state = state::not_in_sequence;
            return false;
         }
         break;

      case state::bracket:
         if (ch >= '0' && ch <= '9') {
            m_viCmdArgs.append(static_cast<std::int16_t>(ch - '0'));
            m_state = state::numeric_arg;
         } else if (ch == ';') {
            m_viCmdArgs.append(0);
         } else if (ch == '?') {
            m_chSeqStart = ch;
         } else {
            run_sequence(ch);
            m_state = state::not_in_sequence;
            return false;
         }
         break;

      case state::numeric_arg:
         if (ch >= '0' && ch <= '9') {
            std::int16_t * piLastCmdArg = (m_viCmdArgs.end() - 1).base();
            *piLastCmdArg = static_cast<std::int16_t>(
               *piLastCmdArg * std::int16_t(10) + (ch - '0')
            );
         } else if (ch == ';') {
            m_viCmdArgs.append(0);
            if (m_chSeqStart == ']') {
               m_state = state::string_arg;
            }
         } else {
            run_sequence(ch);
            m_state = state::not_in_sequence;
            return true;
         }
         break;

      case state::string_arg:
         if (ch == '\b' || (ch == '\\' && m_sCmdArg && *(m_sCmdArg.cend() - 1) == '\x1b')) {
            run_sequence(ch);
            m_state = state::not_in_sequence;
         } else {
            m_sCmdArg += ch;
         }
         break;

      case state::ignore:
         m_state = state::not_in_sequence;
         break;

      default:
         break;
   }
   return true;
}

void ansi_escape_parser::run_erase_display_sequence(int iMode) {
   ABC_TRACE_FUNC(this, iMode);

   std::int16_t iRow, iCol, cRows, cCols;
   get_cursor_pos_and_display_size(&iRow, &iCol, &cRows, &cCols);
   if (iMode == 0) {
      // Erase from the cursor position to end of the display.
      clear_display_area(
         iRow, iCol,
         static_cast<std::size_t>(cCols - iCol) +
            static_cast<std::size_t>(cCols) * static_cast<std::size_t>(cRows - iRow - 1)
      );
   } else if (iMode == 1) {
      // Erase from row 0 up to the cursor position.
      clear_display_area(
         0, 0, static_cast<std::size_t>(cCols) * static_cast<std::size_t>(iRow + iCol)
      );
   } else if (iMode == 2) {
      // Erase the entire display.
      clear_display_area(0, 0, static_cast<std::size_t>(cCols) * static_cast<std::size_t>(cRows));
   }
}

void ansi_escape_parser::run_erase_row_sequence(int iMode) {
   ABC_TRACE_FUNC(this, iMode);

   std::int16_t iRow, iCol, cRows, cCols;
   get_cursor_pos_and_display_size(&iRow, &iCol, &cRows, &cCols);
   if (iMode == 0) {
      // Erase from the cursor position till the end of the row.
      clear_display_area(iRow, iCol, static_cast<std::size_t>(cCols - iCol));
   } else if (iMode == 1) {
      // Erase from column 0 of the cursor row up to the cursor position.
      clear_display_area(iRow, 0, static_cast<std::size_t>(iCol));
   } else if (iMode == 2) {
      // Erase current row.
      clear_display_area(iRow, 0, static_cast<std::size_t>(cCols));
   }
}

void ansi_escape_parser::run_sequence(char_t chCmd) {
   ABC_TRACE_FUNC(this, chCmd);

   if (m_chSeqStart == '[') {
      switch (chCmd) {
         case 'A': // Move cursor up N rows.
            if (got_one_argument(1)) {
               safe_set_cursor_pos(-m_viCmdArgs[0], 0);
            }
            break;
         case 'B': // Move cursor down N rows.
            if (got_one_argument(1)) {
               safe_set_cursor_pos(m_viCmdArgs[0], 0);
            }
            break;
         case 'C': // Move cursor right N columns.
            if (got_one_argument(1)) {
               safe_set_cursor_pos(0, m_viCmdArgs[0]);
            }
            break;
         case 'D': // Move cursor left N columns.
            if (got_one_argument(1)) {
               safe_set_cursor_pos(0, -m_viCmdArgs[0]);
            }
            break;
         case 'E': // Move cursor down N rows, column 1.
            if (got_one_argument(1)) {
               safe_set_cursor_pos(m_viCmdArgs[0], 0, false, true);
            }
            break;
         case 'F': // Move cursor up N rows, column 1.
            if (got_one_argument(1)) {
               safe_set_cursor_pos(-m_viCmdArgs[0], 0, false, true);
            }
            break;
         case 'G': // Move cursor to column N.
            if (got_one_argument(1)) {
               safe_set_cursor_pos(0, m_viCmdArgs[0] - 1, false, true);
            }
            break;
         case 'H': // Move cursor to row N, column M.
         case 'f': // Move cursor to row N, column M.
            if (got_two_arguments(1, 1)) {
               safe_set_cursor_pos(m_viCmdArgs[0] - 1, m_viCmdArgs[1] - 1, true, true);
            }
            break;
         case 'J': // Erase the display from the cursor down (N=0), up (N=1), or everything (N=2).
            if (got_one_argument(0)) {
               run_erase_display_sequence(m_viCmdArgs[0]);
            }
            break;
         case 'K': // Erase chars right (N=0), left (N=1) or both (N=2) sides of the current column.
            if (got_one_argument(0)) {
               run_erase_row_sequence(m_viCmdArgs[0]);
            }
            break;
         case 'S': // Scroll text up N rows.
            if (got_one_argument(1)) {
               scroll_text(static_cast<std::int16_t>(-m_viCmdArgs[0]), 0);
            }
            break;
         case 'T': // Scroll text down N rows.
            if (got_one_argument(1)) {
               scroll_text(m_viCmdArgs[0], 0);
            }
            break;
         case 'd': // Move cursor to row N.
            if (got_one_argument(1)) {
               safe_set_cursor_pos(m_viCmdArgs[0] - 1, 0, true, false);
            }
            break;
         case 'm': // Set character attributes.
            got_one_argument(0);
            run_set_char_attributes_sequence();
            break;
         case 's': // Save cursor position.
            if (!m_viCmdArgs) {
               get_cursor_pos_and_display_size(&m_iSavedRow, &m_iSavedCol, nullptr, nullptr);
            }
            break;
         case 'u': // Restore saved cursor position.
            if (!m_viCmdArgs) {
               safe_set_cursor_pos(m_iSavedRow, m_iSavedCol, true, true);
            }
            break;
      }
   } else if (m_chSeqStart == ']') {
      if (m_viCmdArgs.size() == 1 && m_viCmdArgs[0] == 0) {
         set_window_title(m_sCmdArg);
      }
   } else if (m_chSeqStart == '?') {
      if ((chCmd == 'h' || chCmd == 'l') && m_viCmdArgs.size() == 1 && m_viCmdArgs[0] == 25) {
         set_cursor_visibility(chCmd == 'h');
      }
   }
}

void ansi_escape_parser::run_set_char_attributes_sequence() {
   ABC_TRACE_FUNC(this);

   for (auto it(m_viCmdArgs.cbegin()); it != m_viCmdArgs.cend(); ++it) {
      std::int16_t iCmdArg = *it;
      switch (iCmdArg) {
         case 0:
         case 39:
         case 49:
            if (iCmdArg != 49) {
               m_chattrCurr.clrForeground = m_chattrDefault.clrForeground;
            }
            if (iCmdArg != 39) {
               m_chattrCurr.clrBackground = m_chattrDefault.clrBackground;
            }
            if (iCmdArg == 0) {
               m_chattrCurr.iBlinkSpeed   = m_chattrDefault.iBlinkSpeed;
               m_chattrCurr.bConcealed    = m_chattrDefault.bConcealed;
               m_chattrCurr.bCrossedOut   = m_chattrDefault.bCrossedOut;
               m_chattrCurr.iIntensity    = m_chattrDefault.iIntensity;
               m_chattrCurr.bItalic       = m_chattrDefault.bItalic;
               m_chattrCurr.bReverseVideo = m_chattrDefault.bReverseVideo;
               m_chattrCurr.iUnderline    = m_chattrDefault.iUnderline;
            }
            break;
         case  1: m_chattrCurr.iIntensity    = 2;     break;
         case  2: m_chattrCurr.iIntensity    = 0;     break;
         case  3: m_chattrCurr.bItalic       = true;  break;
         case  4: m_chattrCurr.iUnderline    = 1;     break;
         case  5: m_chattrCurr.iBlinkSpeed   = 1;     break;
         case  6: m_chattrCurr.iBlinkSpeed   = 2;     break;
         case  7: m_chattrCurr.bReverseVideo = true;  break;
         case  8: m_chattrCurr.bConcealed    = true;  break;
         case  9: m_chattrCurr.bCrossedOut   = true;  break;
         case 21: // This would set double underline on rare terminals, but bold off on others.
         case 22: m_chattrCurr.iIntensity    = 1;     break;
         case 23: m_chattrCurr.bItalic       = false; break;
         case 24: m_chattrCurr.iUnderline    = 0;     break;
         case 25: m_chattrCurr.iBlinkSpeed   = 0;     break;
         case 27: m_chattrCurr.bReverseVideo = false; break;
         case 28: m_chattrCurr.bConcealed    = false; break;
         case 29: m_chattrCurr.bCrossedOut   = false; break;

         case 30: case 31: case 32: case 33: case 34: case 35: case 36: case 37:
            m_chattrCurr.clrForeground = static_cast<ansi_terminal_color::enum_type>(iCmdArg - 30);
            break;
         case 40: case 41: case 42: case 43: case 44: case 45: case 46: case 47:
            m_chattrCurr.clrBackground = static_cast<ansi_terminal_color::enum_type>(iCmdArg - 40);
            break;
      }
   }
   set_char_attributes();
}

void ansi_escape_parser::safe_set_cursor_pos(
   int iRow, int iCol, bool bAbsoluteRow /*= false*/, bool bAbsoluteCol /*= false*/
) {
   ABC_TRACE_FUNC(this, iRow, iCol, bAbsoluteRow, bAbsoluteCol);

   std::int16_t iCurrRow, iCurrCol, cRows, cCols;
   get_cursor_pos_and_display_size(&iCurrRow, &iCurrCol, &cRows, &cCols);

   // If iRow is relative, make it absolute; then clip it to the display height.
   if (!bAbsoluteRow) {
      iRow += iCurrRow;
   }
   if (iRow < 0) {
      if (bAbsoluteRow) {
         // A negative value in absolute mode means use the current value.
         iRow = iCurrRow;
      } else {
         iRow = 0;
      }
   } else if (iRow >= cRows) {
      iRow = cRows - 1;
   }

   // If iCol is relative, make it absolute; then clip it to the display width.
   if (!bAbsoluteCol) {
      iCol += iCurrCol;
   }
   if (iCol < 0) {
      if (bAbsoluteCol) {
         // A negative value in absolute mode means use the current value.
         iCol = iCurrCol;
      } else {
         iCol = 0;
      }
   } else if (iCol >= cCols) {
      iCol = cCols - 1;
   }

   set_cursor_pos(static_cast<std::int16_t>(iRow), static_cast<std::int16_t>(iCol));
}

} //namespace text
} //namespace abc

////////////////////////////////////////////////////////////////////////////////////////////////////

<|endoftext|>


<commit_before>/***********************************************************************
filename:   FirstPersonHUD.cpp
created:    11/8/2012
author:     Lukas E Meindl
*************************************************************************/
/***************************************************************************
*   Copyright (C) 2004 - 2012 Paul D Turner & Thce CEGUI Development Team
*
*   Permission is hereby granted, free of charge, to any person obtaining
*   a copy of this software and associated documentation files (the
*   "Software"), to deal in the Software without restriction, including
*   without limitation the rights to use, copy, modify, merge, publish,
*   distribute, sublicense, and/or sell copies of the Software, and to
*   permit persons to whom the Software is furnished to do so, subject to
*   the following conditions:
*
*   The above copyright notice and this permission notice shall be
*   included in all copies or substantial portions of the Software.
*
*   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
*   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
*   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
*   IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
*   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
*   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
*   OTHER DEALINGS IN THE SOFTWARE.
***************************************************************************/
#include "FirstPersonHUD.h"

#include "CEGUI/CEGUI.h"

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

using namespace CEGUI;

struct GamePlate
{
    GamePlate(HUDDemo* hudDemo);
    ~GamePlate();

    void update(float timeSinceLastUpdate);
    int getPoints();

    CEGUI::Window* d_window;
    bool d_isComingFromRight;
    bool d_isDestroyed;
};

GamePlate::GamePlate(HUDDemo* hudDemo)
    : d_isDestroyed(false)
{
    d_window = hudDemo->spawnPlate();

    int randumNumber = rand() % 2;
    d_isComingFromRight = (randumNumber == 0 ? false : true);

    if(d_isComingFromRight)
    {
        d_window->setHorizontalAlignment(HA_RIGHT);
        d_window->setPosition(d_window->getPosition() + CEGUI::UVector2(cegui_reldim(0.2f), cegui_absdim(0.0f)));
    }
    else
        d_window->setPosition(d_window->getPosition() + CEGUI::UVector2(cegui_reldim(-0.2f), cegui_absdim(0.0f)));
}

GamePlate::~GamePlate()
{
    CEGUI::WindowManager& winMgr = CEGUI::WindowManager::getSingleton();
    winMgr.destroyWindow(d_window);
}

void GamePlate::update(float timeSinceLastUpdate)
{
    CEGUI::UVector2 positionOffset;

    if(d_isComingFromRight)
        positionOffset = CEGUI::UVector2(cegui_reldim(timeSinceLastUpdate * -0.11f), cegui_absdim(0.f));
    else
        positionOffset = CEGUI::UVector2(cegui_reldim(timeSinceLastUpdate * 0.11f), cegui_absdim(0.f));

    d_window->setPosition(d_window->getPosition() + positionOffset);

    const CEGUI::UVector2& position = d_window->getPosition();

    if(d_isComingFromRight)
    {
        if(position.d_x.d_scale < -1.2f)
            d_isDestroyed = true;
    }
    else
    {
        if(position.d_x.d_scale > 1.2f)
            d_isDestroyed = true;
    }
}

int GamePlate::getPoints()
{
    CEGUI::Window* window = d_window->getChild("ImageWindowObject");

    CEGUI::String objectImage = window->getProperty("Image");

    if(objectImage.compare(HUDDemo::s_imageNameBread) == 0)
        return 2;
    else if(objectImage.compare(HUDDemo::s_imageNamePoo) == 0)
        return -6;
    else if(objectImage.compare(HUDDemo::s_imageNameSteak) == 0)
        return -13;
    else if(objectImage.compare(HUDDemo::s_imageNamePrizza) == 0)
        return 3;
    else if(objectImage.compare(HUDDemo::s_imageNameVegPeople) == 0)
        return 1;
    else if(objectImage.compare(HUDDemo::s_imageNameVegFruits) == 0)
        return 88;

    return 0;
}

const CEGUI::String HUDDemo::s_imageNamePlate = "HUDDemo/Plate";
const CEGUI::String HUDDemo::s_imageNameBread = "HUDDemo/Bread";
const CEGUI::String HUDDemo::s_imageNamePoo = "HUDDemo/Poo";
const CEGUI::String HUDDemo::s_imageNamePrizza = "HUDDemo/Prizza";
const CEGUI::String HUDDemo::s_imageNameSteak = "HUDDemo/Steak";
const CEGUI::String HUDDemo::s_imageNameVegPeople = "HUDDemo/VegetablePeople";
const CEGUI::String HUDDemo::s_imageNameVegFruits = "HUDDemo/VegetablesAndFruits";

bool HUDDemo::initialise(CEGUI::GUIContext* guiContext)
{
    using namespace CEGUI;

    d_usedFiles = CEGUI::String(__FILE__);
    d_guiContext = guiContext;

    SchemeManager::getSingleton().createFromFile("HUDDemo.scheme");
    SchemeManager::getSingleton().createFromFile("Generic.scheme");
    SchemeManager::getSingleton().createFromFile("VanillaSkin.scheme");

    FontManager::getSingleton().createFromFile("DejaVuSans-14.font");
    FontManager::getSingleton().createFromFile("Jura-13.font");

    CEGUI::WindowManager& winMgr = CEGUI::WindowManager::getSingleton();
    // Load the HUDDemo Layout
    d_root = winMgr.loadLayoutFromFile("HUDDemo.layout");

    CEGUI::Window* bg = winMgr.createWindow("Generic/Image");
    if(!ImageManager::getSingleton().isDefined("HUDBackground"))
        ImageManager::getSingleton().addFromImageFile("HUDBackground", "HUDBackground.jpg");
    bg->setProperty("Image", "HUDBackground");
    bg->setSize(CEGUI::USize(cegui_reldim(1.0f), cegui_reldim(1.0f)));
    bg->setAspectMode(CEGUI::AM_EXPAND);
    bg->setAspectRatio(1.5f);
    bg->setMousePassThroughEnabled(true);
    bg->addChild(d_root);

    d_guiContext->setRootWindow(bg);

    setupMouseCursor();

    srand(static_cast<unsigned int >(time(0)));

    d_score = 0;
    updateScoreWindow();

    return true;
}

/*************************************************************************
Cleans up resources allocated in the initialiseSample call.
*************************************************************************/
void HUDDemo::deinitialise()
{
    while(!d_gamePlates.empty())
    {
        GamePlate* curPlate = d_gamePlates.back();
        delete curPlate;

        d_gamePlates.pop_back();
    }
}

void HUDDemo::onEnteringSample()
{
    d_score = 0;
    updateScoreWindow();
}

void HUDDemo::update(float timeSinceLastUpdate)
{
    static float timeSinceLastSpawn(0.0f);

    timeSinceLastSpawn += timeSinceLastUpdate;

    updateMouseCursor();

    if(timeSinceLastSpawn> 1.2f)
    {
        d_gamePlates.push_back(new GamePlate(this));
        timeSinceLastSpawn -= 1.2f;
    }

    updatePlates(timeSinceLastUpdate);

    delayDestroyWindows();

}

void HUDDemo::setupMouseCursor()
{
    CEGUI::WindowManager& winMgr = CEGUI::WindowManager::getSingleton();

    d_mouseCursorWnd = winMgr.createWindow("Generic/Image");
    d_mouseCursorWnd->setProperty("Image", "HUDDemo/Spoon");
    d_mouseCursorWnd->setAspectMode(CEGUI::AM_EXPAND);
    d_mouseCursorWnd->setAspectRatio(1.f);
    d_mouseCursorWnd->setSize(CEGUI::USize(cegui_absdim(0.0f), cegui_reldim(0.1f)));
    d_mouseCursorWnd->setAlwaysOnTop(true);
    d_mouseCursorWnd->setMousePassThroughEnabled(true);
    d_root->addChild(d_mouseCursorWnd);
}

void HUDDemo::updateMouseCursor()
{
    CEGUI::Vector2f position = d_guiContext->getMouseCursor().getPosition();

    // We want to position the image-window right top of the actual
    // cursor point so we add its height
    float absHeight = d_mouseCursorWnd->getPixelSize().d_height;
    position.d_y -= absHeight;

    d_mouseCursorWnd->setPosition(
        CEGUI::UVector2(
        cegui_absdim(position.d_x - 5.0f), cegui_absdim(position.d_y + 5.0f))
        );
}

CEGUI::Window* HUDDemo::spawnPlate()
{
    CEGUI::WindowManager& winMgr = CEGUI::WindowManager::getSingleton();

    CEGUI::Window* plateRoot = winMgr.createWindow("DefaultWindow");
    plateRoot->setSize(CEGUI::USize(cegui_absdim(0.0f), cegui_reldim(0.16f)));
    plateRoot->setAspectMode(CEGUI::AM_EXPAND);
    plateRoot->setAspectRatio(1.0f);
    plateRoot->setRiseOnClickEnabled(false);
    plateRoot->setPixelAligned(false);
    plateRoot->subscribeEvent(CEGUI::Window::EventMouseClick, Event::Subscriber(&HUDDemo::handlePlateWindowClicked, this));
    d_root->addChild(plateRoot);

    CEGUI::Window* plateImgWnd = winMgr.createWindow("Generic/Image", "ImageWindowPlate");
    plateImgWnd->setProperty("Image", s_imageNamePlate);
    plateImgWnd->setSize(CEGUI::USize(cegui_reldim(1.0f), cegui_absdim(0.0f)));
    plateImgWnd->setAspectRatio(3.308f);
    plateImgWnd->setAspectMode(CEGUI::AM_EXPAND);
    plateImgWnd->setVerticalAlignment(CEGUI::VA_BOTTOM);
    plateImgWnd->setMousePassThroughEnabled(true);
    plateImgWnd->setPixelAligned(false);
    plateRoot->addChild(plateImgWnd);

    CEGUI::String image = getRandomGameImage();

    CEGUI::Window* plateTopping = winMgr.createWindow("Generic/Image", "ImageWindowObject");
    plateTopping->setProperty("Image", image);
    plateTopping->setSize(CEGUI::USize(cegui_reldim(0.88f), cegui_absdim(0.0f)));
    plateTopping->setAspectRatio(1.0f);
    plateTopping->setAspectMode(CEGUI::AM_EXPAND);
    plateTopping->setHorizontalAlignment(CEGUI::HA_CENTRE);
    plateTopping->setMousePassThroughEnabled(true);
    plateTopping->setPixelAligned(false);
    plateRoot->addChild(plateTopping);

    int randumNumber = rand() % 10000;
    float posY = randumNumber / 10000.0f;

    plateRoot->setPosition(CEGUI::UVector2(cegui_absdim(0.0f), cegui_reldim(0.8f * posY)));

    return plateRoot;
}

const CEGUI::String& HUDDemo::getRandomGameImage()
{
    int randumNumber = rand() % 100;

    if(randumNumber > 88)
        return s_imageNamePoo;
    else if(randumNumber > 72)
        return s_imageNamePrizza;
    else if(randumNumber > 55)
        return s_imageNameSteak;
    else if(randumNumber > 52)
        return s_imageNameVegFruits;
    else if(randumNumber > 25)
        return s_imageNameVegPeople;
    else if(randumNumber > 0)
        return s_imageNameBread;

    return s_imageNameBread;
}

void HUDDemo::updatePlates(float timeSinceLastUpdate)
{
    unsigned int vectorSize = d_gamePlates.size();

    for(unsigned int i = 0; i < vectorSize;)
    {
        GamePlate* currentPlate = d_gamePlates[i];

        //CEGUI bug: this shouldnt be necessary
        currentPlate->d_window->invalidate();

        currentPlate->update(timeSinceLastUpdate);
        if(currentPlate->d_isDestroyed)
        {
            delete currentPlate;
            d_gamePlates[i] = d_gamePlates.back();
            d_gamePlates.pop_back();
            vectorSize = d_gamePlates.size();
        }
        else
            ++i;
    }

}

void HUDDemo::updateScoreWindow()
{
    CEGUI::Window* scoreWnd = d_root->getChild("ScoreCounter");
    scoreWnd->setText(CEGUI::PropertyHelper<int>::toString(d_score));
}

bool HUDDemo::handlePlateWindowClicked(const CEGUI::EventArgs& args)
{
    const CEGUI::MouseEventArgs& mouseArgs = static_cast<const CEGUI::MouseEventArgs&>(args);

    for(unsigned int i = 0; i < d_gamePlates.size(); ++i)
    {
        GamePlate* gamePlate = d_gamePlates[i];

        if(gamePlate->d_window == mouseArgs.window)
        {
            int points = gamePlate->getPoints();
            d_score += points;
            updateScoreWindow();

            gamePlate->d_isDestroyed = true;
            createScorePopup(mouseArgs.position, points);
        }
    }

    return false;
}

void HUDDemo::createScorePopup(const CEGUI::Vector2<float>& mousePos, int points)
{
    CEGUI::WindowManager& winMgr = CEGUI::WindowManager::getSingleton();

    CEGUI::Window* popupWindow = winMgr.createWindow("HUDDemo/PopupLabel");
    d_root->addChild(popupWindow);
    popupWindow->setPosition(CEGUI::UVector2(cegui_absdim(mousePos.d_x), cegui_absdim(mousePos.d_y)));
    popupWindow->setText(CEGUI::PropertyHelper<int>::toString(points));
    popupWindow->setRiseOnClickEnabled(false);
    popupWindow->subscribeEvent(AnimationInstance::EventAnimationEnded, Event::Subscriber(&HUDDemo::handleScorePopupAnimationEnded, this));
    popupWindow->setFont("DejaVuSans-14");
    popupWindow->setPixelAligned(false);

    popupWindow->setPosition(popupWindow->getPosition() + CEGUI::UVector2(cegui_reldim(0.03f), cegui_reldim(-0.02f)));

    if(points < 0)
        popupWindow->setProperty("NormalTextColour", "FFDD0000");
    else
    {
        popupWindow->setText( "+" + popupWindow->getText());
        popupWindow->setProperty("NormalTextColour", "FF00CC00");
    }

    CEGUI::EventArgs args;
    popupWindow->fireEvent("StartAnimation", args);
}

bool HUDDemo::handleScorePopupAnimationEnded(const CEGUI::EventArgs& args)
{
    const CEGUI::AnimationEventArgs& animArgs = static_cast<const CEGUI::AnimationEventArgs&>(args);

    CEGUI::Window* window = static_cast<CEGUI::Window*>( animArgs.instance->getTarget());
    d_delayDestroyWindows.push_back(window);

    return false;
}

void HUDDemo::delayDestroyWindows()
{
    CEGUI::WindowManager& winMgr = CEGUI::WindowManager::getSingleton();

    while(!d_delayDestroyWindows.empty())
    {
        CEGUI::Window* curWindow = d_delayDestroyWindows.back();
        winMgr.destroyWindow(curWindow);

        d_delayDestroyWindows.pop_back();
    }
}
/*************************************************************************
Define the module function that returns an instance of the sample
*************************************************************************/
extern "C" SAMPLE_EXPORT Sample& getSampleInstance()
{
    static HUDDemo sample;
    return sample;
}<commit_msg>FIX: Workaround for updating the HUDDemo while running<commit_after>/***********************************************************************
filename:   FirstPersonHUD.cpp
created:    11/8/2012
author:     Lukas E Meindl
*************************************************************************/
/***************************************************************************
*   Copyright (C) 2004 - 2012 Paul D Turner & Thce CEGUI Development Team
*
*   Permission is hereby granted, free of charge, to any person obtaining
*   a copy of this software and associated documentation files (the
*   "Software"), to deal in the Software without restriction, including
*   without limitation the rights to use, copy, modify, merge, publish,
*   distribute, sublicense, and/or sell copies of the Software, and to
*   permit persons to whom the Software is furnished to do so, subject to
*   the following conditions:
*
*   The above copyright notice and this permission notice shall be
*   included in all copies or substantial portions of the Software.
*
*   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
*   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
*   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
*   IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
*   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
*   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
*   OTHER DEALINGS IN THE SOFTWARE.
***************************************************************************/
#include "FirstPersonHUD.h"

#include "CEGUI/CEGUI.h"

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

using namespace CEGUI;

struct GamePlate
{
    GamePlate(HUDDemo* hudDemo);
    ~GamePlate();

    void update(float timeSinceLastUpdate);
    int getPoints();

    CEGUI::Window* d_window;
    bool d_isComingFromRight;
    bool d_isDestroyed;
};

GamePlate::GamePlate(HUDDemo* hudDemo)
    : d_isDestroyed(false)
{
    d_window = hudDemo->spawnPlate();

    int randumNumber = rand() % 2;
    d_isComingFromRight = (randumNumber == 0 ? false : true);

    if(d_isComingFromRight)
    {
        d_window->setHorizontalAlignment(HA_RIGHT);
        d_window->setPosition(d_window->getPosition() + CEGUI::UVector2(cegui_reldim(0.2f), cegui_absdim(0.0f)));
    }
    else
        d_window->setPosition(d_window->getPosition() + CEGUI::UVector2(cegui_reldim(-0.2f), cegui_absdim(0.0f)));
}

GamePlate::~GamePlate()
{
    CEGUI::WindowManager& winMgr = CEGUI::WindowManager::getSingleton();
    winMgr.destroyWindow(d_window);
}

void GamePlate::update(float timeSinceLastUpdate)
{
    CEGUI::UVector2 positionOffset;

    if(d_isComingFromRight)
        positionOffset = CEGUI::UVector2(cegui_reldim(timeSinceLastUpdate * -0.11f), cegui_absdim(0.f));
    else
        positionOffset = CEGUI::UVector2(cegui_reldim(timeSinceLastUpdate * 0.11f), cegui_absdim(0.f));

    d_window->setPosition(d_window->getPosition() + positionOffset);

    const CEGUI::UVector2& position = d_window->getPosition();

    if(d_isComingFromRight)
    {
        if(position.d_x.d_scale < -1.2f)
            d_isDestroyed = true;
    }
    else
    {
        if(position.d_x.d_scale > 1.2f)
            d_isDestroyed = true;
    }
}

int GamePlate::getPoints()
{
    CEGUI::Window* window = d_window->getChild("ImageWindowObject");

    CEGUI::String objectImage = window->getProperty("Image");

    if(objectImage.compare(HUDDemo::s_imageNameBread) == 0)
        return 2;
    else if(objectImage.compare(HUDDemo::s_imageNamePoo) == 0)
        return -6;
    else if(objectImage.compare(HUDDemo::s_imageNameSteak) == 0)
        return -13;
    else if(objectImage.compare(HUDDemo::s_imageNamePrizza) == 0)
        return 3;
    else if(objectImage.compare(HUDDemo::s_imageNameVegPeople) == 0)
        return 1;
    else if(objectImage.compare(HUDDemo::s_imageNameVegFruits) == 0)
        return 88;

    return 0;
}

const CEGUI::String HUDDemo::s_imageNamePlate = "HUDDemo/Plate";
const CEGUI::String HUDDemo::s_imageNameBread = "HUDDemo/Bread";
const CEGUI::String HUDDemo::s_imageNamePoo = "HUDDemo/Poo";
const CEGUI::String HUDDemo::s_imageNamePrizza = "HUDDemo/Prizza";
const CEGUI::String HUDDemo::s_imageNameSteak = "HUDDemo/Steak";
const CEGUI::String HUDDemo::s_imageNameVegPeople = "HUDDemo/VegetablePeople";
const CEGUI::String HUDDemo::s_imageNameVegFruits = "HUDDemo/VegetablesAndFruits";

bool HUDDemo::initialise(CEGUI::GUIContext* guiContext)
{
    using namespace CEGUI;

    d_usedFiles = CEGUI::String(__FILE__);
    d_guiContext = guiContext;

    SchemeManager::getSingleton().createFromFile("HUDDemo.scheme");
    SchemeManager::getSingleton().createFromFile("Generic.scheme");
    SchemeManager::getSingleton().createFromFile("VanillaSkin.scheme");

    FontManager::getSingleton().createFromFile("DejaVuSans-14.font");
    FontManager::getSingleton().createFromFile("Jura-13.font");

    CEGUI::WindowManager& winMgr = CEGUI::WindowManager::getSingleton();
    // Load the HUDDemo Layout
    d_root = winMgr.loadLayoutFromFile("HUDDemo.layout");

    CEGUI::Window* bg = winMgr.createWindow("Generic/Image");
    if(!ImageManager::getSingleton().isDefined("HUDBackground"))
        ImageManager::getSingleton().addFromImageFile("HUDBackground", "HUDBackground.jpg");
    bg->setProperty("Image", "HUDBackground");
    bg->setSize(CEGUI::USize(cegui_reldim(1.0f), cegui_reldim(1.0f)));
    bg->setAspectMode(CEGUI::AM_EXPAND);
    bg->setAspectRatio(1.5f);
    bg->setMousePassThroughEnabled(true);
    bg->addChild(d_root);

    d_guiContext->setRootWindow(bg);

    setupMouseCursor();

    srand(static_cast<unsigned int >(time(0)));

    d_score = 0;
    updateScoreWindow();

    return true;
}

/*************************************************************************
Cleans up resources allocated in the initialiseSample call.
*************************************************************************/
void HUDDemo::deinitialise()
{
    while(!d_gamePlates.empty())
    {
        GamePlate* curPlate = d_gamePlates.back();
        delete curPlate;

        d_gamePlates.pop_back();
    }
}

void HUDDemo::onEnteringSample()
{
    d_score = 0;
    updateScoreWindow();
}

void HUDDemo::update(float timeSinceLastUpdate)
{
    static float timeSinceLastSpawn(0.0f);

    timeSinceLastSpawn += timeSinceLastUpdate;

    updateMouseCursor();

    if(timeSinceLastSpawn> 1.2f)
    {
        d_gamePlates.push_back(new GamePlate(this));
        timeSinceLastSpawn -= 1.2f;
    }

    updatePlates(timeSinceLastUpdate);

    delayDestroyWindows();

    d_guiContext->markAsDirty();
}

void HUDDemo::setupMouseCursor()
{
    CEGUI::WindowManager& winMgr = CEGUI::WindowManager::getSingleton();

    d_mouseCursorWnd = winMgr.createWindow("Generic/Image");
    d_mouseCursorWnd->setProperty("Image", "HUDDemo/Spoon");
    d_mouseCursorWnd->setAspectMode(CEGUI::AM_EXPAND);
    d_mouseCursorWnd->setAspectRatio(1.f);
    d_mouseCursorWnd->setSize(CEGUI::USize(cegui_absdim(0.0f), cegui_reldim(0.1f)));
    d_mouseCursorWnd->setAlwaysOnTop(true);
    d_mouseCursorWnd->setMousePassThroughEnabled(true);
    d_root->addChild(d_mouseCursorWnd);
}

void HUDDemo::updateMouseCursor()
{
    CEGUI::Vector2f position = d_guiContext->getMouseCursor().getPosition();

    // We want to position the image-window right top of the actual
    // cursor point so we add its height
    float absHeight = d_mouseCursorWnd->getPixelSize().d_height;
    position.d_y -= absHeight;

    d_mouseCursorWnd->setPosition(
        CEGUI::UVector2(
        cegui_absdim(position.d_x - 5.0f), cegui_absdim(position.d_y + 5.0f))
        );
}

CEGUI::Window* HUDDemo::spawnPlate()
{
    CEGUI::WindowManager& winMgr = CEGUI::WindowManager::getSingleton();

    CEGUI::Window* plateRoot = winMgr.createWindow("DefaultWindow");
    plateRoot->setSize(CEGUI::USize(cegui_absdim(0.0f), cegui_reldim(0.16f)));
    plateRoot->setAspectMode(CEGUI::AM_EXPAND);
    plateRoot->setAspectRatio(1.0f);
    plateRoot->setRiseOnClickEnabled(false);
    plateRoot->setPixelAligned(false);
    plateRoot->subscribeEvent(CEGUI::Window::EventMouseClick, Event::Subscriber(&HUDDemo::handlePlateWindowClicked, this));
    d_root->addChild(plateRoot);

    CEGUI::Window* plateImgWnd = winMgr.createWindow("Generic/Image", "ImageWindowPlate");
    plateImgWnd->setProperty("Image", s_imageNamePlate);
    plateImgWnd->setSize(CEGUI::USize(cegui_reldim(1.0f), cegui_absdim(0.0f)));
    plateImgWnd->setAspectRatio(3.308f);
    plateImgWnd->setAspectMode(CEGUI::AM_EXPAND);
    plateImgWnd->setVerticalAlignment(CEGUI::VA_BOTTOM);
    plateImgWnd->setMousePassThroughEnabled(true);
    plateImgWnd->setPixelAligned(false);
    plateRoot->addChild(plateImgWnd);

    CEGUI::String image = getRandomGameImage();

    CEGUI::Window* plateTopping = winMgr.createWindow("Generic/Image", "ImageWindowObject");
    plateTopping->setProperty("Image", image);
    plateTopping->setSize(CEGUI::USize(cegui_reldim(0.88f), cegui_absdim(0.0f)));
    plateTopping->setAspectRatio(1.0f);
    plateTopping->setAspectMode(CEGUI::AM_EXPAND);
    plateTopping->setHorizontalAlignment(CEGUI::HA_CENTRE);
    plateTopping->setMousePassThroughEnabled(true);
    plateTopping->setPixelAligned(false);
    plateRoot->addChild(plateTopping);

    int randumNumber = rand() % 10000;
    float posY = randumNumber / 10000.0f;

    plateRoot->setPosition(CEGUI::UVector2(cegui_absdim(0.0f), cegui_reldim(0.8f * posY)));

    return plateRoot;
}

const CEGUI::String& HUDDemo::getRandomGameImage()
{
    int randumNumber = rand() % 100;

    if(randumNumber > 88)
        return s_imageNamePoo;
    else if(randumNumber > 72)
        return s_imageNamePrizza;
    else if(randumNumber > 55)
        return s_imageNameSteak;
    else if(randumNumber > 52)
        return s_imageNameVegFruits;
    else if(randumNumber > 25)
        return s_imageNameVegPeople;
    else if(randumNumber > 0)
        return s_imageNameBread;

    return s_imageNameBread;
}

void HUDDemo::updatePlates(float timeSinceLastUpdate)
{
    unsigned int vectorSize = d_gamePlates.size();

    for(unsigned int i = 0; i < vectorSize;)
    {
        GamePlate* currentPlate = d_gamePlates[i];

        currentPlate->update(timeSinceLastUpdate);
        if(currentPlate->d_isDestroyed)
        {
            delete currentPlate;
            d_gamePlates[i] = d_gamePlates.back();
            d_gamePlates.pop_back();
            vectorSize = d_gamePlates.size();
        }
        else
            ++i;
    }

}

void HUDDemo::updateScoreWindow()
{
    CEGUI::Window* scoreWnd = d_root->getChild("ScoreCounter");
    scoreWnd->setText(CEGUI::PropertyHelper<int>::toString(d_score));
}

bool HUDDemo::handlePlateWindowClicked(const CEGUI::EventArgs& args)
{
    const CEGUI::MouseEventArgs& mouseArgs = static_cast<const CEGUI::MouseEventArgs&>(args);

    for(unsigned int i = 0; i < d_gamePlates.size(); ++i)
    {
        GamePlate* gamePlate = d_gamePlates[i];

        if(gamePlate->d_window == mouseArgs.window)
        {
            int points = gamePlate->getPoints();
            d_score += points;
            updateScoreWindow();

            gamePlate->d_isDestroyed = true;
            createScorePopup(mouseArgs.position, points);
        }
    }

    return false;
}

void HUDDemo::createScorePopup(const CEGUI::Vector2<float>& mousePos, int points)
{
    CEGUI::WindowManager& winMgr = CEGUI::WindowManager::getSingleton();

    CEGUI::Window* popupWindow = winMgr.createWindow("HUDDemo/PopupLabel");
    d_root->addChild(popupWindow);
    popupWindow->setPosition(CEGUI::UVector2(cegui_absdim(mousePos.d_x), cegui_absdim(mousePos.d_y)));
    popupWindow->setText(CEGUI::PropertyHelper<int>::toString(points));
    popupWindow->setRiseOnClickEnabled(false);
    popupWindow->subscribeEvent(AnimationInstance::EventAnimationEnded, Event::Subscriber(&HUDDemo::handleScorePopupAnimationEnded, this));
    popupWindow->setFont("DejaVuSans-14");
    popupWindow->setPixelAligned(false);

    popupWindow->setPosition(popupWindow->getPosition() + CEGUI::UVector2(cegui_reldim(0.03f), cegui_reldim(-0.02f)));

    if(points < 0)
        popupWindow->setProperty("NormalTextColour", "FFDD0000");
    else
    {
        popupWindow->setText( "+" + popupWindow->getText());
        popupWindow->setProperty("NormalTextColour", "FF00CC00");
    }

    CEGUI::EventArgs args;
    popupWindow->fireEvent("StartAnimation", args);
}

bool HUDDemo::handleScorePopupAnimationEnded(const CEGUI::EventArgs& args)
{
    const CEGUI::AnimationEventArgs& animArgs = static_cast<const CEGUI::AnimationEventArgs&>(args);

    CEGUI::Window* window = static_cast<CEGUI::Window*>( animArgs.instance->getTarget());
    d_delayDestroyWindows.push_back(window);

    return false;
}

void HUDDemo::delayDestroyWindows()
{
    CEGUI::WindowManager& winMgr = CEGUI::WindowManager::getSingleton();

    while(!d_delayDestroyWindows.empty())
    {
        CEGUI::Window* curWindow = d_delayDestroyWindows.back();
        winMgr.destroyWindow(curWindow);

        d_delayDestroyWindows.pop_back();
    }
}
/*************************************************************************
Define the module function that returns an instance of the sample
*************************************************************************/
extern "C" SAMPLE_EXPORT Sample& getSampleInstance()
{
    static HUDDemo sample;
    return sample;
}<|endoftext|>


<commit_before>#include "apiserver/ApiServerComponent.h"

void Susi::Api::ApiServerComponent::onConnect( std::string & id ) {
    Susi::Logger::info( "got new connection!" );
    sessionManager->updateSession( id );
}

void Susi::Api::ApiServerComponent::onClose( std::string & id ) {
    Susi::Logger::info( "lost connection..." );
    sessionManager->killSession( id );
    senders.erase( id );
    eventsToAck.erase( id );

    auto & subs = consumerSubscriptions[id];
    for( auto & kv : subs ) {
        eventManager->unsubscribe( kv.second );
    }
    consumerSubscriptions.erase( id );
    auto & subs2 = processorSubscriptions[id];
    for( auto & kv : subs2 ) {
        eventManager->unsubscribe( kv.second );
    }
    processorSubscriptions.erase( id );
}

void Susi::Api::ApiServerComponent::onMessage( std::string & id, Susi::Util::Any & packet ) {
    try {
        Susi::Logger::debug("onMessage: "+packet.toJSONString());
        auto type = packet["type"];
        if( type.isString() ) {
            if( type=="registerConsumer" ) {
                handleRegisterConsumer( id,packet );
            }
            else if( type=="registerProcessor" ) {
                handleRegisterProcessor( id,packet );
            }
            else if( type=="unregisterConsumer" ) {
                handleUnregisterConsumer( id,packet );
            }
            else if( type=="unregisterProcessor" ) {
                handleUnregisterProcessor( id,packet );
            }
            else if( type=="publish" ) {
                handlePublish( id,packet );
            }
            else if( type=="ack" ) {
                handleAck( id,packet );
            }
            else {
                sendFail( id,"type not known" );
            }
        }
        else {
            sendFail( id,"type is not a string" );
        }
    }
    catch( const std::exception & e ) {
        std::string msg = "exception while processing: ";
        msg += e.what();
        sendFail( id,msg );
    }
}

void Susi::Api::ApiServerComponent::handleRegisterConsumer( std::string & id, Susi::Util::Any & packet ) {
    auto & data = packet["data"];
    if( data.isObject() ) {
        char authlevel = 3;
        auto sessionAuthlevel = sessionManager->getSessionAttribute( id,"authlevel" );
        if( sessionAuthlevel.isInteger() ) {
            authlevel = static_cast<char>( sessionAuthlevel );
        }
        std::string topic = data["topic"];
        if(data["authlevel"].isInteger()){
            char supposedAuthlevel = static_cast<char>(data["authlevel"]);
            if(supposedAuthlevel > authlevel){
                authlevel = supposedAuthlevel;
            }
        }
        std::string subName = "";
        if(data["name"].isString()){
            subName = static_cast<std::string>(data["name"]);
        }
        auto & subs = consumerSubscriptions[id];
        if( subs.find( topic+std::to_string((int)authlevel) ) != subs.end() ) {
            sendFail( id,"you are allready subscribed to "+topic );
            return;
        }
        Susi::Events::Consumer callback = [this,id]( Susi::Events::SharedEventPtr event ) {
            Susi::Util::Any::Array headers;
            for( auto & kv : event->headers ) {
                headers.push_back( Susi::Util::Any::Object {{kv.first,kv.second}} );
            }
            Susi::Util::Any packet;
            packet["type"] = "consumerEvent";
            packet["data"] = event->toAny();
            Susi::Logger::debug("got consumer event, try to send it "+packet.toJSONString());
            std::string _id = id;
            send( _id,packet );
        };
        long subid = eventManager->subscribe( topic,callback,authlevel,subName );
        subs[topic+std::to_string((int)authlevel)] = subid;
        sendOk( id );
    }
    else {
        sendFail( id,"data is not a object" );
    }
}

void Susi::Api::ApiServerComponent::handleRegisterProcessor( std::string & id, Susi::Util::Any & packet ) {
    auto & data = packet["data"];
    if( data.isObject() ) {
        char authlevel = 3;
        auto sessionAuthlevel = sessionManager->getSessionAttribute( id,"authlevel" );
        if( sessionAuthlevel.isInteger() ) {
            authlevel = static_cast<char>( sessionAuthlevel );
        }
        std::string topic = data["topic"];
        if(data["authlevel"].isInteger()){
            char supposedAuthlevel = static_cast<char>(data["authlevel"]);
            if(supposedAuthlevel > authlevel){
                authlevel = supposedAuthlevel;
            }
        }
        std::string subName = "";
        if(data["name"].isString()){
            subName = static_cast<std::string>(data["name"]);
        }
        auto & subs = processorSubscriptions[id];
        if( subs.find( topic+std::to_string((int)authlevel) ) != subs.end() ) {
            sendFail( id,"you are allready subscribed to "+topic );
            return;
        }
        long subid = eventManager->subscribe( topic,Susi::Events::Processor{[this,id]( Susi::Events::EventPtr event ) {
            Susi::Util::Any::Array headers;
            for( auto & kv : event->headers ) {
                headers.push_back( Susi::Util::Any::Object {{kv.first,kv.second}} );
            }
            Susi::Util::Any packet;
            packet["type"] = "processorEvent";
            packet["data"] = event->toAny();
            std::string _id = id;
            eventsToAck[_id][event->id] = std::move( event );
            send( _id,packet );
        }},authlevel,subName );
        subs[topic+std::to_string((int)authlevel)] = subid;
        sendOk( id );
    }
    else {
        sendFail( id,"data is not a object" );
    }
}
void Susi::Api::ApiServerComponent::handleUnregisterConsumer( std::string & id, Susi::Util::Any & packet ) {
    auto & data = packet["data"];
    if( data.isObject() ) {
        std::string topic = data["topic"];
        char authlevel = 3;
        if(data["authlevel"].isInteger()){
            char supposedAuthlevel = static_cast<char>(data["authlevel"]);
            if(supposedAuthlevel > authlevel){
                authlevel = supposedAuthlevel;
            }
        }
        auto & subs = consumerSubscriptions[id];
        if( subs.find( topic+std::to_string((int)authlevel) )!=subs.end() ) {
            long subid = subs[topic+std::to_string((int)authlevel)];
            eventManager->unsubscribe( subid );
            sendOk( id );
        }
        else {
            sendFail( id,"you are not registered for "+topic );
        }
    }
    else {
        sendFail( id,"data is not a object" );
    }
}

void Susi::Api::ApiServerComponent::handleUnregisterProcessor( std::string & id, Susi::Util::Any & packet ) {
    auto & data = packet["data"];
    if( data.isObject() ) {
        std::string topic = data["topic"];
        char authlevel = 3;
        if(data["authlevel"].isInteger()){
            char supposedAuthlevel = static_cast<char>(data["authlevel"]);
            if(supposedAuthlevel > authlevel){
                authlevel = supposedAuthlevel;
            }
        }
        auto & subs = processorSubscriptions[id];
        if( subs.find( topic+std::to_string((int)authlevel) )!=subs.end() ) {
            long subid = subs[topic+std::to_string((int)authlevel)];
            eventManager->unsubscribe( subid );
            sendOk( id );
        }
        else {
            sendFail( id,"you are not registered for "+topic );
        }
    }
    else {
        sendFail( id,"data is not a object" );
    }
}
void Susi::Api::ApiServerComponent::handlePublish( std::string & id, Susi::Util::Any & packet ) {
    auto & eventData = packet["data"];
    if( !eventData.isObject() || !eventData["topic"].isString() ) {
        sendFail( id,"publish handler: data is not an object or topic is not set correctly" );
        return;
    }
    char authlevel = 3;
    auto sessionAuthlevel = sessionManager->getSessionAttribute( id, "authlevel" );
    if( sessionAuthlevel.isInteger() ) {
        authlevel = static_cast<char>( sessionAuthlevel );
    }
    else {
        Susi::Logger::debug( "sessionAuthlevelValue: "+sessionAuthlevel.toJSONString() );
    }
    auto event = eventManager->createEvent( eventData["topic"] );
    Susi::Events::Event rawEvent {eventData};
    rawEvent.sessionID = id;
    if( rawEvent.id == "" ) {
        rawEvent.id = std::to_string(std::chrono::system_clock::now().time_since_epoch().count());
    }
    *event = rawEvent;
    if( event->authlevel < authlevel ) {
        event->authlevel = authlevel;
    }
    eventManager->publish( std::move( event ),[this,id]( Susi::Events::SharedEventPtr event ) {
        Susi::Util::Any::Array headers;
        for( auto & kv : event->headers ) {
            headers.push_back( Susi::Util::Any::Object {{kv.first,kv.second}} );
        }
        Susi::Util::Any packet;
        packet["type"] = "ack";
        packet["data"] = event->toAny();
        std::string _id = id;
        send( _id,packet );
    } );

    sendOk( id );
}

void Susi::Api::ApiServerComponent::handleAck( std::string & id, Susi::Util::Any & packet ) {
    auto & eventData = packet["data"];
    if( !eventData.isObject() || !eventData["topic"].isString() ) {
        sendFail( id,"ack handler: data is not an object or topic is not set correctly" );
        return;
    }
    std::string eventID = static_cast<std::string>(eventData["id"]);
    if(!(eventsToAck.count(id)>0) || !(eventsToAck[id].count(eventID)>0)){
        sendFail( id , "unexpected ack" );
        return;
    }
    auto event = std::move( eventsToAck[id][eventID] );
    eventsToAck[id].erase( eventID );
    event->headers.clear();
    event->id = eventID;
    if( eventData["headers"].isArray() ) {
        Susi::Util::Any::Array arr = eventData["headers"];
        for( Susi::Util::Any::Object & val : arr ) {
            for( auto & kv : val ) {
                event->headers.push_back( std::make_pair( kv.first,( std::string )kv.second ) );
            }
        }
    }
    if( !eventData["payload"].isNull() ) {
        event->payload = eventData["payload"];
    }
    eventManager->ack( std::move( event ) );
    sendOk( id );
}

void Susi::Api::ApiServerComponent::sendOk( std::string & id ) {
    Susi::Util::Any response;
    response["type"] = "status";
    response["error"] = false;
    send( id,response );
}

void Susi::Api::ApiServerComponent::sendFail( std::string & id,std::string error ) {
    Susi::Util::Any response;
    response["type"] = "status";
    response["error"] = true;
    response["data"] = error;
    send( id,response );
}<commit_msg>[logging] added logging to apiservercomponent;<commit_after>#include "apiserver/ApiServerComponent.h"

void Susi::Api::ApiServerComponent::onConnect( std::string & id ) {
    Susi::Logger::info( "got new connection!" );
    sessionManager->updateSession( id );
}

void Susi::Api::ApiServerComponent::onClose( std::string & id ) {
    Susi::Logger::info( "lost connection..." );
    sessionManager->killSession( id );
    senders.erase( id );
    eventsToAck.erase( id );

    auto & subs = consumerSubscriptions[id];
    for( auto & kv : subs ) {
        eventManager->unsubscribe( kv.second );
    }
    consumerSubscriptions.erase( id );
    auto & subs2 = processorSubscriptions[id];
    for( auto & kv : subs2 ) {
        eventManager->unsubscribe( kv.second );
    }
    processorSubscriptions.erase( id );
}

void Susi::Api::ApiServerComponent::onMessage( std::string & id, Susi::Util::Any & packet ) {
    try {
        Susi::Logger::debug("onMessage: "+packet.toJSONString());
        auto type = packet["type"];
        if( type.isString() ) {
            if( type=="registerConsumer" ) {
                handleRegisterConsumer( id,packet );
            }
            else if( type=="registerProcessor" ) {
                handleRegisterProcessor( id,packet );
            }
            else if( type=="unregisterConsumer" ) {
                handleUnregisterConsumer( id,packet );
            }
            else if( type=="unregisterProcessor" ) {
                handleUnregisterProcessor( id,packet );
            }
            else if( type=="publish" ) {
                handlePublish( id,packet );
            }
            else if( type=="ack" ) {
                handleAck( id,packet );
            }
            else {
                sendFail( id,"type not known" );
            }
        }
        else {
            sendFail( id,"type is not a string" );
        }
    }
    catch( const std::exception & e ) {
        std::string msg = "exception while processing: ";
        msg += e.what();
        sendFail( id,msg );
    }
}

void Susi::Api::ApiServerComponent::handleRegisterConsumer( std::string & id, Susi::Util::Any & packet ) {
    auto & data = packet["data"];
    if( data.isObject() ) {
        char authlevel = 3;
        auto sessionAuthlevel = sessionManager->getSessionAttribute( id,"authlevel" );
        if( sessionAuthlevel.isInteger() ) {
            authlevel = static_cast<char>( sessionAuthlevel );
        }
        std::string topic = data["topic"];
        if(data["authlevel"].isInteger()){
            char supposedAuthlevel = static_cast<char>(data["authlevel"]);
            if(supposedAuthlevel > authlevel){
                authlevel = supposedAuthlevel;
            }
        }
        std::string subName = "";
        if(data["name"].isString()){
            subName = static_cast<std::string>(data["name"]);
        }
        auto & subs = consumerSubscriptions[id];
        if( subs.find( topic+std::to_string((int)authlevel) ) != subs.end() ) {
            sendFail( id,"you are allready subscribed to "+topic );
            return;
        }
        Susi::Events::Consumer callback = [this,id]( Susi::Events::SharedEventPtr event ) {
            Susi::Util::Any::Array headers;
            for( auto & kv : event->headers ) {
                headers.push_back( Susi::Util::Any::Object {{kv.first,kv.second}} );
            }
            Susi::Util::Any packet;
            packet["type"] = "consumerEvent";
            packet["data"] = event->toAny();
            Susi::Logger::debug("got consumer event, try to send it "+packet.toJSONString());
            std::string _id = id;
            send( _id,packet );
        };
        long subid = eventManager->subscribe( topic,callback,authlevel,subName );
        subs[topic+std::to_string((int)authlevel)] = subid;
        sendOk( id );
    }
    else {
        sendFail( id,"data is not a object" );
    }
}

void Susi::Api::ApiServerComponent::handleRegisterProcessor( std::string & id, Susi::Util::Any & packet ) {
    auto & data = packet["data"];
    if( data.isObject() ) {
        char authlevel = 3;
        auto sessionAuthlevel = sessionManager->getSessionAttribute( id,"authlevel" );
        if( sessionAuthlevel.isInteger() ) {
            authlevel = static_cast<char>( sessionAuthlevel );
        }
        std::string topic = data["topic"];
        if(data["authlevel"].isInteger()){
            char supposedAuthlevel = static_cast<char>(data["authlevel"]);
            if(supposedAuthlevel > authlevel){
                authlevel = supposedAuthlevel;
            }
        }
        std::string subName = "";
        if(data["name"].isString()){
            subName = static_cast<std::string>(data["name"]);
        }
        auto & subs = processorSubscriptions[id];
        if( subs.find( topic+std::to_string((int)authlevel) ) != subs.end() ) {
            sendFail( id,"you are allready subscribed to "+topic );
            return;
        }
        long subid = eventManager->subscribe( topic,Susi::Events::Processor{[this,id]( Susi::Events::EventPtr event ) {
            Susi::Util::Any::Array headers;
            for( auto & kv : event->headers ) {
                headers.push_back( Susi::Util::Any::Object {{kv.first,kv.second}} );
            }
            Susi::Util::Any packet;
            packet["type"] = "processorEvent";
            packet["data"] = event->toAny();
            std::string _id = id;
            eventsToAck[_id][event->id] = std::move( event );
            send( _id,packet );
        }},authlevel,subName );
        subs[topic+std::to_string((int)authlevel)] = subid;
        sendOk( id );
    }
    else {
        sendFail( id,"data is not a object" );
    }
}
void Susi::Api::ApiServerComponent::handleUnregisterConsumer( std::string & id, Susi::Util::Any & packet ) {
    auto & data = packet["data"];
    if( data.isObject() ) {
        std::string topic = data["topic"];
        char authlevel = 3;
        if(data["authlevel"].isInteger()){
            char supposedAuthlevel = static_cast<char>(data["authlevel"]);
            if(supposedAuthlevel > authlevel){
                authlevel = supposedAuthlevel;
            }
        }
        auto & subs = consumerSubscriptions[id];
        if( subs.find( topic+std::to_string((int)authlevel) )!=subs.end() ) {
            long subid = subs[topic+std::to_string((int)authlevel)];
            eventManager->unsubscribe( subid );
            sendOk( id );
        }
        else {
            sendFail( id,"you are not registered for "+topic );
        }
    }
    else {
        sendFail( id,"data is not a object" );
    }
}

void Susi::Api::ApiServerComponent::handleUnregisterProcessor( std::string & id, Susi::Util::Any & packet ) {
    auto & data = packet["data"];
    if( data.isObject() ) {
        std::string topic = data["topic"];
        char authlevel = 3;
        if(data["authlevel"].isInteger()){
            char supposedAuthlevel = static_cast<char>(data["authlevel"]);
            if(supposedAuthlevel > authlevel){
                authlevel = supposedAuthlevel;
            }
        }
        auto & subs = processorSubscriptions[id];
        if( subs.find( topic+std::to_string((int)authlevel) )!=subs.end() ) {
            long subid = subs[topic+std::to_string((int)authlevel)];
            eventManager->unsubscribe( subid );
            sendOk( id );
        }
        else {
            sendFail( id,"you are not registered for "+topic );
        }
    }
    else {
        sendFail( id,"data is not a object" );
    }
}
void Susi::Api::ApiServerComponent::handlePublish( std::string & id, Susi::Util::Any & packet ) {
    Susi::Logger::debug("in publish: "+packet.toJSONString());
    auto & eventData = packet["data"];
    if( !eventData.isObject() || !eventData["topic"].isString() ) {
        sendFail( id,"publish handler: data is not an object or topic is not set correctly" );
        return;
    }
    char authlevel = 3;
    auto sessionAuthlevel = sessionManager->getSessionAttribute( id, "authlevel" );
    Susi::Logger::debug( "sessionID: "+id );
    if( sessionAuthlevel.isInteger() ) {
        authlevel = static_cast<char>( sessionAuthlevel );
    }
    else {
        Susi::Logger::debug( "sessionAuthlevelValue: "+sessionAuthlevel.toJSONString() );
    }
    auto event = eventManager->createEvent( eventData["topic"] );
    Susi::Events::Event rawEvent {eventData};
    rawEvent.sessionID = id;
    if( rawEvent.id == "" ) {
        rawEvent.id = std::to_string(std::chrono::system_clock::now().time_since_epoch().count());
    }
    *event = rawEvent;
    if( event->authlevel < authlevel ) {
        event->authlevel = authlevel;
    }
    eventManager->publish( std::move( event ),[this,id]( Susi::Events::SharedEventPtr event ) {
        Susi::Util::Any::Array headers;
        for( auto & kv : event->headers ) {
            headers.push_back( Susi::Util::Any::Object {{kv.first,kv.second}} );
        }
        Susi::Util::Any packet;
        packet["type"] = "ack";
        packet["data"] = event->toAny();
        std::string _id = id;
        send( _id,packet );
    } );

    sendOk( id );
}

void Susi::Api::ApiServerComponent::handleAck( std::string & id, Susi::Util::Any & packet ) {
    auto & eventData = packet["data"];
    if( !eventData.isObject() || !eventData["topic"].isString() ) {
        sendFail( id,"ack handler: data is not an object or topic is not set correctly" );
        return;
    }
    std::string eventID = static_cast<std::string>(eventData["id"]);
    if(!(eventsToAck.count(id)>0) || !(eventsToAck[id].count(eventID)>0)){
        sendFail( id , "unexpected ack" );
        return;
    }
    auto event = std::move( eventsToAck[id][eventID] );
    eventsToAck[id].erase( eventID );
    event->headers.clear();
    event->id = eventID;
    if( eventData["headers"].isArray() ) {
        Susi::Util::Any::Array arr = eventData["headers"];
        for( Susi::Util::Any::Object & val : arr ) {
            for( auto & kv : val ) {
                event->headers.push_back( std::make_pair( kv.first,( std::string )kv.second ) );
            }
        }
    }
    if( !eventData["payload"].isNull() ) {
        event->payload = eventData["payload"];
    }
    eventManager->ack( std::move( event ) );
    sendOk( id );
}

void Susi::Api::ApiServerComponent::sendOk( std::string & id ) {
    Susi::Util::Any response;
    response["type"] = "status";
    response["error"] = false;
    send( id,response );
}

void Susi::Api::ApiServerComponent::sendFail( std::string & id,std::string error ) {
    Susi::Util::Any response;
    response["type"] = "status";
    response["error"] = true;
    response["data"] = error;
    send( id,response );
}<|endoftext|>


<commit_before>/*
 * Copyright (C) 2006-2021  Music Technology Group - Universitat Pompeu Fabra
 *
 * This file is part of Essentia
 *
 * Essentia is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License as published by the Free
 * Software Foundation (FSF), either version 3 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.
 *
 * You should have received a copy of the Affero GNU General Public License
 * version 3 along with this program.  If not, see http://www.gnu.org/licenses/
 */

#include "resamplefft.h"
#include "essentiamath.h"
#include <essentia/utils/synth_utils.h>

using namespace essentia;
using namespace standard;

const char* ResampleFFT::name = "ResampleFFT";
const char* ResampleFFT::category = "Synthesis";
const char* ResampleFFT::description = DOC("This algorithm resamples a sequence using FFT / IFFT. The input and output sizes must be an even number. (It is meant to be eqivalent to the resample function in Numpy).");

// configure
void ResampleFFT::configure()
{
  _fft->configure("size", parameter("inSize").toInt());
  _ifft->configure("size", parameter("outSize").toInt(), "normalize", false);

}

void ResampleFFT::compute() {

  const std::vector<Real>& input = _input.get();
  std::vector<Real>& output = _output.get();

  std::vector<std::complex<Real> >fftin; // temp vectors
  std::vector<std::complex<Real> >fftout; // temp vectors
  std::vector<Real> ifftout; // temp vectors

  int sizeIn = parameter("inSize").toInt(); // input.size();
  int sizeOut = parameter("outSize").toInt();

  _fft->input("frame").set(input);
  _fft->output("fft").set(fftin);
  _fft->compute();

  int hN = (sizeIn/2.)+1;
  int hNout = (sizeOut/2.)+1;
  initializeFFT(fftout, hNout);
  // fill positive spectrum to hN (upsampling zeros will be padded) or hNout (downsampling and high frequencies will be removed)
  for (int i = 0; i < std::min(hN, hNout); ++i)
  {
    // positive spectrums
    fftout[i].real( fftin[i].real());
    fftout[i].imag( fftin[i].imag());
  }

  _ifft->input("fft").set(fftout);
  _ifft->output("frame").set(ifftout);
  _ifft->compute();

  output.resize(0); // clear output

  // normalize
  Real normalizationGain = 1. / float(sizeIn);
  for (int i = 0; i < sizeOut; ++i)
  {
    output.push_back(ifftout[i] * normalizationGain) ;
  }

}

<commit_msg>Fix typos and improve DOC in ResampleFFT<commit_after>/*
 * Copyright (C) 2006-2021  Music Technology Group - Universitat Pompeu Fabra
 *
 * This file is part of Essentia
 *
 * Essentia is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License as published by the Free
 * Software Foundation (FSF), either version 3 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.
 *
 * You should have received a copy of the Affero GNU General Public License
 * version 3 along with this program.  If not, see http://www.gnu.org/licenses/
 */

#include "resamplefft.h"
#include "essentiamath.h"
#include <essentia/utils/synth_utils.h>

using namespace essentia;
using namespace standard;

const char* ResampleFFT::name = "ResampleFFT";
const char* ResampleFFT::category = "Synthesis";
const char* ResampleFFT::description = DOC("This algorithm resamples a sequence using FFT/IFFT. The input and output sizes must be an even number. The algorithm is a counterpart of the resample function in SciPy.");

// configure
void ResampleFFT::configure()
{
  _fft->configure("size", parameter("inSize").toInt());
  _ifft->configure("size", parameter("outSize").toInt(), "normalize", false);

}

void ResampleFFT::compute() {

  const std::vector<Real>& input = _input.get();
  std::vector<Real>& output = _output.get();

  std::vector<std::complex<Real> >fftin; // temp vectors
  std::vector<std::complex<Real> >fftout; // temp vectors
  std::vector<Real> ifftout; // temp vectors

  int sizeIn = parameter("inSize").toInt(); // input.size();
  int sizeOut = parameter("outSize").toInt();

  _fft->input("frame").set(input);
  _fft->output("fft").set(fftin);
  _fft->compute();

  int hN = (sizeIn/2.)+1;
  int hNout = (sizeOut/2.)+1;
  initializeFFT(fftout, hNout);
  // fill positive spectrum to hN (upsampling zeros will be padded) or hNout (downsampling and high frequencies will be removed)
  for (int i = 0; i < std::min(hN, hNout); ++i)
  {
    // positive spectrums
    fftout[i].real( fftin[i].real());
    fftout[i].imag( fftin[i].imag());
  }

  _ifft->input("fft").set(fftout);
  _ifft->output("frame").set(ifftout);
  _ifft->compute();

  output.resize(0); // clear output

  // normalize
  Real normalizationGain = 1. / float(sizeIn);
  for (int i = 0; i < sizeOut; ++i)
  {
    output.push_back(ifftout[i] * normalizationGain) ;
  }

}

<|endoftext|>