<commit_before>#include "Spmv.hpp"
#include "Converters.hpp"
#include <iostream>
#include <tuple>
#include <dfesnippets/VectorUtils.hpp>
#include <dfesnippets/Timing.hpp>
#include <cassert>

#include "GeneratedImplSupport.hpp"
#include "Utils.hpp"

using namespace cask::spmv;
using ssarch = cask::spmv::Spmv;
namespace cutils = cask::utils;

const int burst_size_bytes = 384;

struct PartitionWriteResult {
  int64_t outStartAddr, outSize, colptrStartAddress, colptrSize;
  int64_t vStartAddress, indptrValuesStartAddress, indptrValuesSize;
};

// how many cycles does it take to resolve the accesses
int ssarch::countComputeCycles(int32_t* v, int size, int inputWidth)
{
  int cycles = 0;
  int crtPos = 0;
  for (int i = 0; i < size; i++) {
    int toread = v[i] - (i > 0 ? v[i - 1] : 0);
    do {
      int canread = std::min(inputWidth - crtPos, toread);
      crtPos += canread;
      crtPos %= inputWidth;
      cycles++;
      toread -= canread;
    } while (toread > 0);
  }
  return cycles;
}
// transform a given matrix with n rows in blocks of size n X blockSize
Partition ssarch::do_blocking(
    const CsrMatrix& m,
    int blockSize,
    int inputWidth)
{
  int rows = m.n;
  int cols = m.m;
  int n = rows;
  //std::cout << "Mat rows " << n << std::endl;
  //std::cout << "Npartitions: " << nPartitions << std::endl;

  std::vector<CsrMatrix> partitions = m.sliceColumns(blockSize); // (nBlocks);
  int nBlocks = partitions.size();

  //if (n > Spmv_maxRows)
    //throw std::invalid_argument(
        //"Matrix has too many rows - maximum supported: "
        //+ std::to_string(Spmv_maxRows));

  std::vector<double> v(cols, 0);
  std::vector<int> m_colptr;
  std::vector<indptr_value> m_indptr_value;

  // now we coalesce partitions
  int cycles = 0;
  int partition = 0;
  int reductionCycles = rows * partitions.size();
  int emptyCycles = 0;
  for (auto& p : partitions) {
    auto pp = preprocessBlock(p, partition++, partitions.size());
    auto& p_colptr = pp.row_ptr;
    auto& p_indptr = pp.col_ind;
    auto& p_values = pp.values;

    int diff = p.row_ptr.size() - p_colptr.size();
    emptyCycles += diff;
    reductionCycles -= diff;
    cycles += this->countComputeCycles(&p.row_ptr[0], n, inputWidth) - diff;

    utils::align(p_indptr, sizeof(int) * inputWidth);
    utils::align(p_values, sizeof(double) * inputWidth);
    copy(p_colptr.begin(), p_colptr.end(), back_inserter(m_colptr));
    for (size_t i = 0; i < p_values.size(); i++)
      m_indptr_value.push_back(indptr_value( p_values[i], p_indptr[i]));
  }

  Partition br;
  br.m_colptr_unpaddedLength = m_colptr.size();
  br.m_indptr_values_unpaddedLength = m_indptr_value.size();
  //std::cout << "m_colptr unaligned size" << m_colptr.size() << std::endl;
  std::vector<double> out(n, 0);
  cutils::align(out, burst_size_bytes);
  cutils::align(v, sizeof(double) * blockSize);

  br.nBlocks = nBlocks;
  br.n = n;
  br.paddingCycles = out.size() - n; // number of cycles required to align to the burst size
  br.totalCycles = cycles + v.size();
  br.vector_load_cycles = v.size() / nBlocks; // per partition
  br.m_indptr_values = m_indptr_value;
  br.m_colptr = m_colptr;
  br.outSize = out.size() * sizeof(double);
  br.emptyCycles = emptyCycles;
  br.reductionCycles = reductionCycles;

  return br;
}

template<typename T>
std::vector<T> msinglearray(int size, int pos, T value) {
  std::vector<T> v(size);
  v[pos] = value;
  return v;
}

/**
 * Ensures input data size is a multiple of burst size, by adding additional
 * zero padding if necessary.
 *
 * @returns the number of bytes written, including padding if added
 */
template<typename T>
int64_t writeAndPad(cask::runtime::GeneratedSpmvImplementation* impl,
    int controllerNum,
    int numControllers,
    int64_t startAddress,
    std::vector<T> data,
    std::string routingString)
{
  cutils::align(data, burst_size_bytes);
  int64_t sizeBytes = cutils::size_bytes(data);
  auto sizes = msinglearray(numControllers, controllerNum, sizeBytes);
  auto addrs = msinglearray(numControllers, controllerNum, startAddress);
  impl->write(sizes[controllerNum],
              &sizes[0],
              &addrs[0],
              (uint8_t*)data.data(),
              routingString.c_str());
  return sizeBytes;
}

// write the data for a partition, starting at the given offset
PartitionWriteResult writeDataForPartition(
    cask::runtime::GeneratedSpmvImplementation *impl,
    int offset,
    const Partition& br,
    const std::vector<double>& v,
    int numControllers,
    int controllerNum) {
  // for each partition write this down
  std::string routingString = "split -> tomem" + std::to_string(controllerNum);
  PartitionWriteResult pwr;
  pwr.indptrValuesStartAddress = cutils::align(offset, burst_size_bytes);
  pwr.indptrValuesSize = writeAndPad(impl,
      controllerNum,
      numControllers,
      pwr.indptrValuesStartAddress,
      br.m_indptr_values,
      routingString);

  pwr.vStartAddress = pwr.indptrValuesStartAddress + pwr.indptrValuesSize;
  // XXX, it may not be safe to pad the vector arbitrarily, if the hardware
  // cannot support unpadding it at runtime
  int64_t sizeV = pwr.colptrSize = writeAndPad(impl,
      controllerNum,
      numControllers,
      pwr.vStartAddress,
      v,
      routingString);

  pwr.colptrStartAddress = pwr.vStartAddress + sizeV;
  pwr.colptrSize = writeAndPad(impl,
      controllerNum,
      numControllers,
      pwr.colptrStartAddress,
      br.m_colptr,
      routingString);

  pwr.outStartAddr = pwr.colptrStartAddress + pwr.colptrSize;
  pwr.outSize = br.outSize;
  return pwr;
}

cask::Vector ssarch::spmv(const cask::Vector& x)
{
  using namespace std;

  if (this->impl.dram_reduction_enabled) {
    // because of DRAM read/write latency we can only hope to get correct
    // answers for large matrices
    // XXX figure out where to place this constant;
    const int minRowsWithDramReduction = 35000;
    if (mat.n < minRowsWithDramReduction ) {
      stringstream ss;
      ss << "Matrix is too small! Minimum supported rows with DRAM reduction: ";
      ss << minRowsWithDramReduction;
      ss << " actual rows: " << mat.n;
      throw invalid_argument(ss.str());
    }
  } else if (impl.max_rows < mat.n) {
      stringstream ss;
      ss << "Matrix is too large! Maximum supported rows: ";
      ss << impl.max_rows;
      ss << " actual rows: " << mat.n;
      throw invalid_argument(ss.str());
  }

  int cacheSize = impl.cache_size;

  vector<double> v = x.data;
  cutils::align(v, sizeof(double) * cacheSize);
  cutils::align(v, burst_size_bytes);

  vector<int> nrows, totalCycles, reductionCycles, paddingCycles, colptrSizes, indptrValuesSizes, outputResultSizes;
  vector<int> colptrUnpaddedSizes, indptrValuesUnpaddedLengths;
  vector<long> outputStartAddresses, colptrStartAddresses;
  vector<long> vStartAddresses, indptrValuesStartAddresses;

  int offset = 0;
  int i = 0;

  assert(partitions.size() == impl.num_pipes && "numPipes should equal numPartitions");
  assert(impl.num_pipes % impl.num_controllers == 0 && "numPipes should be a multiple of numControllers");
  assert(impl.num_controllers <= impl.num_pipes && "numPipes should be larger than numControllers");
  for (auto& p : partitions) {
    nrows.push_back(p.n);
    paddingCycles.push_back(p.paddingCycles);
    totalCycles.push_back(p.totalCycles);
    reductionCycles.push_back(p.reductionCycles);
    colptrUnpaddedSizes.push_back(p.m_colptr_unpaddedLength);
    indptrValuesUnpaddedLengths.push_back(p.m_indptr_values_unpaddedLength);

    int nc = impl.num_controllers;
    int pipesPerController = impl.num_pipes / nc;
    int ctrlId = i / pipesPerController;
    // moving to a new controller, reset offset in memory
    if (i % pipesPerController == 0) {
      offset = 0;
    }
    PartitionWriteResult pr = writeDataForPartition(&this->impl, offset, p, v, nc, ctrlId);
    outputStartAddresses.push_back(pr.outStartAddr);
    outputResultSizes.push_back(pr.outSize);
    colptrStartAddresses.push_back(pr.colptrStartAddress);
    colptrSizes.push_back(cutils::size_bytes(p.m_colptr));
    vStartAddresses.push_back(pr.vStartAddress);
    indptrValuesSizes.push_back(pr.indptrValuesSize);
    indptrValuesStartAddresses.push_back(pr.indptrValuesStartAddress);

    offset = pr.outStartAddr + p.outSize;
    i++;
  }

  // npartitions and vector load cycles should be the same for all partitions
  int nBlocks = this->partitions[0].nBlocks;
  int vector_load_cycles = this->partitions[0].vector_load_cycles;
  cout << "Running on DFE" << endl;

  int nIterations = 2;
  utils::logResult("Total cycles", totalCycles);
  utils::logResult("Padding cycles", paddingCycles);
  utils::logResult("Reduction cycles", reductionCycles);

  auto start = chrono::_V2::system_clock::now();
  impl.Spmv(
      nIterations,
      nBlocks,
      vector_load_cycles,
      &colptrStartAddresses[0],
      &colptrSizes[0],
      &indptrValuesStartAddresses[0],
      &indptrValuesSizes[0],
      &nrows[0],
      &outputStartAddresses[0],
      &reductionCycles[0],
      &totalCycles[0],
      &vStartAddresses[0]
      );
  double took = dfesnippets::timing::clock_diff(start) / nIterations;
  double est =(double) totalCycles[0] / getFrequency();
  double gflopsEst = (2.0 * (double)this->mat.nnzs / est) / 1E9;
  double gflopsActual = (2.0 * (double)this->mat.nnzs / took) / 1E9;

  double bwidthEst = impl.num_pipes * impl.input_width * getFrequency() / (1024.0  *
      1024 * 1024) * (8 + 4);
  utils::logResult("Input width ", impl.input_width);
  utils::logResult("Pipes ", impl.num_pipes);

  utils::logResult("Iterations", nIterations);
  utils::logResult("Took (ms)", took);
  utils::logResult("Est (ms)", est);
  utils::logResult("Gflops (est)", gflopsEst);
  utils::logResult("Gflops (actual)", gflopsActual);
  utils::logResult("BWidth (est)", bwidthEst);

  vector<double> total;
  for (size_t i = 0; i < outputStartAddresses.size(); i++) {
    vector<double> tmp(outputResultSizes[i] / sizeof(double), 0);
    int ctrlId = i / (impl.num_pipes / impl.num_controllers);
    auto sizes = msinglearray(impl.num_controllers, ctrlId, utils::size_bytes(tmp));
    auto addrs = msinglearray(impl.num_controllers, ctrlId, outputStartAddresses[i]);
    string routing = "frommem" + std::to_string(ctrlId) + " -> join";
    impl.read(
        sizes[ctrlId],
        &sizes[0],
        &addrs[0],
        (uint8_t*)&tmp[0],
        routing.c_str()
        );
    copy(tmp.begin(), tmp.begin() + nrows[i], back_inserter(total));
  }

  // remove the elements which were only for padding
  return Vector{total};
}
void ssarch::preprocess(
    const CsrMatrix& mat) {
  this->mat = mat;

  partitions.clear();
  int rowsPerPartition = mat.n / impl.num_pipes;
  int start = 0;
  for (int i = 0; i < impl.num_pipes - 1; i++) {
    this->partitions.push_back(
        do_blocking(
          mat.sliceRows(start, rowsPerPartition),
          impl.cache_size, impl.input_width));
    start += rowsPerPartition;
  }

  // put all rows left in the last partition
  this->partitions.push_back(
      do_blocking(mat.sliceRows(start, mat.n - start),
        impl.cache_size, impl.input_width));

}
<commit_msg>Fix for multiple iterations<commit_after>#include "Spmv.hpp"
#include "Converters.hpp"
#include <iostream>
#include <tuple>
#include <dfesnippets/VectorUtils.hpp>
#include <dfesnippets/Timing.hpp>
#include <cassert>

