<commit_before>/* bzflag
 * Copyright (c) 1993 - 2008 Tim Riker
 *
 * This package is free software;  you can redistribute it and/or
 * modify it under the terms of the license found in the file
 * named COPYING that should have accompanied this file.
 *
 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

// interface headers
#include "HUDuiTypeIn.h"

// system implementation headers
#include <ctype.h>

// common implementation headers
#include "FontManager.h"
#include "bzUnicode.h"
#include "LocalFontFace.h"

//
// HUDuiTypeIn
//

HUDuiTypeIn::HUDuiTypeIn()
: HUDuiControl(), maxLength(0), cursorPos(string.c_str())
{
  allowEdit = true; // allow editing by default
  obfuscate = false;
}

HUDuiTypeIn::~HUDuiTypeIn()
{
}

void		HUDuiTypeIn::setObfuscation(bool on)
{
  obfuscate = on;
}

size_t			HUDuiTypeIn::getMaxLength() const
{
  return maxLength;
}

std::string		HUDuiTypeIn::getString() const
{
  return string;
}

void			HUDuiTypeIn::setMaxLength(size_t _maxLength)
{
  maxLength = _maxLength;
  string = string.substr(0, maxLength);
  if (cursorPos.getCount() > maxLength)
  {
    cursorPos = string.c_str();
    while (*cursorPos)
      ++cursorPos;
  }
  onSetFont();
}

void			HUDuiTypeIn::setString(const std::string& _string)
{
  string = _string;
  cursorPos = string.c_str();
  while (*cursorPos)
    ++cursorPos;
  onSetFont();
}

// allows composing, otherwise not
void			HUDuiTypeIn::setEditing(bool _allowEdit)
{
  allowEdit = _allowEdit;
}

bool			HUDuiTypeIn::doKeyPress(const BzfKeyEvent& key)
{
  static unsigned int backspace = '\b';	// ^H
  static unsigned int whitespace = ' ';

  if (HUDuiControl::doKeyPress(key))
    return true;

  if (!allowEdit) return false; //or return true ??
  unsigned int c = key.chr;
  if (c == 0) switch (key.button) {
    case BzfKeyEvent::Left: {
      size_t pos = cursorPos.getCount();
      // uhh...there's not really any way to reverse over a multibyte string
      // do this the hard way: reset to the beginning and advance to the current
      // position, minus a character.
      if (pos > 0) {
	--pos;
	cursorPos = string.c_str();
	while (cursorPos.getCount() < pos && (*cursorPos))
	  ++cursorPos;
      }
      return true;
    }

    case BzfKeyEvent::Right:
      if (*cursorPos)
	++cursorPos;
      return true;

    case BzfKeyEvent::Home:
      cursorPos = string.c_str();
      return true;

    case BzfKeyEvent::End:
      while (*cursorPos)
        ++cursorPos;
      return true;

    case BzfKeyEvent::Backspace:
      c = backspace;
      break;

    case BzfKeyEvent::Delete:
      if (*cursorPos) {
	++cursorPos;
	c = backspace;
      } else {
	return true;
      }
      break;

    default:
      return false;
  }

  if (!iswprint(c) && c != backspace)
    return false;

  if (c == backspace) {
    size_t pos = cursorPos.getCount();
    if (pos == 1) {
      goto noRoom;
    } else {
      // copy up to cursor position - 1
      cursorPos = string.c_str();
      --pos;
      while (cursorPos.getCount() < pos)
        ++cursorPos;
      std::string temp = string.substr(0, cursorPos.getBufferFromHere() - string.c_str());
      // skip the deleted character
      ++cursorPos;
      // copy the remainder
      pos = (cursorPos.getBufferFromHere() - string.c_str());
      temp += string.substr(pos, string.length() - pos);
      string = temp;
      // new buffer, restart cursor
      pos = cursorPos.getCount();
      cursorPos = string.c_str();
      while (cursorPos.getCount() < (pos - 1))
	++cursorPos;
    }

    onSetFont();
  } else {
    if (iswspace(c))
      c = whitespace;

    CountUTF8StringItr cusi(string.c_str());
    while (*cusi) ++cusi;
    if (cusi.getCount() >= maxLength) goto noRoom;

    bzUTF8Char ch(c);
    size_t pos = (cursorPos.getBufferFromHere() - string.c_str());
    // copy to the current cursor location
    std::string temp = string.substr(0, pos);
    // insert the new character
    temp += ch.str();
    // copy the rest of the string
    temp += string.substr(pos, string.length());
    string = temp;
    // new buffer, restart cursor
    pos = cursorPos.getCount();
    cursorPos = string.c_str();
    while (cursorPos.getCount() < pos)
      ++cursorPos;

    // bump the cursor
    ++cursorPos;
    onSetFont();
  }
  return true;

noRoom:
  // ring bell?
  return true;
}

bool			HUDuiTypeIn::doKeyRelease(const BzfKeyEvent& key)
{
  if (key.chr == '\t' || !iswprint(key.chr))	// ignore non-printing and tab
    return false;

  // slurp up releases
  return true;
}

void			HUDuiTypeIn::doRender()
{
  if (getFontFace() < 0) return;

  // render string
  glColor3fv(hasFocus() ? textColor : dimTextColor);

  FontManager &fm = FontManager::instance();
  std::string renderStr;
  if (obfuscate) {
    CountUTF8StringItr cusi(string.c_str());
    while (*cusi) ++cusi;
    renderStr.append(cusi.getCount(), '*');
  } else {
    renderStr = string;
  }
  fm.drawString(getX(), getY(), 0, getFontFace()->getFMFace(), getFontSize(), renderStr.c_str());

  // find the position of where to draw the input cursor
  float start = fm.getStringWidth(getFontFace()->getFMFace(), getFontSize(),
    renderStr.substr(0, cursorPos.getBufferFromHere() - string.c_str()).c_str());

  if (hasFocus() && allowEdit) {
    fm.drawString(getX() + start, getY(), 0, getFontFace()->getFMFace(), getFontSize(), "_");
  }
}

// Local Variables: ***
// mode: C++ ***
// tab-width: 8 ***
// c-basic-offset: 2 ***
// indent-tabs-mode: t ***
// End: ***
// ex: shiftwidth=2 tabstop=8
<commit_msg>setting a string to itself is not a const operation, don't ever skip the cursorPos reinitialization<commit_after>/* bzflag
 * Copyright (c) 1993 - 2008 Tim Riker
 *
 * This package is free software;  you can redistribute it and/or
 * modify it under the terms of the license found in the file
 * named COPYING that should have accompanied this file.
 *
 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

// interface headers
#include "HUDuiTypeIn.h"

// system implementation headers
#include <ctype.h>

// common implementation headers
#include "FontManager.h"
#include "bzUnicode.h"
#include "LocalFontFace.h"

//
// HUDuiTypeIn
//

HUDuiTypeIn::HUDuiTypeIn()
: HUDuiControl(), maxLength(0), cursorPos(string.c_str())
{
  allowEdit = true; // allow editing by default
  obfuscate = false;
}

HUDuiTypeIn::~HUDuiTypeIn()
{
}

void		HUDuiTypeIn::setObfuscation(bool on)
{
  obfuscate = on;
}

size_t			HUDuiTypeIn::getMaxLength() const
{
  return maxLength;
}

std::string		HUDuiTypeIn::getString() const
{
  return string;
}

void			HUDuiTypeIn::setMaxLength(size_t _maxLength)
{
  maxLength = _maxLength;
  setString(string.substr(0, maxLength));
  onSetFont();
}

void			HUDuiTypeIn::setString(const std::string& _string)
{
  string = _string;
  cursorPos = string.c_str();
  while (*cursorPos)
    ++cursorPos;
  onSetFont();
}

// allows composing, otherwise not
void			HUDuiTypeIn::setEditing(bool _allowEdit)
{
  allowEdit = _allowEdit;
}

bool			HUDuiTypeIn::doKeyPress(const BzfKeyEvent& key)
{
  static unsigned int backspace = '\b';	// ^H
  static unsigned int whitespace = ' ';

  if (HUDuiControl::doKeyPress(key))
    return true;

  if (!allowEdit) return false; //or return true ??
  unsigned int c = key.chr;
  if (c == 0) switch (key.button) {
    case BzfKeyEvent::Left: {
      size_t pos = cursorPos.getCount();
      // uhh...there's not really any way to reverse over a multibyte string
      // do this the hard way: reset to the beginning and advance to the current
      // position, minus a character.
      if (pos > 0) {
	--pos;
	cursorPos = string.c_str();
	while (cursorPos.getCount() < pos && (*cursorPos))
	  ++cursorPos;
      }
      return true;
    }

    case BzfKeyEvent::Right:
      if (*cursorPos)
	++cursorPos;
      return true;

    case BzfKeyEvent::Home:
      cursorPos = string.c_str();
      return true;

    case BzfKeyEvent::End:
      while (*cursorPos)
        ++cursorPos;
      return true;

    case BzfKeyEvent::Backspace:
      c = backspace;
      break;

    case BzfKeyEvent::Delete:
      if (*cursorPos) {
	++cursorPos;
	c = backspace;
      } else {
	return true;
      }
      break;

    default:
      return false;
  }

  if (!iswprint(c) && c != backspace)
    return false;

  if (c == backspace) {
    size_t pos = cursorPos.getCount();
    if (pos == 1) {
      goto noRoom;
    } else {
      // copy up to cursor position - 1
      cursorPos = string.c_str();
      --pos;
      while (cursorPos.getCount() < pos)
        ++cursorPos;
      std::string temp = string.substr(0, cursorPos.getBufferFromHere() - string.c_str());
      // skip the deleted character
      ++cursorPos;
      // copy the remainder
      pos = (cursorPos.getBufferFromHere() - string.c_str());
      temp += string.substr(pos, string.length() - pos);
      string = temp;
      // new buffer, restart cursor
      pos = cursorPos.getCount();
      cursorPos = string.c_str();
      while (cursorPos.getCount() < (pos - 1))
	++cursorPos;
    }

    onSetFont();
  } else {
    if (iswspace(c))
      c = whitespace;

    CountUTF8StringItr cusi(string.c_str());
    while (*cusi) ++cusi;
    if (cusi.getCount() >= maxLength) goto noRoom;

    bzUTF8Char ch(c);
    size_t pos = (cursorPos.getBufferFromHere() - string.c_str());
    // copy to the current cursor location
    std::string temp = string.substr(0, pos);
    // insert the new character
    temp += ch.str();
    // copy the rest of the string
    temp += string.substr(pos, string.length());
    string = temp;
    // new buffer, restart cursor
    pos = cursorPos.getCount();
    cursorPos = string.c_str();
    while (cursorPos.getCount() < pos)
      ++cursorPos;

    // bump the cursor
    ++cursorPos;
    onSetFont();
  }
  return true;

noRoom:
  // ring bell?
  return true;
}

bool			HUDuiTypeIn::doKeyRelease(const BzfKeyEvent& key)
{
  if (key.chr == '\t' || !iswprint(key.chr))	// ignore non-printing and tab
    return false;

  // slurp up releases
  return true;
}

void			HUDuiTypeIn::doRender()
{
  if (getFontFace() < 0) return;

  // render string
  glColor3fv(hasFocus() ? textColor : dimTextColor);

  FontManager &fm = FontManager::instance();
  std::string renderStr;
  if (obfuscate) {
    CountUTF8StringItr cusi(string.c_str());
    while (*cusi) ++cusi;
    renderStr.append(cusi.getCount(), '*');
  } else {
    renderStr = string;
  }
  fm.drawString(getX(), getY(), 0, getFontFace()->getFMFace(), getFontSize(), renderStr.c_str());

  // find the position of where to draw the input cursor
  float start = fm.getStringWidth(getFontFace()->getFMFace(), getFontSize(),
    renderStr.substr(0, cursorPos.getBufferFromHere() - string.c_str()).c_str());

  if (hasFocus() && allowEdit) {
    fm.drawString(getX() + start, getY(), 0, getFontFace()->getFMFace(), getFontSize(), "_");
  }
}

// Local Variables: ***
// mode: C++ ***
// tab-width: 8 ***
// c-basic-offset: 2 ***
// indent-tabs-mode: t ***
// End: ***
// ex: shiftwidth=2 tabstop=8
<|endoftext|>


<commit_before>/* bzflag
 * Copyright (c) 1993 - 2004 Tim Riker
 *
 * This package is free software;  you can redistribute it and/or
 * modify it under the terms of the license found in the file
 * named COPYING that should have accompanied this file.
 *
 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

#include "common.h"

/* interface header */
#include "OptionsMenu.h"

/* system implementation headers */
#include <vector>
#include <string>

/* common implementation headers */
#include "StateDatabase.h"
#include "BZDBCache.h"
#include "TextUtils.h"
#include "FontManager.h"

/* local implementation headers */
#include "MainMenu.h"
#include "HUDDialogStack.h"
#include "ControlPanel.h"
#include "StartupInfo.h"
#include "ServerListCache.h"
#include "HUDuiControl.h"
#include "HUDuiLabel.h"
#include "HUDuiList.h"

/* FIXME - from playing.h */
StartupInfo* getStartupInfo();
extern ControlPanel* controlPanel;

