<commit_before>#include "ingame_state.h"
#include "game.h"
#include "player_context.h"
#include "play_player_state.h"

using namespace space_invaders;

IngameState::IngameState(Game& game)
  : State(game),
    mFooterLine(game),
    mAvatar(game),
    mLeftOobDetector(game),
    mRightOobDetector(game),
    mTopOobDetector(game),
    mAvatarLaser(game),
    mLifesText(game),
    mLifeSprite1(game),
    mLifeSprite2(game),
    mAlienLeftDirector(game),
    mAlienRightDirector(game),
    mGameOverText(game)
{
  // initialoize the green static footer line at the bottom of the screen.
  mFooterLine.setImage(game.getSpriteSheet());
  mFooterLine.setX(0);
  mFooterLine.setY(717);
  mFooterLine.setClip({0, 117, 672, 3});

  // initialize the out-of-bounds detector for the left side of the scene.
  mLeftOobDetector.setX(-100);
  mLeftOobDetector.setY(0);
  mLeftOobDetector.setExtentX(50);
  mLeftOobDetector.setExtentY(768 / 2);

  // initialize the out-of-bounds detector for the right side of the scene.
  mRightOobDetector.setX(672);
  mRightOobDetector.setY(0);
  mRightOobDetector.setExtentX(50);
  mRightOobDetector.setExtentY(768 / 2);

  // initialize the out-of-bounds detector for the top side of the scene.
  mTopOobDetector.setX(0);
  mTopOobDetector.setY(0);
  mTopOobDetector.setExtentX(768 / 2);
  mTopOobDetector.setExtentY(70);

  // initialize the text indicating the amount of lifes.
  auto& ctx = mGame.getActivePlayerContext();
  mLifesText.setText(std::to_string(ctx.getLives()));
  mLifesText.setX(27);
  mLifesText.setY(743 - mLifesText.getHeight() + 5);

  // initialize a sprite describing whether the player has at least one life left.
  mLifeSprite1.setImage(mGame.getSpriteSheet());
  mLifeSprite1.setWidth(40);
  mLifeSprite1.setHeight(24);
  mLifeSprite1.setX(66);
  mLifeSprite1.setY(720);
  mLifeSprite1.setClip({ 85, 5, 40, 24 });
  mLifeSprite1.setVisible(ctx.getLives() > 1);

  // initialize a sprite describing whether the player has at least two lifes left.
  mLifeSprite2.setImage(mGame.getSpriteSheet());
  mLifeSprite2.setWidth(40);
  mLifeSprite2.setHeight(24);
  mLifeSprite2.setX(66 + 49);
  mLifeSprite2.setY(720);
  mLifeSprite2.setClip({ 85, 5, 40, 24 });
  mLifeSprite2.setVisible(ctx.getLives() > 2);

  // get aliens from the user context or create them if not yet created.
  mAliens = ctx.getAliens();
  if (mAliens.empty()) {
    for (auto i = 0; i < 55; i++) {
      mAliens.push_back(std::make_shared<Alien>(game, i));
    }
  }

  // initialize the left alien director for alien movement direction change.
  mAlienLeftDirector.setX(-45);
  mAlienLeftDirector.setY(0);
  mAlienLeftDirector.setExtentX(45);
  mAlienLeftDirector.setExtentY(768 / 2);

  // initialize the right alien director for alien movement direction change.
  mAlienRightDirector.setX(672 - 45);
  mAlienRightDirector.setY(0);
  mAlienRightDirector.setExtentX(45);
  mAlienRightDirector.setExtentY(768 / 2);

  // initialize the text that indicates that the game has ended.
  mGameOverText.setText("GAME OVER");
  mGameOverText.setColor({245, 3, 5, 255 });
  mGameOverText.setX(672 / 2 - mGameOverText.getExtentX());
  mGameOverText.setY(135);
  mGameOverText.setVisible(false);
}

void IngameState::update(unsigned long dt)
{
  // skip logical updates if the game has ended.
  if (mGameOverText.isVisible()) {
    return;
  }

  mFooterLine.update(dt);
  mAvatar.update(dt);
  mAvatarLaser.update(dt);

  // decrement the relaunch timer if it has been activated or handle destruction state.
  auto& ctx = mGame.getActivePlayerContext();
  auto relaunchTimer = ctx.getRelaunchTimer();
  if (relaunchTimer > 0) {
    relaunchTimer--;
    ctx.setRelaunchTimer(relaunchTimer);
    return;
  } else if (mAvatar.isEnabled() == false) {
    const auto playerCount = mGame.getPlayerCount();
    if (playerCount == 1) {
      // ::: SINGLE PLAYER :::
      // check whether it's time to end the game or just reset the avatar.
      if (ctx.getLives() <= 0) {
        // check and update the hi-score if necessary.
        const auto score = ctx.getScore();
        if (mGame.getHiScore() < score) {
          mGame.setHiScore(score);
        }
        mGameOverText.setVisible(true);
        return;
      } else {
        // refresh the amount of lifes indicators.
        const auto lives = ctx.getLives();
        mLifesText.setText(std::to_string(lives));
        if (lives == 2) {
          mLifeSprite2.setVisible(false);
          mLifeSprite1.setVisible(true);
        } else if (lives == 1) {
          mLifeSprite2.setVisible(false);
          mLifeSprite1.setVisible(false);
        }

        // reset the avatar state.
        mAvatar.reset();
      }
    } else {
      // ::: MULTI-PLAYER :::
      // store the current state of the player into the player context.
      ctx.setAliens(mAliens);
      // TODO store shields.

      // perform additional actions based on the currently active player.
      const auto activePlayer = mGame.getActivePlayer();
      if (activePlayer == Game::Player::PLAYER_1) {
        // player one was playing so we can now switch to next player.
        mGame.setActivePlayer(Game::Player::PLAYER_2);
        return;
      } else {
        // player two was playing so we should now check whether to end the game.
        auto& player1Ctx = mGame.getPlayerContext1();
        auto& player2Ctx = mGame.getPlayerContext2();
        if (player2Ctx.getLives() <= 0) {
          // check and update player1 the hi-score if necessary.
          const auto player1Score = player1Ctx.getScore();
          if (mGame.getHiScore() < player1Score) {
            mGame.setHiScore(player1Score);
          }

          // check and update player2 the hi-score if necessary.
          const auto player2Score = player2Ctx.getScore();
          if (mGame.getHiScore() < player2Score) {
            mGame.setHiScore(player2Score);
          }

          // show the game over text and also the score for the 1st player.
          mGameOverText.setVisible(true);
          // view the player 1 score.
          return;
        } else {
          // was not the last life, so we can just switch to next player.
          mGame.setActivePlayer(Game::Player::PLAYER_1);
          return;
        }
      }
    }
  }

  // check that the avatar cannot go out-of-bounds from the scene boundaries.
  const auto avatarDirection = mAvatar.getDirectionX();
  if (avatarDirection < 0.f) {
    if (mLeftOobDetector.collides(mAvatar)) {
      mAvatar.setDirectionX(0.f);
      mAvatar.setX(mLeftOobDetector.getX() + mLeftOobDetector.getWidth());
    }
  } else if (avatarDirection > 0.f) {
    if (mRightOobDetector.collides(mAvatar)) {
      mAvatar.setDirectionX(0.f);
      mAvatar.setX(mRightOobDetector.getX() - mAvatar.getWidth());
    }
  }

  // check whether any of the aliens hits the alien director bounds.
  auto alienMovementChangeRequired = false;
  auto aliensDirection = mAliens[0]->getDirectionX();
  for (auto& alien : mAliens) {
    if (aliensDirection > 0) {
      if (mAlienRightDirector.collides(*alien)) {
        alienMovementChangeRequired = true;
        break;
      }
    } else {
      if (mAlienLeftDirector.collides(*alien)) {
        alienMovementChangeRequired = true;
        break;
      }
    }
  }

  // iterate over aliens and perform alien specific update operations.
  auto activeAlienCount = 0;
  if (mAvatar.isEnabled()) {
    for (auto& alien : mAliens) {
      // drop alien downwards and change the movement if required.
      if (alienMovementChangeRequired) {
        alien->setDirectionX(-aliensDirection);
        alien->setY(alien->getY() + alien->getHeight());
      }

      // perform active calculation and update active aliens.
      if (alien->isVisible()) {
        activeAlienCount++;
        alien->update(dt);

        // check whether the target alien has just landed or hit the player.
        if (alien->collides(mFooterLine)) {
          mAvatar.explode();
        } else if (alien->collides(mAvatar)) {
          mAvatar.explode();
        }
      }
    }
  }

  // check whether all aliens are destroyed i.e. the level is cleared.
  if (activeAlienCount <= 0) {
    ctx.setLevel(ctx.getLevel() + 1);
    auto scene = mGame.getScene();
    scene->setState(std::make_shared<PlayPlayerState>(mGame));
    return;
  }

  // check whether the avatar laser beam hits something.
  if (mAvatarLaser.isEnabled()) {
    if (mAvatarLaser.collides(mTopOobDetector)) {
      mAvatarLaser.setCurrentAnimation("top-wall-hit");
      mAvatarLaser.explode();
    } else {
      for (auto& alien : mAliens) {
        if (mAvatarLaser.collides(*alien)) {
          // create an alien laser to explode at the alien position.
          mAvatarLaser.setCurrentAnimation("alien-hit");
          mAvatarLaser.explode();
          mAvatarLaser.setX(alien->getCenterX() - mAvatarLaser.getExtentX());
          mAvatarLaser.setY(alien->getCenterY() - mAvatarLaser.getExtentY());
          alien->disappear();

          // add the alien row specific amount of points to the player.
          auto& ctx = mGame.getActivePlayerContext();
          auto points = alien->getPoints();
          ctx.addScore(points);

          // speed up the movement of the aliens.
          auto newStepSize = alien->getStepSize() - Alien::STEP_DECREMENT_SIZE;
          for (auto& a : mAliens) {
            a->setStepSize(newStepSize);
            a->setAnimationStepSize(newStepSize);
          }
          break;
        }
      }
    }
  }
}

void IngameState::render(SDL_Renderer& renderer)
{
  mGameOverText.render(renderer);
  mFooterLine.render(renderer);
  mAvatar.render(renderer);
  mAvatarLaser.render(renderer);
  mLifesText.render(renderer);
  mLifeSprite1.render(renderer);
  mLifeSprite2.render(renderer);
  for (auto& alien : mAliens) {
    alien->render(renderer);
  }
}

void IngameState::onEnter()
{
  // ...
}

void IngameState::onExit()
{
  // ...
}

void IngameState::onKeyUp(SDL_KeyboardEvent& event)
{
  switch (event.keysym.sym) {
  case SDLK_LEFT:
    if (mAvatar.isEnabled() && mAvatar.getDirectionX() < 0.f) {
      mAvatar.setDirectionX(0.f);
    }
    break;
  case SDLK_RIGHT:
    if (mAvatar.isEnabled() && mAvatar.getDirectionX() > 0.f) {
      mAvatar.setDirectionX(0.f);
    }
    break;
  case SDLK_SPACE:
    if (mAvatar.isEnabled() && mAvatarLaser.isVisible() == false) {
      mAvatarLaser.setVisible(true);
      mAvatarLaser.setEnabled(true);
      mAvatarLaser.setDirectionY(-1.f);
      mAvatarLaser.setX(mAvatar.getCenterX() - mAvatarLaser.getExtentX());
      mAvatarLaser.setY(mAvatar.getY());
      mAvatarLaser.setCurrentAnimation("normal");
      mAvatarLaser.setAnimationFrame(0);

      // increment the shot count that is used in variety of places.
      auto& ctx = mGame.getActivePlayerContext();
      ctx.setShotCount(ctx.getShotCount() + 1);
    }
    break;
  }
}

void IngameState::onKeyDown(SDL_KeyboardEvent& event)
{
  switch (event.keysym.sym) {
  case SDLK_LEFT:
    if (mAvatar.isEnabled()) {
      mAvatar.setDirectionX(-1.f);
    }
    break;
  case SDLK_RIGHT:
    if (mAvatar.isEnabled()) {
      mAvatar.setDirectionX(1.f);
    }
    break;
  }
}<commit_msg>Show the 1st player score as well when the multi-player game ends.<commit_after>#include "ingame_state.h"
#include "game.h"
#include "player_context.h"
#include "play_player_state.h"

using namespace space_invaders;