#include "GeneratedImplSupport.hpp"
#include "Utils.hpp"

using namespace cask::spmv;
using ssarch = cask::spmv::Spmv;
namespace cutils = cask::utils;

const int burst_size_bytes = 384;

struct PartitionWriteResult {
  int64_t outStartAddr, outSize, colptrStartAddress, colptrSize;
  int64_t vStartAddress, indptrValuesStartAddress, indptrValuesSize;
};

// how many cycles does it take to resolve the accesses
int ssarch::countComputeCycles(int32_t* v, int size, int inputWidth)
{
  int cycles = 0;
  int crtPos = 0;
  for (int i = 0; i < size; i++) {
    int toread = v[i] - (i > 0 ? v[i - 1] : 0);
    do {
      int canread = std::min(inputWidth - crtPos, toread);
      crtPos += canread;
      crtPos %= inputWidth;
      cycles++;
      toread -= canread;
    } while (toread > 0);
  }
  return cycles;
}
// transform a given matrix with n rows in blocks of size n X blockSize
Partition ssarch::do_blocking(
    const CsrMatrix& m,
    int blockSize,
    int inputWidth)
{
  int rows = m.n;
  int cols = m.m;
  int n = rows;
  //std::cout << "Mat rows " << n << std::endl;
  //std::cout << "Npartitions: " << nPartitions << std::endl;

  std::vector<CsrMatrix> partitions = m.sliceColumns(blockSize); // (nBlocks);
  int nBlocks = partitions.size();

  //if (n > Spmv_maxRows)
    //throw std::invalid_argument(
        //"Matrix has too many rows - maximum supported: "
        //+ std::to_string(Spmv_maxRows));

  std::vector<double> v(cols, 0);
  std::vector<int> m_colptr;
  std::vector<indptr_value> m_indptr_value;

  // now we coalesce partitions
  int cycles = 0;
  int partition = 0;
  int reductionCycles = rows * partitions.size();
  int emptyCycles = 0;
  for (auto& p : partitions) {
    auto pp = preprocessBlock(p, partition++, partitions.size());
    auto& p_colptr = pp.row_ptr;
    auto& p_indptr = pp.col_ind;
    auto& p_values = pp.values;

    int diff = p.row_ptr.size() - p_colptr.size();
    emptyCycles += diff;
    reductionCycles -= diff;
    cycles += this->countComputeCycles(&p.row_ptr[0], n, inputWidth) - diff;

    utils::align(p_indptr, sizeof(int) * inputWidth);
    utils::align(p_values, sizeof(double) * inputWidth);
    copy(p_colptr.begin(), p_colptr.end(), back_inserter(m_colptr));
    for (size_t i = 0; i < p_values.size(); i++)
      m_indptr_value.push_back(indptr_value( p_values[i], p_indptr[i]));
  }

  Partition br;
  br.m_colptr_unpaddedLength = m_colptr.size();
  br.m_indptr_values_unpaddedLength = m_indptr_value.size();
  std::vector<double> out(n, 0);
  cutils::align(out, burst_size_bytes);
  cutils::align(v, sizeof(double) * blockSize);

  br.nBlocks = nBlocks;
  br.n = n;
  br.paddingCycles = out.size() - n; // number of cycles required to align to the burst size
  br.totalCycles = cycles + v.size();
  br.vector_load_cycles = v.size() / nBlocks; // per partition
  br.m_indptr_values = m_indptr_value;
  br.m_colptr = m_colptr;
  br.outSize = out.size() * sizeof(double);
  br.emptyCycles = emptyCycles;
  br.reductionCycles = reductionCycles;

  return br;
}

template<typename T>
std::vector<T> msinglearray(int size, int pos, T value) {
  std::vector<T> v(size);
  v[pos] = value;
  return v;
}

/**
 * Ensures input data size is a multiple of burst size, by adding additional
 * zero padding if necessary.
 *
 * @returns the number of bytes written, including padding if added
 */
template<typename T>
int64_t writeAndPad(cask::runtime::GeneratedSpmvImplementation* impl,
    int controllerNum,
    int numControllers,
    int64_t startAddress,
    std::vector<T> data,
    std::string routingString)
{
  cutils::align(data, burst_size_bytes);
  int64_t sizeBytes = cutils::size_bytes(data);
  auto sizes = msinglearray(numControllers, controllerNum, sizeBytes);
  auto addrs = msinglearray(numControllers, controllerNum, startAddress);
  impl->write(sizes[controllerNum],
              &sizes[0],
              &addrs[0],
              (uint8_t*)data.data(),
              routingString.c_str());
  return sizeBytes;
}

// write the data for a partition, starting at the given offset
PartitionWriteResult writeDataForPartition(
    cask::runtime::GeneratedSpmvImplementation *impl,
    int offset,
    const Partition& br,
    const std::vector<double>& v,
    int numControllers,
    int controllerNum) {
  // for each partition write this down
  std::string routingString = "split -> tomem" + std::to_string(controllerNum);
  PartitionWriteResult pwr;
  pwr.indptrValuesStartAddress = cutils::align(offset, burst_size_bytes);
  pwr.indptrValuesSize = writeAndPad(impl,
      controllerNum,
      numControllers,
      pwr.indptrValuesStartAddress,
      br.m_indptr_values,
      routingString);

  pwr.vStartAddress = pwr.indptrValuesStartAddress + pwr.indptrValuesSize;
  // XXX, it may not be safe to pad the vector arbitrarily, if the hardware
  // cannot support unpadding it at runtime
  int64_t sizeV = pwr.colptrSize = writeAndPad(impl,
      controllerNum,
      numControllers,
      pwr.vStartAddress,
      v,
      routingString);

  pwr.colptrStartAddress = pwr.vStartAddress + sizeV;
  pwr.colptrSize = writeAndPad(impl,
      controllerNum,
      numControllers,
      pwr.colptrStartAddress,
      br.m_colptr,
      routingString);

  pwr.outStartAddr = pwr.colptrStartAddress + pwr.colptrSize;
  pwr.outSize = br.outSize;
  return pwr;
}