OptionsMenu::OptionsMenu() : guiOptionsMenu(NULL), effectsMenu(NULL),
			     saveWorldMenu(NULL), inputMenu(NULL),
			     audioMenu(NULL), displayMenu(NULL)
{
  // cache font face ID
  int fontFace = MainMenu::getFontFace();

  // add controls
  std::vector<HUDuiControl*>& list = getControls();
  HUDuiList* option;
  std::vector<std::string>* options;

  HUDuiLabel* label = new HUDuiLabel;
  label->setFontFace(fontFace);
  label->setString("Options");
  list.push_back(label);

  inputSetting = label = new HUDuiLabel;
  label->setFontFace(fontFace);
  label->setLabel("Input Settings");
  list.push_back(label);

  audioSetting = label = new HUDuiLabel;
  label->setFontFace(fontFace);
  label->setLabel("Audio Settings");
  list.push_back(label);

  displaySetting = label = new HUDuiLabel;
  label->setFontFace(fontFace);
  label->setLabel("Display Settings");
  list.push_back(label);

  effectsOptions = label = new HUDuiLabel;
  label->setFontFace(fontFace);
  label->setLabel("Effects Settings");
  list.push_back(label);

  guiOptions = label = new HUDuiLabel;
  label->setFontFace(fontFace);
  label->setLabel("GUI Settings");
  list.push_back(label);

  label = new HUDuiLabel;
  label->setFontFace(fontFace);
  label->setLabel("");
  list.push_back(label);

  option = new HUDuiList;
  option->setFontFace(fontFace);
  option->setLabel("UDP network connection:");
  option->setCallback(callback, (void*)"U");
  options = &option->getList();
  options->push_back(std::string("Off"));
  options->push_back(std::string("On"));
  option->update();
  list.push_back(option);

  option = new HUDuiList;
  option->setFontFace(fontFace);
  option->setLabel("Server List Cache:");
  option->setCallback(callback, (void*)"S");
  options = &option->getList();
  options->push_back(std::string("Off / Backup Mode"));
  options->push_back(std::string("5 Minutes"));
  options->push_back(std::string("15 Minutes"));
  options->push_back(std::string("30 Minutes"));
  options->push_back(std::string("1 Hour"));
  options->push_back(std::string("5 Hours"));
  options->push_back(std::string("15 Hours"));
  options->push_back(std::string("1 day"));
  options->push_back(std::string("15 days"));
  options->push_back(std::string("30 days"));
  option->update();
  list.push_back(option);

  clearCache = label = new HUDuiLabel;
  label->setFontFace(fontFace);
  label->setLabel("Clear Server List Cache");
  list.push_back(label);

  saveWorld = label = new HUDuiLabel;
  label->setFontFace(fontFace);
  label->setLabel("Save World");
  list.push_back(label);

  initNavigation(list, 1,list.size()-1);
}

OptionsMenu::~OptionsMenu()
{
  delete guiOptionsMenu;
  delete effectsMenu;
  delete saveWorldMenu;
  delete inputMenu;
  delete audioMenu;
  delete displayMenu;
}

void OptionsMenu::execute()
{
  HUDuiControl* focus = HUDui::getFocus();
  if (focus == guiOptions) {
    if (!guiOptionsMenu) guiOptionsMenu = new GUIOptionsMenu;
    HUDDialogStack::get()->push(guiOptionsMenu);
  } else if (focus == effectsOptions) {
    if (!effectsMenu) effectsMenu = new EffectsMenu;
    HUDDialogStack::get()->push(effectsMenu);
  } else if (focus == clearCache) {
    if ((ServerListCache::get())->clearCache()){
      controlPanel->addMessage("Cache Cleared");
    } else {
      // already cleared -- do nothing
    }
  } else if (focus == saveWorld) {
    if (!saveWorldMenu) saveWorldMenu = new SaveWorldMenu;
    HUDDialogStack::get()->push(saveWorldMenu);
  } else if (focus == inputSetting) {
    if (!inputMenu) inputMenu = new InputMenu;
    HUDDialogStack::get()->push(inputMenu);
  } else if (focus == audioSetting) {
    if (!audioMenu) audioMenu = new AudioMenu;
    HUDDialogStack::get()->push(audioMenu);
  } else if (focus == displaySetting) {
    if (!displayMenu) displayMenu = new DisplayMenu;
    HUDDialogStack::get()->push(displayMenu);
  }
}

void OptionsMenu::resize(int width, int height)
{
  int i;
  HUDDialog::resize(width, height);

  // use a big font for title, smaller font for the rest
  const float titleFontSize = (float)height / 15.0f;
  const float fontSize = (float)height / 45.0f;
  FontManager &fm = FontManager::instance();

  // reposition title
  std::vector<HUDuiControl*>& list = getControls();
  HUDuiLabel* title = (HUDuiLabel*)list[0];
  title->setFontSize(titleFontSize);
  const float titleWidth = fm.getStrLength(MainMenu::getFontFace(), titleFontSize, title->getString());
  const float titleHeight = fm.getStrHeight(MainMenu::getFontFace(), titleFontSize, " ");
  float x = 0.5f * ((float)width - titleWidth);
  float y = (float)height - titleHeight;
  title->setPosition(x, y);

  // reposition options in two columns
  x = 0.5f * (float)width;
  y -= 0.6f * titleHeight;
  const int count = list.size();
  const float h = fm.getStrHeight(MainMenu::getFontFace(), fontSize, " ");
  for (i = 1; i < count; i++) {
    list[i]->setFontSize(fontSize);
    list[i]->setPosition(x, y);
    y -= 1.0f * h;
  }

  // load current settings
  {
    int i = 7;

    const StartupInfo* info = getStartupInfo();

    // mind the ++i !
    ((HUDuiList*)list[i++])->setIndex(info->useUDPconnection ? 1 : 0);

    // server cache age
    int index = 0;
    switch ((ServerListCache::get())->getMaxCacheAge()){
      case 0: index = 0; break;
      case 5: index = 1; break;
      case 15: index = 2; break;
      case 30: index = 3; break;
      case 60: index = 4; break;
      case 60*5: index = 5; break;
      case 60*15: index = 6; break;
      case 60*24: index = 7; break;
      case 60*24*15: index = 8; break;
      case 60*24*30: index = 9; break;
      default: index = 4;
    }
    ((HUDuiList*)list[i++])->setIndex(index);
    i++; // clear cache label
  }
}

void OptionsMenu::callback(HUDuiControl* w, void* data)
{
  HUDuiList* list = (HUDuiList*)w;

  switch (((const char*)data)[0]) {
    case 'U': {
      StartupInfo* info = getStartupInfo();
      info->useUDPconnection = (list->getIndex() != 0);
      break;
    }

    case 'S': { // server cache
      time_t minutes = 0;
      int index = list->getIndex();
      switch (index){
	case 0: minutes = 0; break;
	case 1: minutes = 5; break;
	case 2: minutes = 15; break;
	case 3: minutes = 30; break;
	case 4: minutes = 60; break;
	case 5: minutes = 60*5; break;
	case 6: minutes = 60*15; break;
	case 7: minutes = 60*24; break;
	case 8: minutes = 60*24*15; break;
	case 9: minutes = 60*24*30; break;
      }
      (ServerListCache::get())->setMaxCacheAge(minutes);
      break;
    }
  }
}

// Local Variables: ***
// mode: C++ ***
// tab-width: 8 ***
// c-basic-offset: 2 ***
// indent-tabs-mode: t ***
// End: ***
// ex: shiftwidth=2 tabstop=8
<commit_msg>when will the header cleanup insanity end.. interface header includes common header when there's an interface<commit_after>/* bzflag
 * Copyright (c) 1993 - 2004 Tim Riker
 *
 * This package is free software;  you can redistribute it and/or
 * modify it under the terms of the license found in the file
 * named COPYING that should have accompanied this file.
 *
 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

/* interface header */
#include "OptionsMenu.h"

/* system implementation headers */
#include <vector>
#include <string>

/* common implementation headers */
#include "BZDBCache.h"
#include "FontManager.h"
#include "StartupInfo.h"
#include "StateDatabase.h"
#include "TextUtils.h"

/* local implementation headers */
#include "MainMenu.h"
#include "HUDDialogStack.h"
#include "ControlPanel.h"
#include "ServerListCache.h"
#include "HUDuiControl.h"
#include "HUDuiLabel.h"
#include "HUDuiList.h"

/* FIXME - from playing.h */
StartupInfo* getStartupInfo();
extern ControlPanel* controlPanel;

OptionsMenu::OptionsMenu() : guiOptionsMenu(NULL), effectsMenu(NULL),
			     saveWorldMenu(NULL), inputMenu(NULL),
			     audioMenu(NULL), displayMenu(NULL)
{
  // cache font face ID
  int fontFace = MainMenu::getFontFace();

  // add controls
  std::vector<HUDuiControl*>& list = getControls();
  HUDuiList* option;
  std::vector<std::string>* options;

  HUDuiLabel* label = new HUDuiLabel;
  label->setFontFace(fontFace);
  label->setString("Options");
  list.push_back(label);

  inputSetting = label = new HUDuiLabel;
  label->setFontFace(fontFace);
  label->setLabel("Input Settings");
  list.push_back(label);

  audioSetting = label = new HUDuiLabel;
  label->setFontFace(fontFace);
  label->setLabel("Audio Settings");
  list.push_back(label);

  displaySetting = label = new HUDuiLabel;
  label->setFontFace(fontFace);
  label->setLabel("Display Settings");
  list.push_back(label);

  effectsOptions = label = new HUDuiLabel;
  label->setFontFace(fontFace);
  label->setLabel("Effects Settings");
  list.push_back(label);

  guiOptions = label = new HUDuiLabel;
  label->setFontFace(fontFace);
  label->setLabel("GUI Settings");
  list.push_back(label);

  label = new HUDuiLabel;
  label->setFontFace(fontFace);
  label->setLabel("");
  list.push_back(label);

  option = new HUDuiList;
  option->setFontFace(fontFace);
  option->setLabel("UDP network connection:");
  option->setCallback(callback, (void*)"U");
  options = &option->getList();
  options->push_back(std::string("Off"));
  options->push_back(std::string("On"));
  option->update();
  list.push_back(option);

  option = new HUDuiList;
  option->setFontFace(fontFace);
  option->setLabel("Server List Cache:");
  option->setCallback(callback, (void*)"S");
  options = &option->getList();
  options->push_back(std::string("Off / Backup Mode"));
  options->push_back(std::string("5 Minutes"));
  options->push_back(std::string("15 Minutes"));
  options->push_back(std::string("30 Minutes"));
  options->push_back(std::string("1 Hour"));
  options->push_back(std::string("5 Hours"));
  options->push_back(std::string("15 Hours"));
  options->push_back(std::string("1 day"));
  options->push_back(std::string("15 days"));
  options->push_back(std::string("30 days"));
  option->update();
  list.push_back(option);

  clearCache = label = new HUDuiLabel;
  label->setFontFace(fontFace);
  label->setLabel("Clear Server List Cache");
  list.push_back(label);

  saveWorld = label = new HUDuiLabel;
  label->setFontFace(fontFace);
  label->setLabel("Save World");
  list.push_back(label);

  initNavigation(list, 1,list.size()-1);
}

OptionsMenu::~OptionsMenu()
{
  delete guiOptionsMenu;
  delete effectsMenu;
  delete saveWorldMenu;
  delete inputMenu;
  delete audioMenu;
  delete displayMenu;
}

void OptionsMenu::execute()
{
  HUDuiControl* focus = HUDui::getFocus();
  if (focus == guiOptions) {
    if (!guiOptionsMenu) guiOptionsMenu = new GUIOptionsMenu;
    HUDDialogStack::get()->push(guiOptionsMenu);
  } else if (focus == effectsOptions) {
    if (!effectsMenu) effectsMenu = new EffectsMenu;
    HUDDialogStack::get()->push(effectsMenu);
  } else if (focus == clearCache) {
    if ((ServerListCache::get())->clearCache()){
      controlPanel->addMessage("Cache Cleared");
    } else {
      // already cleared -- do nothing
    }
  } else if (focus == saveWorld) {
    if (!saveWorldMenu) saveWorldMenu = new SaveWorldMenu;
    HUDDialogStack::get()->push(saveWorldMenu);
  } else if (focus == inputSetting) {
    if (!inputMenu) inputMenu = new InputMenu;
    HUDDialogStack::get()->push(inputMenu);
  } else if (focus == audioSetting) {
    if (!audioMenu) audioMenu = new AudioMenu;
    HUDDialogStack::get()->push(audioMenu);
  } else if (focus == displaySetting) {
    if (!displayMenu) displayMenu = new DisplayMenu;
    HUDDialogStack::get()->push(displayMenu);
  }
}