IngameState::IngameState(Game& game)
  : State(game),
    mFooterLine(game),
    mAvatar(game),
    mLeftOobDetector(game),
    mRightOobDetector(game),
    mTopOobDetector(game),
    mAvatarLaser(game),
    mLifesText(game),
    mLifeSprite1(game),
    mLifeSprite2(game),
    mAlienLeftDirector(game),
    mAlienRightDirector(game),
    mGameOverText(game)
{
  // initialoize the green static footer line at the bottom of the screen.
  mFooterLine.setImage(game.getSpriteSheet());
  mFooterLine.setX(0);
  mFooterLine.setY(717);
  mFooterLine.setClip({0, 117, 672, 3});

  // initialize the out-of-bounds detector for the left side of the scene.
  mLeftOobDetector.setX(-100);
  mLeftOobDetector.setY(0);
  mLeftOobDetector.setExtentX(50);
  mLeftOobDetector.setExtentY(768 / 2);

  // initialize the out-of-bounds detector for the right side of the scene.
  mRightOobDetector.setX(672);
  mRightOobDetector.setY(0);
  mRightOobDetector.setExtentX(50);
  mRightOobDetector.setExtentY(768 / 2);

  // initialize the out-of-bounds detector for the top side of the scene.
  mTopOobDetector.setX(0);
  mTopOobDetector.setY(0);
  mTopOobDetector.setExtentX(768 / 2);
  mTopOobDetector.setExtentY(70);

  // initialize the text indicating the amount of lifes.
  auto& ctx = mGame.getActivePlayerContext();
  mLifesText.setText(std::to_string(ctx.getLives()));
  mLifesText.setX(27);
  mLifesText.setY(743 - mLifesText.getHeight() + 5);

  // initialize a sprite describing whether the player has at least one life left.
  mLifeSprite1.setImage(mGame.getSpriteSheet());
  mLifeSprite1.setWidth(40);
  mLifeSprite1.setHeight(24);
  mLifeSprite1.setX(66);
  mLifeSprite1.setY(720);
  mLifeSprite1.setClip({ 85, 5, 40, 24 });
  mLifeSprite1.setVisible(ctx.getLives() > 1);

  // initialize a sprite describing whether the player has at least two lifes left.
  mLifeSprite2.setImage(mGame.getSpriteSheet());
  mLifeSprite2.setWidth(40);
  mLifeSprite2.setHeight(24);
  mLifeSprite2.setX(66 + 49);
  mLifeSprite2.setY(720);
  mLifeSprite2.setClip({ 85, 5, 40, 24 });
  mLifeSprite2.setVisible(ctx.getLives() > 2);

  // get aliens from the user context or create them if not yet created.
  mAliens = ctx.getAliens();
  if (mAliens.empty()) {
    for (auto i = 0; i < 55; i++) {
      mAliens.push_back(std::make_shared<Alien>(game, i));
    }
  }

  // initialize the left alien director for alien movement direction change.
  mAlienLeftDirector.setX(-45);
  mAlienLeftDirector.setY(0);
  mAlienLeftDirector.setExtentX(45);
  mAlienLeftDirector.setExtentY(768 / 2);

  // initialize the right alien director for alien movement direction change.
  mAlienRightDirector.setX(672 - 45);
  mAlienRightDirector.setY(0);
  mAlienRightDirector.setExtentX(45);
  mAlienRightDirector.setExtentY(768 / 2);

  // initialize the text that indicates that the game has ended.
  mGameOverText.setText("GAME OVER");
  mGameOverText.setColor({245, 3, 5, 255 });
  mGameOverText.setX(672 / 2 - mGameOverText.getExtentX());
  mGameOverText.setY(135);
  mGameOverText.setVisible(false);
}

void IngameState::update(unsigned long dt)
{
  // skip logical updates if the game has ended.
  if (mGameOverText.isVisible()) {
    return;
  }

  mFooterLine.update(dt);
  mAvatar.update(dt);
  mAvatarLaser.update(dt);

  // decrement the relaunch timer if it has been activated or handle destruction state.
  auto& ctx = mGame.getActivePlayerContext();
  auto relaunchTimer = ctx.getRelaunchTimer();
  if (relaunchTimer > 0) {
    relaunchTimer--;
    ctx.setRelaunchTimer(relaunchTimer);
    return;
  } else if (mAvatar.isEnabled() == false) {
    const auto playerCount = mGame.getPlayerCount();
    if (playerCount == 1) {
      // ::: SINGLE PLAYER :::
      // check whether it's time to end the game or just reset the avatar.
      if (ctx.getLives() <= 0) {
        // check and update the hi-score if necessary.
        const auto score = ctx.getScore();
        if (mGame.getHiScore() < score) {
          mGame.setHiScore(score);
        }
        mGameOverText.setVisible(true);
        return;
      } else {
        // refresh the amount of lifes indicators.
        const auto lives = ctx.getLives();
        mLifesText.setText(std::to_string(lives));
        if (lives == 2) {
          mLifeSprite2.setVisible(false);
          mLifeSprite1.setVisible(true);
        } else if (lives == 1) {
          mLifeSprite2.setVisible(false);
          mLifeSprite1.setVisible(false);
        }

        // reset the avatar state.
        mAvatar.reset();
      }
    } else {
      // ::: MULTI-PLAYER :::
      // store the current state of the player into the player context.
      ctx.setAliens(mAliens);
      // TODO store shields.

      // perform additional actions based on the currently active player.
      const auto activePlayer = mGame.getActivePlayer();
      if (activePlayer == Game::Player::PLAYER_1) {
        // player one was playing so we can now switch to next player.
        mGame.setActivePlayer(Game::Player::PLAYER_2);
        return;
      } else {
        // player two was playing so we should now check whether to end the game.
        auto& player1Ctx = mGame.getPlayerContext1();
        auto& player2Ctx = mGame.getPlayerContext2();
        if (player2Ctx.getLives() <= 0) {
          // check and update player1 the hi-score if necessary.
          const auto player1Score = player1Ctx.getScore();
          if (mGame.getHiScore() < player1Score) {
            mGame.setHiScore(player1Score);
          }

          // check and update player2 the hi-score if necessary.
          const auto player2Score = player2Ctx.getScore();
          if (mGame.getHiScore() < player2Score) {
            mGame.setHiScore(player2Score);
          }

          // show the game over text and also the score for the 1st player.
          mGameOverText.setVisible(true);
          auto scene = mGame.getScene();
          auto score1 = scene->getScore1Text();
          score1->setVisible(true);
          return;
        } else {
          // was not the last life, so we can just switch to next player.
          mGame.setActivePlayer(Game::Player::PLAYER_1);
          return;
        }
      }
    }
  }

  // check that the avatar cannot go out-of-bounds from the scene boundaries.
  const auto avatarDirection = mAvatar.getDirectionX();
  if (avatarDirection < 0.f) {
    if (mLeftOobDetector.collides(mAvatar)) {
      mAvatar.setDirectionX(0.f);
      mAvatar.setX(mLeftOobDetector.getX() + mLeftOobDetector.getWidth());
    }
  } else if (avatarDirection > 0.f) {
    if (mRightOobDetector.collides(mAvatar)) {
      mAvatar.setDirectionX(0.f);
      mAvatar.setX(mRightOobDetector.getX() - mAvatar.getWidth());
    }
  }

  // check whether any of the aliens hits the alien director bounds.
  auto alienMovementChangeRequired = false;
  auto aliensDirection = mAliens[0]->getDirectionX();
  for (auto& alien : mAliens) {
    if (aliensDirection > 0) {
      if (mAlienRightDirector.collides(*alien)) {
        alienMovementChangeRequired = true;
        break;
      }
    } else {
      if (mAlienLeftDirector.collides(*alien)) {
        alienMovementChangeRequired = true;
        break;
      }
    }
  }

  // iterate over aliens and perform alien specific update operations.
  auto activeAlienCount = 0;
  if (mAvatar.isEnabled()) {
    for (auto& alien : mAliens) {
      // drop alien downwards and change the movement if required.
      if (alienMovementChangeRequired) {
        alien->setDirectionX(-aliensDirection);
        alien->setY(alien->getY() + alien->getHeight());
      }

      // perform active calculation and update active aliens.
      if (alien->isVisible()) {
        activeAlienCount++;
        alien->update(dt);

        // check whether the target alien has just landed or hit the player.
        if (alien->collides(mFooterLine)) {
          mAvatar.explode();
        } else if (alien->collides(mAvatar)) {
          mAvatar.explode();
        }
      }
    }
  }

  // check whether all aliens are destroyed i.e. the level is cleared.
  if (activeAlienCount <= 0) {
    ctx.setLevel(ctx.getLevel() + 1);
    auto scene = mGame.getScene();
    scene->setState(std::make_shared<PlayPlayerState>(mGame));
    return;
  }

  // check whether the avatar laser beam hits something.
  if (mAvatarLaser.isEnabled()) {
    if (mAvatarLaser.collides(mTopOobDetector)) {
      mAvatarLaser.setCurrentAnimation("top-wall-hit");
      mAvatarLaser.explode();
    } else {
      for (auto& alien : mAliens) {
        if (mAvatarLaser.collides(*alien)) {
          // create an alien laser to explode at the alien position.
          mAvatarLaser.setCurrentAnimation("alien-hit");
          mAvatarLaser.explode();
          mAvatarLaser.setX(alien->getCenterX() - mAvatarLaser.getExtentX());
          mAvatarLaser.setY(alien->getCenterY() - mAvatarLaser.getExtentY());
          alien->disappear();

          // add the alien row specific amount of points to the player.
          auto& ctx = mGame.getActivePlayerContext();
          auto points = alien->getPoints();
          ctx.addScore(points);

          // speed up the movement of the aliens.
          auto newStepSize = alien->getStepSize() - Alien::STEP_DECREMENT_SIZE;
          for (auto& a : mAliens) {
            a->setStepSize(newStepSize);
            a->setAnimationStepSize(newStepSize);
          }
          break;
        }
      }
    }
  }
}

void IngameState::render(SDL_Renderer& renderer)
{
  mGameOverText.render(renderer);
  mFooterLine.render(renderer);
  mAvatar.render(renderer);
  mAvatarLaser.render(renderer);
  mLifesText.render(renderer);
  mLifeSprite1.render(renderer);
  mLifeSprite2.render(renderer);
  for (auto& alien : mAliens) {
    alien->render(renderer);
  }
}

void IngameState::onEnter()
{
  // ...
}

void IngameState::onExit()
{
  // ...
}

void IngameState::onKeyUp(SDL_KeyboardEvent& event)
{
  switch (event.keysym.sym) {
  case SDLK_LEFT:
    if (mAvatar.isEnabled() && mAvatar.getDirectionX() < 0.f) {
      mAvatar.setDirectionX(0.f);
    }
    break;
  case SDLK_RIGHT:
    if (mAvatar.isEnabled() && mAvatar.getDirectionX() > 0.f) {
      mAvatar.setDirectionX(0.f);
    }
    break;
  case SDLK_SPACE:
    if (mAvatar.isEnabled() && mAvatarLaser.isVisible() == false) {
      mAvatarLaser.setVisible(true);
      mAvatarLaser.setEnabled(true);
      mAvatarLaser.setDirectionY(-1.f);
      mAvatarLaser.setX(mAvatar.getCenterX() - mAvatarLaser.getExtentX());
      mAvatarLaser.setY(mAvatar.getY());
      mAvatarLaser.setCurrentAnimation("normal");
      mAvatarLaser.setAnimationFrame(0);

      // increment the shot count that is used in variety of places.
      auto& ctx = mGame.getActivePlayerContext();
      ctx.setShotCount(ctx.getShotCount() + 1);
    }
    break;
  }
}

void IngameState::onKeyDown(SDL_KeyboardEvent& event)
{
  switch (event.keysym.sym) {
  case SDLK_LEFT:
    if (mAvatar.isEnabled()) {
      mAvatar.setDirectionX(-1.f);
    }
    break;
  case SDLK_RIGHT:
    if (mAvatar.isEnabled()) {
      mAvatar.setDirectionX(1.f);
    }
    break;
  }
}<|endoftext|>


<commit_before>// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "build/build_config.h"
#include "base/debug_util.h"

#include <errno.h>
#include <execinfo.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/sysctl.h>
#include <sys/types.h>
#include <unistd.h>

#include "base/basictypes.h"
#include "base/eintr_wrapper.h"
#include "base/logging.h"
#include "base/scoped_ptr.h"
#include "base/string_piece.h"

// static
bool DebugUtil::SpawnDebuggerOnProcess(unsigned /* process_id */) {
  NOTIMPLEMENTED();
  return false;
}

#if defined(OS_MACOSX)

// Based on Apple's recommended method as described in
// http://developer.apple.com/qa/qa2004/qa1361.html
// static
bool DebugUtil::BeingDebugged() {
  // If the process is sandboxed then we can't use the sysctl, so cache the
  // value.
  static bool is_set = false;
  static bool being_debugged = false;

  if (is_set) {
    return being_debugged;
  }

  // Initialize mib, which tells sysctl what info we want.  In this case,
  // we're looking for information about a specific process ID.
  int mib[] = {
    CTL_KERN,
    KERN_PROC,
    KERN_PROC_PID,
    getpid()
  };

  // Caution: struct kinfo_proc is marked __APPLE_API_UNSTABLE.  The source and
  // binary interfaces may change.
  struct kinfo_proc info;
  size_t info_size = sizeof(info);

  int sysctl_result = sysctl(mib, arraysize(mib), &info, &info_size, NULL, 0);
  DCHECK(sysctl_result == 0);
  if (sysctl_result != 0) {
    is_set = true;
    being_debugged = false;
    return being_debugged;
  }

  // This process is being debugged if the P_TRACED flag is set.
  is_set = true;
  being_debugged = (info.kp_proc.p_flag & P_TRACED) != 0;
  return being_debugged;
}