cask::Vector ssarch::spmv(const cask::Vector& x)
{
  using namespace std;

  if (this->impl.dram_reduction_enabled) {
    // because of DRAM read/write latency we can only hope to get correct
    // answers for large matrices
    // XXX figure out where to place this constant;
    const int minRowsWithDramReduction = 35000;
    if (mat.n < minRowsWithDramReduction ) {
      stringstream ss;
      ss << "Matrix is too small! Minimum supported rows with DRAM reduction: ";
      ss << minRowsWithDramReduction;
      ss << " actual rows: " << mat.n;
      throw invalid_argument(ss.str());
    }
  } else if (impl.max_rows < mat.n) {
      stringstream ss;
      ss << "Matrix is too large! Maximum supported rows: ";
      ss << impl.max_rows;
      ss << " actual rows: " << mat.n;
      throw invalid_argument(ss.str());
  }

  int cacheSize = impl.cache_size;

  vector<double> v = x.data;
  cutils::align(v, sizeof(double) * cacheSize);
  cutils::align(v, burst_size_bytes);

  vector<int> nrows, totalCycles, reductionCycles, paddingCycles, colptrSizes, indptrValuesSizes, outputResultSizes;
  vector<long> outputStartAddresses, colptrStartAddresses;
  vector<long> vStartAddresses, indptrValuesStartAddresses;

  int offset = 0;
  int i = 0;

  assert(partitions.size() == impl.num_pipes && "numPipes should equal numPartitions");
  assert(impl.num_pipes % impl.num_controllers == 0 && "numPipes should be a multiple of numControllers");
  assert(impl.num_controllers <= impl.num_pipes && "numPipes should be larger than numControllers");
  for (auto& p : partitions) {
    nrows.push_back(p.n);
    paddingCycles.push_back(p.paddingCycles);
    totalCycles.push_back(p.totalCycles);
    reductionCycles.push_back(p.reductionCycles);

    int nc = impl.num_controllers;
    int pipesPerController = impl.num_pipes / nc;
    int ctrlId = i / pipesPerController;
    // moving to a new controller, reset offset in memory
    if (i % pipesPerController == 0) {
      offset = 0;
    }
    PartitionWriteResult pr = writeDataForPartition(&this->impl, offset, p, v, nc, ctrlId);
    outputStartAddresses.push_back(pr.outStartAddr);
    outputResultSizes.push_back(pr.outSize);
    colptrStartAddresses.push_back(pr.colptrStartAddress);
    colptrSizes.push_back(cutils::size_bytes(p.m_colptr));
    vStartAddresses.push_back(pr.vStartAddress);
    indptrValuesSizes.push_back(cutils::size_bytes(p.m_indptr_values));
    indptrValuesStartAddresses.push_back(pr.indptrValuesStartAddress);

    offset = pr.outStartAddr + p.outSize;
    i++;
  }

  // npartitions and vector load cycles should be the same for all partitions
  int nBlocks = this->partitions[0].nBlocks;
  int vector_load_cycles = this->partitions[0].vector_load_cycles;
  cout << "Running on DFE" << endl;

  int nIterations = 2;
  utils::logResult("Total cycles", totalCycles);
  utils::logResult("Padding cycles", paddingCycles);
  utils::logResult("Reduction cycles", reductionCycles);

  auto start = chrono::_V2::system_clock::now();
  impl.Spmv(
      nIterations,
      nBlocks,
      vector_load_cycles,
      &colptrStartAddresses[0],
      &colptrSizes[0],
      &indptrValuesStartAddresses[0],
      &indptrValuesSizes[0],
      &nrows[0],
      &outputStartAddresses[0],
      &reductionCycles[0],
      &totalCycles[0],
      &vStartAddresses[0]
      );
  double took = dfesnippets::timing::clock_diff(start) / nIterations;
  double est =(double) totalCycles[0] / getFrequency();
  double gflopsEst = (2.0 * (double)this->mat.nnzs / est) / 1E9;
  double gflopsActual = (2.0 * (double)this->mat.nnzs / took) / 1E9;

  double bwidthEst = impl.num_pipes * impl.input_width * getFrequency() / (1024.0  *
      1024 * 1024) * (8 + 4);
  utils::logResult("Input width ", impl.input_width);
  utils::logResult("Pipes ", impl.num_pipes);

  utils::logResult("Iterations", nIterations);
  utils::logResult("Took (ms)", took);
  utils::logResult("Est (ms)", est);
  utils::logResult("Gflops (est)", gflopsEst);
  utils::logResult("Gflops (actual)", gflopsActual);
  utils::logResult("BWidth (est)", bwidthEst);

  vector<double> total;
  for (size_t i = 0; i < outputStartAddresses.size(); i++) {
    vector<double> tmp(outputResultSizes[i] / sizeof(double), 0);
    int ctrlId = i / (impl.num_pipes / impl.num_controllers);
    auto sizes = msinglearray(impl.num_controllers, ctrlId, utils::size_bytes(tmp));
    auto addrs = msinglearray(impl.num_controllers, ctrlId, outputStartAddresses[i]);
    string routing = "frommem" + std::to_string(ctrlId) + " -> join";
    impl.read(
        sizes[ctrlId],
        &sizes[0],
        &addrs[0],
        (uint8_t*)&tmp[0],
        routing.c_str()
        );
    copy(tmp.begin(), tmp.begin() + nrows[i], back_inserter(total));
  }

  // remove the elements which were only for padding
  return Vector{total};
}
void ssarch::preprocess(
    const CsrMatrix& mat) {
  this->mat = mat;

  partitions.clear();
  int rowsPerPartition = mat.n / impl.num_pipes;
  int start = 0;
  for (int i = 0; i < impl.num_pipes - 1; i++) {
    this->partitions.push_back(
        do_blocking(
          mat.sliceRows(start, rowsPerPartition),
          impl.cache_size, impl.input_width));
    start += rowsPerPartition;
  }

  // put all rows left in the last partition
  this->partitions.push_back(
      do_blocking(mat.sliceRows(start, mat.n - start),
        impl.cache_size, impl.input_width));

}
<|endoftext|>


<commit_before>/*      _______   __   __   __   ______   __   __   _______   __   __
 *     / _____/\ / /\ / /\ / /\ / ____/\ / /\ / /\ / ___  /\ /  |\/ /\
 *    / /\____\// / // / // / // /\___\// /_// / // /\_/ / // , |/ / /
 *   / / /__   / / // / // / // / /    / ___  / // ___  / // /| ' / /
 *  / /_// /\ / /_// / // / // /_/_   / / // / // /\_/ / // / |  / /
 * /______/ //______/ //_/ //_____/\ /_/ //_/ //_/ //_/ //_/ /|_/ /
 * \______\/ \______\/ \_\/ \_____\/ \_\/ \_\/ \_\/ \_\/ \_\/ \_\/
 *
 * Copyright (c) 2004, 2005, 2006, 2007 Olof Naessn and Per Larsson
 *
 *                                                         Js_./
 * Per Larsson a.k.a finalman                          _RqZ{a<^_aa
 * Olof Naessn a.k.a jansem/yakslem                _asww7!uY`>  )\a//
 *                                                 _Qhm`] _f "'c  1!5m
 * Visit: http://guichan.darkbits.org             )Qk<P ` _: :+' .'  "{[
 *                                               .)j(] .d_/ '-(  P .   S
 * License: (BSD)                                <Td/Z <fP"5(\"??"\a.  .L
 * Redistribution and use in source and          _dV>ws?a-?'      ._/L  #'
 * binary forms, with or without                 )4d[#7r, .   '     )d`)[
 * modification, are permitted provided         _Q-5'5W..j/?'   -?!\)cam'
 * that the following conditions are met:       j<<WP+k/);.        _W=j f
 * 1. Redistributions of source code must       .$%w\/]Q  . ."'  .  mj$
 *    retain the above copyright notice,        ]E.pYY(Q]>.   a     J@\
 *    this list of conditions and the           j(]1u<sE"L,. .   ./^ ]{a
 *    following disclaimer.                     4'_uomm\.  )L);-4     (3=
 * 2. Redistributions in binary form must        )_]X{Z('a_"a7'<a"a,  ]"[
 *    reproduce the above copyright notice,       #}<]m7`Za??4,P-"'7. ).m
 *    this list of conditions and the            ]d2e)Q(<Q(  ?94   b-  LQ/
 *    following disclaimer in the                <B!</]C)d_, '(<' .f. =C+m
 *    documentation and/or other materials      .Z!=J ]e []('-4f _ ) -.)m]'
 *    provided with the distribution.          .w[5]' _[ /.)_-"+?   _/ <W"
 * 3. Neither the name of Guichan nor the      :$we` _! + _/ .        j?
 *    names of its contributors may be used     =3)= _f  (_yQmWW$#(    "
 *    to endorse or promote products derived     -   W,  sQQQQmZQ#Wwa]..
 *    from this software without specific        (js, \[QQW$QWW#?!V"".
 *    prior written permission.                    ]y:.<\..          .
 *                                                 -]n w/ '         [.
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT       )/ )/           !
 * HOLDERS AND CONTRIBUTORS "AS IS" AND ANY         <  (; sac    ,    '
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING,               ]^ .-  %
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF            c <   r
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR            aga<  <La
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE          5%  )P'-3L
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR        _bQf` y`..)a
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,          ,J?4P'.P"_(\?d'.,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES               _Pa,)!f/<[]/  ?"
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT      _2-..:. .r+_,.. .
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,     ?a.<%"'  " -'.a_ _,
 * 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.
 */

/*
 * For comments regarding functions please see the header file.
 */

#include "guichan/sdl/sdlinput.hpp"

#include "guichan/exception.hpp"

namespace gcn
{
    SDLInput::SDLInput()
    {
        mMouseInWindow = true;
        mMouseDown = false;
    }

    bool SDLInput::isKeyQueueEmpty()
    {
        return mKeyInputQueue.empty();
    }

    KeyInput SDLInput::dequeueKeyInput()
    {
        KeyInput keyInput;

        if (mKeyInputQueue.empty())
        {
            throw GCN_EXCEPTION("The queue is empty.");
        }

        keyInput = mKeyInputQueue.front();
        mKeyInputQueue.pop();

        return keyInput;
    }

    bool SDLInput::isMouseQueueEmpty()
    {
        return mMouseInputQueue.empty();
    }

    MouseInput SDLInput::dequeueMouseInput()
    {
        MouseInput mouseInput;

        if (mMouseInputQueue.empty())
        {
            throw GCN_EXCEPTION("The queue is empty.");
        }

        mouseInput = mMouseInputQueue.front();
        mMouseInputQueue.pop();

        return mouseInput;
    }

    void SDLInput::pushInput(SDL_Event event)
    {
        KeyInput keyInput;
        MouseInput mouseInput;

        switch (event.type)
        {
          case SDL_KEYDOWN:
              keyInput.setKey(Key(convertKeyCharacter(event.key.keysym)));
              keyInput.setType(KeyInput::KEY_PRESSED);
              keyInput.setShiftPressed(event.key.keysym.mod & KMOD_SHIFT);
              keyInput.setControlPressed(event.key.keysym.mod & KMOD_CTRL);
              keyInput.setAltPressed(event.key.keysym.mod & KMOD_ALT);
              keyInput.setMetaPressed(event.key.keysym.mod & KMOD_META);
              keyInput.setNumericPad(event.key.keysym.sym >= SDLK_KP0
                                     && event.key.keysym.sym <= SDLK_KP_EQUALS);

              mKeyInputQueue.push(keyInput);
              break;

          case SDL_KEYUP:
              keyInput.setKey(Key(convertKeyCharacter(event.key.keysym)));
              keyInput.setType(KeyInput::KEY_RELEASED);
              keyInput.setShiftPressed(event.key.keysym.mod & KMOD_SHIFT);
              keyInput.setControlPressed(event.key.keysym.mod & KMOD_CTRL);
              keyInput.setAltPressed(event.key.keysym.mod & KMOD_ALT);
              keyInput.setMetaPressed(event.key.keysym.mod & KMOD_META);
              keyInput.setNumericPad(event.key.keysym.sym >= SDLK_KP0
                                     && event.key.keysym.sym <= SDLK_KP_EQUALS);

              mKeyInputQueue.push(keyInput);
              break;

          case SDL_MOUSEBUTTONDOWN:
              mMouseDown = true;
              mouseInput.setX(event.button.x);
              mouseInput.setY(event.button.y);
              mouseInput.setButton(convertMouseButton(event.button.button));

              if (event.button.button == SDL_BUTTON_WHEELDOWN)
              {
                  mouseInput.setType(MouseInput::MOUSE_WHEEL_MOVED_DOWN);
              }
              else if (event.button.button == SDL_BUTTON_WHEELUP)
              {
                  mouseInput.setType(MouseInput::MOUSE_WHEEL_MOVED_UP);
              }
              else
              {
                  mouseInput.setType(MouseInput::MOUSE_PRESSED);
              }
              mouseInput.setTimeStamp(SDL_GetTicks());
              mMouseInputQueue.push(mouseInput);
              break;

          case SDL_MOUSEBUTTONUP:
              mMouseDown = false;
              mouseInput.setX(event.button.x);
              mouseInput.setY(event.button.y);
              mouseInput.setButton(convertMouseButton(event.button.button));
              mouseInput.setType(MouseInput::MOUSE_RELEASED);
              mouseInput.setTimeStamp(SDL_GetTicks());
              mMouseInputQueue.push(mouseInput);
              break;

          case SDL_MOUSEMOTION:
              mouseInput.setX(event.button.x);
              mouseInput.setY(event.button.y);
              mouseInput.setButton(MouseInput::MOUSE_BUTTON_EMPTY);
              mouseInput.setType(MouseInput::MOUSE_MOVED);
              mouseInput.setTimeStamp(SDL_GetTicks());
              mMouseInputQueue.push(mouseInput);
              break;

          case SDL_ACTIVEEVENT:
              /*
               * This occurs when the mouse leaves the window and the Gui-chan
               * application loses its mousefocus.
               */
              if ((event.active.state & SDL_APPMOUSEFOCUS)
                  && !event.active.gain)
              {
                  mMouseInWindow = false;

                  if (!mMouseDown)
                  {
                      mouseInput.setX(-1);
                      mouseInput.setY(-1);
                      mouseInput.setButton(MouseInput::MOUSE_BUTTON_EMPTY);
                      mouseInput.setType(MouseInput::MOUSE_MOVED);
                      mMouseInputQueue.push(mouseInput);
                  }
              }

              if ((event.active.state & SDL_APPMOUSEFOCUS)
                  && event.active.gain)
              {
                  mMouseInWindow = true;
              }
              break;

        } // end switch
    }