void OptionsMenu::resize(int width, int height)
{
  int i;
  HUDDialog::resize(width, height);

  // use a big font for title, smaller font for the rest
  const float titleFontSize = (float)height / 15.0f;
  const float fontSize = (float)height / 45.0f;
  FontManager &fm = FontManager::instance();

  // reposition title
  std::vector<HUDuiControl*>& list = getControls();
  HUDuiLabel* title = (HUDuiLabel*)list[0];
  title->setFontSize(titleFontSize);
  const float titleWidth = fm.getStrLength(MainMenu::getFontFace(), titleFontSize, title->getString());
  const float titleHeight = fm.getStrHeight(MainMenu::getFontFace(), titleFontSize, " ");
  float x = 0.5f * ((float)width - titleWidth);
  float y = (float)height - titleHeight;
  title->setPosition(x, y);

  // reposition options in two columns
  x = 0.5f * (float)width;
  y -= 0.6f * titleHeight;
  const int count = list.size();
  const float h = fm.getStrHeight(MainMenu::getFontFace(), fontSize, " ");
  for (i = 1; i < count; i++) {
    list[i]->setFontSize(fontSize);
    list[i]->setPosition(x, y);
    y -= 1.0f * h;
  }

  // load current settings
  {
    int i = 7;

    const StartupInfo* info = getStartupInfo();

    // mind the ++i !
    ((HUDuiList*)list[i++])->setIndex(info->useUDPconnection ? 1 : 0);

    // server cache age
    int index = 0;
    switch ((ServerListCache::get())->getMaxCacheAge()){
      case 0: index = 0; break;
      case 5: index = 1; break;
      case 15: index = 2; break;
      case 30: index = 3; break;
      case 60: index = 4; break;
      case 60*5: index = 5; break;
      case 60*15: index = 6; break;
      case 60*24: index = 7; break;
      case 60*24*15: index = 8; break;
      case 60*24*30: index = 9; break;
      default: index = 4;
    }
    ((HUDuiList*)list[i++])->setIndex(index);
    i++; // clear cache label
  }
}

void OptionsMenu::callback(HUDuiControl* w, void* data)
{
  HUDuiList* list = (HUDuiList*)w;

  switch (((const char*)data)[0]) {
    case 'U': {
      StartupInfo* info = getStartupInfo();
      info->useUDPconnection = (list->getIndex() != 0);
      break;
    }

    case 'S': { // server cache
      time_t minutes = 0;
      int index = list->getIndex();
      switch (index){
	case 0: minutes = 0; break;
	case 1: minutes = 5; break;
	case 2: minutes = 15; break;
	case 3: minutes = 30; break;
	case 4: minutes = 60; break;
	case 5: minutes = 60*5; break;
	case 6: minutes = 60*15; break;
	case 7: minutes = 60*24; break;
	case 8: minutes = 60*24*15; break;
	case 9: minutes = 60*24*30; break;
      }
      (ServerListCache::get())->setMaxCacheAge(minutes);
      break;
    }
  }
}

// Local Variables: ***
// mode: C++ ***
// tab-width: 8 ***
// c-basic-offset: 2 ***
// indent-tabs-mode: t ***
// End: ***
// ex: shiftwidth=2 tabstop=8
<|endoftext|>


<commit_before>/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
#ident "$Id$"
#ident "Copyright (c) 2007-2012 Tokutek Inc.  All rights reserved."
#ident "The technology is licensed by the Massachusetts Institute of Technology, Rutgers State University of New Jersey, and the Research Foundation of State University of New York at Stony Brook under United States of America Serial No. 11/760379 and to the patents and/or patent applications resulting from it."

#include "locktree_unit_test.h"

#include <toku_time.h>

__attribute__((__unused__))
static long current_time_usec(void) {
    struct timeval t;
    gettimeofday(&t, NULL);
    return t.tv_usec + t.tv_sec * 1000000;
}

namespace toku {

// test simple, non-overlapping read locks and then write locks
void locktree_unit_test::test_simple_lock(void) {
    locktree::manager mgr;
    mgr.create(nullptr, nullptr);
    DESCRIPTOR desc = nullptr;
    DICTIONARY_ID dict_id = { 1 };
    locktree *lt = mgr.get_lt(dict_id, desc, compare_dbts, nullptr);

    int r;
    TXNID txnid_a = 1001;
    TXNID txnid_b = 2001;
    TXNID txnid_c = 3001;
    TXNID txnid_d = 4001;
    const DBT *one = get_dbt(1);
    const DBT *two = get_dbt(2);
    const DBT *three = get_dbt(3);
    const DBT *four = get_dbt(4);

    for (int test_run = 0; test_run < 2; test_run++) {
        // test_run == 0 means test with read lock
        // test_run == 1 means test with write lock
#define ACQUIRE_LOCK(txn, left, right, conflicts) \
        test_run == 0 ? lt->acquire_read_lock(txn, left, right, conflicts) \
                      : lt->acquire_write_lock(txn, left, right, conflicts)

        // four txns, four points
        r = ACQUIRE_LOCK(txnid_a, one, one, nullptr);
        invariant(r == 0);
        invariant(num_row_locks(lt) == 1);
        r = ACQUIRE_LOCK(txnid_b, two, two, nullptr);
        invariant(r == 0);
        invariant(num_row_locks(lt) == 2);
        r = ACQUIRE_LOCK(txnid_c, three, three, nullptr);
        invariant(r == 0);
        invariant(num_row_locks(lt) == 3);
        r = ACQUIRE_LOCK(txnid_d, four, four, nullptr);
        invariant(r == 0);
        invariant(num_row_locks(lt) == 4);
        lt->remove_overlapping_locks_for_txnid(txnid_a, one, one);
        invariant(num_row_locks(lt) == 3);
        lt->remove_overlapping_locks_for_txnid(txnid_b, two, two);
        invariant(num_row_locks(lt) == 2);
        lt->remove_overlapping_locks_for_txnid(txnid_c, three, three);
        invariant(num_row_locks(lt) == 1);
        lt->remove_overlapping_locks_for_txnid(txnid_d, four, four);
        invariant(num_row_locks(lt) == 0);

        // two txns, two ranges
        r = ACQUIRE_LOCK(txnid_c, one, two, nullptr);
        invariant(r == 0);
        invariant(num_row_locks(lt) == 1);
        r = ACQUIRE_LOCK(txnid_b, three, four, nullptr);
        invariant(r == 0);
        invariant(num_row_locks(lt) == 2);
        lt->remove_overlapping_locks_for_txnid(txnid_c, one, two);
        invariant(num_row_locks(lt) == 1);
        lt->remove_overlapping_locks_for_txnid(txnid_b, three, four);
        invariant(num_row_locks(lt) == 0);

        // one txn, one range, one point
        r = ACQUIRE_LOCK(txnid_a, two, three, nullptr);
        invariant(r == 0);
        invariant(num_row_locks(lt) == 1);
        r = ACQUIRE_LOCK(txnid_a, four, four, nullptr);
        invariant(r == 0);
        invariant(num_row_locks(lt) == 2);
        lt->remove_overlapping_locks_for_txnid(txnid_a, two, three);
        invariant(num_row_locks(lt) == 1);
        lt->remove_overlapping_locks_for_txnid(txnid_a, four, four);
        invariant(num_row_locks(lt) == 0);

        // two txns, one range, one point
        r = ACQUIRE_LOCK(txnid_c, three, four, nullptr);
        invariant(r == 0);
        invariant(num_row_locks(lt) == 1);
        r = ACQUIRE_LOCK(txnid_d, one, one, nullptr);
        invariant(r == 0);
        invariant(num_row_locks(lt) == 2);
        lt->remove_overlapping_locks_for_txnid(txnid_c, three, four);
        invariant(num_row_locks(lt) == 1);
        lt->remove_overlapping_locks_for_txnid(txnid_d, one, one);
        invariant(num_row_locks(lt) == 0);

#undef ACQUIRE_LOCK
    }

    // TODO: test read locks from different txns that overlap!

#if 0
    const int64_t num_locks = 1000000;
#else
    const int64_t num_locks = 10000;
#endif

    long t0, t1;
    int64_t *keys = (int64_t *) toku_malloc(num_locks * sizeof(int64_t));
    for (int64_t i = 0; i < num_locks; i++) {
        keys[i] = i;
    }
    for (int64_t i = 0; i < num_locks; i++) {
        int64_t k = rand() % num_locks;
        int64_t tmp = keys[k];
        keys[k] = keys[i];
        keys[i] = tmp;
    }

    r = mgr.set_max_lock_memory((num_locks + 1) * 500);
    invariant_zero(r);

    DBT k;
    k.ulen = 0;
    k.size = sizeof(keys[0]);
    k.flags = DB_DBT_USERMEM;

    t0 = current_time_usec();
    for (int64_t i = 0; i < num_locks; i++) {
        k.data = (void *) &keys[i];
        r = lt->acquire_read_lock(txnid_a, &k, &k, nullptr);
        invariant(r == 0);
    }
    t1 = current_time_usec();

    t0 = current_time_usec();
    for (int64_t i = 0; i < num_locks; i++) {
        k.data = (void *) &keys[i];
        lt->remove_overlapping_locks_for_txnid(txnid_a, &k, &k);
    }
    t1 = current_time_usec();

    toku_free(keys);

    mgr.release_lt(lt);
    mgr.destroy();
}

} /* namespace toku */

int main(void) {
    toku::locktree_unit_test test;
    test.test_simple_lock();
    return 0;
}
<commit_msg>refs #5351 fix test<commit_after>/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
#ident "$Id$"
#ident "Copyright (c) 2007-2012 Tokutek Inc.  All rights reserved."
#ident "The technology is licensed by the Massachusetts Institute of Technology, Rutgers State University of New Jersey, and the Research Foundation of State University of New York at Stony Brook under United States of America Serial No. 11/760379 and to the patents and/or patent applications resulting from it."

#include "locktree_unit_test.h"

#include <toku_time.h>

__attribute__((__unused__))
static long current_time_usec(void) {
    struct timeval t;
    gettimeofday(&t, NULL);
    return t.tv_usec + t.tv_sec * 1000000;
}

namespace toku {

// test simple, non-overlapping read locks and then write locks
void locktree_unit_test::test_simple_lock(void) {
    locktree::manager mgr;
    mgr.create(nullptr, nullptr);
    DESCRIPTOR desc = nullptr;
    DICTIONARY_ID dict_id = { 1 };
    locktree *lt = mgr.get_lt(dict_id, desc, compare_dbts, nullptr);

    int r;
    TXNID txnid_a = 1001;
    TXNID txnid_b = 2001;
    TXNID txnid_c = 3001;
    TXNID txnid_d = 4001;
    const DBT *one = get_dbt(1);
    const DBT *two = get_dbt(2);
    const DBT *three = get_dbt(3);
    const DBT *four = get_dbt(4);

    for (int test_run = 0; test_run < 2; test_run++) {
        // test_run == 0 means test with read lock
        // test_run == 1 means test with write lock
#define ACQUIRE_LOCK(txn, left, right, conflicts) \
        test_run == 0 ? lt->acquire_read_lock(txn, left, right, conflicts) \
                      : lt->acquire_write_lock(txn, left, right, conflicts)

        // four txns, four points
        r = ACQUIRE_LOCK(txnid_a, one, one, nullptr);
        invariant(r == 0);
        invariant(num_row_locks(lt) == 1);
        r = ACQUIRE_LOCK(txnid_b, two, two, nullptr);
        invariant(r == 0);
        invariant(num_row_locks(lt) == 2);
        r = ACQUIRE_LOCK(txnid_c, three, three, nullptr);
        invariant(r == 0);
        invariant(num_row_locks(lt) == 3);
        r = ACQUIRE_LOCK(txnid_d, four, four, nullptr);
        invariant(r == 0);
        invariant(num_row_locks(lt) == 4);
        lt->remove_overlapping_locks_for_txnid(txnid_a, one, one);
        invariant(num_row_locks(lt) == 3);
        lt->remove_overlapping_locks_for_txnid(txnid_b, two, two);
        invariant(num_row_locks(lt) == 2);
        lt->remove_overlapping_locks_for_txnid(txnid_c, three, three);
        invariant(num_row_locks(lt) == 1);
        lt->remove_overlapping_locks_for_txnid(txnid_d, four, four);
        invariant(num_row_locks(lt) == 0);

        // two txns, two ranges
        r = ACQUIRE_LOCK(txnid_c, one, two, nullptr);
        invariant(r == 0);
        invariant(num_row_locks(lt) == 1);
        r = ACQUIRE_LOCK(txnid_b, three, four, nullptr);
        invariant(r == 0);
        invariant(num_row_locks(lt) == 2);
        lt->remove_overlapping_locks_for_txnid(txnid_c, one, two);
        invariant(num_row_locks(lt) == 1);
        lt->remove_overlapping_locks_for_txnid(txnid_b, three, four);
        invariant(num_row_locks(lt) == 0);

        // one txn, one range, one point
        r = ACQUIRE_LOCK(txnid_a, two, three, nullptr);
        invariant(r == 0);
        invariant(num_row_locks(lt) == 1);
        r = ACQUIRE_LOCK(txnid_a, four, four, nullptr);
        invariant(r == 0);
        invariant(num_row_locks(lt) == 2);
        lt->remove_overlapping_locks_for_txnid(txnid_a, two, three);
        invariant(num_row_locks(lt) == 1);
        lt->remove_overlapping_locks_for_txnid(txnid_a, four, four);
        invariant(num_row_locks(lt) == 0);

        // two txns, one range, one point
        r = ACQUIRE_LOCK(txnid_c, three, four, nullptr);
        invariant(r == 0);
        invariant(num_row_locks(lt) == 1);
        r = ACQUIRE_LOCK(txnid_d, one, one, nullptr);
        invariant(r == 0);
        invariant(num_row_locks(lt) == 2);
        lt->remove_overlapping_locks_for_txnid(txnid_c, three, four);
        invariant(num_row_locks(lt) == 1);
        lt->remove_overlapping_locks_for_txnid(txnid_d, one, one);
        invariant(num_row_locks(lt) == 0);

#undef ACQUIRE_LOCK
    }

    const int64_t num_locks = 10000;

    int64_t *keys = (int64_t *) toku_malloc(num_locks * sizeof(int64_t));
    for (int64_t i = 0; i < num_locks; i++) {
        keys[i] = i;
    }
    for (int64_t i = 0; i < num_locks; i++) {
        int64_t k = rand() % num_locks;
        int64_t tmp = keys[k];
        keys[k] = keys[i];
        keys[i] = tmp;
    }

    r = mgr.set_max_lock_memory((num_locks + 1) * 500);
    invariant_zero(r);

    DBT k;
    k.ulen = 0;
    k.size = sizeof(keys[0]);
    k.flags = DB_DBT_USERMEM;

    for (int64_t i = 0; i < num_locks; i++) {
        k.data = (void *) &keys[i];
        r = lt->acquire_read_lock(txnid_a, &k, &k, nullptr);
        invariant(r == 0);
    }

    for (int64_t i = 0; i < num_locks; i++) {
        k.data = (void *) &keys[i];
        lt->remove_overlapping_locks_for_txnid(txnid_a, &k, &k);
    }

    toku_free(keys);

    mgr.release_lt(lt);
    mgr.destroy();
}

} /* namespace toku */