#elif defined(OS_LINUX)

// We can look in /proc/self/status for TracerPid.  We are likely used in crash
// handling, so we are careful not to use the heap or have side effects.
// Another option that is common is to try to ptrace yourself, but then we
// can't detach without forking(), and that's not so great.
// static
bool DebugUtil::BeingDebugged() {
  int status_fd = open("/proc/self/status", O_RDONLY);
  if (status_fd == -1)
    return false;

  // We assume our line will be in the first 1024 characters and that we can
  // read this much all at once.  In practice this will generally be true.
  // This simplifies and speeds up things considerably.
  char buf[1024];

  ssize_t num_read = HANDLE_EINTR(read(status_fd, buf, sizeof(buf)));
  HANDLE_EINTR(close(status_fd));

  if (num_read <= 0)
    return false;

  StringPiece status(buf, num_read);
  StringPiece tracer("TracerPid:\t");

  StringPiece::size_type pid_index = status.find(tracer);
  if (pid_index == StringPiece::npos)
    return false;

  // Our pid is 0 without a debugger, assume this for any pid starting with 0.
  pid_index += tracer.size();
  return pid_index < status.size() && status[pid_index] != '0';
}

#endif  // OS_LINUX

// static
void DebugUtil::BreakDebugger() {
#if !defined(ARCH_CPU_ARM_FAMILY)
  asm ("int3");
#endif
}

StackTrace::StackTrace() {
  const int kMaxCallers = 256;

  void* callers[kMaxCallers];
  int count = backtrace(callers, kMaxCallers);

  // Though the backtrace API man page does not list any possible negative
  // return values, we still still exclude them because they would break the
  // memcpy code below.
  if (count > 0) {
    trace_.resize(count);
    memcpy(&trace_[0], callers, sizeof(callers[0]) * count);
  } else {
    trace_.resize(0);
  }
}

void StackTrace::PrintBacktrace() {
  fflush(stderr);
  backtrace_symbols_fd(&trace_[0], trace_.size(), STDERR_FILENO);
}

void StackTrace::OutputToStream(std::ostream* os) {
  scoped_ptr_malloc<char*> trace_symbols(
      backtrace_symbols(&trace_[0], trace_.size()));

  // If we can't retrieve the symbols, print an error and just dump the raw
  // addresses.
  if (trace_symbols.get() == NULL) {
    (*os) << "Unable get symbols for backtrace (" << strerror(errno)
          << "). Dumping raw addresses in trace:\n";
    for (size_t i = 0; i < trace_.size(); ++i) {
      (*os) << "\t" << trace_[i] << "\n";
    }
  } else {
    (*os) << "Backtrace:\n";
    for (size_t i = 0; i < trace_.size(); ++i) {
      (*os) << "\t" << trace_symbols.get()[i] << "\n";
    }
  }
}
<commit_msg>Implement BreakDebugger for ARM.<commit_after>// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "build/build_config.h"
#include "base/debug_util.h"

#include <errno.h>
#include <execinfo.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/sysctl.h>
#include <sys/types.h>
#include <unistd.h>

#include "base/basictypes.h"
#include "base/eintr_wrapper.h"
#include "base/logging.h"
#include "base/scoped_ptr.h"
#include "base/string_piece.h"

// static
bool DebugUtil::SpawnDebuggerOnProcess(unsigned /* process_id */) {
  NOTIMPLEMENTED();
  return false;
}

#if defined(OS_MACOSX)

// Based on Apple's recommended method as described in
// http://developer.apple.com/qa/qa2004/qa1361.html
// static
bool DebugUtil::BeingDebugged() {
  // If the process is sandboxed then we can't use the sysctl, so cache the
  // value.
  static bool is_set = false;
  static bool being_debugged = false;

  if (is_set) {
    return being_debugged;
  }

  // Initialize mib, which tells sysctl what info we want.  In this case,
  // we're looking for information about a specific process ID.
  int mib[] = {
    CTL_KERN,
    KERN_PROC,
    KERN_PROC_PID,
    getpid()
  };

  // Caution: struct kinfo_proc is marked __APPLE_API_UNSTABLE.  The source and
  // binary interfaces may change.
  struct kinfo_proc info;
  size_t info_size = sizeof(info);

  int sysctl_result = sysctl(mib, arraysize(mib), &info, &info_size, NULL, 0);
  DCHECK(sysctl_result == 0);
  if (sysctl_result != 0) {
    is_set = true;
    being_debugged = false;
    return being_debugged;
  }

  // This process is being debugged if the P_TRACED flag is set.
  is_set = true;
  being_debugged = (info.kp_proc.p_flag & P_TRACED) != 0;
  return being_debugged;
}

#elif defined(OS_LINUX)

// We can look in /proc/self/status for TracerPid.  We are likely used in crash
// handling, so we are careful not to use the heap or have side effects.
// Another option that is common is to try to ptrace yourself, but then we
// can't detach without forking(), and that's not so great.
// static
bool DebugUtil::BeingDebugged() {
  int status_fd = open("/proc/self/status", O_RDONLY);
  if (status_fd == -1)
    return false;

  // We assume our line will be in the first 1024 characters and that we can
  // read this much all at once.  In practice this will generally be true.
  // This simplifies and speeds up things considerably.
  char buf[1024];

  ssize_t num_read = HANDLE_EINTR(read(status_fd, buf, sizeof(buf)));
  HANDLE_EINTR(close(status_fd));

  if (num_read <= 0)
    return false;

  StringPiece status(buf, num_read);
  StringPiece tracer("TracerPid:\t");

  StringPiece::size_type pid_index = status.find(tracer);
  if (pid_index == StringPiece::npos)
    return false;

  // Our pid is 0 without a debugger, assume this for any pid starting with 0.
  pid_index += tracer.size();
  return pid_index < status.size() && status[pid_index] != '0';
}

#endif  // OS_LINUX

// static
void DebugUtil::BreakDebugger() {
#if defined(ARCH_CPU_ARM_FAMILY)
  asm("bkpt 0");
#else
  asm("int3");
#endif
}

StackTrace::StackTrace() {
  const int kMaxCallers = 256;

  void* callers[kMaxCallers];
  int count = backtrace(callers, kMaxCallers);

  // Though the backtrace API man page does not list any possible negative
  // return values, we still still exclude them because they would break the
  // memcpy code below.
  if (count > 0) {
    trace_.resize(count);
    memcpy(&trace_[0], callers, sizeof(callers[0]) * count);
  } else {
    trace_.resize(0);
  }
}

void StackTrace::PrintBacktrace() {
  fflush(stderr);
  backtrace_symbols_fd(&trace_[0], trace_.size(), STDERR_FILENO);
}

void StackTrace::OutputToStream(std::ostream* os) {
  scoped_ptr_malloc<char*> trace_symbols(
      backtrace_symbols(&trace_[0], trace_.size()));

  // If we can't retrieve the symbols, print an error and just dump the raw
  // addresses.
  if (trace_symbols.get() == NULL) {
    (*os) << "Unable get symbols for backtrace (" << strerror(errno)
          << "). Dumping raw addresses in trace:\n";
    for (size_t i = 0; i < trace_.size(); ++i) {
      (*os) << "\t" << trace_[i] << "\n";
    }
  } else {
    (*os) << "Backtrace:\n";
    for (size_t i = 0; i < trace_.size(); ++i) {
      (*os) << "\t" << trace_symbols.get()[i] << "\n";
    }
  }
}
<|endoftext|>


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

#include <iostream>
#include <fstream>

#include <ngtcp2/ngtcp2_crypto_boringssl.h>

#include <openssl/err.h>

#include "server_base.h"
#include "template.h"

extern Config config;

TLSServerContext::TLSServerContext() : ssl_ctx_{nullptr} {}

TLSServerContext::~TLSServerContext() {
  if (ssl_ctx_) {
    SSL_CTX_free(ssl_ctx_);
  }
}

SSL_CTX *TLSServerContext::get_native_handle() const { return ssl_ctx_; }

namespace {
int alpn_select_proto_h3_cb(SSL *ssl, const unsigned char **out,
                            unsigned char *outlen, const unsigned char *in,
                            unsigned int inlen, void *arg) {
  auto h = static_cast<HandlerBase *>(SSL_get_app_data(ssl));
  const uint8_t *alpn;
  size_t alpnlen;
  auto version = ngtcp2_conn_get_negotiated_version(h->conn());

  switch (version) {
  case QUIC_VER_DRAFT29:
    alpn = reinterpret_cast<const uint8_t *>(H3_ALPN_DRAFT29);
    alpnlen = str_size(H3_ALPN_DRAFT29);
    break;
  case QUIC_VER_DRAFT30:
    alpn = reinterpret_cast<const uint8_t *>(H3_ALPN_DRAFT30);
    alpnlen = str_size(H3_ALPN_DRAFT30);
    break;
  case QUIC_VER_DRAFT31:
    alpn = reinterpret_cast<const uint8_t *>(H3_ALPN_DRAFT31);
    alpnlen = str_size(H3_ALPN_DRAFT31);
    break;
  case QUIC_VER_DRAFT32:
    alpn = reinterpret_cast<const uint8_t *>(H3_ALPN_DRAFT32);
    alpnlen = str_size(H3_ALPN_DRAFT32);
    break;
  default:
    if (!config.quiet) {
      std::cerr << "Unexpected quic protocol version: " << std::hex << "0x"
                << version << std::dec << std::endl;
    }
    return SSL_TLSEXT_ERR_ALERT_FATAL;
  }

  for (auto p = in, end = in + inlen; p + alpnlen <= end; p += *p + 1) {
    if (std::equal(alpn, alpn + alpnlen, p)) {
      *out = p + 1;
      *outlen = *p;
      return SSL_TLSEXT_ERR_OK;
    }
  }

  if (!config.quiet) {
    std::cerr << "Client did not present ALPN " << &alpn[1] << std::endl;
  }

  return SSL_TLSEXT_ERR_ALERT_FATAL;
}
} // namespace

namespace {
int alpn_select_proto_hq_cb(SSL *ssl, const unsigned char **out,
                            unsigned char *outlen, const unsigned char *in,
                            unsigned int inlen, void *arg) {
  auto h = static_cast<HandlerBase *>(SSL_get_app_data(ssl));
  const uint8_t *alpn;
  size_t alpnlen;
  auto version = ngtcp2_conn_get_negotiated_version(h->conn());

  switch (version) {
  case QUIC_VER_DRAFT29:
    alpn = reinterpret_cast<const uint8_t *>(HQ_ALPN_DRAFT29);
    alpnlen = str_size(HQ_ALPN_DRAFT29);
    break;
  case QUIC_VER_DRAFT30:
    alpn = reinterpret_cast<const uint8_t *>(HQ_ALPN_DRAFT30);
    alpnlen = str_size(HQ_ALPN_DRAFT30);
    break;
  case QUIC_VER_DRAFT31:
    alpn = reinterpret_cast<const uint8_t *>(HQ_ALPN_DRAFT31);
    alpnlen = str_size(HQ_ALPN_DRAFT31);
    break;
  case QUIC_VER_DRAFT32:
    alpn = reinterpret_cast<const uint8_t *>(HQ_ALPN_DRAFT32);
    alpnlen = str_size(HQ_ALPN_DRAFT32);
    break;
  default:
    if (!config.quiet) {
      std::cerr << "Unexpected quic protocol version: " << std::hex << "0x"
                << version << std::dec << std::endl;
    }
    return SSL_TLSEXT_ERR_ALERT_FATAL;
  }

  for (auto p = in, end = in + inlen; p + alpnlen <= end; p += *p + 1) {
    if (std::equal(alpn, alpn + alpnlen, p)) {
      *out = p + 1;
      *outlen = *p;
      return SSL_TLSEXT_ERR_OK;
    }
  }

  if (!config.quiet) {
    std::cerr << "Client did not present ALPN " << &alpn[1] << std::endl;
  }

  return SSL_TLSEXT_ERR_ALERT_FATAL;
}
} // namespace

namespace {
int alpn_select_proto_perf_cb(SSL *ssl, const unsigned char **out,
                              unsigned char *outlen, const unsigned char *in,
                              unsigned int inlen, void *arg) {
  constexpr static uint8_t alpn[] = "perf";
  size_t alpnlen = str_size(alpn);

  for (auto p = in, end = in + inlen; p + alpnlen <= end; p += *p + 1) {
    if (std::equal(alpn, alpn + alpnlen, p)) {
      *out = p + 1;
      *outlen = *p;
      return SSL_TLSEXT_ERR_OK;
    }
  }

  if (!config.quiet) {
    std::cerr << "Client did not present ALPN " << &alpn[1] << std::endl;
  }

  return SSL_TLSEXT_ERR_ALERT_FATAL;
}
} // namespace