    int SDLInput::convertMouseButton(int button)
    {
        switch (button)
        {
          case SDL_BUTTON_LEFT:
              return MouseInput::MOUSE_BUTTON_LEFT;
              break;
          case SDL_BUTTON_RIGHT:
              return MouseInput::MOUSE_BUTTON_RIGHT;
              break;
          case SDL_BUTTON_MIDDLE:
              return MouseInput::MOUSE_BUTTON_MIDDLE;
              break;
          default:
              // We have an unknown mouse type which is ignored.
              return button;
        }
    }

    int SDLInput::convertKeyCharacter(SDL_keysym keysym)
    {
        int value = 0;

        if (keysym.unicode < 255)
        {
            value = (int)keysym.unicode;
        }

        switch (keysym.sym)
        {
          case SDLK_TAB:
              value = Key::TAB;
              break;
          case SDLK_LALT:
              value = Key::LEFT_ALT;
              break;
          case SDLK_RALT:
              value = Key::RIGHT_ALT;
              break;
          case SDLK_LSHIFT:
              value = Key::LEFT_SHIFT;
              break;
          case SDLK_RSHIFT:
              value = Key::RIGHT_SHIFT;
              break;
          case SDLK_LCTRL:
              value = Key::LEFT_CONTROL;
              break;
          case SDLK_RCTRL:
              value = Key::RIGHT_CONTROL;
              break;
          case SDLK_BACKSPACE:
              value = Key::BACKSPACE;
              break;
          case SDLK_PAUSE:
              value = Key::PAUSE;
              break;
          case SDLK_SPACE:
              value = Key::SPACE;
              break;
          case SDLK_ESCAPE:
              value = Key::ESCAPE;
              break;
          case SDLK_DELETE:
              value = Key::DELETE;
              break;
          case SDLK_INSERT:
              value = Key::INSERT;
              break;
          case SDLK_HOME:
              value = Key::HOME;
              break;
          case SDLK_END:
              value = Key::END;
              break;
          case SDLK_PAGEUP:
              value = Key::PAGE_UP;
              break;
          case SDLK_PRINT:
              value = Key::PRINT_SCREEN;
              break;
          case SDLK_PAGEDOWN:
              value = Key::PAGE_DOWN;
              break;
          case SDLK_F1:
              value = Key::F1;
              break;
          case SDLK_F2:
              value = Key::F2;
              break;
          case SDLK_F3:
              value = Key::F3;
              break;
          case SDLK_F4:
              value = Key::F4;
              break;
          case SDLK_F5:
              value = Key::F5;
              break;
          case SDLK_F6:
              value = Key::F6;
              break;
          case SDLK_F7:
              value = Key::F7;
              break;
          case SDLK_F8:
              value = Key::F8;
              break;
          case SDLK_F9:
              value = Key::F9;
              break;
          case SDLK_F10:
              value = Key::F10;
              break;
          case SDLK_F11:
              value = Key::F11;
              break;
          case SDLK_F12:
              value = Key::F12;
              break;
          case SDLK_F13:
              value = Key::F13;
              break;
          case SDLK_F14:
              value = Key::F14;
              break;
          case SDLK_F15:
              value = Key::F15;
              break;
          case SDLK_NUMLOCK:
              value = Key::NUM_LOCK;
              break;
          case SDLK_CAPSLOCK:
              value = Key::CAPS_LOCK;
              break;
          case SDLK_SCROLLOCK:
              value = Key::SCROLL_LOCK;
              break;
          case SDLK_RMETA:
              value = Key::RIGHT_META;
              break;
          case SDLK_LMETA:
              value = Key::LEFT_META;
              break;
          case SDLK_LSUPER:
              value = Key::LEFT_SUPER;
              break;
          case SDLK_RSUPER:
              value = Key::RIGHT_SUPER;
              break;
          case SDLK_MODE:
              value = Key::ALT_GR;
              break;
          case SDLK_UP:
              value = Key::UP;
              break;
          case SDLK_DOWN:
              value = Key::DOWN;
              break;
          case SDLK_LEFT:
              value = Key::LEFT;
              break;
          case SDLK_RIGHT:
              value = Key::RIGHT;
              break;
          case SDLK_RETURN:
              value = Key::ENTER;
              break;
          case SDLK_KP_ENTER:
              value = Key::ENTER;
              break;

          default:
              break;
        }

        if (!(keysym.mod & KMOD_NUM))
        {
            switch (keysym.sym)
            {
              case SDLK_KP0:
                  value = Key::INSERT;
                  break;
              case SDLK_KP1:
                  value = Key::END;
                  break;
              case SDLK_KP2:
                  value = Key::DOWN;
                  break;
              case SDLK_KP3:
                  value = Key::PAGE_DOWN;
                  break;
              case SDLK_KP4:
                  value = Key::LEFT;
                  break;
              case SDLK_KP5:
                  value = 0;
                  break;
              case SDLK_KP6:
                  value = Key::RIGHT;
                  break;
              case SDLK_KP7:
                  value = Key::HOME;
                  break;
              case SDLK_KP8:
                  value = Key::UP;
                  break;
              case SDLK_KP9:
                  value = Key::PAGE_UP;
                  break;
              default:
                  break;
            }
        }

        return value;
    }
}
<commit_msg>Fix applied suggested by malbatahan when characters like ~ (tilde) weren't propageted correctly.<commit_after>/*      _______   __   __   __   ______   __   __   _______   __   __
 *     / _____/\ / /\ / /\ / /\ / ____/\ / /\ / /\ / ___  /\ /  |\/ /\
 *    / /\____\// / // / // / // /\___\// /_// / // /\_/ / // , |/ / /
 *   / / /__   / / // / // / // / /    / ___  / // ___  / // /| ' / /
 *  / /_// /\ / /_// / // / // /_/_   / / // / // /\_/ / // / |  / /
 * /______/ //______/ //_/ //_____/\ /_/ //_/ //_/ //_/ //_/ /|_/ /
 * \______\/ \______\/ \_\/ \_____\/ \_\/ \_\/ \_\/ \_\/ \_\/ \_\/
 *
 * Copyright (c) 2004, 2005, 2006, 2007 Olof Naessn and Per Larsson
 *
 *                                                         Js_./
 * Per Larsson a.k.a finalman                          _RqZ{a<^_aa
 * Olof Naessn a.k.a jansem/yakslem                _asww7!uY`>  )\a//
 *                                                 _Qhm`] _f "'c  1!5m
 * Visit: http://guichan.darkbits.org             )Qk<P ` _: :+' .'  "{[
 *                                               .)j(] .d_/ '-(  P .   S
 * License: (BSD)                                <Td/Z <fP"5(\"??"\a.  .L
 * Redistribution and use in source and          _dV>ws?a-?'      ._/L  #'
 * binary forms, with or without                 )4d[#7r, .   '     )d`)[
 * modification, are permitted provided         _Q-5'5W..j/?'   -?!\)cam'
 * that the following conditions are met:       j<<WP+k/);.        _W=j f
 * 1. Redistributions of source code must       .$%w\/]Q  . ."'  .  mj$
 *    retain the above copyright notice,        ]E.pYY(Q]>.   a     J@\
 *    this list of conditions and the           j(]1u<sE"L,. .   ./^ ]{a
 *    following disclaimer.                     4'_uomm\.  )L);-4     (3=
 * 2. Redistributions in binary form must        )_]X{Z('a_"a7'<a"a,  ]"[
 *    reproduce the above copyright notice,       #}<]m7`Za??4,P-"'7. ).m
 *    this list of conditions and the            ]d2e)Q(<Q(  ?94   b-  LQ/
 *    following disclaimer in the                <B!</]C)d_, '(<' .f. =C+m
 *    documentation and/or other materials      .Z!=J ]e []('-4f _ ) -.)m]'
 *    provided with the distribution.          .w[5]' _[ /.)_-"+?   _/ <W"
 * 3. Neither the name of Guichan nor the      :$we` _! + _/ .        j?
 *    names of its contributors may be used     =3)= _f  (_yQmWW$#(    "
 *    to endorse or promote products derived     -   W,  sQQQQmZQ#Wwa]..
 *    from this software without specific        (js, \[QQW$QWW#?!V"".
 *    prior written permission.                    ]y:.<\..          .
 *                                                 -]n w/ '         [.
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT       )/ )/           !
 * HOLDERS AND CONTRIBUTORS "AS IS" AND ANY         <  (; sac    ,    '
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING,               ]^ .-  %
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF            c <   r
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR            aga<  <La
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE          5%  )P'-3L
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR        _bQf` y`..)a
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,          ,J?4P'.P"_(\?d'.,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES               _Pa,)!f/<[]/  ?"
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT      _2-..:. .r+_,.. .
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,     ?a.<%"'  " -'.a_ _,
 * 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.
 */

/*
 * For comments regarding functions please see the header file.
 */

#include "guichan/sdl/sdlinput.hpp"

#include "guichan/exception.hpp"

namespace gcn
{
    SDLInput::SDLInput()
    {
        mMouseInWindow = true;
        mMouseDown = false;
    }

    bool SDLInput::isKeyQueueEmpty()
    {
        return mKeyInputQueue.empty();
    }

    KeyInput SDLInput::dequeueKeyInput()
    {
        KeyInput keyInput;

        if (mKeyInputQueue.empty())
        {
            throw GCN_EXCEPTION("The queue is empty.");
        }

        keyInput = mKeyInputQueue.front();
        mKeyInputQueue.pop();

        return keyInput;
    }

    bool SDLInput::isMouseQueueEmpty()
    {
        return mMouseInputQueue.empty();
    }

    MouseInput SDLInput::dequeueMouseInput()
    {
        MouseInput mouseInput;

        if (mMouseInputQueue.empty())
        {
            throw GCN_EXCEPTION("The queue is empty.");
        }

        mouseInput = mMouseInputQueue.front();
        mMouseInputQueue.pop();

        return mouseInput;
    }

    void SDLInput::pushInput(SDL_Event event)
    {
        KeyInput keyInput;
        MouseInput mouseInput;

        switch (event.type)
        {
          case SDL_KEYDOWN:
              keyInput.setKey(Key(convertKeyCharacter(event.key.keysym)));
              keyInput.setType(KeyInput::KEY_PRESSED);
              keyInput.setShiftPressed(event.key.keysym.mod & KMOD_SHIFT);
              keyInput.setControlPressed(event.key.keysym.mod & KMOD_CTRL);
              keyInput.setAltPressed(event.key.keysym.mod & KMOD_ALT);
              keyInput.setMetaPressed(event.key.keysym.mod & KMOD_META);
              keyInput.setNumericPad(event.key.keysym.sym >= SDLK_KP0
                                     && event.key.keysym.sym <= SDLK_KP_EQUALS);

              mKeyInputQueue.push(keyInput);
              break;

          case SDL_KEYUP:
              keyInput.setKey(Key(convertKeyCharacter(event.key.keysym)));
              keyInput.setType(KeyInput::KEY_RELEASED);
              keyInput.setShiftPressed(event.key.keysym.mod & KMOD_SHIFT);
              keyInput.setControlPressed(event.key.keysym.mod & KMOD_CTRL);
              keyInput.setAltPressed(event.key.keysym.mod & KMOD_ALT);
              keyInput.setMetaPressed(event.key.keysym.mod & KMOD_META);
              keyInput.setNumericPad(event.key.keysym.sym >= SDLK_KP0
                                     && event.key.keysym.sym <= SDLK_KP_EQUALS);

              mKeyInputQueue.push(keyInput);
              break;

          case SDL_MOUSEBUTTONDOWN:
              mMouseDown = true;
              mouseInput.setX(event.button.x);
              mouseInput.setY(event.button.y);
              mouseInput.setButton(convertMouseButton(event.button.button));

              if (event.button.button == SDL_BUTTON_WHEELDOWN)
              {
                  mouseInput.setType(MouseInput::MOUSE_WHEEL_MOVED_DOWN);
              }
              else if (event.button.button == SDL_BUTTON_WHEELUP)
              {
                  mouseInput.setType(MouseInput::MOUSE_WHEEL_MOVED_UP);
              }
              else
              {
                  mouseInput.setType(MouseInput::MOUSE_PRESSED);
              }
              mouseInput.setTimeStamp(SDL_GetTicks());
              mMouseInputQueue.push(mouseInput);
              break;

          case SDL_MOUSEBUTTONUP:
              mMouseDown = false;
              mouseInput.setX(event.button.x);
              mouseInput.setY(event.button.y);
              mouseInput.setButton(convertMouseButton(event.button.button));
              mouseInput.setType(MouseInput::MOUSE_RELEASED);
              mouseInput.setTimeStamp(SDL_GetTicks());
              mMouseInputQueue.push(mouseInput);
              break;

          case SDL_MOUSEMOTION:
              mouseInput.setX(event.button.x);
              mouseInput.setY(event.button.y);
              mouseInput.setButton(MouseInput::MOUSE_BUTTON_EMPTY);
              mouseInput.setType(MouseInput::MOUSE_MOVED);
              mouseInput.setTimeStamp(SDL_GetTicks());
              mMouseInputQueue.push(mouseInput);
              break;

          case SDL_ACTIVEEVENT:
              /*
               * This occurs when the mouse leaves the window and the Gui-chan
               * application loses its mousefocus.
               */
              if ((event.active.state & SDL_APPMOUSEFOCUS)
                  && !event.active.gain)
              {
                  mMouseInWindow = false;

                  if (!mMouseDown)
                  {
                      mouseInput.setX(-1);
                      mouseInput.setY(-1);
                      mouseInput.setButton(MouseInput::MOUSE_BUTTON_EMPTY);
                      mouseInput.setType(MouseInput::MOUSE_MOVED);
                      mMouseInputQueue.push(mouseInput);
                  }
              }

              if ((event.active.state & SDL_APPMOUSEFOCUS)
                  && event.active.gain)
              {
                  mMouseInWindow = true;
              }
              break;

        } // end switch
    }

    int SDLInput::convertMouseButton(int button)
    {
        switch (button)
        {
          case SDL_BUTTON_LEFT:
              return MouseInput::MOUSE_BUTTON_LEFT;
              break;
          case SDL_BUTTON_RIGHT:
              return MouseInput::MOUSE_BUTTON_RIGHT;
              break;
          case SDL_BUTTON_MIDDLE:
              return MouseInput::MOUSE_BUTTON_MIDDLE;
              break;
          default:
              // We have an unknown mouse type which is ignored.
              return button;
        }
    }

    int SDLInput::convertKeyCharacter(SDL_keysym keysym)
    {
        int value = 0;

        if (keysym.unicode < 255)
        {
            value = (int)keysym.unicode;
        }

        switch (keysym.sym)
        {
          case SDLK_TAB:
              value = Key::TAB;
              break;
          case SDLK_LALT:
              value = Key::LEFT_ALT;
              break;
          case SDLK_RALT:
              value = Key::RIGHT_ALT;
              break;
          case SDLK_LSHIFT:
              value = Key::LEFT_SHIFT;
              break;
          case SDLK_RSHIFT:
              value = Key::RIGHT_SHIFT;
              break;
          case SDLK_LCTRL:
              value = Key::LEFT_CONTROL;
              break;
          case SDLK_RCTRL:
              value = Key::RIGHT_CONTROL;
              break;
          case SDLK_BACKSPACE:
              value = Key::BACKSPACE;
              break;
          case SDLK_PAUSE:
              value = Key::PAUSE;
              break;
          case SDLK_SPACE:
              // Special characters like ~ (tilde) ends up
              // with the keysym.sym SDLK_SPACE which
              // without this check would be lost.
              if (keysym.unicode == ' ')
              {
                  value = Key::SPACE;
              }
              break;
          case SDLK_ESCAPE:
              value = Key::ESCAPE;
              break;
          case SDLK_DELETE:
              value = Key::DELETE;
              break;
          case SDLK_INSERT:
              value = Key::INSERT;
              break;
          case SDLK_HOME:
              value = Key::HOME;
              break;
          case SDLK_END:
              value = Key::END;
              break;
          case SDLK_PAGEUP:
              value = Key::PAGE_UP;
              break;
          case SDLK_PRINT:
              value = Key::PRINT_SCREEN;
              break;
          case SDLK_PAGEDOWN:
              value = Key::PAGE_DOWN;
              break;
          case SDLK_F1:
              value = Key::F1;
              break;
          case SDLK_F2:
              value = Key::F2;
              break;
          case SDLK_F3:
              value = Key::F3;
              break;
          case SDLK_F4:
              value = Key::F4;
              break;
          case SDLK_F5:
              value = Key::F5;
              break;
          case SDLK_F6:
              value = Key::F6;
              break;
          case SDLK_F7:
              value = Key::F7;
              break;
          case SDLK_F8:
              value = Key::F8;
              break;
          case SDLK_F9:
              value = Key::F9;
              break;
          case SDLK_F10:
              value = Key::F10;
              break;
          case SDLK_F11:
              value = Key::F11;
              break;
          case SDLK_F12:
              value = Key::F12;
              break;
          case SDLK_F13:
              value = Key::F13;
              break;
          case SDLK_F14:
              value = Key::F14;
              break;
          case SDLK_F15:
              value = Key::F15;
              break;
          case SDLK_NUMLOCK:
              value = Key::NUM_LOCK;
              break;
          case SDLK_CAPSLOCK:
              value = Key::CAPS_LOCK;
              break;
          case SDLK_SCROLLOCK:
              value = Key::SCROLL_LOCK;
              break;
          case SDLK_RMETA:
              value = Key::RIGHT_META;
              break;
          case SDLK_LMETA:
              value = Key::LEFT_META;
              break;
          case SDLK_LSUPER:
              value = Key::LEFT_SUPER;
              break;
          case SDLK_RSUPER:
              value = Key::RIGHT_SUPER;
              break;
          case SDLK_MODE:
              value = Key::ALT_GR;
              break;
          case SDLK_UP:
              value = Key::UP;
              break;
          case SDLK_DOWN:
              value = Key::DOWN;
              break;
          case SDLK_LEFT:
              value = Key::LEFT;
              break;
          case SDLK_RIGHT:
              value = Key::RIGHT;
              break;
          case SDLK_RETURN:
              value = Key::ENTER;
              break;
          case SDLK_KP_ENTER:
              value = Key::ENTER;
              break;

          default:
              break;
        }

        if (!(keysym.mod & KMOD_NUM))
        {
            switch (keysym.sym)
            {
              case SDLK_KP0:
                  value = Key::INSERT;
                  break;
              case SDLK_KP1:
                  value = Key::END;
                  break;
              case SDLK_KP2:
                  value = Key::DOWN;
                  break;
              case SDLK_KP3:
                  value = Key::PAGE_DOWN;
                  break;
              case SDLK_KP4:
                  value = Key::LEFT;
                  break;
              case SDLK_KP5:
                  value = 0;
                  break;
              case SDLK_KP6:
                  value = Key::RIGHT;
                  break;
              case SDLK_KP7:
                  value = Key::HOME;
                  break;
              case SDLK_KP8:
                  value = Key::UP;
                  break;
              case SDLK_KP9:
                  value = Key::PAGE_UP;
                  break;
              default:
                  break;
            }
        }

        return value;
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include <string.h>
#include <unistd.h>
#include <algorithm>
#include <functional>
#include <mutex>
#include <string>
#include <vector>

#include "contents.hh"
#include "file_contents.hh"
#include "operating_system_macros.hh"
#include "../lib.hh"
#include "split.hh"
#include "print_contents.hh"

namespace vick {
namespace shell_command {

std::string quote_string_windows(const std::string& s) {
    std::string ret;
    ret.reserve(s.size());

    // escape by wrapping double quotes and escaping double quotes
    // inside arg
    ret += '"';
    std::string::const_iterator i = s.begin(), e = s.end(), p;
beg:
    p = i;
    i = std::find(i, e, '"');
    ret.append(p, i);
    if (i != e) {
        ret += '\\';
        ret += '"';
        ++i;
        goto beg;
    }
    ret += '"';

    return ret;
}

std::string quote_string_linux(const std::string& s) {
    std::string ret;
    ret.reserve(s.size());

    // escape by wrapping single quotes and escaping single quotes and
    // raw `\n`s
    ret += '\'';
    std::string::const_iterator i = s.begin(), e = s.end(), p;
beg:
    p = i;
    i = std::find(i, e, '\'');
    ret.append(p, i);
    if (i != e) {
        ret += "'\\''";
        ++i;
        goto beg;
    }
    ret += '\'';

    return ret;
}

std::string quote_string(const std::string& s) {
    if (IS_OS_WIN) {
        return quote_string_windows(s);
    } else {
        return quote_string_linux(s);
    }
}

class exec_shell_command_exception : public std::exception {
    std::string message;

public:
    exec_shell_command_exception(std::string message)
        : message(std::move(message)) {}
    virtual const char* what() const noexcept override {
        return message.c_str();
    }
};

void exec_shell_command(const std::string& cmd, std::string* out_,
                        std::string* err_) {
    *out_ = "";
    *err_ = "";

    int out[2], err[2];

    if (pipe(out) == -1)
        throw exec_shell_command_exception(
            "Couldn't open stdout pipe");
    if (pipe(err) == -1)
        throw exec_shell_command_exception(
            "Coudln't open stderr pipe");

    switch (fork()) {
        case -1:
            throw exec_shell_command_exception(
                "Forking error on stdout");
        case 0: {
            close(out[0]);
            close(err[0]);
            dup2(out[1], STDOUT_FILENO);
            dup2(err[1], STDERR_FILENO);
            system(cmd.c_str());
            _Exit(-1);
        }
        default: {
            close(out[1]);
            close(err[1]);
            char readbuffer[1024];
            memset(readbuffer, '\0', sizeof(readbuffer));
            ssize_t nbytes = sizeof(readbuffer) - 1;
            while (std::find(readbuffer, readbuffer + nbytes, EOF) ==
                       readbuffer + nbytes and
                   nbytes) {
                nbytes =
                    read(out[0], readbuffer, sizeof(readbuffer) - 1);
                readbuffer[nbytes] = 0;
                *out_ += readbuffer;
            }
            close(out[0]);

            memset(readbuffer, '\0', sizeof(readbuffer));
            nbytes = sizeof(readbuffer) - 1;
            while (std::find(readbuffer, readbuffer + nbytes, EOF) ==
                       readbuffer + nbytes and
                   nbytes) {
                nbytes =
                    read(err[0], readbuffer, sizeof(readbuffer) - 1);
                readbuffer[nbytes] = 0;
                *err_ += readbuffer;
            }
        }
    }
}

void exec_shell_command(const std::string& cmd, std::string* out_) {
    *out_ = "";

    int out[2];

    if (pipe(out) == -1) {
        throw exec_shell_command_exception(
            "Couldn't generate a pipe");
    }

    switch (fork()) {
        case -1:
            throw exec_shell_command_exception(
                "Couldn't fork the process");
        case 0: { // pump data into pipe
            close(out[0]);
            dup2(out[1], STDOUT_FILENO);
            dup2(out[1], STDERR_FILENO);
            std::vector<const char*> command = {"sh", "-c",
                                                cmd.c_str(), nullptr};
            execv("/bin/sh", const_cast<char* const*>(
                                 static_cast<const char* const*>(
                                     command.data())));
            _Exit(-1);
        }
        default: { // transfer pipe to param out_
            close(out[1]);
            char readbuffer[1024];
            memset(readbuffer, '\0', sizeof(readbuffer));
            int nbytes = sizeof(readbuffer) - 1;
            while (std::find(readbuffer, readbuffer + nbytes, EOF) ==
                       readbuffer + nbytes and
                   nbytes) {
                nbytes =
                    read(out[0], readbuffer, sizeof(readbuffer) - 1);
                readbuffer[nbytes] = 0;
                *out_ += std::string(readbuffer);
            }
        }
    }
}

void exec_shell_command(const std::string& cmd,
                        std::function<void(std::string)> out_,
                        size_t buffer_size) {
    int out[2];

    if (pipe(out) == -1) {
        throw exec_shell_command_exception(
            "Couldn't generate a pipe");
    }

    switch (fork()) {
        case -1:
            throw exec_shell_command_exception(
                "Couldn't fork the process");
        case 0: {
            close(out[0]);
            dup2(out[1], STDOUT_FILENO);
            dup2(out[1], STDERR_FILENO);
            const char* const args[] = {"/bin/sh", "-c", cmd.c_str(),
                                        nullptr};
            execv("/bin/sh", const_cast<char* const*>(args));
            _Exit(1);
        }
        default: {
            close(out[1]);
            char readbuffer[buffer_size];
            memset(readbuffer, 0, sizeof(readbuffer));
            int nbytes = buffer_size;
            bool cont = false;
            while (nbytes and
                   std::find(readbuffer, readbuffer + nbytes, EOF) ==
                       readbuffer + nbytes) {
                if (cont) {
                    out_(readbuffer);
                    cont = false;
                }

                nbytes = read(out[0], readbuffer, buffer_size);
                readbuffer[nbytes] = 0;

                if (readbuffer[0] == '\0')
                    cont = true;
                else
                    out_(readbuffer);
            }
        }
    }
}

void exec_shell_command(const std::string& cmd,
                        std::function<void(std::string)> out_,
                        std::function<void(std::string)> err_,
                        size_t buffer_size) {
    int out[2], err[2];

    if (pipe(out) == -1 or pipe(err) == -1) {
        throw exec_shell_command_exception(
            "Couldn't generate a pipe");
    }

    pid_t p_read, p_err;
    if ((p_read = fork()) == -1 or (p_err = fork()) == -1)
        throw exec_shell_command_exception(
            "Couldn't fork the process");
    if (not p_read and p_err)
        _Exit(-1);
    if (not p_read) {
        close(out[0]);
        dup2(out[1], STDOUT_FILENO);
        dup2(err[1], STDERR_FILENO);
        std::vector<const char*> command = {"sh", "-c", cmd.c_str(),
                                            nullptr};
        execv("/bin/sh",
              const_cast<char* const*>(
                  static_cast<const char* const*>(command.data())));
        _Exit(-1);
    } else if (p_err) {
        close(out[0]);
        close(out[1]);
        close(err[1]);
        char readbuffer[buffer_size + 1];
        memset(readbuffer, '\0', buffer_size);
        int nbytes = buffer_size;
        bool cont = false;
        while (nbytes and
               std::find(readbuffer, readbuffer + nbytes, EOF) ==
                   readbuffer + nbytes) {
            if (cont) {
                err_(readbuffer);
                cont = false;
            }

            nbytes = read(err[0], readbuffer, buffer_size);
            readbuffer[nbytes] = 0;
            if (readbuffer[0] == '\0')
                cont = true;
            else
                err_(readbuffer);
        }
    } else {
        close(out[1]);
        close(err[0]);
        close(err[1]);
        char readbuffer[buffer_size + 1];
        memset(readbuffer, '\0', buffer_size);
        int nbytes = buffer_size;
        bool cont = false;
        while (nbytes and
               std::find(readbuffer, readbuffer + nbytes, EOF) ==
                   readbuffer + nbytes) {
            if (cont) {
                out_(readbuffer);
                cont = false;
            }

            nbytes = read(out[0], readbuffer, buffer_size);
            readbuffer[nbytes] = 0;

            if (readbuffer[0] == '\0')
                cont = true;
            else
                out_(readbuffer);
        }
        _Exit(-1);
    }
}

void exec_shell_command(const std::string& cmd, contents& cont) {
    cont.cont.clear();
    cont.x = 0;
    cont.y = 0;
    std::mutex mut;
    bool pb_empty;
    exec_shell_command(cmd,
                       [&cont, &mut, &pb_empty](std::string str) {
                           std::lock_guard<std::mutex> _(mut);
                           if (pb_empty) {
                               pb_empty = false;
                               cont.cont.push_back("");
                           }

                           if ((str.empty()) or
                               (str.size() == 1 and str[0] == '\n'))
                               ;
                           else if (str.find('\n') ==
                                    std::string::npos) {
                               if (cont.cont.empty())
                                   cont.cont.push_back(str);
                               else
                                   cont.cont.back() += str;
                           } else {
                               std::vector<std::string> splitted =
                                   split(str, '\n');
                               auto itr = splitted.begin();
                               if (cont.cont.empty())
                                   cont.cont.push_back(*itr);
                               else
                                   cont.cont.back() += *itr;
                               ++itr;
                               bool contin = false;
                               for (auto end = splitted.end();
                                    itr != end; ++itr) {
                                   if (contin) {
                                       contin = false;
                                       cont.cont.push_back(*itr);
                                   }
                                   if (itr->empty()) {
                                       contin = true;
                                       continue;
                                   }
                                   cont.cont.push_back(*itr);
                               }
                           }

                           if (str.size() and
                               str[str.size() - 1] == '\n')
                               pb_empty = true;
                           print_contents(cont);
                       },
                       1024);
}
}
}
<commit_msg>Format<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include <string.h>
#include <unistd.h>
#include <algorithm>
#include <functional>
#include <mutex>
#include <string>
#include <vector>

#include "../lib.hh"
#include "contents.hh"
#include "file_contents.hh"
#include "operating_system_macros.hh"
#include "print_contents.hh"
#include "split.hh"

namespace vick {
namespace shell_command {

std::string quote_string_windows(const std::string& s) {
    std::string ret;
    ret.reserve(s.size());

    // escape by wrapping double quotes and escaping double quotes
    // inside arg
    ret += '"';
    std::string::const_iterator i = s.begin(), e = s.end(), p;
beg:
    p = i;
    i = std::find(i, e, '"');
    ret.append(p, i);
    if (i != e) {
        ret += '\\';
        ret += '"';
        ++i;
        goto beg;
    }
    ret += '"';

    return ret;
}

std::string quote_string_linux(const std::string& s) {
    std::string ret;
    ret.reserve(s.size());

    // escape by wrapping single quotes and escaping single quotes and
    // raw `\n`s
    ret += '\'';
    std::string::const_iterator i = s.begin(), e = s.end(), p;
beg:
    p = i;
    i = std::find(i, e, '\'');
    ret.append(p, i);
    if (i != e) {
        ret += "'\\''";
        ++i;
        goto beg;
    }
    ret += '\'';

    return ret;
}

std::string quote_string(const std::string& s) {
    if (IS_OS_WIN) {
        return quote_string_windows(s);
    } else {
        return quote_string_linux(s);
    }
}

class exec_shell_command_exception : public std::exception {
    std::string message;

public:
    exec_shell_command_exception(std::string message)
        : message(std::move(message)) {}
    virtual const char* what() const noexcept override {
        return message.c_str();
    }
};

void exec_shell_command(const std::string& cmd, std::string* out_,
                        std::string* err_) {
    *out_ = "";
    *err_ = "";

    int out[2], err[2];

    if (pipe(out) == -1)
        throw exec_shell_command_exception(
            "Couldn't open stdout pipe");
    if (pipe(err) == -1)
        throw exec_shell_command_exception(
            "Coudln't open stderr pipe");

    switch (fork()) {
        case -1:
            throw exec_shell_command_exception(
                "Forking error on stdout");
        case 0: {
            close(out[0]);
            close(err[0]);
            dup2(out[1], STDOUT_FILENO);
            dup2(err[1], STDERR_FILENO);
            system(cmd.c_str());
            _Exit(-1);
        }
        default: {
            close(out[1]);
            close(err[1]);
            char readbuffer[1024];
            memset(readbuffer, '\0', sizeof(readbuffer));
            ssize_t nbytes = sizeof(readbuffer) - 1;
            while (std::find(readbuffer, readbuffer + nbytes, EOF) ==
                       readbuffer + nbytes and
                   nbytes) {
                nbytes =
                    read(out[0], readbuffer, sizeof(readbuffer) - 1);
                readbuffer[nbytes] = 0;
                *out_ += readbuffer;
            }
            close(out[0]);

            memset(readbuffer, '\0', sizeof(readbuffer));
            nbytes = sizeof(readbuffer) - 1;
            while (std::find(readbuffer, readbuffer + nbytes, EOF) ==
                       readbuffer + nbytes and
                   nbytes) {
                nbytes =
                    read(err[0], readbuffer, sizeof(readbuffer) - 1);
                readbuffer[nbytes] = 0;
                *err_ += readbuffer;
            }
        }
    }
}

void exec_shell_command(const std::string& cmd, std::string* out_) {
    *out_ = "";

    int out[2];

    if (pipe(out) == -1) {
        throw exec_shell_command_exception(
            "Couldn't generate a pipe");
    }

    switch (fork()) {
        case -1:
            throw exec_shell_command_exception(
                "Couldn't fork the process");
        case 0: { // pump data into pipe
            close(out[0]);
            dup2(out[1], STDOUT_FILENO);
            dup2(out[1], STDERR_FILENO);
            std::vector<const char*> command = {"sh", "-c",
                                                cmd.c_str(), nullptr};
            execv("/bin/sh", const_cast<char* const*>(
                                 static_cast<const char* const*>(
                                     command.data())));
            _Exit(-1);
        }
        default: { // transfer pipe to param out_
            close(out[1]);
            char readbuffer[1024];
            memset(readbuffer, '\0', sizeof(readbuffer));
            int nbytes = sizeof(readbuffer) - 1;
            while (std::find(readbuffer, readbuffer + nbytes, EOF) ==
                       readbuffer + nbytes and
                   nbytes) {
                nbytes =
                    read(out[0], readbuffer, sizeof(readbuffer) - 1);
                readbuffer[nbytes] = 0;
                *out_ += std::string(readbuffer);
            }
        }
    }
}

void exec_shell_command(const std::string& cmd,
                        std::function<void(std::string)> out_,
                        size_t buffer_size) {
    int out[2];

    if (pipe(out) == -1) {
        throw exec_shell_command_exception(
            "Couldn't generate a pipe");
    }

    switch (fork()) {
        case -1:
            throw exec_shell_command_exception(
                "Couldn't fork the process");
        case 0: {
            close(out[0]);
            dup2(out[1], STDOUT_FILENO);
            dup2(out[1], STDERR_FILENO);
            const char* const args[] = {"/bin/sh", "-c", cmd.c_str(),
                                        nullptr};
            execv("/bin/sh", const_cast<char* const*>(args));
            _Exit(1);
        }
        default: {
            close(out[1]);
            char readbuffer[buffer_size];
            memset(readbuffer, 0, sizeof(readbuffer));
            int nbytes = buffer_size;
            bool cont = false;
            while (nbytes and
                   std::find(readbuffer, readbuffer + nbytes, EOF) ==
                       readbuffer + nbytes) {
                if (cont) {
                    out_(readbuffer);
                    cont = false;
                }

                nbytes = read(out[0], readbuffer, buffer_size);
                readbuffer[nbytes] = 0;

                if (readbuffer[0] == '\0')
                    cont = true;
                else
                    out_(readbuffer);
            }
        }
    }
}

void exec_shell_command(const std::string& cmd,
                        std::function<void(std::string)> out_,
                        std::function<void(std::string)> err_,
                        size_t buffer_size) {
    int out[2], err[2];

    if (pipe(out) == -1 or pipe(err) == -1) {
        throw exec_shell_command_exception(
            "Couldn't generate a pipe");
    }

    pid_t p_read, p_err;
    if ((p_read = fork()) == -1 or (p_err = fork()) == -1)
        throw exec_shell_command_exception(
            "Couldn't fork the process");
    if (not p_read and p_err)
        _Exit(-1);
    if (not p_read) {
        close(out[0]);
        dup2(out[1], STDOUT_FILENO);
        dup2(err[1], STDERR_FILENO);
        std::vector<const char*> command = {"sh", "-c", cmd.c_str(),
                                            nullptr};
        execv("/bin/sh",
              const_cast<char* const*>(
                  static_cast<const char* const*>(command.data())));
        _Exit(-1);
    } else if (p_err) {
        close(out[0]);
        close(out[1]);
        close(err[1]);
        char readbuffer[buffer_size + 1];
        memset(readbuffer, '\0', buffer_size);
        int nbytes = buffer_size;
        bool cont = false;
        while (nbytes and
               std::find(readbuffer, readbuffer + nbytes, EOF) ==
                   readbuffer + nbytes) {
            if (cont) {
                err_(readbuffer);
                cont = false;
            }

            nbytes = read(err[0], readbuffer, buffer_size);
            readbuffer[nbytes] = 0;
            if (readbuffer[0] == '\0')
                cont = true;
            else
                err_(readbuffer);
        }
    } else {
        close(out[1]);
        close(err[0]);
        close(err[1]);
        char readbuffer[buffer_size + 1];
        memset(readbuffer, '\0', buffer_size);
        int nbytes = buffer_size;
        bool cont = false;
        while (nbytes and
               std::find(readbuffer, readbuffer + nbytes, EOF) ==
                   readbuffer + nbytes) {
            if (cont) {
                out_(readbuffer);
                cont = false;
            }

            nbytes = read(out[0], readbuffer, buffer_size);
            readbuffer[nbytes] = 0;

            if (readbuffer[0] == '\0')
                cont = true;
            else
                out_(readbuffer);
        }
        _Exit(-1);
    }
}

void exec_shell_command(const std::string& cmd, contents& cont) {
    cont.cont.clear();
    cont.x = 0;
    cont.y = 0;
    std::mutex mut;
    bool pb_empty;
    exec_shell_command(cmd,
                       [&cont, &mut, &pb_empty](std::string str) {
                           std::lock_guard<std::mutex> _(mut);
                           if (pb_empty) {
                               pb_empty = false;
                               cont.cont.push_back("");
                           }

                           if ((str.empty()) or
                               (str.size() == 1 and str[0] == '\n'))
                               ;
                           else if (str.find('\n') ==
                                    std::string::npos) {
                               if (cont.cont.empty())
                                   cont.cont.push_back(str);
                               else
                                   cont.cont.back() += str;
                           } else {
                               std::vector<std::string> splitted =
                                   split(str, '\n');
                               auto itr = splitted.begin();
                               if (cont.cont.empty())
                                   cont.cont.push_back(*itr);
                               else
                                   cont.cont.back() += *itr;
                               ++itr;
                               bool contin = false;
                               for (auto end = splitted.end();
                                    itr != end; ++itr) {
                                   if (contin) {
                                       contin = false;
                                       cont.cont.push_back(*itr);
                                   }
                                   if (itr->empty()) {
                                       contin = true;
                                       continue;
                                   }
                                   cont.cont.push_back(*itr);
                               }
                           }

                           if (str.size() and
                               str[str.size() - 1] == '\n')
                               pb_empty = true;
                           print_contents(cont);
                       },
                       1024);
}
}
}
<|endoftext|>


<commit_before>#include <XEUI/Box.hpp>

#include <XERenderer/GUI/WLayer.hpp>
#include <XERenderer/GUI/WRectangle.hpp>
#include <iterator>

namespace XE {

Box::Box(WLayer&  layer, Orientation orientation, float spacing ) :
	m_spacing( spacing ),
	m_orientation( orientation )
	,m_layer(layer)
{
	m_shapesContainer.shapes.emplace_back(std::move(std::unique_ptr<WRectangle>(new WRectangle(layer, 0, 0, 0, 0))));
	m_DebugRectangle = static_cast<WRectangle*>(m_shapesContainer.shapes.back().get());
}

Box::~Box()
{

}

Box::Ptr Box::Create(WLayer&  layer, Orientation orientation, float spacing ) {
	return Ptr( new Box( layer, orientation, spacing ) );
}

void Box::draw() {

	//m_DebugRectangle.setBackgroundImage("item_ball_hover.png");
	m_DebugRectangle->setBackground(Ogre::ColourValue::Blue);

	m_DebugRectangle->setPosition(Widget::getPosition());
	m_DebugRectangle->setSize(size);
	//sf::FloatRect req(GetAllocation());
	//sf::FloatRect parentAllocation(GetParent()->GetAllocation());

	//m_Label.setPosition(sf::Vector2f(parentAllocation.left + req.left, parentAllocation.top + req.top));
	//m_Label.setSize(req.width, req.height);
	//m_Label.SetText(GetText());
	//return Context::Get().GetEngine().CreateLabelDrawable( std::dynamic_pointer_cast<const Label>( shared_from_this() ) );
}

const std::string& Box::GetName() const {
	static const std::string name( "Box" );
	return name;
}

void Box::PackEnd( Widget::Ptr widget, bool expand, bool fill ) {
	if( IsChild( widget ) ) {
		return;
	}

	// It's important to create the ChildInfo object first, so that the
	// HandleAdd() method recognized the widget as a correctly packed one.
	m_box_children.push_back( ChildInfo( widget, expand, fill ) );
	Add( widget );

	size.x = std::max(size.x, widget->size.x);
	size.y += widget->size.y;

	AllocateChildren();
}

void Box::PackStart( Widget::Ptr widget, bool expand, bool fill ) {
	if( IsChild( widget ) ) {
		return;
	}

	// It's important to create the ChildInfo object first, so that the
	// HandleAdd() method recognized the widget as a correctly packed one.
	m_box_children.push_front( ChildInfo( widget, expand, fill ) );
	Add( widget );
}

void Box::Pack( Widget::Ptr widget, bool expand, bool fill ) {
	PackEnd( widget, expand, fill );
}

void Box::ReorderChild( Widget::Ptr widget, std::size_t position ) {
	ChildrenCont::iterator iter( std::find( m_box_children.begin(), m_box_children.end(), widget ) );

	if( iter == m_box_children.end() ) {
		return;
	}

	position = std::min( position, m_box_children.size() - 1 );

	ChildrenCont::iterator insertion_point( m_box_children.begin() );
	std::advance( insertion_point, position );
	m_box_children.insert( insertion_point, *iter );
	m_box_children.erase( iter );

	Refresh();
	AllocateChildren();
}

//void Box::HandleRemove( Widget::Ptr child ) {
//	ChildrenCont::iterator iter( std::find( m_box_children.begin(), m_box_children.end(), child ) );
//
//	if( iter != m_box_children.end() ) {
//		m_box_children.erase( iter );
//	}
//
//	Refresh();
//	AllocateChildren();
//}

Box::ChildInfo::ChildInfo( Widget::Ptr widget_, bool expand_, bool fill_ ) :
	widget( widget_ ),
	expand( expand_ ),
	fill( fill_ )
{
}

bool Box::ChildInfo::operator==( const ChildInfo& rhs ) const {
	return widget == rhs.widget;
}

void Box::SetSpacing( float spacing ) {
	m_spacing = spacing;
}

float Box::GetSpacing() const {
	return m_spacing;
}

void Box::setPosition(const sf::Vector2f& pos) {

	Widget::setPosition(pos);

	AllocateChildren();
}

void Box::AllocateChildren() const {
	unsigned int num_expand( 0 );
	unsigned int num_visible( 0 );

	// Count number of visible and expanded children.
	for( const auto& child : m_box_children ) {
		if( !IsChildInteresting( child.widget ) ) {
			continue;
		}

		++num_visible;

		if( child.expand ) {
			++num_expand;
		}
	}

	// Calculate extra width pre expanded widget.
	float extra( 0.f );

	if( num_expand > 0 ) {
		if( m_orientation == Orientation::HORIZONTAL ) {
			extra = std::max( 0.f, size.x - Widget::getPosition().x ) / static_cast<float>( num_expand );
		}
		else {
			extra = std::max( 0.f, size.y - Widget::getPosition().y ) / static_cast<float>( num_expand );
		}
	}

	// Allocate children.
	float gap(0); //KH Context::Get().GetEngine().GetProperty<float>("Gap", shared_from_this()) );
	sf::Vector2f allocation( 0.f, 0.f );
	sf::Vector2f tmpPosition(Widget::getPosition()); // gap, gap );

	for( const auto& child : m_box_children ) {
		if( !IsChildInteresting( child.widget ) ) {
			continue;
		}

		if( m_orientation == Orientation::HORIZONTAL ) {

		/*	allocation.x = child.widget->getPosition().x + ( child.expand ? extra : 0.f );
			allocation.y = size.y - 2 * gap;
*/
			child.widget->setPosition(tmpPosition);
			//child.widget->size(sf::FloatRect(position.x, position.y, allocation.x - (child.expand && !child.fill ? extra : 0.f), allocation.y));
		//	tmpPosition.x += allocation.x + GetSpacing();
			tmpPosition.x += child.widget->size.x + GetSpacing();
		}
		else {
			child.widget->setPosition(tmpPosition);
			tmpPosition.y += child.widget->size.y + GetSpacing();
		}

		--num_visible;
	}
}

bool Box::IsChildInteresting( Widget::PtrConst child ) const {
	return 	child->IsLocallyVisible() &&
		(child->getPosition().x > 0.f || child->size.x > 0.0f) &&
		(child->getPosition().y > 0.f || child->size.y > 0.0f)
	;
}

}
<commit_msg>remove visibility check , no auto reposition<commit_after>#include <XEUI/Box.hpp>

#include <XERenderer/GUI/WLayer.hpp>
#include <XERenderer/GUI/WRectangle.hpp>
#include <iterator>

namespace XE {

Box::Box(WLayer&  layer, Orientation orientation, float spacing ) :
	m_spacing( spacing ),
	m_orientation( orientation )
	,m_layer(layer)
{
	m_shapesContainer.shapes.emplace_back(std::move(std::unique_ptr<WRectangle>(new WRectangle(layer, 0, 0, 0, 0))));
	m_DebugRectangle = static_cast<WRectangle*>(m_shapesContainer.shapes.back().get());
}

Box::~Box()
{

}

Box::Ptr Box::Create(WLayer&  layer, Orientation orientation, float spacing ) {
	return Ptr( new Box( layer, orientation, spacing ) );
}

void Box::draw() {

	//m_DebugRectangle.setBackgroundImage("item_ball_hover.png");
	m_DebugRectangle->setBackground(Ogre::ColourValue::Blue);

	m_DebugRectangle->setPosition(Widget::getPosition());
	m_DebugRectangle->setSize(size);
	//sf::FloatRect req(GetAllocation());
	//sf::FloatRect parentAllocation(GetParent()->GetAllocation());

	//m_Label.setPosition(sf::Vector2f(parentAllocation.left + req.left, parentAllocation.top + req.top));
	//m_Label.setSize(req.width, req.height);
	//m_Label.SetText(GetText());
	//return Context::Get().GetEngine().CreateLabelDrawable( std::dynamic_pointer_cast<const Label>( shared_from_this() ) );
}

const std::string& Box::GetName() const {
	static const std::string name( "Box" );
	return name;
}

void Box::PackEnd( Widget::Ptr widget, bool expand, bool fill ) {
	if( IsChild( widget ) ) {
		return;
	}

	// It's important to create the ChildInfo object first, so that the
	// HandleAdd() method recognized the widget as a correctly packed one.
	m_box_children.push_back( ChildInfo( widget, expand, fill ) );
	Add( widget );

	size.x = std::max(size.x, widget->size.x);
	size.y += widget->size.y;

	AllocateChildren();
}

void Box::PackStart( Widget::Ptr widget, bool expand, bool fill ) {
	if( IsChild( widget ) ) {
		return;
	}

	// It's important to create the ChildInfo object first, so that the
	// HandleAdd() method recognized the widget as a correctly packed one.
	m_box_children.push_front( ChildInfo( widget, expand, fill ) );
	Add( widget );
}

void Box::Pack( Widget::Ptr widget, bool expand, bool fill ) {
	PackEnd( widget, expand, fill );
}

void Box::ReorderChild( Widget::Ptr widget, std::size_t position ) {
	ChildrenCont::iterator iter( std::find( m_box_children.begin(), m_box_children.end(), widget ) );

	if( iter == m_box_children.end() ) {
		return;
	}

	position = std::min( position, m_box_children.size() - 1 );

	ChildrenCont::iterator insertion_point( m_box_children.begin() );
	std::advance( insertion_point, position );
	m_box_children.insert( insertion_point, *iter );
	m_box_children.erase( iter );

	Refresh();
	AllocateChildren();
}

//void Box::HandleRemove( Widget::Ptr child ) {
//	ChildrenCont::iterator iter( std::find( m_box_children.begin(), m_box_children.end(), child ) );
//
//	if( iter != m_box_children.end() ) {
//		m_box_children.erase( iter );
//	}
//
//	Refresh();
//	AllocateChildren();
//}

Box::ChildInfo::ChildInfo( Widget::Ptr widget_, bool expand_, bool fill_ ) :
	widget( widget_ ),
	expand( expand_ ),
	fill( fill_ )
{
}

bool Box::ChildInfo::operator==( const ChildInfo& rhs ) const {
	return widget == rhs.widget;
}

void Box::SetSpacing( float spacing ) {
	m_spacing = spacing;
}

float Box::GetSpacing() const {
	return m_spacing;
}

void Box::setPosition(const sf::Vector2f& pos) {

	Widget::setPosition(pos);

	AllocateChildren();
}

void Box::AllocateChildren() const {
	unsigned int num_expand( 0 );
	unsigned int num_visible( 0 );

	// Count number of visible and expanded children.
	for( const auto& child : m_box_children ) {
		if( !IsChildInteresting( child.widget ) ) {
			continue;
		}

		++num_visible;

		if( child.expand ) {
			++num_expand;
		}
	}

	// Calculate extra width pre expanded widget.
	float extra( 0.f );

	if( num_expand > 0 ) {
		if( m_orientation == Orientation::HORIZONTAL ) {
			extra = std::max( 0.f, size.x - Widget::getPosition().x ) / static_cast<float>( num_expand );
		}
		else {
			extra = std::max( 0.f, size.y - Widget::getPosition().y ) / static_cast<float>( num_expand );
		}
	}

	// Allocate children.
	float gap(0); //KH Context::Get().GetEngine().GetProperty<float>("Gap", shared_from_this()) );
	sf::Vector2f allocation( 0.f, 0.f );
	sf::Vector2f tmpPosition(Widget::getPosition()); // gap, gap );

	for( const auto& child : m_box_children ) {
		if( !IsChildInteresting( child.widget ) ) {
			continue;
		}

		if( m_orientation == Orientation::HORIZONTAL ) {

		/*	allocation.x = child.widget->getPosition().x + ( child.expand ? extra : 0.f );
			allocation.y = size.y - 2 * gap;
*/
			child.widget->setPosition(tmpPosition);
			//child.widget->size(sf::FloatRect(position.x, position.y, allocation.x - (child.expand && !child.fill ? extra : 0.f), allocation.y));
		//	tmpPosition.x += allocation.x + GetSpacing();
			tmpPosition.x += child.widget->size.x + GetSpacing();
		}
		else {
			child.widget->setPosition(tmpPosition);
			tmpPosition.y += child.widget->size.y + GetSpacing();
		}

		--num_visible;
	}
}

bool Box::IsChildInteresting( Widget::PtrConst child ) const {
	return  //	child->IsLocallyVisible() &&
		(child->getPosition().x > 0.f || child->size.x > 0.0f) &&
		(child->getPosition().y > 0.f || child->size.y > 0.0f)
	;
}

}
<|endoftext|>