int main(void) {
    toku::locktree_unit_test test;
    test.test_simple_lock();
    return 0;
}
<|endoftext|>


<commit_before>#ifndef MYVECTOR_HPP
#define MYVECTOR_HPP 1

#include <algorithm>
#include <cstddef>
#include <iostream>
#include <memory>

namespace sandsnip3r {
	template<class Type>
	class MyVectorIterator {
	private:
	};

	template<class Type, class Allocator = std::allocator<Type>>
	class MyVector {
	public:
		using value_type = Type;
		using allocator_type = Allocator;
		using size_type = size_t;
		using difference_type = ptrdiff_t;
		using reference = Type&;
		using const_reference = const Type&;
		using pointer = Type*;
		using const_pointer = const Type*;
		//iterator
		//const_iterator
		//reverse_iterator
		//const_reverse_iterator
	private:
		using allocatorTraits = std::allocator_traits<allocator_type>;
		allocator_type vectorAllocator;
		std::unique_ptr<value_type[], std::function<void(value_type*)>> vectorData;
		size_t vectorSize{0};
		size_t vectorCapacity{0};

		void reallocateIfNecessary() {
			//Arguments state the the golden ratio is the most appropriate growth factor
			const auto GROWTH_FACTOR = 1.618;
			if (this->size() == this->capacity()) {
				//Need to reallocate to make space
				size_type newCapacity = (vectorCapacity == 0 ? 1 : vectorCapacity * GROWTH_FACTOR);
				reallocate(newCapacity);
			}
		}

		void reallocateToNewSizeIfNecessary(size_type newCapacity) {
			if (this->capacity() < newCapacity) {
				reallocate(newCapacity);
			}
		}

		void reallocate(size_type newCapacity) {
			//Allocate memory for the new data
			if (newCapacity > this->max_size()) {
				throw std::length_error("MyVector::reallocate() newCapacity (which is "+std::to_string(newCapacity)+") > max_size (which is "+std::to_string(this->max_size())+")");
			}
			//	store it in a unique_ptr
			std::unique_ptr<value_type[], std::function<void(value_type*)>> newData(allocatorTraits::allocate(vectorAllocator, newCapacity), [this, newCapacity](value_type *ptr){
				allocatorTraits::deallocate(vectorAllocator, ptr, newCapacity);
			});
			//Move the old data into out new storage chunk
			const auto &dataBegin = vectorData.get();
			std::move(dataBegin, std::next(dataBegin, vectorSize), newData.get());
			//Assign the new data to be the current
			vectorData = std::move(newData);
			//Update the capacity
			vectorCapacity = newCapacity;
		}

		reference elementAt(size_type pos) {
			return vectorData[pos];
		}

		const_reference elementAt(size_type pos) const {
			return vectorData[pos];
		}

		void throwIfOutOfBounds(size_type pos, const std::string &methodName) const {
			if (!(pos < this->size())) {
				throw std::out_of_range("MyVector::"+methodName+"() pos (which is "+std::to_string(pos)+") >= size (which is "+std::to_string(this->size())+")");
			}
		}

		void throwIfEmpty(const std::string &methodName) const {
			if (this->empty()) {
				throw std::out_of_range("MyVector::"+methodName+"() is empty");
			}
		}

		void resizeDown(size_type count) {
			for (size_type i=count; i<this->size(); ++i) {
				allocatorTraits::destroy(vectorAllocator, &vectorData[i]);
			}
			vectorSize = count;
		}

	public:
		//constructor

		virtual ~MyVector() {
			//destroy all elements
			this->resizeDown(0);
			//std::unique_ptr will handle deallocation
		}

		MyVector& operator=(const MyVector &other) {
			this->vectorAllocator = other.vectorAllocator;
			//Destroy everything in this container
			this->resizeDown(0);
			//Allocate for higher capacity if neccessary
			//	if 'other' has a smaller capacity, we dont reduce ours
			this->reallocateToNewSizeIfNecessary(other.size());
			//Copy construct all elements into this list
			for (size_type i=0; i<other.size(); ++i) {
				allocatorTraits::construct(vectorAllocator, &vectorData[i], other[i]);
				++vectorSize;
			}
			return *this;
		}

		MyVector& operator=(MyVector &&other) {
			this->vectorAllocator = std::move(other.vectorAllocator);
			//Destroy everything in this container
			this->resizeDown(0);
			//Take ownership of everything from the other vector
			this->vectorSize = std::move(other.vectorSize);
			other.vectorSize = 0;
			this->vectorCapacity = std::move(other.vectorCapacity);
			this->vectorData = std::move(other.vectorData);
			return *this;
		}

		MyVector& operator=(std::initializer_list<value_type> ilist) {
			//Destroy everything in this container
			this->resizeDown(0);
			//Reallocate if this is larger than our current container
			const size_type length = std::distance(ilist.begin(), ilist.end());
			reallocateToNewSizeIfNecessary(length);
			for (auto it=ilist.begin(), end=ilist.end(); it!=end; ++it) {
				emplace_back(*it);
			}
			return *this;
		}
		//operator= initializer list

		//assign

		allocator_type get_allocator() const {
			return vectorAllocator;
		}

		reference at(size_type pos) {
			this->throwIfOutOfBounds(pos, "at");
			return this->elementAt(pos);
		}

		const_reference at(size_type pos) const {
			this->throwIfOutOfBounds(pos, "at");
			return this->elementAt(pos);
		}

		reference operator[](size_type pos) {
			return this->elementAt(pos);
		}

		const_reference operator[](size_type pos) const {
			return this->elementAt(pos);
		}

		reference front() {
			this->throwIfEmpty("front");
			return this->elementAt(0);
		}

		const_reference front() const {
			this->throwIfEmpty("front");
			return this->elementAt(0);
		}

		reference back() {
			this->throwIfEmpty("back");
			return this->elementAt(this->size()-1);
		}

		const_reference back() const {
			this->throwIfEmpty("back");
			return this->elementAt(this->size()-1);
		}

		pointer data() {
			return vectorData.get();
		}

		const_pointer data() const {
			return vectorData.get();
		}

		//begin, cbegin
		//end, cend
		//rbegin, crbegin
		//rend, crend

		bool empty() const {
			return (this->size() == 0);
		}

		size_type size() const {
			return vectorSize;
		}

		size_type max_size() const {
			return allocatorTraits::max_size(vectorAllocator);
		}

		void reserve(size_type newCapacity) {
			this->reallocateToNewSizeIfNecessary(newCapacity);
		}

		size_type capacity() const {
			return vectorCapacity;
		}

		void shrink_to_fit() {
			if (this->size() < this->capacity()) {
				this->reallocate(vectorSize);
			}
		}

		void clear() {
			this->resizeDown(0);
		}

		//insert
		//emplace
		//erase

		void push_back(const value_type &obj) {
			this->reallocateIfNecessary();
			allocatorTraits::construct(vectorAllocator, &vectorData[vectorSize], obj);
			++vectorSize;
		}

		void push_back(value_type &&obj) {
			this->reallocateIfNecessary();
			allocatorTraits::construct(vectorAllocator, &vectorData[vectorSize], std::move(obj));
			++vectorSize;
		}

		template<class... Args>
		void emplace_back(Args&&... args) {
			this->reallocateIfNecessary();
			allocatorTraits::construct(vectorAllocator, &vectorData[vectorSize], std::forward<Args>(args)...);
			++vectorSize;
		}

		void pop_back() {
			this->throwIfEmpty("pop_back");
			this->resizeDown(this->size()-1);
		}

		void resize(size_type count) {
			if (this->size() < count) {
				this->reallocateToNewSizeIfNecessary(count);
				while (vectorSize < count) {
					//Fill with default constructed elements
					allocatorTraits::construct(vectorAllocator, &vectorData[vectorSize]);
					++vectorSize;
				}
			} else if (this->size() > count) {
				this->resizeDown(count);
			}
		}

		void resize(size_type count, const value_type &value) {
			if (this->size() < count) {
				this->reallocateToNewSizeIfNecessary(count);
				while (vectorSize < count) {
					//Fill with default constructed elements
					allocatorTraits::construct(vectorAllocator, &vectorData[vectorSize], value);
					++vectorSize;
				}
			} else if (this->size() > count) {
				this->resizeDown(count);
			}
		}

		//swap

		//operators:
		//	==
		//	!=
		//	<
		//	<=
		//	>
		//	>=

		//std::swap
	};
}

#endif //MYVECTOR_HPP<commit_msg>removed comment to do initializer_list assignment<commit_after>#ifndef MYVECTOR_HPP
#define MYVECTOR_HPP 1

#include <algorithm>
#include <cstddef>
#include <iostream>
#include <memory>

namespace sandsnip3r {
	template<class Type>
	class MyVectorIterator {
	private:
	};

	template<class Type, class Allocator = std::allocator<Type>>
	class MyVector {
	public:
		using value_type = Type;
		using allocator_type = Allocator;
		using size_type = size_t;
		using difference_type = ptrdiff_t;
		using reference = Type&;
		using const_reference = const Type&;
		using pointer = Type*;
		using const_pointer = const Type*;
		//iterator
		//const_iterator
		//reverse_iterator
		//const_reverse_iterator
	private:
		using allocatorTraits = std::allocator_traits<allocator_type>;
		allocator_type vectorAllocator;
		std::unique_ptr<value_type[], std::function<void(value_type*)>> vectorData;
		size_t vectorSize{0};
		size_t vectorCapacity{0};

		void reallocateIfNecessary() {
			//Arguments state the the golden ratio is the most appropriate growth factor
			const auto GROWTH_FACTOR = 1.618;
			if (this->size() == this->capacity()) {
				//Need to reallocate to make space
				size_type newCapacity = (vectorCapacity == 0 ? 1 : vectorCapacity * GROWTH_FACTOR);
				reallocate(newCapacity);
			}
		}

		void reallocateToNewSizeIfNecessary(size_type newCapacity) {
			if (this->capacity() < newCapacity) {
				reallocate(newCapacity);
			}
		}

		void reallocate(size_type newCapacity) {
			//Allocate memory for the new data
			if (newCapacity > this->max_size()) {
				throw std::length_error("MyVector::reallocate() newCapacity (which is "+std::to_string(newCapacity)+") > max_size (which is "+std::to_string(this->max_size())+")");
			}
			//	store it in a unique_ptr
			std::unique_ptr<value_type[], std::function<void(value_type*)>> newData(allocatorTraits::allocate(vectorAllocator, newCapacity), [this, newCapacity](value_type *ptr){
				allocatorTraits::deallocate(vectorAllocator, ptr, newCapacity);
			});
			//Move the old data into out new storage chunk
			const auto &dataBegin = vectorData.get();
			std::move(dataBegin, std::next(dataBegin, vectorSize), newData.get());
			//Assign the new data to be the current
			vectorData = std::move(newData);
			//Update the capacity
			vectorCapacity = newCapacity;
		}

		reference elementAt(size_type pos) {
			return vectorData[pos];
		}

		const_reference elementAt(size_type pos) const {
			return vectorData[pos];
		}

		void throwIfOutOfBounds(size_type pos, const std::string &methodName) const {
			if (!(pos < this->size())) {
				throw std::out_of_range("MyVector::"+methodName+"() pos (which is "+std::to_string(pos)+") >= size (which is "+std::to_string(this->size())+")");
			}
		}

		void throwIfEmpty(const std::string &methodName) const {
			if (this->empty()) {
				throw std::out_of_range("MyVector::"+methodName+"() is empty");
			}
		}

		void resizeDown(size_type count) {
			for (size_type i=count; i<this->size(); ++i) {
				allocatorTraits::destroy(vectorAllocator, &vectorData[i]);
			}
			vectorSize = count;
		}

	public:
		//constructor

		virtual ~MyVector() {
			//destroy all elements
			this->resizeDown(0);
			//std::unique_ptr will handle deallocation
		}