namespace {
int set_read_secret(SSL *ssl, enum ssl_encryption_level_t ssl_level,
                    const SSL_CIPHER *cipher, const uint8_t *secret,
                    size_t secretlen) {
  auto h = static_cast<HandlerBase *>(SSL_get_app_data(ssl));
  auto level = ngtcp2_crypto_boringssl_from_ssl_encryption_level(ssl_level);

  if (auto rv = h->on_rx_key(level, secret, secretlen); rv != 0) {
    return 0;
  }

  return 1;
}
} // namespace

namespace {
int set_write_secret(SSL *ssl, enum ssl_encryption_level_t ssl_level,
                     const SSL_CIPHER *cipher, const uint8_t *secret,
                     size_t secretlen) {
  auto h = static_cast<HandlerBase *>(SSL_get_app_data(ssl));
  auto level = ngtcp2_crypto_boringssl_from_ssl_encryption_level(ssl_level);

  if (auto rv = h->on_tx_key(level, secret, secretlen); rv != 0) {
    return 0;
  }

  if (level == NGTCP2_CRYPTO_LEVEL_APPLICATION &&
      h->call_application_tx_key_cb() != 0) {
    return 0;
  }

  return 1;
}
} // namespace

namespace {
int add_handshake_data(SSL *ssl, enum ssl_encryption_level_t ssl_level,
                       const uint8_t *data, size_t len) {
  auto h = static_cast<HandlerBase *>(SSL_get_app_data(ssl));
  auto level = ngtcp2_crypto_boringssl_from_ssl_encryption_level(ssl_level);
  h->write_server_handshake(level, data, len);
  return 1;
}
} // namespace

namespace {
int flush_flight(SSL *ssl) { return 1; }
} // namespace

namespace {
int send_alert(SSL *ssl, enum ssl_encryption_level_t level, uint8_t alert) {
  auto h = static_cast<HandlerBase *>(SSL_get_app_data(ssl));
  h->set_tls_alert(alert);
  return 1;
}
} // namespace

namespace {
auto quic_method = SSL_QUIC_METHOD{
    set_read_secret, set_write_secret, add_handshake_data,
    flush_flight,    send_alert,
};
} // namespace

namespace {
int verify_cb(int preverify_ok, X509_STORE_CTX *ctx) {
  // We don't verify the client certificate.  Just request it for the
  // testing purpose.
  return 1;
}
} // namespace

int TLSServerContext::init(const char *private_key_file, const char *cert_file,
                           AppProtocol app_proto) {
  constexpr static unsigned char sid_ctx[] = "ngtcp2 server";

  ssl_ctx_ = SSL_CTX_new(TLS_server_method());

  constexpr auto ssl_opts = (SSL_OP_ALL & ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS) |
                            SSL_OP_SINGLE_ECDH_USE |
                            SSL_OP_CIPHER_SERVER_PREFERENCE;

  SSL_CTX_set_options(ssl_ctx_, ssl_opts);

  if (SSL_CTX_set1_curves_list(ssl_ctx_, config.groups) != 1) {
    std::cerr << "SSL_CTX_set1_curves_list failed" << std::endl;
    return -1;
  }

  SSL_CTX_set_mode(ssl_ctx_, SSL_MODE_RELEASE_BUFFERS);

  SSL_CTX_set_min_proto_version(ssl_ctx_, TLS1_3_VERSION);
  SSL_CTX_set_max_proto_version(ssl_ctx_, TLS1_3_VERSION);

  switch (app_proto) {
  case AppProtocol::H3:
    SSL_CTX_set_alpn_select_cb(ssl_ctx_, alpn_select_proto_h3_cb, nullptr);
    break;
  case AppProtocol::HQ:
    SSL_CTX_set_alpn_select_cb(ssl_ctx_, alpn_select_proto_hq_cb, nullptr);
    break;
  case AppProtocol::Perf:
    SSL_CTX_set_alpn_select_cb(ssl_ctx_, alpn_select_proto_perf_cb, nullptr);
    break;
  }

  SSL_CTX_set_default_verify_paths(ssl_ctx_);

  if (SSL_CTX_use_PrivateKey_file(ssl_ctx_, private_key_file,
                                  SSL_FILETYPE_PEM) != 1) {
    std::cerr << "SSL_CTX_use_PrivateKey_file: "
              << ERR_error_string(ERR_get_error(), nullptr) << std::endl;
    return -1;
  }

  if (SSL_CTX_use_certificate_chain_file(ssl_ctx_, cert_file) != 1) {
    std::cerr << "SSL_CTX_use_certificate_file: "
              << ERR_error_string(ERR_get_error(), nullptr) << std::endl;
    return -1;
  }

  if (SSL_CTX_check_private_key(ssl_ctx_) != 1) {
    std::cerr << "SSL_CTX_check_private_key: "
              << ERR_error_string(ERR_get_error(), nullptr) << std::endl;
    return -1;
  }

  SSL_CTX_set_session_id_context(ssl_ctx_, sid_ctx, sizeof(sid_ctx) - 1);

  if (config.verify_client) {
    SSL_CTX_set_verify(ssl_ctx_,
                       SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE |
                           SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
                       verify_cb);
  }

  SSL_CTX_set_quic_method(ssl_ctx_, &quic_method);

  return 0;
}

extern std::ofstream keylog_file;

namespace {
void keylog_callback(const SSL *ssl, const char *line) {
  keylog_file.write(line, strlen(line));
  keylog_file.put('\n');
  keylog_file.flush();
}
} // namespace