		MyVector& operator=(const MyVector &other) {
			this->vectorAllocator = other.vectorAllocator;
			//Destroy everything in this container
			this->resizeDown(0);
			//Allocate for higher capacity if neccessary
			//	if 'other' has a smaller capacity, we dont reduce ours
			this->reallocateToNewSizeIfNecessary(other.size());
			//Copy construct all elements into this list
			for (size_type i=0; i<other.size(); ++i) {
				allocatorTraits::construct(vectorAllocator, &vectorData[i], other[i]);
				++vectorSize;
			}
			return *this;
		}

		MyVector& operator=(MyVector &&other) {
			this->vectorAllocator = std::move(other.vectorAllocator);
			//Destroy everything in this container
			this->resizeDown(0);
			//Take ownership of everything from the other vector
			this->vectorSize = std::move(other.vectorSize);
			other.vectorSize = 0;
			this->vectorCapacity = std::move(other.vectorCapacity);
			this->vectorData = std::move(other.vectorData);
			return *this;
		}

		MyVector& operator=(std::initializer_list<value_type> ilist) {
			//Destroy everything in this container
			this->resizeDown(0);
			//Reallocate if this is larger than our current container
			const size_type length = std::distance(ilist.begin(), ilist.end());
			reallocateToNewSizeIfNecessary(length);
			for (auto it=ilist.begin(), end=ilist.end(); it!=end; ++it) {
				emplace_back(*it);
			}
			return *this;
		}

		//assign

		allocator_type get_allocator() const {
			return vectorAllocator;
		}

		reference at(size_type pos) {
			this->throwIfOutOfBounds(pos, "at");
			return this->elementAt(pos);
		}

		const_reference at(size_type pos) const {
			this->throwIfOutOfBounds(pos, "at");
			return this->elementAt(pos);
		}

		reference operator[](size_type pos) {
			return this->elementAt(pos);
		}

		const_reference operator[](size_type pos) const {
			return this->elementAt(pos);
		}

		reference front() {
			this->throwIfEmpty("front");
			return this->elementAt(0);
		}

		const_reference front() const {
			this->throwIfEmpty("front");
			return this->elementAt(0);
		}

		reference back() {
			this->throwIfEmpty("back");
			return this->elementAt(this->size()-1);
		}

		const_reference back() const {
			this->throwIfEmpty("back");
			return this->elementAt(this->size()-1);
		}

		pointer data() {
			return vectorData.get();
		}

		const_pointer data() const {
			return vectorData.get();
		}

		//begin, cbegin
		//end, cend
		//rbegin, crbegin
		//rend, crend

		bool empty() const {
			return (this->size() == 0);
		}

		size_type size() const {
			return vectorSize;
		}

		size_type max_size() const {
			return allocatorTraits::max_size(vectorAllocator);
		}

		void reserve(size_type newCapacity) {
			this->reallocateToNewSizeIfNecessary(newCapacity);
		}

		size_type capacity() const {
			return vectorCapacity;
		}

		void shrink_to_fit() {
			if (this->size() < this->capacity()) {
				this->reallocate(vectorSize);
			}
		}

		void clear() {
			this->resizeDown(0);
		}

		//insert
		//emplace
		//erase

		void push_back(const value_type &obj) {
			this->reallocateIfNecessary();
			allocatorTraits::construct(vectorAllocator, &vectorData[vectorSize], obj);
			++vectorSize;
		}

		void push_back(value_type &&obj) {
			this->reallocateIfNecessary();
			allocatorTraits::construct(vectorAllocator, &vectorData[vectorSize], std::move(obj));
			++vectorSize;
		}

		template<class... Args>
		void emplace_back(Args&&... args) {
			this->reallocateIfNecessary();
			allocatorTraits::construct(vectorAllocator, &vectorData[vectorSize], std::forward<Args>(args)...);
			++vectorSize;
		}

		void pop_back() {
			this->throwIfEmpty("pop_back");
			this->resizeDown(this->size()-1);
		}

		void resize(size_type count) {
			if (this->size() < count) {
				this->reallocateToNewSizeIfNecessary(count);
				while (vectorSize < count) {
					//Fill with default constructed elements
					allocatorTraits::construct(vectorAllocator, &vectorData[vectorSize]);
					++vectorSize;
				}
			} else if (this->size() > count) {
				this->resizeDown(count);
			}
		}

		void resize(size_type count, const value_type &value) {
			if (this->size() < count) {
				this->reallocateToNewSizeIfNecessary(count);
				while (vectorSize < count) {
					//Fill with default constructed elements
					allocatorTraits::construct(vectorAllocator, &vectorData[vectorSize], value);
					++vectorSize;
				}
			} else if (this->size() > count) {
				this->resizeDown(count);
			}
		}

		//swap

		//operators:
		//	==
		//	!=
		//	<
		//	<=
		//	>
		//	>=

		//std::swap
	};
}

#endif //MYVECTOR_HPP<|endoftext|>


<commit_before>/*
-------------------------------------------------------------------------------
Name:
	FPL-Demo | Crackout
Description:
	A breakout-like game based on FPL
Requirements:
	- C++ Compiler
	- GLM
	- Box2D
	- Final Dynamic OpenGL
Author:
	Torsten Spaete
Changelog:
	## 2018-05-05:
	- Updated description & todo
	## 2018-04-26:
	- Game implemented
	## 2018-04-24:
	- Initial creation
Todo:
	- Add Score + Lifes
	- Text rendering (Score, Lifes, Level + Seed)
	- Main menu
	- Pause menu (Detect pause)
	- Music
	- Sound
	- Multiball
	- Brick types (Harder, Metal)
	- Items (Ball speed, Paddle grow, Autoglue, Multiball, Player Up)
	- Re-create sprites in HD
-------------------------------------------------------------------------------
*/

#define FPL_IMPLEMENTATION
#include <final_platform_layer.h>

#include <math.h> // abs

#include "game.h"

static void UpdateKeyboardButtonState(const bool isDown, ButtonState &targetButton) {
	if(isDown != targetButton.isDown) {
		targetButton.isDown = isDown;
		++targetButton.halfTransitionCount;
	}
}

static void UpdateDigitalButtonState(const bool isDown, const ButtonState &oldState, ButtonState &newState) {
	newState.isDown = isDown;
	newState.halfTransitionCount = (oldState.isDown != newState.isDown) ? 1 : 0;
}

static void UpdateDefaultController(Input *currentInput, int newIndex) {
	if(newIndex != -1) {
		currentInput->defaultControllerIndex = newIndex;
	} else {
		currentInput->defaultControllerIndex = -1;
		for(int i = FPL_ARRAYCOUNT(currentInput->controllers) - 1; i > 0; i--) {
			if(currentInput->controllers[i].isConnected) {
				currentInput->defaultControllerIndex = i;
				break;
			}
		}
	}
}

static void ProcessEvents(Input *currentInput, Input *prevInput, bool &isWindowActive, Vec2i &lastMousePos) {
	Controller *currentKeyboardController = &currentInput->keyboard;
	Controller *prevKeyboardController = &prevInput->keyboard;
	fplEvent event;
	while(fplPollEvent(&event)) {
		switch(event.type) {
			case fplEventType_Window:
			{
				switch(event.window.type) {
					case fplWindowEventType_GotFocus:
						isWindowActive = true;
						break;
					case fplWindowEventType_LostFocus:
						isWindowActive = false;
						break;
				}
			} break;

			case fplEventType_Gamepad:
			{
				// @CLEANUP: For now we just use the device index, but later it should be "added" to the controllers array and remembered somehow
				uint32_t controllerIndex = 1 + event.gamepad.deviceIndex;
				FPL_ASSERT(controllerIndex < FPL_ARRAYCOUNT(currentInput->controllers));
				Controller *currentController = &currentInput->controllers[controllerIndex];
				Controller *prevController = &prevInput->controllers[controllerIndex];
				switch(event.gamepad.type) {
					case fplGamepadEventType_Connected:
					{
						currentController->isConnected = true;
						UpdateDefaultController(currentInput, controllerIndex);
					} break;
					case fplGamepadEventType_Disconnected:
					{
						currentController->isConnected = false;
						UpdateDefaultController(currentInput, -1);
					} break;
					case fplGamepadEventType_StateChanged:
					{
						fplGamepadState &padstate = event.gamepad.state;
						assert(currentController->isConnected);
						if(abs(padstate.leftStickX) > 0.0f || abs(padstate.leftStickY) > 0.0f) {
							currentController->isAnalog = true;
							currentController->analogMovement.x = padstate.leftStickX;
							currentController->analogMovement.y = padstate.leftStickY;
						} else {
							currentController->isAnalog = false;
							UpdateDigitalButtonState(padstate.dpadDown.isDown, prevController->moveDown, currentController->moveDown);
							UpdateDigitalButtonState(padstate.dpadUp.isDown, prevController->moveUp, currentController->moveUp);
							UpdateDigitalButtonState(padstate.dpadLeft.isDown, prevController->moveLeft, currentController->moveLeft);
							UpdateDigitalButtonState(padstate.dpadRight.isDown, prevController->moveRight, currentController->moveRight);
						}
						UpdateDigitalButtonState(padstate.actionA.isDown, prevController->actionDown, currentController->actionDown);
						UpdateDigitalButtonState(padstate.actionB.isDown, prevController->actionRight, currentController->actionRight);
						UpdateDigitalButtonState(padstate.actionX.isDown, prevController->actionLeft, currentController->actionLeft);
						UpdateDigitalButtonState(padstate.actionY.isDown, prevController->actionUp, currentController->actionUp);
					} break;
				}
			} break;

			case fplEventType_Mouse:
			{
				switch(event.mouse.type) {
					case fplMouseEventType_Move:
					{
						currentInput->mouse.pos = lastMousePos = MakeVec2i(event.mouse.mouseX, event.mouse.mouseY);
					} break;

					case fplMouseEventType_ButtonDown:
					case fplMouseEventType_ButtonUp:
					{
						bool isDown = event.mouse.type == fplMouseEventType_ButtonDown;
						if(event.mouse.mouseButton == fplMouseButtonType_Left) {
							UpdateKeyboardButtonState(isDown, currentInput->mouse.left);
						} else if(event.mouse.mouseButton == fplMouseButtonType_Right) {
							UpdateKeyboardButtonState(isDown, currentInput->mouse.right);
						} else if(event.mouse.mouseButton == fplMouseButtonType_Middle) {
							UpdateKeyboardButtonState(isDown, currentInput->mouse.middle);
						}
					} break;

					case fplMouseEventType_Wheel:
					{
						currentInput->mouse.wheelDelta = event.mouse.wheelDelta;
					} break;
				}
			} break;

			case fplEventType_Keyboard:
			{
				switch(event.keyboard.type) {
					case fplKeyboardEventType_CharInput:
					{
					} break;
					case fplKeyboardEventType_KeyDown:
					case fplKeyboardEventType_KeyUp:
					{
						if(!currentKeyboardController->isConnected) {
							currentKeyboardController->isConnected = true;
							if(currentInput->defaultControllerIndex == -1) {
								UpdateDefaultController(currentInput, 0);
							}
						}
						bool isDown = (event.keyboard.type == fplKeyboardEventType_KeyDown) ? 1 : 0;
						switch(event.keyboard.mappedKey) {
							case fplKey_F1:
								UpdateKeyboardButtonState(isDown, currentKeyboardController->editorToggle);
								break;
							case fplKey_A:
							case fplKey_Left:
								UpdateKeyboardButtonState(isDown, currentKeyboardController->moveLeft);
								break;
							case fplKey_D:
							case fplKey_Right:
								UpdateKeyboardButtonState(isDown, currentKeyboardController->moveRight);
								break;
							case fplKey_W:
							case fplKey_Up:
								UpdateKeyboardButtonState(isDown, currentKeyboardController->moveUp);
								break;
							case fplKey_S:
							case fplKey_Down:
								UpdateKeyboardButtonState(isDown, currentKeyboardController->moveDown);
								break;
							case fplKey_Space:
								UpdateKeyboardButtonState(isDown, currentKeyboardController->actionDown);
								break;
							case fplKey_F:
							{
								if (!isDown) {
									bool wasFullscreen = fplIsWindowFullscreen();
									fplSetWindowFullscreen(!wasFullscreen, 0, 0, 0);
								}
							} break;
						}
					} break;
				}
			} break;
		}
	}
}

int main(int argc, char *argv[]) {
	fplSettings settings = fplMakeDefaultSettings();
	settings.video.driver = fplVideoDriverType_OpenGL;
	settings.video.graphics.opengl.compabilityFlags = fplOpenGLCompabilityFlags_Legacy;
	settings.video.isVSync = true;
	fplCopyAnsiString("FPL Demo | Crackout", settings.window.windowTitle, FPL_ARRAYCOUNT(settings.window.windowTitle));
	int result = 0;
	if(fplPlatformInit(fplInitFlags_All, &settings)) {
		GameState *game = GameCreate();
		if(game != nullptr) {
			const double TargetDeltaTime = 1.0 / 60.0;

			fplSetWindowCursorEnabled(false);

			Input inputs[2] = {};
			Input *curInput = &inputs[0];
			Input *prevInput = &inputs[1];
			Vec2i lastMousePos = MakeVec2i(-1, -1);
			bool isWindowActive = true;
			curInput->defaultControllerIndex = -1;

			uint32_t frameCount = 0;
			uint32_t updateCount = 0;
			double lastTime = fplGetTimeInSecondsHP();
			double fpsTimerInSecs = fplGetTimeInSecondsHP();
			double frameAccumulator = TargetDeltaTime;

			while(!IsGameExiting(*game) && fplWindowUpdate()) {
				// Window size
				fplWindowSize winArea;
				if(fplGetWindowArea(&winArea)) {
					curInput->windowSize.x = winArea.width;
					curInput->windowSize.y = winArea.height;
				}

				// Remember previous keyboard and mouse state
				Controller *currentKeyboardController = &curInput->keyboard;
				Controller *prevKeyboardController = &prevInput->keyboard;
				Mouse *currentMouse = &curInput->mouse;
				Mouse *prevMouse = &prevInput->mouse;
				*currentKeyboardController = {};
				*currentMouse = {};
				currentKeyboardController->isConnected = prevKeyboardController->isConnected;
				for(uint32_t buttonIndex = 0; buttonIndex < FPL_ARRAYCOUNT(currentKeyboardController->buttons); ++buttonIndex) {
					currentKeyboardController->buttons[buttonIndex].isDown = prevKeyboardController->buttons[buttonIndex].isDown;
				}
				for(uint32_t buttonIndex = 0; buttonIndex < FPL_ARRAYCOUNT(currentMouse->buttons); ++buttonIndex) {
					currentMouse->buttons[buttonIndex] = prevMouse->buttons[buttonIndex];
					currentMouse->buttons[buttonIndex].halfTransitionCount = 0;
				}
				currentMouse->pos = lastMousePos;

				// Remember previous gamepad connected states
				for(uint32_t controllerIndex = 1; controllerIndex < FPL_ARRAYCOUNT(curInput->controllers); ++controllerIndex) {
					Controller *currentGamepadController = &curInput->controllers[controllerIndex];
					Controller *prevGamepadController = &prevInput->controllers[controllerIndex];
					currentGamepadController->isConnected = prevGamepadController->isConnected;
					currentGamepadController->isAnalog = prevGamepadController->isAnalog;
				}

				// Set time states
				curInput->deltaTime = (float)TargetDeltaTime;

				// Events
				ProcessEvents(curInput, prevInput, isWindowActive, lastMousePos);

				// Game Update
				GameInput(*game, *curInput, isWindowActive);
#if 1
				while(frameAccumulator >= TargetDeltaTime) {
					GameUpdate(*game, *curInput, isWindowActive);
					frameAccumulator -= TargetDeltaTime;
					++updateCount;
				}
#else
				GameUpdate(*game, *curInput, isWindowActive);
				++updateCount;
#endif

				// @TODO(final): Yield thread when we are running too fast
				{
					double endWorkTime = fplGetTimeInSecondsHP();
					double workDuration = endWorkTime - lastTime;
				}

				// Render
				GameDraw(*game);
				fplVideoFlip();
				++frameCount;

				// Timing
				double endTime = fplGetTimeInSecondsHP();
				double frameDuration = endTime - lastTime;
				frameAccumulator += frameDuration;
				frameAccumulator = FPL_MIN(0.1, frameAccumulator);
				lastTime = endTime;
				if(endTime >= (fpsTimerInSecs + 1.0)) {
					fpsTimerInSecs = endTime;
					char charBuffer[256];
					fplFormatAnsiString(charBuffer, FPL_ARRAYCOUNT(charBuffer), "Fps: %d, Ups: %d\n", frameCount, updateCount);
					//OutputDebugStringA(charBuffer);
					frameCount = 0;
					updateCount = 0;
				}

				// Swap input
				{
					Input *tmp = curInput;
					curInput = prevInput;
					prevInput = tmp;
				}
			}

			fplSetWindowCursorEnabled(true);

			GameDestroy(game);
		} else {
			result = -1;
		}
		fplPlatformRelease();
	} else {
		result = -1;
	}
	return (result);
}<commit_msg>Enable logging and debug out for crackout<commit_after>/*
-------------------------------------------------------------------------------
Name:
	FPL-Demo | Crackout
Description:
	A breakout-like game based on FPL
Requirements:
	- C++ Compiler
	- GLM
	- Box2D
	- Final Dynamic OpenGL
Author:
	Torsten Spaete
Changelog:
	## 2018-05-05:
	- Updated description & todo
	## 2018-04-26:
	- Game implemented
	## 2018-04-24:
	- Initial creation
Todo:
	- Add Score + Lifes
	- Text rendering (Score, Lifes, Level + Seed)
	- Main menu
	- Pause menu (Detect pause)
	- Music
	- Sound
	- Multiball
	- Brick types (Harder, Metal)
	- Items (Ball speed, Paddle grow, Autoglue, Multiball, Player Up)
	- Re-create sprites in HD
-------------------------------------------------------------------------------
*/

#define FPL_IMPLEMENTATION
#define FPL_LOGGING
#define FPL_LOG_TO_DEBUGOUT
#include <final_platform_layer.h>

#include <math.h> // abs

#include "game.h"

static void UpdateKeyboardButtonState(const bool isDown, ButtonState &targetButton) {
	if(isDown != targetButton.isDown) {
		targetButton.isDown = isDown;
		++targetButton.halfTransitionCount;
	}
}

static void UpdateDigitalButtonState(const bool isDown, const ButtonState &oldState, ButtonState &newState) {
	newState.isDown = isDown;
	newState.halfTransitionCount = (oldState.isDown != newState.isDown) ? 1 : 0;
}

static void UpdateDefaultController(Input *currentInput, int newIndex) {
	if(newIndex != -1) {
		currentInput->defaultControllerIndex = newIndex;
	} else {
		currentInput->defaultControllerIndex = -1;
		for(int i = FPL_ARRAYCOUNT(currentInput->controllers) - 1; i > 0; i--) {
			if(currentInput->controllers[i].isConnected) {
				currentInput->defaultControllerIndex = i;
				break;
			}
		}
	}
}

static void ProcessEvents(Input *currentInput, Input *prevInput, bool &isWindowActive, Vec2i &lastMousePos) {
	Controller *currentKeyboardController = &currentInput->keyboard;
	Controller *prevKeyboardController = &prevInput->keyboard;
	fplEvent event;
	while(fplPollEvent(&event)) {
		switch(event.type) {
			case fplEventType_Window:
			{
				switch(event.window.type) {
					case fplWindowEventType_GotFocus:
						isWindowActive = true;
						break;
					case fplWindowEventType_LostFocus:
						isWindowActive = false;
						break;
				}
			} break;

			case fplEventType_Gamepad:
			{
				// @CLEANUP: For now we just use the device index, but later it should be "added" to the controllers array and remembered somehow
				uint32_t controllerIndex = 1 + event.gamepad.deviceIndex;
				FPL_ASSERT(controllerIndex < FPL_ARRAYCOUNT(currentInput->controllers));
				Controller *currentController = &currentInput->controllers[controllerIndex];
				Controller *prevController = &prevInput->controllers[controllerIndex];
				switch(event.gamepad.type) {
					case fplGamepadEventType_Connected:
					{
						currentController->isConnected = true;
						UpdateDefaultController(currentInput, controllerIndex);
					} break;
					case fplGamepadEventType_Disconnected:
					{
						currentController->isConnected = false;
						UpdateDefaultController(currentInput, -1);
					} break;
					case fplGamepadEventType_StateChanged:
					{
						fplGamepadState &padstate = event.gamepad.state;
						assert(currentController->isConnected);
						if(abs(padstate.leftStickX) > 0.0f || abs(padstate.leftStickY) > 0.0f) {
							currentController->isAnalog = true;
							currentController->analogMovement.x = padstate.leftStickX;
							currentController->analogMovement.y = padstate.leftStickY;
						} else {
							currentController->isAnalog = false;
							UpdateDigitalButtonState(padstate.dpadDown.isDown, prevController->moveDown, currentController->moveDown);
							UpdateDigitalButtonState(padstate.dpadUp.isDown, prevController->moveUp, currentController->moveUp);
							UpdateDigitalButtonState(padstate.dpadLeft.isDown, prevController->moveLeft, currentController->moveLeft);
							UpdateDigitalButtonState(padstate.dpadRight.isDown, prevController->moveRight, currentController->moveRight);
						}
						UpdateDigitalButtonState(padstate.actionA.isDown, prevController->actionDown, currentController->actionDown);
						UpdateDigitalButtonState(padstate.actionB.isDown, prevController->actionRight, currentController->actionRight);
						UpdateDigitalButtonState(padstate.actionX.isDown, prevController->actionLeft, currentController->actionLeft);
						UpdateDigitalButtonState(padstate.actionY.isDown, prevController->actionUp, currentController->actionUp);
					} break;
				}
			} break;

			case fplEventType_Mouse:
			{
				switch(event.mouse.type) {
					case fplMouseEventType_Move:
					{
						currentInput->mouse.pos = lastMousePos = MakeVec2i(event.mouse.mouseX, event.mouse.mouseY);
					} break;

					case fplMouseEventType_ButtonDown:
					case fplMouseEventType_ButtonUp:
					{
						bool isDown = event.mouse.type == fplMouseEventType_ButtonDown;
						if(event.mouse.mouseButton == fplMouseButtonType_Left) {
							UpdateKeyboardButtonState(isDown, currentInput->mouse.left);
						} else if(event.mouse.mouseButton == fplMouseButtonType_Right) {
							UpdateKeyboardButtonState(isDown, currentInput->mouse.right);
						} else if(event.mouse.mouseButton == fplMouseButtonType_Middle) {
							UpdateKeyboardButtonState(isDown, currentInput->mouse.middle);
						}
					} break;

					case fplMouseEventType_Wheel:
					{
						currentInput->mouse.wheelDelta = event.mouse.wheelDelta;
					} break;
				}
			} break;

			case fplEventType_Keyboard:
			{
				switch(event.keyboard.type) {
					case fplKeyboardEventType_CharInput:
					{
					} break;
					case fplKeyboardEventType_KeyDown:
					case fplKeyboardEventType_KeyUp:
					{
						if(!currentKeyboardController->isConnected) {
							currentKeyboardController->isConnected = true;
							if(currentInput->defaultControllerIndex == -1) {
								UpdateDefaultController(currentInput, 0);
							}
						}
						bool isDown = (event.keyboard.type == fplKeyboardEventType_KeyDown) ? 1 : 0;
						switch(event.keyboard.mappedKey) {
							case fplKey_F1:
								UpdateKeyboardButtonState(isDown, currentKeyboardController->editorToggle);
								break;
							case fplKey_A:
							case fplKey_Left:
								UpdateKeyboardButtonState(isDown, currentKeyboardController->moveLeft);
								break;
							case fplKey_D:
							case fplKey_Right:
								UpdateKeyboardButtonState(isDown, currentKeyboardController->moveRight);
								break;
							case fplKey_W:
							case fplKey_Up:
								UpdateKeyboardButtonState(isDown, currentKeyboardController->moveUp);
								break;
							case fplKey_S:
							case fplKey_Down:
								UpdateKeyboardButtonState(isDown, currentKeyboardController->moveDown);
								break;
							case fplKey_Space:
								UpdateKeyboardButtonState(isDown, currentKeyboardController->actionDown);
								break;
							case fplKey_F:
							{
								if (!isDown) {
									bool wasFullscreen = fplIsWindowFullscreen();
									fplSetWindowFullscreen(!wasFullscreen, 0, 0, 0);
								}
							} break;
						}
					} break;
				}
			} break;
		}
	}
}