void TLSServerContext::enable_keylog() {
  SSL_CTX_set_keylog_callback(ssl_ctx_, keylog_callback);
}
<commit_msg>bsslserver: Fix perf alpn<commit_after>/*
 * ngtcp2
 *
 * Copyright (c) 2021 ngtcp2 contributors
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
#include "tls_server_context_boringssl.h"

#include <iostream>
#include <fstream>

#include <ngtcp2/ngtcp2_crypto_boringssl.h>

#include <openssl/err.h>

#include "server_base.h"
#include "template.h"

extern Config config;

TLSServerContext::TLSServerContext() : ssl_ctx_{nullptr} {}

TLSServerContext::~TLSServerContext() {
  if (ssl_ctx_) {
    SSL_CTX_free(ssl_ctx_);
  }
}

SSL_CTX *TLSServerContext::get_native_handle() const { return ssl_ctx_; }

namespace {
int alpn_select_proto_h3_cb(SSL *ssl, const unsigned char **out,
                            unsigned char *outlen, const unsigned char *in,
                            unsigned int inlen, void *arg) {
  auto h = static_cast<HandlerBase *>(SSL_get_app_data(ssl));
  const uint8_t *alpn;
  size_t alpnlen;
  auto version = ngtcp2_conn_get_negotiated_version(h->conn());

  switch (version) {
  case QUIC_VER_DRAFT29:
    alpn = reinterpret_cast<const uint8_t *>(H3_ALPN_DRAFT29);
    alpnlen = str_size(H3_ALPN_DRAFT29);
    break;
  case QUIC_VER_DRAFT30:
    alpn = reinterpret_cast<const uint8_t *>(H3_ALPN_DRAFT30);
    alpnlen = str_size(H3_ALPN_DRAFT30);
    break;
  case QUIC_VER_DRAFT31:
    alpn = reinterpret_cast<const uint8_t *>(H3_ALPN_DRAFT31);
    alpnlen = str_size(H3_ALPN_DRAFT31);
    break;
  case QUIC_VER_DRAFT32:
    alpn = reinterpret_cast<const uint8_t *>(H3_ALPN_DRAFT32);
    alpnlen = str_size(H3_ALPN_DRAFT32);
    break;
  default:
    if (!config.quiet) {
      std::cerr << "Unexpected quic protocol version: " << std::hex << "0x"
                << version << std::dec << std::endl;
    }
    return SSL_TLSEXT_ERR_ALERT_FATAL;
  }

  for (auto p = in, end = in + inlen; p + alpnlen <= end; p += *p + 1) {
    if (std::equal(alpn, alpn + alpnlen, p)) {
      *out = p + 1;
      *outlen = *p;
      return SSL_TLSEXT_ERR_OK;
    }
  }

  if (!config.quiet) {
    std::cerr << "Client did not present ALPN " << &alpn[1] << std::endl;
  }

  return SSL_TLSEXT_ERR_ALERT_FATAL;
}
} // namespace

namespace {
int alpn_select_proto_hq_cb(SSL *ssl, const unsigned char **out,
                            unsigned char *outlen, const unsigned char *in,
                            unsigned int inlen, void *arg) {
  auto h = static_cast<HandlerBase *>(SSL_get_app_data(ssl));
  const uint8_t *alpn;
  size_t alpnlen;
  auto version = ngtcp2_conn_get_negotiated_version(h->conn());

  switch (version) {
  case QUIC_VER_DRAFT29:
    alpn = reinterpret_cast<const uint8_t *>(HQ_ALPN_DRAFT29);
    alpnlen = str_size(HQ_ALPN_DRAFT29);
    break;
  case QUIC_VER_DRAFT30:
    alpn = reinterpret_cast<const uint8_t *>(HQ_ALPN_DRAFT30);
    alpnlen = str_size(HQ_ALPN_DRAFT30);
    break;
  case QUIC_VER_DRAFT31:
    alpn = reinterpret_cast<const uint8_t *>(HQ_ALPN_DRAFT31);
    alpnlen = str_size(HQ_ALPN_DRAFT31);
    break;
  case QUIC_VER_DRAFT32:
    alpn = reinterpret_cast<const uint8_t *>(HQ_ALPN_DRAFT32);
    alpnlen = str_size(HQ_ALPN_DRAFT32);
    break;
  default:
    if (!config.quiet) {
      std::cerr << "Unexpected quic protocol version: " << std::hex << "0x"
                << version << std::dec << std::endl;
    }
    return SSL_TLSEXT_ERR_ALERT_FATAL;
  }

  for (auto p = in, end = in + inlen; p + alpnlen <= end; p += *p + 1) {
    if (std::equal(alpn, alpn + alpnlen, p)) {
      *out = p + 1;
      *outlen = *p;
      return SSL_TLSEXT_ERR_OK;
    }
  }

  if (!config.quiet) {
    std::cerr << "Client did not present ALPN " << &alpn[1] << std::endl;
  }

  return SSL_TLSEXT_ERR_ALERT_FATAL;
}
} // namespace

namespace {
int alpn_select_proto_perf_cb(SSL *ssl, const unsigned char **out,
                              unsigned char *outlen, const unsigned char *in,
                              unsigned int inlen, void *arg) {
  constexpr static uint8_t alpn[] = "\x4perf";
  size_t alpnlen = str_size(alpn);

  for (auto p = in, end = in + inlen; p + alpnlen <= end; p += *p + 1) {
    if (std::equal(alpn, alpn + alpnlen, p)) {
      *out = p + 1;
      *outlen = *p;
      return SSL_TLSEXT_ERR_OK;
    }
  }

  if (!config.quiet) {
    std::cerr << "Client did not present ALPN " << &alpn[1] << std::endl;
  }

  return SSL_TLSEXT_ERR_ALERT_FATAL;
}
} // namespace

namespace {
int set_read_secret(SSL *ssl, enum ssl_encryption_level_t ssl_level,
                    const SSL_CIPHER *cipher, const uint8_t *secret,
                    size_t secretlen) {
  auto h = static_cast<HandlerBase *>(SSL_get_app_data(ssl));
  auto level = ngtcp2_crypto_boringssl_from_ssl_encryption_level(ssl_level);

  if (auto rv = h->on_rx_key(level, secret, secretlen); rv != 0) {
    return 0;
  }

  return 1;
}
} // namespace

namespace {
int set_write_secret(SSL *ssl, enum ssl_encryption_level_t ssl_level,
                     const SSL_CIPHER *cipher, const uint8_t *secret,
                     size_t secretlen) {
  auto h = static_cast<HandlerBase *>(SSL_get_app_data(ssl));
  auto level = ngtcp2_crypto_boringssl_from_ssl_encryption_level(ssl_level);

  if (auto rv = h->on_tx_key(level, secret, secretlen); rv != 0) {
    return 0;
  }

  if (level == NGTCP2_CRYPTO_LEVEL_APPLICATION &&
      h->call_application_tx_key_cb() != 0) {
    return 0;
  }

  return 1;
}
} // namespace

namespace {
int add_handshake_data(SSL *ssl, enum ssl_encryption_level_t ssl_level,
                       const uint8_t *data, size_t len) {
  auto h = static_cast<HandlerBase *>(SSL_get_app_data(ssl));
  auto level = ngtcp2_crypto_boringssl_from_ssl_encryption_level(ssl_level);
  h->write_server_handshake(level, data, len);
  return 1;
}
} // namespace

namespace {
int flush_flight(SSL *ssl) { return 1; }
} // namespace

namespace {
int send_alert(SSL *ssl, enum ssl_encryption_level_t level, uint8_t alert) {
  auto h = static_cast<HandlerBase *>(SSL_get_app_data(ssl));
  h->set_tls_alert(alert);
  return 1;
}
} // namespace

namespace {
auto quic_method = SSL_QUIC_METHOD{
    set_read_secret, set_write_secret, add_handshake_data,
    flush_flight,    send_alert,
};
} // namespace

namespace {
int verify_cb(int preverify_ok, X509_STORE_CTX *ctx) {
  // We don't verify the client certificate.  Just request it for the
  // testing purpose.
  return 1;
}
} // namespace

int TLSServerContext::init(const char *private_key_file, const char *cert_file,
                           AppProtocol app_proto) {
  constexpr static unsigned char sid_ctx[] = "ngtcp2 server";

  ssl_ctx_ = SSL_CTX_new(TLS_server_method());

  constexpr auto ssl_opts = (SSL_OP_ALL & ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS) |
                            SSL_OP_SINGLE_ECDH_USE |
                            SSL_OP_CIPHER_SERVER_PREFERENCE;

  SSL_CTX_set_options(ssl_ctx_, ssl_opts);

  if (SSL_CTX_set1_curves_list(ssl_ctx_, config.groups) != 1) {
    std::cerr << "SSL_CTX_set1_curves_list failed" << std::endl;
    return -1;
  }

  SSL_CTX_set_mode(ssl_ctx_, SSL_MODE_RELEASE_BUFFERS);

  SSL_CTX_set_min_proto_version(ssl_ctx_, TLS1_3_VERSION);
  SSL_CTX_set_max_proto_version(ssl_ctx_, TLS1_3_VERSION);

  switch (app_proto) {
  case AppProtocol::H3:
    SSL_CTX_set_alpn_select_cb(ssl_ctx_, alpn_select_proto_h3_cb, nullptr);
    break;
  case AppProtocol::HQ:
    SSL_CTX_set_alpn_select_cb(ssl_ctx_, alpn_select_proto_hq_cb, nullptr);
    break;
  case AppProtocol::Perf:
    SSL_CTX_set_alpn_select_cb(ssl_ctx_, alpn_select_proto_perf_cb, nullptr);
    break;
  }

  SSL_CTX_set_default_verify_paths(ssl_ctx_);

  if (SSL_CTX_use_PrivateKey_file(ssl_ctx_, private_key_file,
                                  SSL_FILETYPE_PEM) != 1) {
    std::cerr << "SSL_CTX_use_PrivateKey_file: "
              << ERR_error_string(ERR_get_error(), nullptr) << std::endl;
    return -1;
  }

  if (SSL_CTX_use_certificate_chain_file(ssl_ctx_, cert_file) != 1) {
    std::cerr << "SSL_CTX_use_certificate_file: "
              << ERR_error_string(ERR_get_error(), nullptr) << std::endl;
    return -1;
  }

  if (SSL_CTX_check_private_key(ssl_ctx_) != 1) {
    std::cerr << "SSL_CTX_check_private_key: "
              << ERR_error_string(ERR_get_error(), nullptr) << std::endl;
    return -1;
  }

  SSL_CTX_set_session_id_context(ssl_ctx_, sid_ctx, sizeof(sid_ctx) - 1);

  if (config.verify_client) {
    SSL_CTX_set_verify(ssl_ctx_,
                       SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE |
                           SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
                       verify_cb);
  }

  SSL_CTX_set_quic_method(ssl_ctx_, &quic_method);

  return 0;
}

extern std::ofstream keylog_file;

namespace {
void keylog_callback(const SSL *ssl, const char *line) {
  keylog_file.write(line, strlen(line));
  keylog_file.put('\n');
  keylog_file.flush();
}
} // namespace

void TLSServerContext::enable_keylog() {
  SSL_CTX_set_keylog_callback(ssl_ctx_, keylog_callback);
}
<|endoftext|>


<commit_before>#include "graph.h"

void graph::add_edge(edge *e, vertex *from, vertex *to, conductor_info elect_info, double direction, uint index) {
	e->endpoint = to;
	e->next_edge = from->first_edge;
	from->first_edge = e;
	e->direction = direction;
	e->in_tree = false;
	e->index = index;
	e->elect_info = elect_info;
}

void graph::find_tree_path(uint index, vertex *x, vertex *y, std::vector<edge *> &path){
	for (int i = 0; i < vertex_number; ++i) {
		vertex_memory_pool[i].find_tree_mark = false;
		vertex_memory_pool[i].prev_vertex = NULL;
		vertex_memory_pool[i].prev_edge = NULL;
	}
	std::queue<vertex *> Q;
	Q.push(x);
	x->find_tree_mark = true;
	while (!Q.empty()) {
		vertex *u = Q.front();
		Q.pop();
		if (u == y) break;
		for (edge *e = u->first_edge; e; e = e->next_edge) {
			if (e->in_tree) {
				vertex *v = e->endpoint;
				if (!v->find_tree_mark) {
					v->prev_vertex = u;
					v->prev_edge = e;
					Q.push(v);
					v->find_tree_mark = true;
				}
			}
		}
	}
	for (vertex *u = y; u != x; u = u->prev_vertex){
		path.insert(path.begin(), u->prev_edge);
	}
}

arma::cx_rowvec graph::flow_conservation_equation(vertex *x) {
	arma::cx_rowvec edgedir(edge_number, arma::fill::zeros);
	for (edge *e = x->first_edge; e; e = e->next_edge) {
		edgedir(e->index) = e->direction;
	}
	return edgedir;
}

std::pair<arma::cx_rowvec, comp> graph::circular_equation(vertex *from, edge *e) {
	std::vector<edge *> path;
	find_tree_path(e->index, from, e->endpoint, path);
	path.push_back(e->opposite_edge);

	arma::cx_rowvec edgeimp(edge_number, arma::fill::zeros);
	comp totemf;

	for (std::vector<edge *>::iterator it = path.begin(); it != path.end(); ++it) {
		edgeimp((*it)->index) = (*it)->direction * (*it)->elect_info.imp;
		totemf += (*it)->direction * (*it)->elect_info.emf;
	}
	return std::pair<arma::cx_rowvec, comp>(edgeimp, totemf);
}

void graph::bfs(vertex *start, arma::cx_mat &A, arma::cx_vec &b, uint &current_row) {
	std::queue<vertex *> Q;
	Q.push(start);
	start->bfs_mark = true;
	while (!Q.empty()) {
		vertex *x = Q.front();
		Q.pop();
		for (edge *e = x->first_edge; e; e = e->next_edge) {
			if (!e->endpoint->bfs_mark) {
				Q.push(x);
				e->endpoint->bfs_mark = true;
				e->in_tree = true;
				e->opposite_edge->in_tree = true;

				arma::cx_rowvec edgedir = flow_conservation_equation(e->endpoint);
				A.row(current_row++) = edgedir;
			}
		}
	}
}

void graph::find_all_circular(arma::cx_mat &A, arma::cx_vec &b, uint &current_row) {
	for (uint i = 0; i < vertex_number; ++i) {
		vertex *x = vertex_memory_pool + i;
		for (edge *e = x->first_edge; e; e = e->next_edge) {
			if (!e->in_tree && e->direction > 0.0) {
				std::pair<arma::cx_rowvec, comp> equa = circular_equation(x, e);
				A.row(current_row) = equa.first;
				b(current_row) = equa.second;
				++current_row;
			}
		}
	}
}

graph::graph(uint V, const std::vector<conductor> &conductors) {
	vertex_number = V;
	vertex_memory_pool = new vertex[vertex_number];
	edge_number = conductors.size();
	edge_memory_pool = new edge[2 * edge_number];
	for (uint i = 0; i < edge_number; ++i) {
		vertex *x = vertex_memory_pool + conductors[i].edge_info.from;
		vertex *y = vertex_memory_pool + conductors[i].edge_info.to;
		edge *ep = edge_memory_pool + (2 * i);
		edge *en = edge_memory_pool + (2 * i + 1);
		add_edge(ep, x, y, conductors[i].elect_info,  1.0, i);
		add_edge(en, y, x, conductors[i].elect_info, -1.0, i);
		ep->opposite_edge = en;
		en->opposite_edge = ep;
	}
}

void graph::get_current(std::vector<comp> &current) {
	arma::cx_mat A(edge_number, edge_number, arma::fill::zeros);
	arma::cx_vec b(edge_number, arma::fill::zeros);
	uint current_row = 0;
	for (uint i = 0; i < vertex_number; ++i) {
		if (!vertex_memory_pool[i].bfs_mark) {
			bfs(vertex_memory_pool + i, A, b, current_row);
		}
	}
	find_all_circular(A, b, current_row);
	arma::cx_vec I = arma::solve(A, b);
	for (uint i = 0; i < edge_number; ++i) {
		current.push_back(I(i));
	}
}

graph::~graph() {
	delete[] vertex_memory_pool;
	delete[] edge_memory_pool;
}

graph::edge::edge() {
	in_tree = false;
}

graph::vertex::vertex() {
	first_edge = NULL;
	bfs_mark = false;
	find_tree_mark = false;
	prev_edge = NULL;
	prev_vertex = NULL;
}
<commit_msg>fix a crucial bug<commit_after>#include "graph.h"

void graph::add_edge(edge *e, vertex *from, vertex *to, conductor_info elect_info, double direction, uint index) {
	e->endpoint = to;
	e->next_edge = from->first_edge;
	from->first_edge = e;
	e->direction = direction;
	e->in_tree = false;
	e->index = index;
	e->elect_info = elect_info;
}

void graph::find_tree_path(uint index, vertex *x, vertex *y, std::vector<edge *> &path){
	for (int i = 0; i < vertex_number; ++i) {
		vertex_memory_pool[i].find_tree_mark = false;
		vertex_memory_pool[i].prev_vertex = NULL;
		vertex_memory_pool[i].prev_edge = NULL;
	}
	std::queue<vertex *> Q;
	Q.push(x);
	x->find_tree_mark = true;
	while (!Q.empty()) {
		vertex *u = Q.front();
		Q.pop();
		if (u == y) break;
		for (edge *e = u->first_edge; e; e = e->next_edge) {
			if (e->in_tree) {
				vertex *v = e->endpoint;
				if (!v->find_tree_mark) {
					v->prev_vertex = u;
					v->prev_edge = e;
					Q.push(v);
					v->find_tree_mark = true;
				}
			}
		}
	}
	for (vertex *u = y; u != x; u = u->prev_vertex){
		path.insert(path.begin(), u->prev_edge);
	}
}

arma::cx_rowvec graph::flow_conservation_equation(vertex *x) {
	arma::cx_rowvec edgedir(edge_number, arma::fill::zeros);
	for (edge *e = x->first_edge; e; e = e->next_edge) {
		edgedir(e->index) = e->direction;
	}
	return edgedir;
}

std::pair<arma::cx_rowvec, comp> graph::circular_equation(vertex *from, edge *e) {
	std::vector<edge *> path;
	find_tree_path(e->index, from, e->endpoint, path);
	path.push_back(e->opposite_edge);

	arma::cx_rowvec edgeimp(edge_number, arma::fill::zeros);
	comp totemf;

	for (std::vector<edge *>::iterator it = path.begin(); it != path.end(); ++it) {
		edgeimp((*it)->index) = (*it)->direction * (*it)->elect_info.imp;
		totemf += (*it)->direction * (*it)->elect_info.emf;
	}
	return std::pair<arma::cx_rowvec, comp>(edgeimp, totemf);
}

void graph::bfs(vertex *start, arma::cx_mat &A, arma::cx_vec &b, uint &current_row) {
	std::queue<vertex *> Q;
	Q.push(start);
	start->bfs_mark = true;
	while (!Q.empty()) {
		vertex *x = Q.front();
		Q.pop();
		for (edge *e = x->first_edge; e; e = e->next_edge) {
			if (!e->endpoint->bfs_mark) {
				Q.push(e->endpoint);
				e->endpoint->bfs_mark = true;
				e->in_tree = true;
				e->opposite_edge->in_tree = true;

				arma::cx_rowvec edgedir = flow_conservation_equation(e->endpoint);
				A.row(current_row++) = edgedir;
			}
		}
	}
}

void graph::find_all_circular(arma::cx_mat &A, arma::cx_vec &b, uint &current_row) {
	for (uint i = 0; i < vertex_number; ++i) {
		vertex *x = vertex_memory_pool + i;
		for (edge *e = x->first_edge; e; e = e->next_edge) {
			if (!e->in_tree && e->direction > 0.0) {
				std::pair<arma::cx_rowvec, comp> equa = circular_equation(x, e);
				A.row(current_row) = equa.first;
				b(current_row) = equa.second;
				++current_row;
			}
		}
	}
}

graph::graph(uint V, const std::vector<conductor> &conductors) {
	vertex_number = V;
	vertex_memory_pool = new vertex[vertex_number];
	edge_number = conductors.size();
	edge_memory_pool = new edge[2 * edge_number];
	for (uint i = 0; i < edge_number; ++i) {
		vertex *x = vertex_memory_pool + conductors[i].edge_info.from;
		vertex *y = vertex_memory_pool + conductors[i].edge_info.to;
		edge *ep = edge_memory_pool + (2 * i);
		edge *en = edge_memory_pool + (2 * i + 1);
		add_edge(ep, x, y, conductors[i].elect_info,  1.0, i);
		add_edge(en, y, x, conductors[i].elect_info, -1.0, i);
		ep->opposite_edge = en;
		en->opposite_edge = ep;
	}
}

void graph::get_current(std::vector<comp> &current) {
	arma::cx_mat A(edge_number, edge_number, arma::fill::zeros);
	arma::cx_vec b(edge_number, arma::fill::zeros);
	uint current_row = 0;
	for (uint i = 0; i < vertex_number; ++i) {
		if (!vertex_memory_pool[i].bfs_mark) {
			bfs(vertex_memory_pool + i, A, b, current_row);
		}
	}
	find_all_circular(A, b, current_row);
	arma::cx_vec I = arma::solve(A, b);
	for (uint i = 0; i < edge_number; ++i) {
		current.push_back(I(i));
	}
}

graph::~graph() {
	delete[] vertex_memory_pool;
	delete[] edge_memory_pool;
}

graph::edge::edge() {
	in_tree = false;
}

graph::vertex::vertex() {
	first_edge = NULL;
	bfs_mark = false;
	find_tree_mark = false;
	prev_edge = NULL;
	prev_vertex = NULL;
}
<|endoftext|>


<commit_before>#pragma once

#include <string>
#include <functional>
#include <athena/Types.hpp>
#include <athena/Global.hpp>
#include <athena/DNAYaml.hpp>

namespace hecl {
namespace DNACVAR {
enum class EType : atUint8 { Boolean, Integer, Float, Literal, Vec4f };

enum EFlags {
  None = -1,
  System = (1 << 0),
  Game = (1 << 1),
  Editor = (1 << 2),
  Gui = (1 << 3),
  Cheat = (1 << 4),
  Hidden = (1 << 5),
  ReadOnly = (1 << 6),
  Archive = (1 << 7),
  InternalArchivable = (1 << 8),
  Modified = (1 << 9),
  ModifyRestart = (1 << 10) /*!< If this bit is set, any modification will inform the user that a restart is required */
};
ENABLE_BITWISE_ENUM(EFlags)