int main(int argc, char *argv[]) {
	fplSettings settings = fplMakeDefaultSettings();
	settings.video.driver = fplVideoDriverType_OpenGL;
	settings.video.graphics.opengl.compabilityFlags = fplOpenGLCompabilityFlags_Legacy;
	settings.video.isVSync = true;
	fplCopyAnsiString("FPL Demo | Crackout", settings.window.windowTitle, FPL_ARRAYCOUNT(settings.window.windowTitle));
	int result = 0;
	if(fplPlatformInit(fplInitFlags_All, &settings)) {
		GameState *game = GameCreate();
		if(game != nullptr) {
			const double TargetDeltaTime = 1.0 / 60.0;

			fplSetWindowCursorEnabled(false);

			Input inputs[2] = {};
			Input *curInput = &inputs[0];
			Input *prevInput = &inputs[1];
			Vec2i lastMousePos = MakeVec2i(-1, -1);
			bool isWindowActive = true;
			curInput->defaultControllerIndex = -1;

			uint32_t frameCount = 0;
			uint32_t updateCount = 0;
			double lastTime = fplGetTimeInSecondsHP();
			double fpsTimerInSecs = fplGetTimeInSecondsHP();
			double frameAccumulator = TargetDeltaTime;

			while(!IsGameExiting(*game) && fplWindowUpdate()) {
				// Window size
				fplWindowSize winArea;
				if(fplGetWindowArea(&winArea)) {
					curInput->windowSize.x = winArea.width;
					curInput->windowSize.y = winArea.height;
				}

				// Remember previous keyboard and mouse state
				Controller *currentKeyboardController = &curInput->keyboard;
				Controller *prevKeyboardController = &prevInput->keyboard;
				Mouse *currentMouse = &curInput->mouse;
				Mouse *prevMouse = &prevInput->mouse;
				*currentKeyboardController = {};
				*currentMouse = {};
				currentKeyboardController->isConnected = prevKeyboardController->isConnected;
				for(uint32_t buttonIndex = 0; buttonIndex < FPL_ARRAYCOUNT(currentKeyboardController->buttons); ++buttonIndex) {
					currentKeyboardController->buttons[buttonIndex].isDown = prevKeyboardController->buttons[buttonIndex].isDown;
				}
				for(uint32_t buttonIndex = 0; buttonIndex < FPL_ARRAYCOUNT(currentMouse->buttons); ++buttonIndex) {
					currentMouse->buttons[buttonIndex] = prevMouse->buttons[buttonIndex];
					currentMouse->buttons[buttonIndex].halfTransitionCount = 0;
				}
				currentMouse->pos = lastMousePos;

				// Remember previous gamepad connected states
				for(uint32_t controllerIndex = 1; controllerIndex < FPL_ARRAYCOUNT(curInput->controllers); ++controllerIndex) {
					Controller *currentGamepadController = &curInput->controllers[controllerIndex];
					Controller *prevGamepadController = &prevInput->controllers[controllerIndex];
					currentGamepadController->isConnected = prevGamepadController->isConnected;
					currentGamepadController->isAnalog = prevGamepadController->isAnalog;
				}

				// Set time states
				curInput->deltaTime = (float)TargetDeltaTime;

				// Events
				ProcessEvents(curInput, prevInput, isWindowActive, lastMousePos);

				// Game Update
				GameInput(*game, *curInput, isWindowActive);
#if 1
				while(frameAccumulator >= TargetDeltaTime) {
					GameUpdate(*game, *curInput, isWindowActive);
					frameAccumulator -= TargetDeltaTime;
					++updateCount;
				}
#else
				GameUpdate(*game, *curInput, isWindowActive);
				++updateCount;
#endif

				// @TODO(final): Yield thread when we are running too fast
				{
					double endWorkTime = fplGetTimeInSecondsHP();
					double workDuration = endWorkTime - lastTime;
				}

				// Render
				GameDraw(*game);
				fplVideoFlip();
				++frameCount;

				// Timing
				double endTime = fplGetTimeInSecondsHP();
				double frameDuration = endTime - lastTime;
				frameAccumulator += frameDuration;
				frameAccumulator = FPL_MIN(0.1, frameAccumulator);
				lastTime = endTime;
				if(endTime >= (fpsTimerInSecs + 1.0)) {
					fpsTimerInSecs = endTime;
					char charBuffer[256];
					fplFormatAnsiString(charBuffer, FPL_ARRAYCOUNT(charBuffer), "Fps: %d, Ups: %d\n", frameCount, updateCount);
					//OutputDebugStringA(charBuffer);
					frameCount = 0;
					updateCount = 0;
				}

				// Swap input
				{
					Input *tmp = curInput;
					curInput = prevInput;
					prevInput = tmp;
				}
			}

			fplSetWindowCursorEnabled(true);

			GameDestroy(game);
		} else {
			result = -1;
		}
		fplPlatformRelease();
	} else {
		result = -1;
	}
	return (result);
}<|endoftext|>


<commit_before>#line 2 "quanta/core/scripting/scripting.hpp"
/**
@copyright MIT license; see @ref index or the accompanying LICENSE file.

@file
@brief Scripting interface.
@ingroup lib_core_scripting
*/

#pragma once

#include <quanta/core/config.hpp>
#include <quanta/core/types.hpp>

#include <togo/core/error/assert.hpp>
#include <togo/core/utility/utility.hpp>
#include <togo/core/string/types.hpp>

#include <luajit-2.0/lua.hpp>

namespace quanta {

// Forward declarations
namespace object {
	struct Object;
}

namespace lua {

/**
	@addtogroup lua
	@{
*/

/// Get an argument from the stack as light userdata.
inline void* arg_lud(lua_State* L, signed narg, bool require = true) {
	luaL_checktype(L, narg, LUA_TLIGHTUSERDATA);
	auto p = lua_touserdata(L, narg);
	TOGO_ASSERTE(!require || p);
	return p;
}

/// Get an argument from the stack as a boolean.
inline bool arg_boolean(lua_State* L, signed narg) {
	luaL_checktype(L, narg, LUA_TBOOLEAN);
	return lua_toboolean(L, narg);
}

/// Get a string from the stack as a string reference.
inline StringRef arg_string(lua_State* L, signed narg, bool require = true) {
	size_t size = 0;
	auto data = luaL_checklstring(L, narg, &size);
	TOGO_ASSERTE(!require || data);
	return {data, static_cast<unsigned>(size)};
}

/// Push a string reference to the stack.
inline void push_string_ref(lua_State* L, StringRef str) {
	lua_pushlstring(L, str.data ? str.data : "", unsigned_cast(str.size));
}

/// Get an argument from the stack as an Object (LUD).
inline object::Object* arg_object(lua_State* L, signed narg, bool require = true) {
	return static_cast<object::Object*>(arg_lud(L, narg, require));
}

/** @} */ // end of doc-group lua

} // namespace lua
} // namespace quanta
<commit_msg>lib/core/scripting/scripting: only include the Lua headers needed.<commit_after>#line 2 "quanta/core/scripting/scripting.hpp"
/**
@copyright MIT license; see @ref index or the accompanying LICENSE file.

@file
@brief Scripting interface.
@ingroup lib_core_scripting
*/

#pragma once

#include <quanta/core/config.hpp>
#include <quanta/core/types.hpp>

#include <togo/core/error/assert.hpp>
#include <togo/core/utility/utility.hpp>
#include <togo/core/string/types.hpp>

extern "C" {
	#include <luajit-2.0/lua.h>
	#include <luajit-2.0/lauxlib.h>
	#include <luajit-2.0/lualib.h>
}

namespace quanta {

// Forward declarations
namespace object {
	struct Object;
}

namespace lua {

/**
	@addtogroup lua
	@{
*/

/// Get an argument from the stack as light userdata.
inline void* arg_lud(lua_State* L, signed narg, bool require = true) {
	luaL_checktype(L, narg, LUA_TLIGHTUSERDATA);
	auto p = lua_touserdata(L, narg);
	TOGO_ASSERTE(!require || p);
	return p;
}

/// Get an argument from the stack as a boolean.
inline bool arg_boolean(lua_State* L, signed narg) {
	luaL_checktype(L, narg, LUA_TBOOLEAN);
	return lua_toboolean(L, narg);
}

/// Get a string from the stack as a string reference.
inline StringRef arg_string(lua_State* L, signed narg, bool require = true) {
	size_t size = 0;
	auto data = luaL_checklstring(L, narg, &size);
	TOGO_ASSERTE(!require || data);
	return {data, static_cast<unsigned>(size)};
}

/// Push a string reference to the stack.
inline void push_string_ref(lua_State* L, StringRef str) {
	lua_pushlstring(L, str.data ? str.data : "", unsigned_cast(str.size));
}

/// Get an argument from the stack as an Object (LUD).
inline object::Object* arg_object(lua_State* L, signed narg, bool require = true) {
	return static_cast<object::Object*>(arg_lud(L, narg, require));
}

/** @} */ // end of doc-group lua

} // namespace lua
} // namespace quanta
<|endoftext|>


<commit_before>/*
The OpenTRV project licenses this file to you
under the Apache Licence, Version 2.0 (the "Licence");
you may not use this file except in compliance
with the Licence. You may obtain a copy of the Licence at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing,
software distributed under the Licence is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the Licence for the
specific language governing permissions and limitations
under the Licence.

Author(s) / Copyright (s): Damon Hart-Davis 2016
*/

/*
 * OTRadValve SIM900 tests.
 */

#include <gtest/gtest.h>
#include <stdio.h>
#include <stdlib.h>

#include "OTSIM900Link.h"

// Test the getter function definitely does what it should.
TEST(OTSIM900Link, getterFunction)
{
    const char SIM900_PIN[] = "1111";
    const OTSIM900Link::OTSIM900LinkConfig_t SIM900Config(false, SIM900_PIN, NULL, NULL, NULL);
    EXPECT_EQ(SIM900_PIN[0], SIM900Config.get((const uint8_t *)SIM900Config.PIN));
}

// Test for general sanity of OTSIM900Link.
// Make sure that an instance can be created and does not die horribly.
// Underlying simulated serial/SIM900 never accepts data or responds, eg like a dead card.
TEST(OTSIM900Link,basicsDeadCard)
{
    const bool verbose = false;

    class NULLSerialStream final : public Stream
      {
      public:
        void begin(unsigned long) { }
        void begin(unsigned long, uint8_t);
        void end();

        virtual size_t write(uint8_t c) override { if(verbose) { fprintf(stderr, "%c\n", (char) c); } return(0); }
        virtual int available() override { return(-1); }
        virtual int read() override { return(-1); }
        virtual int peek() override { return(-1); }
        virtual void flush() override { }
      };
    const char SIM900_PIN[] = "1111";
    const char SIM900_APN[] = "apn";
    const char SIM900_UDP_ADDR[] = "0.0.0.0"; // ORS server
    const char SIM900_UDP_PORT[] = "9999";
    const OTSIM900Link::OTSIM900LinkConfig_t SIM900Config(false, SIM900_PIN, SIM900_APN, SIM900_UDP_ADDR, SIM900_UDP_PORT);
    const OTRadioLink::OTRadioChannelConfig l0Config(&SIM900Config, true);
    OTSIM900Link::OTSIM900Link<0, 0, 0, NULLSerialStream> l0;
    EXPECT_TRUE(l0.configure(1, &l0Config));
    EXPECT_TRUE(l0.begin());
    EXPECT_EQ(OTSIM900Link::GET_STATE, l0._getState());
    // Try to hang just by calling poll() repeatedly.
    for(int i = 0; i < 100; ++i) { l0.poll(); }
    EXPECT_GE(OTSIM900Link::START_UP, l0._getState()) << "should keep trying to start with GET_STATE, RETRY_GET_STATE";
    // ...
    l0.end();
}

// Walk through state space of OTSIM900Link.
// Make sure that an instance can be created and does not die horribly.
// Is meant to mainly walk through all the normal expected SIM900 behaviour when all is well.
// Other tests can look at error handling including unexpected/garbage responses.
namespace B1
{
const bool verbose = true;

// Does a simple simulation of SIM900, responding sensibly to all commands needed by the OTSIM900Link impl.
// Allows for exercise of every major non-PANIC state of the OTSIM900Link implementation.
class GoodSimulator final : public Stream
  {
  public:
    // Events exposed.
    static bool haveSeenCommandStart;

  private:
    // Command being collected from OTSIM900Link.
    bool waitingForCommand = true;
    bool collectingCommand = false;
    // Entire request starting "AT"; no trailing CR or LF stored.
    std::string command;

    // Reply (postfix) being returned to OTSIM900Link: empty if none.
    std::string reply;

    // Keep track (crudely) of state.
    // 0: Default
    // 1: CIPSTATUS asked for once.
    // 2: CIPSTATUS asked for twice.
    uint8_t sim900LinkState = 0;

  public:
    void begin(unsigned long) { }
    void begin(unsigned long, uint8_t);
    void end();