class CVar : public athena::io::DNA<athena::Big> {
public:
  AT_DECL_DNA
  String<-1> m_name;
  String<-1> m_value;
};

struct CVarContainer : public athena::io::DNA<athena::Big> {
  AT_DECL_DNA
  Value<atUint32> magic = 'CVAR';
  Value<atUint32> cvarCount;
  Vector<CVar, AT_DNA_COUNT(cvarCount)> cvars;
};

} // namespace DNACVAR

class CVarManager;
class CVar : protected DNACVAR::CVar {
  friend class CVarManager;
  Delete _d;

public:
  typedef std::function<void(CVar*)> ListenerFunc;

  using EType = DNACVAR::EType;
  using EFlags = DNACVAR::EFlags;

  CVar(std::string_view name, std::string_view value, std::string_view help, EType type, EFlags flags,
       CVarManager& parent);
  CVar(std::string_view name, std::string_view value, std::string_view help, EFlags flags, CVarManager& parent);
  CVar(std::string_view name, float value, std::string_view help, EFlags flags, CVarManager& parent);
  CVar(std::string_view name, bool value, std::string_view help, EFlags flags, CVarManager& parent);
  CVar(std::string_view name, int value, std::string_view help, EFlags flags, CVarManager& parent);
  CVar(std::string_view name, const atVec4f& value, std::string_view help, EFlags flags, CVarManager& parent);

  std::string_view name() const { return m_name; }
  std::string_view rawHelp() const { return m_help; }
  std::string help() const;
  std::string value() const { return m_value; }

  atVec4f toVec4f(bool* isValid = nullptr) const;
  float toFloat(bool* isValid = nullptr) const;
  bool toBoolean(bool* isValid = nullptr) const;
  int toInteger(bool* isValid = nullptr) const;
  std::wstring toWideLiteral(bool* isValid = nullptr) const;
  std::string toLiteral(bool* isValid = nullptr) const;

  bool fromVec4f(const atVec4f& val);
  bool fromFloat(float val);
  bool fromBoolean(bool val);
  bool fromInteger(int val);
  bool fromLiteral(std::string_view val);
  bool fromLiteral(std::wstring_view val);
  bool fromLiteralToType(std::string_view val, bool setDefault = false);
  bool fromLiteralToType(std::wstring_view val, bool setDefault = false);

  bool isFloat() const { return m_type == EType::Float; }
  bool isBoolean() const { return m_type == EType::Boolean; }
  bool isInteger() const { return m_type == EType::Integer; }
  bool isLiteral() const { return m_type == EType::Literal; }
  bool isVec4f() const { return m_type == EType::Vec4f; }
  bool isModified() const;
  bool modificationRequiresRestart() const;
  bool isReadOnly() const;
  bool isCheat() const;
  bool isHidden() const;
  bool isArchive() const;
  bool isInternalArchivable() const;
  bool wasDeserialized() const;
  bool hasDefaultValue() const;
  void clearModified();
  void setModified();

  EType type() const { return m_type; }
  EFlags flags() const { return (m_unlocked ? m_oldFlags : m_flags); }

  /*!
   * \brief Unlocks the CVar for writing if it is ReadOnly.
   * Handle with care!!! if you use unlock(), make sure
   * you lock the cvar using lock()
   * \see lock
   */
  void unlock();

  /*!
   * \brief Locks the CVar to prevent writing if it is ReadOnly.
   * Unlike its partner function unlock, lock is harmless
   * \see unlock
   */
  void lock();

  void addListener(ListenerFunc func) { m_listeners.push_back(func); }

private:
  void dispatch();
  EType m_type;
  std::string m_help;
  std::string m_defaultValue;
  EFlags m_flags;
  EFlags m_oldFlags;
  bool m_unlocked = false;
  bool m_wasDeserialized = false;

  CVarManager& m_mgr;

  std::vector<ListenerFunc> m_listeners;
};

class CVarUnlocker {
  CVar* m_cvar;

public:
  CVarUnlocker(CVar* cvar) : m_cvar(cvar) {
    if (m_cvar)
      m_cvar->unlock();
  }
  ~CVarUnlocker() {
    if (m_cvar)
      m_cvar->lock();
  }
};

} // namespace hecl
<commit_msg>CVar: std::move listeners within addListener()<commit_after>#pragma once

#include <string>
#include <functional>
#include <athena/Types.hpp>
#include <athena/Global.hpp>
#include <athena/DNAYaml.hpp>

namespace hecl {
namespace DNACVAR {
enum class EType : atUint8 { Boolean, Integer, Float, Literal, Vec4f };

enum EFlags {
  None = -1,
  System = (1 << 0),
  Game = (1 << 1),
  Editor = (1 << 2),
  Gui = (1 << 3),
  Cheat = (1 << 4),
  Hidden = (1 << 5),
  ReadOnly = (1 << 6),
  Archive = (1 << 7),
  InternalArchivable = (1 << 8),
  Modified = (1 << 9),
  ModifyRestart = (1 << 10) /*!< If this bit is set, any modification will inform the user that a restart is required */
};
ENABLE_BITWISE_ENUM(EFlags)

class CVar : public athena::io::DNA<athena::Big> {
public:
  AT_DECL_DNA
  String<-1> m_name;
  String<-1> m_value;
};

struct CVarContainer : public athena::io::DNA<athena::Big> {
  AT_DECL_DNA
  Value<atUint32> magic = 'CVAR';
  Value<atUint32> cvarCount;
  Vector<CVar, AT_DNA_COUNT(cvarCount)> cvars;
};

} // namespace DNACVAR

class CVarManager;
class CVar : protected DNACVAR::CVar {
  friend class CVarManager;
  Delete _d;

public:
  typedef std::function<void(CVar*)> ListenerFunc;

  using EType = DNACVAR::EType;
  using EFlags = DNACVAR::EFlags;

  CVar(std::string_view name, std::string_view value, std::string_view help, EType type, EFlags flags,
       CVarManager& parent);
  CVar(std::string_view name, std::string_view value, std::string_view help, EFlags flags, CVarManager& parent);
  CVar(std::string_view name, float value, std::string_view help, EFlags flags, CVarManager& parent);
  CVar(std::string_view name, bool value, std::string_view help, EFlags flags, CVarManager& parent);
  CVar(std::string_view name, int value, std::string_view help, EFlags flags, CVarManager& parent);
  CVar(std::string_view name, const atVec4f& value, std::string_view help, EFlags flags, CVarManager& parent);

  std::string_view name() const { return m_name; }
  std::string_view rawHelp() const { return m_help; }
  std::string help() const;
  std::string value() const { return m_value; }

  atVec4f toVec4f(bool* isValid = nullptr) const;
  float toFloat(bool* isValid = nullptr) const;
  bool toBoolean(bool* isValid = nullptr) const;
  int toInteger(bool* isValid = nullptr) const;
  std::wstring toWideLiteral(bool* isValid = nullptr) const;
  std::string toLiteral(bool* isValid = nullptr) const;

  bool fromVec4f(const atVec4f& val);
  bool fromFloat(float val);
  bool fromBoolean(bool val);
  bool fromInteger(int val);
  bool fromLiteral(std::string_view val);
  bool fromLiteral(std::wstring_view val);
  bool fromLiteralToType(std::string_view val, bool setDefault = false);
  bool fromLiteralToType(std::wstring_view val, bool setDefault = false);

  bool isFloat() const { return m_type == EType::Float; }
  bool isBoolean() const { return m_type == EType::Boolean; }
  bool isInteger() const { return m_type == EType::Integer; }
  bool isLiteral() const { return m_type == EType::Literal; }
  bool isVec4f() const { return m_type == EType::Vec4f; }
  bool isModified() const;
  bool modificationRequiresRestart() const;
  bool isReadOnly() const;
  bool isCheat() const;
  bool isHidden() const;
  bool isArchive() const;
  bool isInternalArchivable() const;
  bool wasDeserialized() const;
  bool hasDefaultValue() const;
  void clearModified();
  void setModified();

  EType type() const { return m_type; }
  EFlags flags() const { return (m_unlocked ? m_oldFlags : m_flags); }

  /*!
   * \brief Unlocks the CVar for writing if it is ReadOnly.
   * Handle with care!!! if you use unlock(), make sure
   * you lock the cvar using lock()
   * \see lock
   */
  void unlock();

  /*!
   * \brief Locks the CVar to prevent writing if it is ReadOnly.
   * Unlike its partner function unlock, lock is harmless
   * \see unlock
   */
  void lock();

  void addListener(ListenerFunc func) { m_listeners.push_back(std::move(func)); }

private:
  void dispatch();
  EType m_type;
  std::string m_help;
  std::string m_defaultValue;
  EFlags m_flags;
  EFlags m_oldFlags;
  bool m_unlocked = false;
  bool m_wasDeserialized = false;

  CVarManager& m_mgr;

  std::vector<ListenerFunc> m_listeners;
};

class CVarUnlocker {
  CVar* m_cvar;

public:
  CVarUnlocker(CVar* cvar) : m_cvar(cvar) {
    if (m_cvar)
      m_cvar->unlock();
  }
  ~CVarUnlocker() {
    if (m_cvar)
      m_cvar->lock();
  }
};

} // namespace hecl
<|endoftext|>


<commit_before>/******************************************************************************
* Copyright (c) 2011, Michael P. Gerlek (mpg@flaxen.com)
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following
* conditions are met:
*
*     * Redistributions of source code must retain the above copyright
*       notice, this list of conditions and the following disclaimer.
*     * Redistributions in binary form must reproduce the above copyright
*       notice, this list of conditions and the following disclaimer in
*       the documentation and/or other materials provided
*       with the distribution.
*     * Neither the name of Hobu, Inc. or Flaxen Geo Consulting nor the
*       names of its contributors may be used to endorse or promote
*       products derived from this software without specific prior
*       written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
****************************************************************************/

//
#include <boost/scoped_ptr.hpp>

#include <pdal/Writer.hpp>
#include <pdal/StageIterator.hpp>
#include <pdal/Stage.hpp>
#include <pdal/PointBuffer.hpp>
#include <pdal/UserCallback.hpp>

#include <pdal/PipelineWriter.hpp>

#ifdef PDAL_COMPILER_MSVC
#  pragma warning(disable: 4127)  // conditional expression is constant
#endif

namespace pdal
{

Writer::Writer(Stage& prevStage, const Options& options)
    : StageBase(StageBase::makeVector(prevStage), options)
    , m_chunkSize(options.getValueOrDefault("chunk_size", s_defaultChunkSize))
    , m_userCallback(0)
    , m_writer_buffer(0)
{
    return;
}

void Writer::initialize()
{
    StageBase::initialize();

    return;
}

Writer::~Writer()
{
    if (m_writer_buffer != 0)
        delete m_writer_buffer;
}

void Writer::setChunkSize(boost::uint32_t chunkSize)
{
    m_chunkSize = chunkSize;
}

boost::uint32_t Writer::getChunkSize() const
{
    return m_chunkSize;
}

const SpatialReference& Writer::getSpatialReference() const
{
    return m_spatialReference;
}

void Writer::setSpatialReference(const SpatialReference& srs)
{
    m_spatialReference = srs;
}

void Writer::setUserCallback(UserCallback* userCallback)
{
    m_userCallback = userCallback;
}

UserCallback* Writer::getUserCallback() const
{
    return m_userCallback;
}

static void do_callback(double perc, UserCallback* callback)
{
    if (!callback) return;

    bool ok = callback->check(perc);
    if (!ok)
    {
        throw pipeline_interrupt("user requested interrupt");
    }

    return;
}

static void do_callback(boost::uint64_t pointsWritten, boost::uint64_t pointsToWrite, UserCallback* callback)
{
    if (!callback) return;

    bool ok = false;
    if (pointsToWrite == 0)
    {
        ok = callback->check();
    }
    else
    {
        double perc = ((double)pointsWritten / (double)pointsToWrite) * 100.0;
        ok = callback->check(perc);
    }
    if (!ok)
    {
        throw pipeline_interrupt("user requested interrupt");
    }

    return;
}

boost::uint64_t Writer::write(  boost::uint64_t targetNumPointsToWrite,
                                boost::uint64_t startingPosition)
{
    if (!isInitialized())
    {
        throw pdal_error("stage not initialized");
    }

    boost::uint64_t actualNumPointsWritten = 0;

    UserCallback* callback = getUserCallback();
    do_callback(0.0, callback);

    const Schema& schema = getPrevStage().getSchema();

    if (m_writer_buffer == 0)
    {
        boost::uint64_t capacity(targetNumPointsToWrite);
        if (capacity == 0)
        {
            capacity = m_chunkSize;
        } else
        {
            capacity = (std::min)(static_cast<boost::uint64_t>(m_chunkSize), targetNumPointsToWrite) ;
        }
        m_writer_buffer = new PointBuffer (schema, capacity);

    }

    boost::scoped_ptr<StageSequentialIterator> iter(getPrevStage().createSequentialIterator(*m_writer_buffer));

    if (startingPosition)
        iter->skip(startingPosition);

    if (!iter) throw pdal_error("Unable to obtain iterator from previous stage!");

    // if we don't have an SRS, try to forward the one from the prev stage
    if (m_spatialReference.empty()) m_spatialReference = getPrevStage().getSpatialReference();

    writeBegin(targetNumPointsToWrite);

    iter->readBegin();

    //
    // The user has requested a specific number of points: proceed a
    // chunk at a time until we reach that number.  (If that number
    // is 0, we proceed until no more points can be read.)
    //
    // If the user requests an interrupt while we're running, we'll throw.
    //
    while (true)
    {
        // have we hit the end already?
        if (iter->atEnd()) break;

        // rebuild our PointBuffer, if it needs to hold less than the default max chunk size
        if (targetNumPointsToWrite != 0)
        {
            const boost::uint64_t numRemainingPointsToRead = targetNumPointsToWrite - actualNumPointsWritten;

            const boost::uint64_t numPointsToReadThisChunk64 = std::min<boost::uint64_t>(numRemainingPointsToRead, m_chunkSize);
            // this case is safe because m_chunkSize is a uint32
            const boost::uint32_t numPointsToReadThisChunk = static_cast<boost::uint32_t>(numPointsToReadThisChunk64);

            // we are reusing the buffer, so we may need to adjust the capacity for the last (and likely undersized) chunk
            if (m_writer_buffer->getCapacity() < numPointsToReadThisChunk)
            {
                m_writer_buffer->resize(numPointsToReadThisChunk);
            }
        }

        // read...
        iter->readBufferBegin(*m_writer_buffer);
        const boost::uint32_t numPointsReadThisChunk = iter->readBuffer(*m_writer_buffer);
        iter->readBufferEnd(*m_writer_buffer);

        assert(numPointsReadThisChunk == m_writer_buffer->getNumPoints());
        assert(numPointsReadThisChunk <= m_writer_buffer->getCapacity());

        // have we reached the end yet?
        if (numPointsReadThisChunk == 0) break;

        // write...
        writeBufferBegin(*m_writer_buffer);
        const boost::uint32_t numPointsWrittenThisChunk = writeBuffer(*m_writer_buffer);
        assert(numPointsWrittenThisChunk == numPointsReadThisChunk);
        writeBufferEnd(*m_writer_buffer);

        // update count
        actualNumPointsWritten += numPointsWrittenThisChunk;

        do_callback(actualNumPointsWritten, targetNumPointsToWrite, callback);

        if (targetNumPointsToWrite != 0)
        {
            // have we done enough yet?
            if (actualNumPointsWritten >= targetNumPointsToWrite) break;
        }

        // reset the buffer, so we can use it again
        m_writer_buffer->setNumPoints(0);
    }

    iter->readEnd();

    writeEnd(actualNumPointsWritten);

    assert((targetNumPointsToWrite == 0) || (actualNumPointsWritten <= targetNumPointsToWrite));

    do_callback(100.0, callback);

    return actualNumPointsWritten;
}

boost::property_tree::ptree Writer::serializePipeline() const
{
    boost::property_tree::ptree tree;

    tree.add("<xmlattr>.type", getName());

    PipelineWriter::write_option_ptree(tree, getOptions());

    const Stage& stage = getPrevStage();
    boost::property_tree::ptree subtree = stage.serializePipeline();

    tree.add_child(subtree.begin()->first, subtree.begin()->second);

    boost::property_tree::ptree root;
    root.add_child("Writer", tree);

    return root;
}

boost::property_tree::ptree Writer::toPTree() const
{
    boost::property_tree::ptree tree = StageBase::toPTree();

    // (nothing to add for a Writer)

    return tree;
}

} // namespace pdal
<commit_msg>throw an exception if the capacity given for a PointBuffer is going to end up being larger than boost::uint32_t::max()<commit_after>/******************************************************************************
* Copyright (c) 2011, Michael P. Gerlek (mpg@flaxen.com)
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following
* conditions are met:
*
*     * Redistributions of source code must retain the above copyright
*       notice, this list of conditions and the following disclaimer.
*     * Redistributions in binary form must reproduce the above copyright
*       notice, this list of conditions and the following disclaimer in
*       the documentation and/or other materials provided
*       with the distribution.
*     * Neither the name of Hobu, Inc. or Flaxen Geo Consulting nor the
*       names of its contributors may be used to endorse or promote
*       products derived from this software without specific prior
*       written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
****************************************************************************/

//
#include <boost/scoped_ptr.hpp>

#include <pdal/Writer.hpp>
#include <pdal/StageIterator.hpp>
#include <pdal/Stage.hpp>
#include <pdal/PointBuffer.hpp>
#include <pdal/UserCallback.hpp>

#include <pdal/PipelineWriter.hpp>

#ifdef PDAL_COMPILER_MSVC
#  pragma warning(disable: 4127)  // conditional expression is constant
#endif

namespace pdal
{

Writer::Writer(Stage& prevStage, const Options& options)
    : StageBase(StageBase::makeVector(prevStage), options)
    , m_chunkSize(options.getValueOrDefault("chunk_size", s_defaultChunkSize))
    , m_userCallback(0)
    , m_writer_buffer(0)
{
    return;
}

void Writer::initialize()
{
    StageBase::initialize();

    return;
}

Writer::~Writer()
{
    if (m_writer_buffer != 0)
        delete m_writer_buffer;
}

void Writer::setChunkSize(boost::uint32_t chunkSize)
{
    m_chunkSize = chunkSize;
}

boost::uint32_t Writer::getChunkSize() const
{
    return m_chunkSize;
}

const SpatialReference& Writer::getSpatialReference() const
{
    return m_spatialReference;
}

void Writer::setSpatialReference(const SpatialReference& srs)
{
    m_spatialReference = srs;
}

void Writer::setUserCallback(UserCallback* userCallback)
{
    m_userCallback = userCallback;
}

UserCallback* Writer::getUserCallback() const
{
    return m_userCallback;
}

static void do_callback(double perc, UserCallback* callback)
{
    if (!callback) return;

    bool ok = callback->check(perc);
    if (!ok)
    {
        throw pipeline_interrupt("user requested interrupt");
    }

    return;
}

static void do_callback(boost::uint64_t pointsWritten, boost::uint64_t pointsToWrite, UserCallback* callback)
{
    if (!callback) return;

    bool ok = false;
    if (pointsToWrite == 0)
    {
        ok = callback->check();
    }
    else
    {
        double perc = ((double)pointsWritten / (double)pointsToWrite) * 100.0;
        ok = callback->check(perc);
    }
    if (!ok)
    {
        throw pipeline_interrupt("user requested interrupt");
    }

    return;
}

boost::uint64_t Writer::write(  boost::uint64_t targetNumPointsToWrite,
                                boost::uint64_t startingPosition)
{
    if (!isInitialized())
    {
        throw pdal_error("stage not initialized");
    }

    boost::uint64_t actualNumPointsWritten = 0;

    UserCallback* callback = getUserCallback();
    do_callback(0.0, callback);

    const Schema& schema = getPrevStage().getSchema();

    if (m_writer_buffer == 0)
    {
        boost::uint64_t capacity(targetNumPointsToWrite);
        if (capacity == 0)
        {
            capacity = m_chunkSize;
        } else
        {
            capacity = (std::min)(static_cast<boost::uint64_t>(m_chunkSize), targetNumPointsToWrite) ;
        }

        if (capacity > std::numeric_limits<boost::uint32_t>::max())
            throw pdal_error("Buffer capacity is larger than 2^32 points!");
        m_writer_buffer = new PointBuffer (schema, static_cast<boost::uint32_t>(capacity));

    }

    boost::scoped_ptr<StageSequentialIterator> iter(getPrevStage().createSequentialIterator(*m_writer_buffer));

    if (startingPosition)
        iter->skip(startingPosition);

    if (!iter) throw pdal_error("Unable to obtain iterator from previous stage!");

    // if we don't have an SRS, try to forward the one from the prev stage
    if (m_spatialReference.empty()) m_spatialReference = getPrevStage().getSpatialReference();

    writeBegin(targetNumPointsToWrite);

    iter->readBegin();

    //
    // The user has requested a specific number of points: proceed a
    // chunk at a time until we reach that number.  (If that number
    // is 0, we proceed until no more points can be read.)
    //
    // If the user requests an interrupt while we're running, we'll throw.
    //
    while (true)
    {
        // have we hit the end already?
        if (iter->atEnd()) break;

        // rebuild our PointBuffer, if it needs to hold less than the default max chunk size
        if (targetNumPointsToWrite != 0)
        {
            const boost::uint64_t numRemainingPointsToRead = targetNumPointsToWrite - actualNumPointsWritten;

            const boost::uint64_t numPointsToReadThisChunk64 = std::min<boost::uint64_t>(numRemainingPointsToRead, m_chunkSize);
            // this case is safe because m_chunkSize is a uint32
            const boost::uint32_t numPointsToReadThisChunk = static_cast<boost::uint32_t>(numPointsToReadThisChunk64);

            // we are reusing the buffer, so we may need to adjust the capacity for the last (and likely undersized) chunk
            if (m_writer_buffer->getCapacity() < numPointsToReadThisChunk)
            {
                m_writer_buffer->resize(numPointsToReadThisChunk);
            }
        }

        // read...
        iter->readBufferBegin(*m_writer_buffer);
        const boost::uint32_t numPointsReadThisChunk = iter->readBuffer(*m_writer_buffer);
        iter->readBufferEnd(*m_writer_buffer);

        assert(numPointsReadThisChunk == m_writer_buffer->getNumPoints());
        assert(numPointsReadThisChunk <= m_writer_buffer->getCapacity());

        // have we reached the end yet?
        if (numPointsReadThisChunk == 0) break;

        // write...
        writeBufferBegin(*m_writer_buffer);
        const boost::uint32_t numPointsWrittenThisChunk = writeBuffer(*m_writer_buffer);
        assert(numPointsWrittenThisChunk == numPointsReadThisChunk);
        writeBufferEnd(*m_writer_buffer);

        // update count
        actualNumPointsWritten += numPointsWrittenThisChunk;

        do_callback(actualNumPointsWritten, targetNumPointsToWrite, callback);

        if (targetNumPointsToWrite != 0)
        {
            // have we done enough yet?
            if (actualNumPointsWritten >= targetNumPointsToWrite) break;
        }

        // reset the buffer, so we can use it again
        m_writer_buffer->setNumPoints(0);
    }

    iter->readEnd();

    writeEnd(actualNumPointsWritten);

    assert((targetNumPointsToWrite == 0) || (actualNumPointsWritten <= targetNumPointsToWrite));

    do_callback(100.0, callback);

    return actualNumPointsWritten;
}

boost::property_tree::ptree Writer::serializePipeline() const
{
    boost::property_tree::ptree tree;

    tree.add("<xmlattr>.type", getName());

    PipelineWriter::write_option_ptree(tree, getOptions());

    const Stage& stage = getPrevStage();
    boost::property_tree::ptree subtree = stage.serializePipeline();

    tree.add_child(subtree.begin()->first, subtree.begin()->second);

    boost::property_tree::ptree root;
    root.add_child("Writer", tree);

    return root;
}

boost::property_tree::ptree Writer::toPTree() const
{
    boost::property_tree::ptree tree = StageBase::toPTree();

    // (nothing to add for a Writer)

    return tree;
}

} // namespace pdal
<|endoftext|>