    virtual size_t write(uint8_t uc) override
      {
      const char c = (char)uc;
      if(waitingForCommand)
        {
        // Look for leading 'A' of 'AT' to start a command.
        if('A' == c)
          {
          waitingForCommand = false;
          collectingCommand = true;
          command = 'A';
          haveSeenCommandStart = true; // Note at least one command start.
          }
        }
      else
        {
        // Look for CR (or LF) to terminate a command.
        if(('\r' == c) || ('\n' == c))
          {
          waitingForCommand = true;
          collectingCommand = false;
          if(verbose) { fprintf(stderr, "command received: %s\n", command.c_str()); }
          // Respond to particular commands...
          if("AT" == command) { reply = "AT\r\n\r\nOK\r\n"; }  // Relevant states: GET_STATE, RETRY_GET_STATE, START_UP
          else if("AT+CPIN?" == command) { reply = /* (random() & 1) ? "No PIN\r" : */ "AT+CPIN?\r\n\r\n+CPIN: READY\r\n\r\nOK\r\n"; }  // Relevant states: CHECK_PIN
          else if("AT+CREG?" == command) { reply = /* (random() & 1) ? "+CREG: 0,0\r" : */ "AT+CREG?\r\n\r\n+CREG: 0,5\r\n\r\n'OK\r\n"; } // Relevant states: WAIT_FOR_REGISTRATION
          else if("AT+CSTT=apn" == command) { reply =  "AT+CSTT\r\n\r\nOK\r"; } // Relevant states: SET_APN
          else if("AT+CIPSTATUS" == command) {
              switch (sim900LinkState){
                  case 0:
                      sim900LinkState = 1;
                      reply = "AT+CIPSTATUS\r\n\r\nOK\r\n\r\nSTATE: IP START\r\n";
                      break;
                  case 1:
                      sim900LinkState = 2;
                      reply = "AT+CIPSTATUS\r\n\r\nOK\r\n\r\nSTATE: IP GPRSACT\r\n";
                      break;
                  case 2:
                      reply = "AT+CIPSTATUS\r\n\r\nOK\r\nSTATE: CONNECT OK\r\n";
                      break;
                  default: break;
              }
          }  // Relevant states: START_GPRS, WAIT_FOR_UDP
          else if("AT+CIICR" == command) { reply = "AT+CIICR\r\n\r\nOK\r\n"; }  // Relevant states: START_GPRS
          else if("AT+CIFSR" == command) { reply = "AT+CIFSR\r\n\r\n172.16.101.199\r\n"; }  // Relevant States: GET_IP
          else if("AT+CIPSTART" == command) { reply = "AT+CIPSTART=\"UDP\",\"0.0.0.0\",\"9999\"\r\n\r\nOK\r\n\r\nCONNECT OK\r\n"; }  // Relevant states: OPEN_UDP fixme command probably wrong
          else if("AT+CIPSEND=3" == command) { reply = "AT+CIPSEND=3\r\n\r\n>"; }  // Relevant states:  SENDING
          else if("123" == command) { reply = "123\r\nSEND OK\r\n"; }  // Relevant states: SENDING
          }
        else if(collectingCommand) { command += c; }
        }
      if(verbose) { if(isprint(c)) { fprintf(stderr, "<%c\n", c); } else { fprintf(stderr, "< %d\n", (int)c); } }
      return(1);
      }
    virtual int read() override
        {
        if(0 == reply.size()) { return(-1); }
        const char c = reply[0];
        if(verbose) { if(isprint(c)) { fprintf(stderr, ">%c\n", c); } else { fprintf(stderr, "> %d\n", (int)c); } }
        reply.erase(0, 1);
        return(c);
        }
    virtual int available() override { return(-1); }
    virtual int peek() override { return(-1); }
    virtual void flush() override { }
  };
// Events exposed.
bool GoodSimulator::haveSeenCommandStart;
}
TEST(OTSIM900Link,basicsSimpleSimulator)
{
//    const bool verbose = B1::verbose;

    srandom(::testing::UnitTest::GetInstance()->random_seed()); // Seed random() for use in simulator; --gtest_shuffle will force it to change.

    const char SIM900_PIN[] = "1111";
    const char SIM900_APN[] = "apn";
    const char SIM900_UDP_ADDR[] = "0.0.0.0"; // ORS server
    const char SIM900_UDP_PORT[] = "9999";
    const OTSIM900Link::OTSIM900LinkConfig_t SIM900Config(false, SIM900_PIN, SIM900_APN, SIM900_UDP_ADDR, SIM900_UDP_PORT);
    const OTRadioLink::OTRadioChannelConfig l0Config(&SIM900Config, true);

    ASSERT_FALSE(B1::GoodSimulator::haveSeenCommandStart);
    OTSIM900Link::OTSIM900Link<0, 0, 0, B1::GoodSimulator> l0;
    EXPECT_TRUE(l0.configure(1, &l0Config));
    EXPECT_TRUE(l0.begin());
    EXPECT_EQ(OTSIM900Link::GET_STATE, l0._getState());
    // Try to hang just by calling poll() repeatedly.
    for(int i = 0; i < 100; ++i) { l0.poll(); }
    EXPECT_TRUE(B1::GoodSimulator::haveSeenCommandStart) << "should see some attempt to communicate with SIM900";
    EXPECT_LE(OTSIM900Link::WAIT_FOR_UDP, l0._getState()) << "should make it to at least WAIT_FOR_REGISTRATION";
    // ...
    l0.end();
}

<commit_msg>Completed main test.<commit_after>/*
The OpenTRV project licenses this file to you
under the Apache Licence, Version 2.0 (the "Licence");
you may not use this file except in compliance
with the Licence. You may obtain a copy of the Licence at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing,
software distributed under the Licence is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the Licence for the
specific language governing permissions and limitations
under the Licence.

Author(s) / Copyright (s): Damon Hart-Davis 2016
*/

/*
 * OTRadValve SIM900 tests.
 */

#include <gtest/gtest.h>
#include <stdio.h>
#include <stdlib.h>

#include "OTSIM900Link.h"

// Test the getter function definitely does what it should.
TEST(OTSIM900Link, getterFunction)
{
    const char SIM900_PIN[] = "1111";
    const OTSIM900Link::OTSIM900LinkConfig_t SIM900Config(false, SIM900_PIN, NULL, NULL, NULL);
    EXPECT_EQ(SIM900_PIN[0], SIM900Config.get((const uint8_t *)SIM900Config.PIN));
}

// Test for general sanity of OTSIM900Link.
// Make sure that an instance can be created and does not die horribly.
// Underlying simulated serial/SIM900 never accepts data or responds, eg like a dead card.
TEST(OTSIM900Link,basicsDeadCard)
{
    const bool verbose = false;

    class NULLSerialStream final : public Stream
      {
      public:
        void begin(unsigned long) { }
        void begin(unsigned long, uint8_t);
        void end();

        virtual size_t write(uint8_t c) override { if(verbose) { fprintf(stderr, "%c\n", (char) c); } return(0); }
        virtual int available() override { return(-1); }
        virtual int read() override { return(-1); }
        virtual int peek() override { return(-1); }
        virtual void flush() override { }
      };
    const char SIM900_PIN[] = "1111";
    const char SIM900_APN[] = "apn";
    const char SIM900_UDP_ADDR[] = "0.0.0.0"; // ORS server
    const char SIM900_UDP_PORT[] = "9999";
    const OTSIM900Link::OTSIM900LinkConfig_t SIM900Config(false, SIM900_PIN, SIM900_APN, SIM900_UDP_ADDR, SIM900_UDP_PORT);
    const OTRadioLink::OTRadioChannelConfig l0Config(&SIM900Config, true);
    OTSIM900Link::OTSIM900Link<0, 0, 0, NULLSerialStream> l0;
    EXPECT_TRUE(l0.configure(1, &l0Config));
    EXPECT_TRUE(l0.begin());
    EXPECT_EQ(OTSIM900Link::GET_STATE, l0._getState());
    // Try to hang just by calling poll() repeatedly.
    for(int i = 0; i < 100; ++i) { l0.poll(); }
    EXPECT_GE(OTSIM900Link::START_UP, l0._getState()) << "should keep trying to start with GET_STATE, RETRY_GET_STATE";
    // ...
    l0.end();
}

// Walk through state space of OTSIM900Link.
// Make sure that an instance can be created and does not die horribly.
// Is meant to mainly walk through all the normal expected SIM900 behaviour when all is well.
// Other tests can look at error handling including unexpected/garbage responses.
namespace B1
{
const bool verbose = true;

// Does a simple simulation of SIM900, responding sensibly to all commands needed by the OTSIM900Link impl.
// Allows for exercise of every major non-PANIC state of the OTSIM900Link implementation.
class GoodSimulator final : public Stream
  {
  public:
    // Events exposed.
    static bool haveSeenCommandStart;

  private:
    // Command being collected from OTSIM900Link.
    bool waitingForCommand = true;
    bool collectingCommand = false;
    // Entire request starting "AT"; no trailing CR or LF stored.
    std::string command;

    // Reply (postfix) being returned to OTSIM900Link: empty if none.
    std::string reply;

    // Keep track (crudely) of state. Corresponds to OTSIM900LinkState values.
    uint8_t sim900LinkState = 0;

  public:
    void begin(unsigned long) { }
    void begin(unsigned long, uint8_t);
    void end();

    virtual size_t write(uint8_t uc) override
      {
      const char c = (char)uc;
      if(waitingForCommand)
        {
        // Look for leading 'A' of 'AT' to start a command.
        if('A' == c)
          {
          waitingForCommand = false;
          collectingCommand = true;
          command = 'A';
          haveSeenCommandStart = true; // Note at least one command start.
          }
        }
      else
        {
        // Look for CR (or LF) to terminate a command.
        if(('\r' == c) || ('\n' == c))
          {
          waitingForCommand = true;
          collectingCommand = false;
          if(verbose) { fprintf(stderr, "command received: %s\n", command.c_str()); }
          // Respond to particular commands...
          if("AT" == command) { // Relevant states: GET_STATE, RETRY_GET_STATE, START_UP
              if(sim900LinkState == OTSIM900Link::GET_STATE) {
                  reply = "vfd";  // garbage to force into RETRY_GET_STATE
                  sim900LinkState = OTSIM900Link::RETRY_GET_STATE;
              } else reply = "AT\r\n\r\nOK\r\n";
          }
          else if("AT+CPIN?" == command) { reply = /* (random() & 1) ? "No PIN\r" : */ "AT+CPIN?\r\n\r\n+CPIN: READY\r\n\r\nOK\r\n"; }  // Relevant states: CHECK_PIN
          else if("AT+CREG?" == command) { reply = /* (random() & 1) ? "+CREG: 0,0\r" : */ "AT+CREG?\r\n\r\n+CREG: 0,5\r\n\r\n'OK\r\n"; } // Relevant states: WAIT_FOR_REGISTRATION
          else if("AT+CSTT=apn" == command) { reply =  "AT+CSTT\r\n\r\nOK\r"; } // Relevant states: SET_APN
          else if("AT+CIPSTATUS" == command) {
              switch (sim900LinkState){
                  case OTSIM900Link::RETRY_GET_STATE:  // GPRS inactive)
                      sim900LinkState = OTSIM900Link::START_GPRS;
                      reply = "AT+CIPSTATUS\r\n\r\nOK\r\n\r\nSTATE: IP START\r\n";
                      break;
                  case OTSIM900Link::START_GPRS:          // GPRS is activated.
                      sim900LinkState = OTSIM900Link::GET_IP;
                      reply = "AT+CIPSTATUS\r\n\r\nOK\r\n\r\nSTATE: IP GPRSACT\r\n";
                      break;
                  case OTSIM900Link::GET_IP:    // UDP connected.
                      reply = "AT+CIPSTATUS\r\n\r\nOK\r\nSTATE: CONNECT OK\r\n";
                      break;
                  default: break;
              }
          }  // Relevant states: START_GPRS, WAIT_FOR_UDP
          else if("AT+CIICR" == command) { reply = "AT+CIICR\r\n\r\nOK\r\n"; }  // Relevant states: START_GPRS
          else if("AT+CIFSR" == command) { reply = "AT+CIFSR\r\n\r\n172.16.101.199\r\n"; }  // Relevant States: GET_IP
          else if("AT+CIPSTART=\"UDP\",\"0.0.0.0\",\"9999\"" == command) { reply = "AT+CIPSTART=\"UDP\",\"0.0.0.0\",\"9999\"\r\n\r\nOK\r\n\r\nCONNECT OK\r\n"; }  // Relevant states: OPEN_UDP
          else if("AT+CIPSEND=3" == command) { reply = "AT+CIPSEND=3\r\n\r\n>"; }  // Relevant states:  SENDING
          else if("123" == command) { reply = "123\r\nSEND OK\r\n"; }  // Relevant states: SENDING
          }
        else if(collectingCommand) { command += c; }
        }
      if(verbose) { if(isprint(c)) { fprintf(stderr, "<%c\n", c); } else { fprintf(stderr, "< %d\n", (int)c); } }
      return(1);
      }
    virtual int read() override
        {
        if(0 == reply.size()) { return(-1); }
        const char c = reply[0];
        if(verbose) { if(isprint(c)) { fprintf(stderr, ">%c\n", c); } else { fprintf(stderr, "> %d\n", (int)c); } }
        reply.erase(0, 1);
        return(c);
        }
    virtual int available() override { return(-1); }
    virtual int peek() override { return(-1); }
    virtual void flush() override { }
  };
// Events exposed.
bool GoodSimulator::haveSeenCommandStart;
}
TEST(OTSIM900Link,basicsSimpleSimulator)
{
//    const bool verbose = B1::verbose;

    srandom(::testing::UnitTest::GetInstance()->random_seed()); // Seed random() for use in simulator; --gtest_shuffle will force it to change.

    // Vector of bools containing states to check. This covers all states expected in normal use. RESET and PANIC are not covered.
    std::vector<bool> statesChecked(OTSIM900Link::RESET, false);
    // Message to send.
    const char message[] = "123";

    const char SIM900_PIN[] = "1111";
    const char SIM900_APN[] = "apn";
    const char SIM900_UDP_ADDR[] = "0.0.0.0"; // ORS server
    const char SIM900_UDP_PORT[] = "9999";
    const OTSIM900Link::OTSIM900LinkConfig_t SIM900Config(false, SIM900_PIN, SIM900_APN, SIM900_UDP_ADDR, SIM900_UDP_PORT);
    const OTRadioLink::OTRadioChannelConfig l0Config(&SIM900Config, true);

    ASSERT_FALSE(B1::GoodSimulator::haveSeenCommandStart);
    OTSIM900Link::OTSIM900Link<0, 0, 0, B1::GoodSimulator> l0;
    EXPECT_TRUE(l0.configure(1, &l0Config));
    EXPECT_TRUE(l0.begin());
    EXPECT_EQ(OTSIM900Link::GET_STATE, l0._getState());

    // Queue a message to send.
    // Try to hang just by calling poll() repeatedly.
    for(int i = 0; i < 100; ++i) { statesChecked[l0._getState()] = true; l0.poll(); if(l0._getState() == OTSIM900Link::IDLE) break;}
    l0.queueToSend((const uint8_t *)message, (uint8_t)sizeof(message)-1, (int8_t) 0, OTRadioLink::OTRadioLink::TXnormal);
    for(int i = 0; i < 100; ++i) { statesChecked[l0._getState()] = true; l0.poll(); }
    EXPECT_TRUE(B1::GoodSimulator::haveSeenCommandStart) << "should see some attempt to communicate with SIM900";
    for(int i = 0; i < OTSIM900Link::RESET; i++) EXPECT_TRUE(statesChecked[i]) << "state " << i << " not seen.";  // Check what states have been seen.
    // ...
    l0.end();
}

<|endoftext|>