<commit_before>/*
 * GLDisplay.cpp
 *
 *  Created on: Jan 9, 2010
 *      Author: Craig Rasmussen
 */

#include "GLDisplay.hpp"
#include "io.h"
#include "../layers/HyPerLayer.hpp"

#include <assert.h>
#include <GLUT/glut.h>

//#include "../layers/HyPerLayer.hpp"
//#include "io.h"

#ifdef __cplusplus
extern "C" {
#endif

// glut callbacks and gl prototypes
//
void glut_resize(int wWidth, int wHeight);
void glut_display(void);
void glut_timer_func(void);
static int glut_init(int * argc, char * argv[], int wWidth, int wHeight);
static void gl_init();
static void gl_draw_texture(int id);

#ifdef __cplusplus
}
#endif

class PV::GLDisplay;

// global variables
//

PV::GLDisplay * glProbe = NULL;

float g_msecs = 0.0;  /* timer interval */

int glwWidth  = 512;   /* window width */
int glwHeight = 512;   /* window height */

bool glFirst = true;

namespace PV {

/**
 * This class runs an OpenGL probe.  It MUST be a singleton (only one instance).
 */
GLDisplay::GLDisplay(int * argc, char * argv[], HyPerCol * hc, float msecs)
{
   this->parent = hc;
   hc->setDelegate(this);

   // start with time before start for initial display refresh
   lastUpdateTime = hc->simulationTime() - hc->getDeltaTime();

   glProbe = this;
   g_msecs = msecs;    // glut timer delay

   time     = 0.0;
   stopTime = 0.0;

   image = NULL;

   glut_init(argc, argv, glwWidth, glwHeight);
}

GLDisplay::~GLDisplay()
{
}

void GLDisplay::setImage(Image * image)
{
   this->image = image;
}

void GLDisplay::run(float time, float stopTime)
{
   this->time = time;
   this->stopTime = stopTime;
   glutMainLoop(); // we never return...
}

int GLDisplay::loadTexture(int id, Image * im)
{
   int status = 0;

   if (im == NULL) return -1;

   LayerLoc loc = im->getImageLoc();

   const int n = loc.nx * loc.ny * loc.nBands;
   unsigned char * buf = new unsigned char[n];
   assert(buf != NULL);

   status = im->copyToInteriorBuffer(buf);

//   float * data = im->getImageBuffer();

   const int width  = loc.nx;
   const int height = loc.ny;

   glBindTexture(GL_TEXTURE_2D, id);
   glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
   glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

   glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE,
                width, height, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, buf);

   return 0;
}

void GLDisplay::drawDisplay()
{
   const bool exitOnFinish = true;
   const int texId = 13;

   time = parent->advanceTime(time);
   if (time >= stopTime) {
      parent->exitRunLoop(exitOnFinish);
   }

//   if (glFirst) {
//      glFirst = false;
//      loadTexture(texId, image);
//      lastUpdateTime = time;
//   }
   if (lastUpdateTime < image->lastUpdate()) {
      loadTexture(texId, image);
      lastUpdateTime = time;
   }

   if (!glwHeight) {
      return;
   }

   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

   glMatrixMode(GL_MODELVIEW);
   glLoadIdentity();

   // PetaVision coordinate system is y==0 at top
   //
   glRotatef(180, 0, 0, 1);

   // this transformation for lower left-hand corner
   glScalef(0.75, 0.75, 1);
   glTranslatef(10, 10, -20);

   // this frame takes up entire display
   //glTranslatef(0, 0, -11);

   gl_draw_texture(texId);

   glutSwapBuffers();

   // force redraw to advance PetaVision time
   //
   if (g_msecs == 0.0f) {
      glutPostRedisplay();
   }
}

} // namespace PV

static
void gl_draw_texture(int id)
{
   glEnable (GL_TEXTURE_2D); /* enable texture mapping */
   glBindTexture (GL_TEXTURE_2D, id); /* bind to our texture */

   glBegin (GL_QUADS);
   glTexCoord2f (0.0f,0.0f); /* lower left corner of image */
   glVertex3f (-10.0f, -10.0f, 0.0f);
   glTexCoord2f (1.0f, 0.0f); /* lower right corner of image */
   glVertex3f (10.0f, -10.0f, 0.0f);
   glTexCoord2f (1.0f, 1.0f); /* upper right corner of image */
   glVertex3f (10.0f, 10.0f, 0.0f);
   glTexCoord2f (0.0f, 1.0f); /* upper left corner of image */
   glVertex3f (-10.0f, 10.0f, 0.0f);
   glEnd ();

   glDisable (GL_TEXTURE_2D); /* disable texture mapping */
}

void glut_keyboard(unsigned char key, int x, int y)
{
   switch (key) {
   /* exit the program */
   case 27:
   case 'q':
   case 'Q':
//      parent->finish();
      exit(1);
      break;
   }
}

void glut_display(void)
{
   glProbe->drawDisplay();
}

void glut_timer_func(int value)
{
   glProbe->drawDisplay();
   glutTimerFunc(g_msecs, glut_timer_func, value);
}

/**
 * Resize function.  Called when window is created and resized.
 */
void glut_resize(int wWidth, int wHeight)
{
   glwWidth  = wWidth;
   glwHeight = wHeight;

   glViewport(0, 0, glwWidth, glwHeight);

   glMatrixMode(GL_PROJECTION);
   glLoadIdentity();

   gluPerspective(90, glwWidth / glwHeight, 1, 9999);

   glutPostRedisplay();
}

static
void gl_init()
{
   glEnable(GL_DEPTH_TEST);
   glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
}

static
int glut_init(int * argc, char * argv[], int wWidth, int wHeight)
{
   glutInit(argc, argv);

   // initialize display window
   //
   glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
   glutInitWindowSize(wWidth, wHeight);
   glutInitWindowPosition(0, 0);
   glutCreateWindow("PetaVision Realtime Display");

   // register callbacks
   //
   glutKeyboardFunc(glut_keyboard);
   glutDisplayFunc(glut_display);
   glutReshapeFunc(glut_resize);
   if (g_msecs > 0.0f) {
      glutTimerFunc(g_msecs, glut_timer_func, 3);
   }

   gl_init();

   return 0;
}
<commit_msg>LayerLoc -> PVLayerLoc<commit_after>/*
 * GLDisplay.cpp
 *
 *  Created on: Jan 9, 2010
 *      Author: Craig Rasmussen
 */

#include "GLDisplay.hpp"
#include "io.h"
#include "../layers/HyPerLayer.hpp"

#include <assert.h>
#include <GLUT/glut.h>

//#include "../layers/HyPerLayer.hpp"
//#include "io.h"

#ifdef __cplusplus
extern "C" {
#endif

// glut callbacks and gl prototypes
//
void glut_resize(int wWidth, int wHeight);
void glut_display(void);
void glut_timer_func(void);
static int glut_init(int * argc, char * argv[], int wWidth, int wHeight);
static void gl_init();
static void gl_draw_texture(int id);

#ifdef __cplusplus
}
#endif

class PV::GLDisplay;

// global variables
//

PV::GLDisplay * glProbe = NULL;

float g_msecs = 0.0;  /* timer interval */

int glwWidth  = 512;   /* window width */
int glwHeight = 512;   /* window height */

bool glFirst = true;

namespace PV {

/**
 * This class runs an OpenGL probe.  It MUST be a singleton (only one instance).
 */
GLDisplay::GLDisplay(int * argc, char * argv[], HyPerCol * hc, float msecs)
{
   this->parent = hc;
   hc->setDelegate(this);

   // start with time before start for initial display refresh
   lastUpdateTime = hc->simulationTime() - hc->getDeltaTime();

   glProbe = this;
   g_msecs = msecs;    // glut timer delay

   time     = 0.0;
   stopTime = 0.0;

   image = NULL;

   glut_init(argc, argv, glwWidth, glwHeight);
}

GLDisplay::~GLDisplay()
{
}

void GLDisplay::setImage(Image * image)
{
   this->image = image;
}

void GLDisplay::run(float time, float stopTime)
{
   this->time = time;
   this->stopTime = stopTime;
   glutMainLoop(); // we never return...
}

int GLDisplay::loadTexture(int id, Image * im)
{
   int status = 0;

   if (im == NULL) return -1;

   PVLayerLoc loc = im->getImageLoc();

   const int n = loc.nx * loc.ny * loc.nBands;
   unsigned char * buf = new unsigned char[n];
   assert(buf != NULL);

   status = im->copyToInteriorBuffer(buf);

//   float * data = im->getImageBuffer();

   const int width  = loc.nx;
   const int height = loc.ny;

   glBindTexture(GL_TEXTURE_2D, id);
   glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
   glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

   glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE,
                width, height, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, buf);

   return 0;
}

void GLDisplay::drawDisplay()
{
   const bool exitOnFinish = true;
   const int texId = 13;

   time = parent->advanceTime(time);
   if (time >= stopTime) {
      parent->exitRunLoop(exitOnFinish);
   }

//   if (glFirst) {
//      glFirst = false;
//      loadTexture(texId, image);
//      lastUpdateTime = time;
//   }
   if (lastUpdateTime < image->lastUpdate()) {
      loadTexture(texId, image);
      lastUpdateTime = time;
   }

   if (!glwHeight) {
      return;
   }

   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

   glMatrixMode(GL_MODELVIEW);
   glLoadIdentity();

   // PetaVision coordinate system is y==0 at top
   //
   glRotatef(180, 0, 0, 1);

   // this transformation for lower left-hand corner
   glScalef(0.75, 0.75, 1);
   glTranslatef(10, 10, -20);

   // this frame takes up entire display
   //glTranslatef(0, 0, -11);

   gl_draw_texture(texId);

   glutSwapBuffers();

   // force redraw to advance PetaVision time
   //
   if (g_msecs == 0.0f) {
      glutPostRedisplay();
   }
}

} // namespace PV

static
void gl_draw_texture(int id)
{
   glEnable (GL_TEXTURE_2D); /* enable texture mapping */
   glBindTexture (GL_TEXTURE_2D, id); /* bind to our texture */

   glBegin (GL_QUADS);
   glTexCoord2f (0.0f,0.0f); /* lower left corner of image */
   glVertex3f (-10.0f, -10.0f, 0.0f);
   glTexCoord2f (1.0f, 0.0f); /* lower right corner of image */
   glVertex3f (10.0f, -10.0f, 0.0f);
   glTexCoord2f (1.0f, 1.0f); /* upper right corner of image */
   glVertex3f (10.0f, 10.0f, 0.0f);
   glTexCoord2f (0.0f, 1.0f); /* upper left corner of image */
   glVertex3f (-10.0f, 10.0f, 0.0f);
   glEnd ();

   glDisable (GL_TEXTURE_2D); /* disable texture mapping */
}

void glut_keyboard(unsigned char key, int x, int y)
{
   switch (key) {
   /* exit the program */
   case 27:
   case 'q':
   case 'Q':
//      parent->finish();
      exit(1);
      break;
   }
}

void glut_display(void)
{
   glProbe->drawDisplay();
}

void glut_timer_func(int value)
{
   glProbe->drawDisplay();
   glutTimerFunc(g_msecs, glut_timer_func, value);
}

/**
 * Resize function.  Called when window is created and resized.
 */
void glut_resize(int wWidth, int wHeight)
{
   glwWidth  = wWidth;
   glwHeight = wHeight;

   glViewport(0, 0, glwWidth, glwHeight);

   glMatrixMode(GL_PROJECTION);
   glLoadIdentity();

   gluPerspective(90, glwWidth / glwHeight, 1, 9999);

   glutPostRedisplay();
}

static
void gl_init()
{
   glEnable(GL_DEPTH_TEST);
   glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
}

static
int glut_init(int * argc, char * argv[], int wWidth, int wHeight)
{
   glutInit(argc, argv);

   // initialize display window
   //
   glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
   glutInitWindowSize(wWidth, wHeight);
   glutInitWindowPosition(0, 0);
   glutCreateWindow("PetaVision Realtime Display");

   // register callbacks
   //
   glutKeyboardFunc(glut_keyboard);
   glutDisplayFunc(glut_display);
   glutReshapeFunc(glut_resize);
   if (g_msecs > 0.0f) {
      glutTimerFunc(g_msecs, glut_timer_func, 3);
   }

   gl_init();

   return 0;
}
<|endoftext|>