<commit_before>// Copyright 2010-2014, Google Inc.
// 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 Google Inc. 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 "storage/louds/bit_vector_based_array.h"

#include "base/logging.h"

namespace mozc {
namespace storage {
namespace louds {

namespace {
inline int ReadInt32(const uint8 *data) {
  return *reinterpret_cast<const int32*>(data);
}
}  // namespace

void BitVectorBasedArray::Open(const uint8 *image) {
  const int index_length = ReadInt32(image);
  const int base_length = ReadInt32(image + 4);
  const int step_length = ReadInt32(image + 8);
  // Check 0 padding.
  CHECK_EQ(ReadInt32(image + 12), 0);

  index_.Init(image + 16, index_length);
  base_length_ = base_length;
  step_length_ = step_length;
  data_ = reinterpret_cast<const char*>(image + 16 + index_length);
}

void BitVectorBasedArray::Close() {
  index_.Reset();
  base_length_ = 0;
  step_length_ = 0;
  data_ = 0;
}

const char *BitVectorBasedArray::Get(size_t index, size_t *length) const {
  DCHECK(length);
  const int bit_index = index_.Select0(index + 1);
  const int data_index =
      base_length_ * index + step_length_ * index_.Rank1(bit_index);
  // Linear search.
  int i = bit_index + 1;
  while (index_.Get(i)) {
    ++i;
  }
  *length = base_length_ + step_length_ * (i - bit_index - 1);
  return data_ + data_index;
}

}  // namespace louds
}  // namespace storage
}  // namespace mozc
<commit_msg>Fix 32 bit alignment issue<commit_after>// Copyright 2010-2014, Google Inc.
// 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 Google Inc. 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 "storage/louds/bit_vector_based_array.h"

#include "base/logging.h"

#include <cstring>

namespace mozc {
namespace storage {
namespace louds {

namespace {
inline int ReadInt32(const uint8 *data) {
  int32 ret;
  std::memcpy(&ret, data, sizeof(ret));
  return ret;
}
}  // namespace

void BitVectorBasedArray::Open(const uint8 *image) {
  const int index_length = ReadInt32(image);
  const int base_length = ReadInt32(image + 4);
  const int step_length = ReadInt32(image + 8);
  // Check 0 padding.
  CHECK_EQ(ReadInt32(image + 12), 0);

  index_.Init(image + 16, index_length);
  base_length_ = base_length;
  step_length_ = step_length;
  data_ = reinterpret_cast<const char*>(image + 16 + index_length);
}

void BitVectorBasedArray::Close() {
  index_.Reset();
  base_length_ = 0;
  step_length_ = 0;
  data_ = 0;
}

const char *BitVectorBasedArray::Get(size_t index, size_t *length) const {
  DCHECK(length);
  const int bit_index = index_.Select0(index + 1);
  const int data_index =
      base_length_ * index + step_length_ * index_.Rank1(bit_index);
  // Linear search.
  int i = bit_index + 1;
  while (index_.Get(i)) {
    ++i;
  }
  *length = base_length_ + step_length_ * (i - bit_index - 1);
  return data_ + data_index;
}

}  // namespace louds
}  // namespace storage
}  // namespace mozc
<|endoftext|>


<commit_before>#include <deque>
#include <SFML/Graphics.hpp>

#define WIN_WIDTH 640
#define WIN_HEIGHT 480
#define size_factor 40
#define COLUMNS (WIN_WIDTH / size_factor)
#define ROWS (WIN_HEIGHT / size_factor)

sf::Vector2i handle_input(const sf::Event event) {
    switch (event.key.code) {
        case sf::Keyboard::Left:
        case sf::Keyboard::H:
            return sf::Vector2i{-1, 0};
        case sf::Keyboard::Up:
        case sf::Keyboard::K:
            return sf::Vector2i{0, -1};
        case sf::Keyboard::Right:
        case sf::Keyboard::L:
            return sf::Vector2i{1, 0};
        case sf::Keyboard::Down:
        case sf::Keyboard::J:
            return sf::Vector2i{0, 1};
        default:
            return sf::Vector2i{0, 0};
    }
}

sf::Vector2i clamp(const sf::Vector2i val, const sf::Vector2i boundary) {
    if (val.x == boundary.x)
        return clamp(sf::Vector2i{0, val.y}, boundary);
    else if (val.x < 0)
        return clamp(sf::Vector2i{boundary.x - 1, val.y}, boundary);
    if (val.y == boundary.y)
        return clamp(sf::Vector2i{val.x, 0}, boundary);
    else if (val.y < 0)
        return clamp(sf::Vector2i{val.x, boundary.y - 1}, boundary);
    return val;
}

template<typename Container, class T>
bool contains(const Container & container, const T & t) {
    for (const auto element : container) {
        if (element == t)
            return true;
    }
    return false;
}

struct Board {
    sf::Vector2f cell_size{WIN_WIDTH / COLUMNS, WIN_HEIGHT / ROWS};
    std::deque<sf::Vector2i> snake;
    sf::Vector2i apple;
    sf::Vector2i direction;
    int rng_seed = 0;
};

std::array<sf::Vertex, 4> quad(const sf::Vector2f pos, const sf::Vector2f size,
        const sf::Color color)
{
    const float x = size.x, y = size.y;
    return std::array<sf::Vertex, 4>{
        sf::Vertex{pos, color}, sf::Vertex{pos + sf::Vector2f{x, 0}, color},
        sf::Vertex{pos + size, color},
        sf::Vertex{pos + sf::Vector2f{0, x}, color}};
}

sf::VertexArray vertices(const Board & board) {
    sf::VertexArray vertices{sf::Quads};
    const float pad = 1;
    const auto cell_color = sf::Color{200, 210, 240};
    const auto snake_color = sf::Color{110, 240, 100};
    const auto apple_color = sf::Color{240, 100, 110};
    for (int y = 0; y < ROWS; y++) {
        for (int x = 0; x < COLUMNS; x++) {
            const auto size = board.cell_size;
            const auto pos = sf::Vector2f(x * size.x, y * size.y);
            const auto color = contains(board.snake, sf::Vector2i{x, y})
                             ? snake_color : cell_color;
            for (const auto vertex : quad(pos + sf::Vector2f{pad, pad},
                                     size - sf::Vector2f{2 * pad, 2 * pad},
                                     color))
                vertices.append(vertex);
            if (board.apple == sf::Vector2i{x, y}) {
                for (const auto vert : quad(pos + sf::Vector2f{pad, pad},
                                       size - sf::Vector2f{2 * pad, 2 * pad},
                                       apple_color))
                    vertices.append(vert);
            }
        }
    }
    return vertices;
}

Board init_board(const sf::Vector2i dir) {
    Board board;
    board.direction = dir;
    return board;
}

template<typename T>
Board init_snake(const Board & board, const T t) {
    Board _board = board;
    _board.snake.push_front(t);
    return _board;
}

template<typename T, typename... Args>
Board init_snake(const Board & board, const T t, Args... args) {
    return init_snake(init_snake(board, t), args...);
}

Board init_apple(const Board & board, const std::size_t seed) {
    std::deque<sf::Vector2i> available_cells;
    for (int y = 0; y < ROWS; y++) {
        for (int x = 0; x < COLUMNS; x++) {
            if (!contains(board.snake, sf::Vector2i{x, y}))
                available_cells.emplace_back(x, y);
        }
    }
    std::mt19937 rng;
    rng.seed(seed);
    std::uniform_int_distribution<int> dist(0, available_cells.size() - 1);
    auto _board = board;
    _board.apple = available_cells[dist(rng)];
    return _board;
}

Board update_snake(const Board & board, const sf::Vector2i dir) {
    auto next = board;
    if (board.snake.front() == board.apple) {
        next.rng_seed++;
        next.snake.push_back(next.snake.back()); // counteract pop_back
        return update_snake(init_apple(next, next.rng_seed), dir);
    }
    if (dir != sf::Vector2i{0, 0} && dir != -1 * board.direction)
        next.direction = dir;
    next.snake.push_front(clamp(board.snake.front() + next.direction,
                          sf::Vector2i{COLUMNS, ROWS}));
    next.snake.pop_back();
    return next;
}

bool game_over(const Board & board) {
    for (auto it = ++board.snake.cbegin(); it != board.snake.cend(); it++) {
        if (*it == board.snake.front()) // crashed into itself
            return true;
    }
    return board.snake.size() + 1 == COLUMNS * ROWS; // win
}

int main() {
    sf::RenderWindow window{sf::VideoMode{WIN_WIDTH, WIN_HEIGHT}, "funkysnake"};
    window.setFramerateLimit(60);

START:
    auto board = init_apple(init_snake(init_board(
        sf::Vector2i{1, 0}), // initial direction <- right
        sf::Vector2i{0, 0}), // initial snake segment coordinates
        std::random_device()()); // initialize board seed and apple position

    while (window.isOpen()) {
        sf::Clock clock;
        sf::Vector2i cmd{0, 0};
        while (clock.getElapsedTime() < sf::milliseconds(100)) {
            sf::Event event;
            while (window.pollEvent(event)) {
                if (event.type == sf::Event::KeyPressed) {
                    if (event.key.code == sf::Keyboard::Q
                    || event.key.code == sf::Keyboard::Space
                    || event.key.code == sf::Keyboard::Return)
                        window.close();
                    else if (event.key.code == sf::Keyboard::R)
                        goto START;
                    else
                        cmd = handle_input(event);
                }
            }
            window.clear(sf::Color::White);
            window.draw(vertices(board));
            window.display();
        }
        if (game_over(board))
            goto START;
        else
            board = update_snake(board, cmd);
    }
}
<commit_msg>Refactor update_snake to be simpler.<commit_after>#include <deque>
#include <SFML/Graphics.hpp>

#define WIN_WIDTH 640
#define WIN_HEIGHT 480
#define size_factor 40
#define COLUMNS (WIN_WIDTH / size_factor)
#define ROWS (WIN_HEIGHT / size_factor)

sf::Vector2i handle_input(const sf::Event event) {
    switch (event.key.code) {
        case sf::Keyboard::Left:
        case sf::Keyboard::H:
            return sf::Vector2i{-1, 0};
        case sf::Keyboard::Up:
        case sf::Keyboard::K:
            return sf::Vector2i{0, -1};
        case sf::Keyboard::Right:
        case sf::Keyboard::L:
            return sf::Vector2i{1, 0};
        case sf::Keyboard::Down:
        case sf::Keyboard::J:
            return sf::Vector2i{0, 1};
        default:
            return sf::Vector2i{0, 0};
    }
}

sf::Vector2i clamp(const sf::Vector2i val, const sf::Vector2i boundary) {
    if (val.x == boundary.x)
        return clamp(sf::Vector2i{0, val.y}, boundary);
    else if (val.x < 0)
        return clamp(sf::Vector2i{boundary.x - 1, val.y}, boundary);
    if (val.y == boundary.y)
        return clamp(sf::Vector2i{val.x, 0}, boundary);
    else if (val.y < 0)
        return clamp(sf::Vector2i{val.x, boundary.y - 1}, boundary);
    return val;
}

template<typename Container, class T>
bool contains(const Container & container, const T & t) {
    for (const auto element : container) {
        if (element == t)
            return true;
    }
    return false;
}

struct Board {
    sf::Vector2f cell_size{WIN_WIDTH / COLUMNS, WIN_HEIGHT / ROWS};
    std::deque<sf::Vector2i> snake;
    sf::Vector2i apple;
    sf::Vector2i direction;
    int rng_seed = 0;
};

std::array<sf::Vertex, 4> quad(const sf::Vector2f pos, const sf::Vector2f size,
        const sf::Color color)
{
    return std::array<sf::Vertex, 4>{{
        sf::Vertex{pos, color}, sf::Vertex{pos + sf::Vector2f{size.x,0}, color},
        sf::Vertex{pos + size, color},
        sf::Vertex{pos + sf::Vector2f{0, size.y}, color}}};
}

sf::VertexArray vertices(const Board & board) {
    sf::VertexArray vertices{sf::Quads};
    const float pad = 1;
    const auto cell_color = sf::Color{200, 210, 240};
    const auto snake_color = sf::Color{110, 240, 100};
    const auto apple_color = sf::Color{240, 100, 110};
    for (int y = 0; y < ROWS; y++) {
        for (int x = 0; x < COLUMNS; x++) {
            const auto size = board.cell_size;
            const auto pos = sf::Vector2f(x * size.x, y * size.y);
            const auto color = contains(board.snake, sf::Vector2i{x, y})
                             ? snake_color : cell_color;
            for (const auto vertex : quad(pos + sf::Vector2f{pad, pad},
                                     size - sf::Vector2f{2 * pad, 2 * pad},
                                     color))
                vertices.append(vertex);
            if (board.apple == sf::Vector2i{x, y}) {
                for (const auto vert : quad(pos + sf::Vector2f{pad, pad},
                                       size - sf::Vector2f{2 * pad, 2 * pad},
                                       apple_color))
                    vertices.append(vert);
            }
        }
    }
    return vertices;
}

Board init_board(const sf::Vector2i dir) {
    Board board;
    board.direction = dir;
    return board;
}

template<typename T>
Board init_snake(const Board & board, const T t) {
    Board _board = board;
    _board.snake.push_front(t);
    return _board;
}

template<typename T, typename... Args>
Board init_snake(const Board & board, const T t, Args... args) {
    return init_snake(init_snake(board, t), args...);
}

Board init_apple(const Board & board, const std::size_t seed) {
    std::deque<sf::Vector2i> available_cells;
    for (int y = 0; y < ROWS; y++) {
        for (int x = 0; x < COLUMNS; x++) {
            if (!contains(board.snake, sf::Vector2i{x, y}))
                available_cells.emplace_back(x, y);
        }
    }
    std::mt19937 rng;
    rng.seed(seed);
    std::uniform_int_distribution<int> dist(0, available_cells.size() - 1);
    auto _board = board;
    _board.apple = available_cells[dist(rng)];
    return _board;
}

Board update_snake(const Board & board, const sf::Vector2i dir) {
    auto next = board;
    if (dir != sf::Vector2i{0, 0} && dir != -1 * board.direction)
        next.direction = dir;
    next.snake.push_front(clamp(board.snake.front() + next.direction,
                          sf::Vector2i{COLUMNS, ROWS}));
    if (contains(next.snake, board.apple)) {
        next.rng_seed++;
        next = init_apple(next, next.rng_seed);
    }
    else
        next.snake.pop_back();
    return next;
}

bool game_over(const Board & board) {
    for (auto it = ++board.snake.cbegin(); it != board.snake.cend(); it++) {
        if (*it == board.snake.front()) // crashed into itself
            return true;
    }
    return board.snake.size() + 1 == COLUMNS * ROWS; // win
}

int main() {
    sf::RenderWindow window{sf::VideoMode{WIN_WIDTH, WIN_HEIGHT}, "funkysnake"};
    window.setFramerateLimit(60);

START:
    auto board = init_apple(init_snake(init_board(
        sf::Vector2i{1, 0}), // initial direction <- right
        sf::Vector2i{0, 0}), // initial snake segment coordinates
        std::random_device()()); // initialize board seed and apple position

    while (window.isOpen()) {
        sf::Clock clock;
        sf::Vector2i cmd{0, 0};
        while (clock.getElapsedTime() < sf::milliseconds(100)) {
            sf::Event event;
            while (window.pollEvent(event)) {
                if (event.type == sf::Event::KeyPressed) {
                    if (event.key.code == sf::Keyboard::Q
                    || event.key.code == sf::Keyboard::Space
                    || event.key.code == sf::Keyboard::Return)
                        window.close();
                    else if (event.key.code == sf::Keyboard::R)
                        goto START;
                    else
                        cmd = handle_input(event);
                }
            }
            window.clear(sf::Color::White);
            window.draw(vertices(board));
            window.display();
        }
        if (game_over(board))
            goto START;
        else
            board = update_snake(board, cmd);
    }
}
<|endoftext|>


<commit_before>#pragma once
#include <SDL.h>
#include <sstream>
#include <functional>

namespace sdl
{
  class Error: public std::runtime_error
  {
  public:
    using std::runtime_error::runtime_error;
  };
  class Init
  {
  public:
    template <typename ...Args>
    Init(Args... args)
    {
      auto res = SDL_Init(args...);
      if (res < 0)
      {
        std::ostringstream strm;
        strm << "Init::Init(): " << SDL_GetError();
        throw Error(strm.str());
      }
    }
    ~Init()
    {
      SDL_Quit();
    }
  };
#define SDL_CLASS(X)                                    \
  class X                                               \
  {                                                     \
  public:                                               \
    template <typename ...Args>                         \
      X(Args... args)                                   \
    {                                                   \
      handle = SDL_Create##X(args...);                  \
      if (!handle)                                      \
      {                                                 \
        std::ostringstream strm;                        \
        strm << #X"::"#X"(): " << SDL_GetError();       \
        throw Error(strm.str());                        \
      }                                                 \
    }                                                   \
    X(SDL_##X *handle): handle(handle)                  \
    {}                                                  \
    X(const X&) = delete;                               \
    X&operator=(const X&) = delete;                     \
    ~X()                                                \
    {                                                   \
      SDL_Destroy##X(handle);                           \
    }                                                   \
    SDL_##X *operator&()                                \
    {                                                   \
      return handle;                                    \
    }                                                   \
    const SDL_##X *operator&() const                    \
    {                                                   \
      return handle;                                    \
    }                                                   \
  private:                                              \
    SDL_##X *handle;                                    \
  public:                                               \
    SDL_CLASS_METHOD_LIST                               \
      }

  template <typename R, typename ...Args2, typename ...Args>
  auto callSdl(R (f)(Args2...), Args... args) -> decltype(f(args...))
  {
    return f(args...);
  }

  template <typename R, typename ...Args2, typename ...Args>
  auto callSdl(R *(f)(Args2...), Args... args) -> decltype(f(args...))
  {
    auto r = f(args...);
    if (r == nullptr)
    {
      throw Error(std::string("Error string: ") + SDL_GetError());
    }
    return r;
  }

  template <typename ...Args2, typename ...Args>
  auto callSdl(int (f)(Args2...), Args... args) -> decltype(f(args...))
  {
    auto r = f(args...);
    if (r == -1)
    {
      throw Error("Error code: " + std::to_string(r) + " Error string: " + SDL_GetError());
    }
    return r;
  }

  template <typename ...Args2, typename ...Args>
  auto callSdl(void (f)(Args2...), Args... args) -> decltype(f(args...))
  {
    f(args...);
  }

#define METHOD(X, Y)                                            \
  template <typename ...Args>                                   \
  auto X(Args... args) -> decltype(SDL_##Y(handle, args...))    \
  {                                                             \
    return callSdl(SDL_##Y, handle, args...);                   \
  }

#define SDL_CLASS_METHOD_LIST                           \
  METHOD(glCreateContext, GL_CreateContext);            \
  METHOD(glGetDrawableSize, GL_GetDrawableSize);        \
  METHOD(glMakeCurrent, GL_MakeCurrent);                \
  METHOD(glSwap, GL_SwapWindow);                        \
  METHOD(getBrightness, GetWindowBrightness);           \
  METHOD(getData, GetWindowData);                       \
  METHOD(getDisplayIndex, GetWindowDisplayIndex);       \
  METHOD(getDisplayMode, GetWindowDisplayMode);         \
  METHOD(getFlags, GetWindowFlags);                     \
  METHOD(getGammaRamp, GetWindowGammaRamp);             \
  METHOD(getGrab, GetWindowGrab);                       \
  METHOD(getID, GetWindowID);                           \
  METHOD(getMaximumSize, GetWindowMaximumSize);         \
  METHOD(getMinimumSize, GetWindowMinimumSize);         \
  METHOD(getPixelFormat, GetWindowPixelFormat);         \
  METHOD(getPosition, GetWindowPosition);               \
  METHOD(getSize, GetWindowSize);                       \
  METHOD(getSurface, GetWindowSurface);                 \
  METHOD(getTitle, GetWindowTitle);                     \
  METHOD(hide, HideWindow);                             \
  METHOD(maximize, MaximizeWindow);                     \
  METHOD(minimize, MinimizeWindow);                     \
  METHOD(raise, RaiseWindow);                           \
  METHOD(restore, RestoreWindow);                       \
  METHOD(setBordered, SetWindowBordered);               \
  METHOD(setBrightness, SetWindowBrightness);           \
  METHOD(setData, SetWindowData);                       \
  METHOD(setDisplayMode, SetWindowDisplayMode);         \
  METHOD(setFullscreen, SetWindowFullscreen);           \
  METHOD(setGammaRamp, SetWindowGammaRamp);             \
  METHOD(setGrab, SetWindowGrab);                       \
  METHOD(setHitTest, SetWindowHitTest);                 \
  METHOD(setIcon, SetWindowIcon);                       \
  METHOD(setMaximumSize, SetWindowMaximumSize);         \
  METHOD(setMinimumSize, SetWindowMinimumSize);         \
  METHOD(setPosition, SetWindowPosition);               \
  METHOD(setSize, SetWindowSize);                       \
  METHOD(setTitle, SetWindowTitle);                     \
  METHOD(show, ShowWindow);                             \
  METHOD(updateSurface, UpdateWindowSurface);           \
  METHOD(updateSurfaceRects, UpdateWindowSurfaceRects);

  SDL_CLASS(Window);
#undef SDL_CLASS_METHOD_LIST
#define SDL_CLASS_METHOD_LIST                           \
  METHOD(getDrawBlendMode, GetRenderDrawBlendMode);     \
  METHOD(getDrawColor, GetRenderDrawColor);             \
  METHOD(getDriverInfo, GetRenderDriverInfo);           \
  METHOD(getTarget, GetRenderTarget);                   \
  METHOD(getInfo, GetRendererInfo);                     \
  METHOD(getOutputSize, GetRendererOutputSize);         \
  METHOD(clear, RenderClear);                           \
  METHOD(copy, RenderCopy);                             \
  METHOD(copyEx, RenderCopyEx);                         \
  METHOD(drawLine, RenderDrawLine);                     \
  METHOD(drawLines, RenderDrawLines);                   \
  METHOD(drawPoint, RenderDrawPoint);                   \
  METHOD(drawPoints, RenderDrawPoints);                 \
  METHOD(drawRect, RenderDrawRect);                     \
  METHOD(drawRects, RenderDrawRects);                   \
  METHOD(fillRect, RenderFillRect);                     \
  METHOD(fillRects, RenderFillRects);                   \
  METHOD(getClipRect, RenderGetClipRect);               \
  METHOD(getLogicalSize, RenderGetLogicalSize);         \
  METHOD(getScale, RenderGetScale);                     \
  METHOD(getViewport, RenderGetViewport);               \
  METHOD(isClipEnabled, RenderIsClipEnabled);           \
  METHOD(present, RenderPresent);                       \
  METHOD(readPixels, RenderReadPixels);                 \
  METHOD(setClipRect, RenderSetClipRect);               \
  METHOD(setLogicalSize, RenderSetLogicalSize);         \
  METHOD(setScale, RenderSetScale);                     \
  METHOD(setViewport, RenderSetViewport);               \
  METHOD(targetSupported, RenderTargetSupported);       \
  METHOD(setDrawBlendMode, SetRenderDrawBlendMode);     \
  METHOD(setDrawColor, SetRenderDrawColor);             \
  METHOD(setTarget, SetRenderTarget);
  SDL_CLASS(Renderer);

#undef SDL_CLASS_METHOD_LIST
#define SDL_CLASS_METHOD_LIST                   \
  METHOD(glBind, GL_BindTexture);               \
  METHOD(glUnbind, GL_UnbindTexture);           \
  METHOD(getAlphaMod, GetTextureAlphaMod);      \
  METHOD(getBlendMode, GetTextureBlendMode);    \
  METHOD(getColorMod, GetTextureColorMod);      \
  METHOD(lock, LockTexture);                    \
  METHOD(query, QueryTexture);                  \
  METHOD(setAlphaMod, SetTextureAlphaMod);      \
  METHOD(setBlendMode, SetTextureBlendMode);    \
  METHOD(setColorMod, SetTextureColorMod);      \
  METHOD(unlock, UnlockTexture);                \
  METHOD(update, UpdateTexture);                \
  METHOD(updateYuv, UpdateYUVTexture);
  SDL_CLASS(Texture);

  class Surface
  {
    void checkErrors()
    {
      if (!handle)
      {
        std::ostringstream strm;
        strm << "Surface::Surface(): " << SDL_GetError();
        throw Error(strm.str());
      }
    }
  public:
    Surface(Uint32 flags, int width, int height, int depth,
            Uint32 Rmask, Uint32 Gmask, Uint32 Bmask, Uint32 Amask):
      handle(SDL_CreateRGBSurface(flags, width, height, depth,
                                  Rmask, Gmask, Bmask, Amask))
    {
      checkErrors();
    }
    Surface(SDL_RWops *src, int freesrc):
      handle(SDL_LoadBMP_RW(src, freesrc))
    {
      checkErrors();
    }
    Surface(const std::string &file):
      handle(SDL_LoadBMP(file.c_str()))
    {
      checkErrors();
    }
    Surface(SDL_Surface * src, const SDL_PixelFormat * fmt, Uint32 flags):
      handle(SDL_ConvertSurface(src, fmt, flags))
    {
      checkErrors();
    }
    Surface(SDL_Surface * src, Uint32 pixel_format, Uint32 flags):
      handle(SDL_ConvertSurfaceFormat(src, pixel_format, flags))
    {
      checkErrors();
    }
    Surface(SDL_Surface *handle): handle(handle)
    {}
    Surface(const Surface &) = delete;
    Surface& operator=(const Surface &) = delete;
    ~Surface()
    {
      SDL_FreeSurface(handle);
    }
    SDL_Surface *operator&()
    {
      return handle;
    }
    const SDL_Surface *operator&() const
    {
      return handle;
    }
  private:
    SDL_Surface *handle;
  public:
    METHOD(setPalette, SetSurfacePalette);
    METHOD(lock, LockSurface);
    METHOD(unlock, UnlockSurface);
    METHOD(setColorKey, SetColorKey);
    METHOD(getColorKey, GetColorKey);
    METHOD(setColorMode, SetSurfaceColorMod);
    METHOD(getColorMode, GetSurfaceColorMod);
    METHOD(setClipRect, SetClipRect);
    METHOD(getClipRect, GetClipRect);
    METHOD(fillRect, FillRect);
    METHOD(fillRects, FillRects);
    METHOD(blit, BlitSurface);
    METHOD(softStretch, SoftStretch);
    METHOD(blitScaled, BlitScaled);
  };

  class Audio
  {
  public:
    Audio(const char *device,
          bool iscapture,
          const SDL_AudioSpec *desired,
          SDL_AudioSpec *obtained,
          int allowed_changes,
          std::function<void(Uint8 *stream, int len)> callback = nullptr):
      callback(callback)
    {
      if (desired)
      {
        SDL_AudioSpec tmp;
        tmp = *desired;
        if (callback)
        {
          tmp.callback = Audio::internalCallback;
          tmp.userdata = this;
        }
        else
        {
          tmp.callback = nullptr;
        }
        handle = SDL_OpenAudioDevice(device, iscapture, &tmp, obtained, allowed_changes);
      }
      else
      {
        handle = SDL_OpenAudioDevice(device, iscapture, desired, obtained, allowed_changes);
      }
      if (handle <= 0)
      {
        std::ostringstream strm;
        strm << "Audio::Audio(): " << SDL_GetError();
        throw Error(strm.str());
      }
    }
    Audio(SDL_AudioDeviceID handle): handle(handle)
    {}
    Audio(const Audio&) = delete;
    Audio&operator=(const Audio&) = delete;
    ~Audio()
    {
      SDL_CloseAudioDevice(handle);
    }
    SDL_AudioDeviceID operator&()
    {
      return handle;
    }
    const SDL_AudioDeviceID operator&() const
    {
      return handle;
    }
  private:
    SDL_AudioDeviceID handle;
    std::function<void(Uint8 *stream, int len)> callback;
    static void internalCallback(void *userdata, Uint8 *stream, int len)
    {
      static_cast<Audio *>(userdata)->callback(stream, len);
    }
  public:
    METHOD(clearQueued, ClearQueuedAudio);
    METHOD(getStatus, GetAudioDeviceStatus);
    METHOD(getQueuedSize, GetQueuedAudioSize);
    METHOD(lock, LockAudioDevice);
    METHOD(pause, PauseAudioDevice)
    METHOD(queue, QueueAudio);
    METHOD(unlock, UnlockAudioDevice);
  };
  class EventHandler
  {
  public:
    bool poll()
    {
      SDL_Event e;
      if (SDL_PollEvent(&e))
      {
        handleEvent(e);
        return true;
      }
      else
        return false;
    }
    void wait(int timeout = -1)
    {
      SDL_Event e;
      int res;
      if (timeout >= 0)
        res = SDL_WaitEvent(&e);
      else
        res = SDL_WaitEventTimeout(&e, timeout);
      if (res)
        handleEvent(e);
      else
      {
        std::ostringstream strm;
        strm << "EventHandler::wait(): " << SDL_GetError();
        throw Error(strm.str());
      }
    }
#define SDL_EVENTS                                                      \
    EVENT(SDL_AUDIODEVICEADDED, audioDeviceAdded, adevice);             \
    EVENT(SDL_AUDIODEVICEREMOVED, audioDeviceRemoved, adevice);         \
    EVENT(SDL_CONTROLLERAXISMOTION, controllerAxisMotion, caxis);       \
    EVENT(SDL_CONTROLLERBUTTONDOWN, controllerButtonDown, cbutton);     \
    EVENT(SDL_CONTROLLERBUTTONUP, controllerButtonUp, cbutton);         \
    EVENT(SDL_CONTROLLERDEVICEADDED, controllerDeviceAdded, cdevice);   \
    EVENT(SDL_CONTROLLERDEVICEREMOVED, controllerDeviceRemoved, cdevice); \
    EVENT(SDL_CONTROLLERDEVICEREMAPPED, controllerDeviceRemapped, cdevice); \
    EVENT(SDL_DOLLARGESTURE, dollarGesture, dgesture);                  \
    EVENT(SDL_DOLLARRECORD, dollarRecord, dgesture);                    \
    EVENT(SDL_DROPFILE, dropFile, drop);                                \
    EVENT(SDL_FINGERMOTION, fingerMotion, tfinger);                     \
    EVENT(SDL_FINGERDOWN, fingerDown, tfinger);                         \
    EVENT(SDL_FINGERUP, fingerUp, tfinger);                             \
    EVENT(SDL_KEYDOWN, keyDown, key);                                   \
    EVENT(SDL_KEYUP, keyUp, key);                                       \
    EVENT(SDL_JOYAXISMOTION, joyAxisMotion, jaxis);                     \
    EVENT(SDL_JOYBALLMOTION, joyBallMotion, jball);                     \
    EVENT(SDL_JOYHATMOTION, joyHatMotion, jhat);                        \
    EVENT(SDL_JOYBUTTONDOWN, joyButtonDown, jbutton);                   \
    EVENT(SDL_JOYBUTTONUP, joyButtonUp, jbutton);                       \
    EVENT(SDL_JOYDEVICEADDED, joyDeviceAdded, jdevice);                 \
    EVENT(SDL_JOYDEVICEREMOVED, joyDeviceRemoved, jdevice);             \
    EVENT(SDL_MOUSEMOTION, mouseMotion, motion);                        \
    EVENT(SDL_MOUSEBUTTONDOWN, mouseButtonDown, button);                \
    EVENT(SDL_MOUSEBUTTONUP, mouseButtonUp, button);                    \
    EVENT(SDL_MOUSEWHEEL, mouseWheel, wheel);                           \
    EVENT(SDL_MULTIGESTURE, multiGesture, mgesture);                    \
    EVENT(SDL_QUIT, quit, quit);                                        \
    EVENT(SDL_SYSWMEVENT, sysWmEvent, syswm);                           \
    EVENT(SDL_TEXTEDITING, textEditing, edit);                          \
    EVENT(SDL_TEXTINPUT, textInput, text);                              \
    EVENT(SDL_USEREVENT, userEvent, user);                              \
    EVENT(SDL_WINDOWEVENT, windowEvent, window);

#define EVENT(x, y, z) std::function<void(const decltype(SDL_Event::z) &)> y
    SDL_EVENTS
#undef EVENT
    private:
    void handleEvent(const SDL_Event &e)
    {
      switch (e.type)
      {
#define EVENT(x, y, z)                          \
        case x:                                 \
          if (y)                                \
            y(e.z);                             \
          break;
        SDL_EVENTS
#undef EVENT
      default:
        break;
      }
    }
  };
}
<commit_msg>Add moving constructor and assignment operator<commit_after>#pragma once
#include <SDL.h>
#include <sstream>
#include <functional>

namespace sdl
{
  class Error: public std::runtime_error
  {
  public:
    using std::runtime_error::runtime_error;
  };
  class Init
  {
  public:
    template <typename ...Args>
    Init(Args... args)
    {
      auto res = SDL_Init(args...);
      if (res < 0)
      {
        std::ostringstream strm;
        strm << "Init::Init(): " << SDL_GetError();
        throw Error(strm.str());
      }
    }
    ~Init()
    {
      SDL_Quit();
    }
  };
#define SDL_CLASS(X)                                    \
  class X                                               \
  {                                                     \
  public:                                               \
    template <typename ...Args>                         \
      X(Args... args)                                   \
    {                                                   \
      handle = SDL_Create##X(args...);                  \
      if (!handle)                                      \
      {                                                 \
        std::ostringstream strm;                        \
        strm << #X"::"#X"(): " << SDL_GetError();       \
        throw Error(strm.str());                        \
      }                                                 \
    }                                                   \
    X(SDL_##X *handle): handle(handle)                  \
    {}                                                  \
    X(const X&) = delete;                               \
    X&operator=(const X&) = delete;                     \
    X&operator=(X&&x)                                   \
      {                                                 \
        handle = x.handle;                              \
        x.handle = 0;                                   \
        return *this;                                   \
      }                                                 \
    X(X&& x):                                           \
      handle(x.handle)                                  \
    {                                                   \
      x.handle = 0;                                     \
    }                                                   \
    ~X()                                                \
    {                                                   \
      if (handle)                                       \
        SDL_Destroy##X(handle);                         \
    }                                                   \
    SDL_##X *operator&()                                \
    {                                                   \
      return handle;                                    \
    }                                                   \
    const SDL_##X *operator&() const                    \
    {                                                   \
      return handle;                                    \
    }                                                   \
  private:                                              \
    SDL_##X *handle;                                    \
  public:                                               \
    SDL_CLASS_METHOD_LIST                               \
      }

  template <typename R, typename ...Args2, typename ...Args>
  auto callSdl(R (f)(Args2...), Args... args) -> decltype(f(args...))
  {
    return f(args...);
  }

  template <typename R, typename ...Args2, typename ...Args>
  auto callSdl(R *(f)(Args2...), Args... args) -> decltype(f(args...))
  {
    auto r = f(args...);
    if (r == nullptr)
    {
      throw Error(std::string("Error string: ") + SDL_GetError());
    }
    return r;
  }

  template <typename ...Args2, typename ...Args>
  auto callSdl(int (f)(Args2...), Args... args) -> decltype(f(args...))
  {
    auto r = f(args...);
    if (r == -1)
    {
      throw Error("Error code: " + std::to_string(r) + " Error string: " + SDL_GetError());
    }
    return r;
  }

  template <typename ...Args2, typename ...Args>
  auto callSdl(void (f)(Args2...), Args... args) -> decltype(f(args...))
  {
    f(args...);
  }

#define METHOD(X, Y)                                            \
  template <typename ...Args>                                   \
  auto X(Args... args) -> decltype(SDL_##Y(handle, args...))    \
  {                                                             \
    return callSdl(SDL_##Y, handle, args...);                   \
  }

#define SDL_CLASS_METHOD_LIST                           \
  METHOD(glCreateContext, GL_CreateContext);            \
  METHOD(glGetDrawableSize, GL_GetDrawableSize);        \
  METHOD(glMakeCurrent, GL_MakeCurrent);                \
  METHOD(glSwap, GL_SwapWindow);                        \
  METHOD(getBrightness, GetWindowBrightness);           \
  METHOD(getData, GetWindowData);                       \
  METHOD(getDisplayIndex, GetWindowDisplayIndex);       \
  METHOD(getDisplayMode, GetWindowDisplayMode);         \
  METHOD(getFlags, GetWindowFlags);                     \
  METHOD(getGammaRamp, GetWindowGammaRamp);             \
  METHOD(getGrab, GetWindowGrab);                       \
  METHOD(getID, GetWindowID);                           \
  METHOD(getMaximumSize, GetWindowMaximumSize);         \
  METHOD(getMinimumSize, GetWindowMinimumSize);         \
  METHOD(getPixelFormat, GetWindowPixelFormat);         \
  METHOD(getPosition, GetWindowPosition);               \
  METHOD(getSize, GetWindowSize);                       \
  METHOD(getSurface, GetWindowSurface);                 \
  METHOD(getTitle, GetWindowTitle);                     \
  METHOD(hide, HideWindow);                             \
  METHOD(maximize, MaximizeWindow);                     \
  METHOD(minimize, MinimizeWindow);                     \
  METHOD(raise, RaiseWindow);                           \
  METHOD(restore, RestoreWindow);                       \
  METHOD(setBordered, SetWindowBordered);               \
  METHOD(setBrightness, SetWindowBrightness);           \
  METHOD(setData, SetWindowData);                       \
  METHOD(setDisplayMode, SetWindowDisplayMode);         \
  METHOD(setFullscreen, SetWindowFullscreen);           \
  METHOD(setGammaRamp, SetWindowGammaRamp);             \
  METHOD(setGrab, SetWindowGrab);                       \
  METHOD(setHitTest, SetWindowHitTest);                 \
  METHOD(setIcon, SetWindowIcon);                       \
  METHOD(setMaximumSize, SetWindowMaximumSize);         \
  METHOD(setMinimumSize, SetWindowMinimumSize);         \
  METHOD(setPosition, SetWindowPosition);               \
  METHOD(setSize, SetWindowSize);                       \
  METHOD(setTitle, SetWindowTitle);                     \
  METHOD(show, ShowWindow);                             \
  METHOD(updateSurface, UpdateWindowSurface);           \
  METHOD(updateSurfaceRects, UpdateWindowSurfaceRects);

  SDL_CLASS(Window);
#undef SDL_CLASS_METHOD_LIST
#define SDL_CLASS_METHOD_LIST                           \
  METHOD(getDrawBlendMode, GetRenderDrawBlendMode);     \
  METHOD(getDrawColor, GetRenderDrawColor);             \
  METHOD(getDriverInfo, GetRenderDriverInfo);           \
  METHOD(getTarget, GetRenderTarget);                   \
  METHOD(getInfo, GetRendererInfo);                     \
  METHOD(getOutputSize, GetRendererOutputSize);         \
  METHOD(clear, RenderClear);                           \
  METHOD(copy, RenderCopy);                             \
  METHOD(copyEx, RenderCopyEx);                         \
  METHOD(drawLine, RenderDrawLine);                     \
  METHOD(drawLines, RenderDrawLines);                   \
  METHOD(drawPoint, RenderDrawPoint);                   \
  METHOD(drawPoints, RenderDrawPoints);                 \
  METHOD(drawRect, RenderDrawRect);                     \
  METHOD(drawRects, RenderDrawRects);                   \
  METHOD(fillRect, RenderFillRect);                     \
  METHOD(fillRects, RenderFillRects);                   \
  METHOD(getClipRect, RenderGetClipRect);               \
  METHOD(getLogicalSize, RenderGetLogicalSize);         \
  METHOD(getScale, RenderGetScale);                     \
  METHOD(getViewport, RenderGetViewport);               \
  METHOD(isClipEnabled, RenderIsClipEnabled);           \
  METHOD(present, RenderPresent);                       \
  METHOD(readPixels, RenderReadPixels);                 \
  METHOD(setClipRect, RenderSetClipRect);               \
  METHOD(setLogicalSize, RenderSetLogicalSize);         \
  METHOD(setScale, RenderSetScale);                     \
  METHOD(setViewport, RenderSetViewport);               \
  METHOD(targetSupported, RenderTargetSupported);       \
  METHOD(setDrawBlendMode, SetRenderDrawBlendMode);     \
  METHOD(setDrawColor, SetRenderDrawColor);             \
  METHOD(setTarget, SetRenderTarget);
  SDL_CLASS(Renderer);

#undef SDL_CLASS_METHOD_LIST
#define SDL_CLASS_METHOD_LIST                                   \
  Texture(SDL_Renderer *renderer, SDL_Surface *surface):        \
    handle(SDL_CreateTextureFromSurface(renderer, surface))     \
  {                                                             \
    if (!handle)                                                \
    {                                                           \
      std::ostringstream strm;                                  \
      strm << "Texture::Texture(): " << SDL_GetError();         \
      throw Error(strm.str());                                  \
    }                                                           \
  }                                                             \
  METHOD(glBind, GL_BindTexture);                               \
  METHOD(glUnbind, GL_UnbindTexture);                           \
  METHOD(getAlphaMod, GetTextureAlphaMod);                      \
  METHOD(getBlendMode, GetTextureBlendMode);                    \
  METHOD(getColorMod, GetTextureColorMod);                      \
  METHOD(lock, LockTexture);                                    \
  METHOD(query, QueryTexture);                                  \
  METHOD(setAlphaMod, SetTextureAlphaMod);                      \
  METHOD(setBlendMode, SetTextureBlendMode);                    \
  METHOD(setColorMod, SetTextureColorMod);                      \
  METHOD(unlock, UnlockTexture);                                \
  METHOD(update, UpdateTexture);                                \
  METHOD(updateYuv, UpdateYUVTexture);
  SDL_CLASS(Texture);

  class Surface
  {
    void checkErrors()
    {
      if (!handle)
      {
        std::ostringstream strm;
        strm << "Surface::Surface(): " << SDL_GetError();
        throw Error(strm.str());
      }
    }
  public:
    Surface(Uint32 flags, int width, int height, int depth,
            Uint32 Rmask, Uint32 Gmask, Uint32 Bmask, Uint32 Amask):
      handle(SDL_CreateRGBSurface(flags, width, height, depth,
                                  Rmask, Gmask, Bmask, Amask))
    {
      checkErrors();
    }
    Surface(SDL_RWops *src, int freesrc):
      handle(SDL_LoadBMP_RW(src, freesrc))
    {
      checkErrors();
    }
    Surface(const std::string &file):
      handle(SDL_LoadBMP(file.c_str()))
    {
      checkErrors();
    }
    Surface(SDL_Surface * src, const SDL_PixelFormat * fmt, Uint32 flags):
      handle(SDL_ConvertSurface(src, fmt, flags))
    {
      checkErrors();
    }
    Surface(SDL_Surface * src, Uint32 pixel_format, Uint32 flags):
      handle(SDL_ConvertSurfaceFormat(src, pixel_format, flags))
    {
      checkErrors();
    }
    Surface(SDL_Surface *handle): handle(handle)
    {}
    Surface(const Surface &) = delete;
    Surface& operator=(const Surface &) = delete;
    ~Surface()
    {
      SDL_FreeSurface(handle);
    }
    SDL_Surface *operator&()
    {
      return handle;
    }
    const SDL_Surface *operator&() const
    {
      return handle;
    }
  private:
    SDL_Surface *handle;
  public:
    METHOD(setPalette, SetSurfacePalette);
    METHOD(lock, LockSurface);
    METHOD(unlock, UnlockSurface);
    METHOD(setColorKey, SetColorKey);
    METHOD(getColorKey, GetColorKey);
    METHOD(setColorMode, SetSurfaceColorMod);
    METHOD(getColorMode, GetSurfaceColorMod);
    METHOD(setClipRect, SetClipRect);
    METHOD(getClipRect, GetClipRect);
    METHOD(fillRect, FillRect);
    METHOD(fillRects, FillRects);
    METHOD(blit, BlitSurface);
    METHOD(softStretch, SoftStretch);
    METHOD(blitScaled, BlitScaled);
  };

  class Audio
  {
  public:
    Audio(const char *device,
          bool iscapture,
          const SDL_AudioSpec *desired,
          SDL_AudioSpec *obtained,
          int allowed_changes,
          std::function<void(Uint8 *stream, int len)> callback = nullptr):
      callback(callback)
    {
      if (desired)
      {
        SDL_AudioSpec tmp;
        tmp = *desired;
        if (callback)
        {
          tmp.callback = Audio::internalCallback;
          tmp.userdata = this;
        }
        else
        {
          tmp.callback = nullptr;
        }
        handle = SDL_OpenAudioDevice(device, iscapture, &tmp, obtained, allowed_changes);
      }
      else
      {
        handle = SDL_OpenAudioDevice(device, iscapture, desired, obtained, allowed_changes);
      }
      if (handle <= 0)
      {
        std::ostringstream strm;
        strm << "Audio::Audio(): " << SDL_GetError();
        throw Error(strm.str());
      }
    }
    Audio(SDL_AudioDeviceID handle): handle(handle)
    {}
    Audio(const Audio&) = delete;
    Audio&operator=(const Audio&) = delete;
    ~Audio()
    {
      SDL_CloseAudioDevice(handle);
    }
    SDL_AudioDeviceID operator&()
    {
      return handle;
    }
    const SDL_AudioDeviceID operator&() const
    {
      return handle;
    }
  private:
    SDL_AudioDeviceID handle;
    std::function<void(Uint8 *stream, int len)> callback;
    static void internalCallback(void *userdata, Uint8 *stream, int len)
    {
      static_cast<Audio *>(userdata)->callback(stream, len);
    }
  public:
    METHOD(clearQueued, ClearQueuedAudio);
    METHOD(getStatus, GetAudioDeviceStatus);
    METHOD(getQueuedSize, GetQueuedAudioSize);
    METHOD(lock, LockAudioDevice);
    METHOD(pause, PauseAudioDevice)
    METHOD(queue, QueueAudio);
    METHOD(unlock, UnlockAudioDevice);
  };
  class EventHandler
  {
  public:
    bool poll()
    {
      SDL_Event e;
      if (SDL_PollEvent(&e))
      {
        handleEvent(e);
        return true;
      }
      else
        return false;
    }
    void wait(int timeout = -1)
    {
      SDL_Event e;
      int res;
      if (timeout >= 0)
        res = SDL_WaitEvent(&e);
      else
        res = SDL_WaitEventTimeout(&e, timeout);
      if (res)
        handleEvent(e);
      else
      {
        std::ostringstream strm;
        strm << "EventHandler::wait(): " << SDL_GetError();
        throw Error(strm.str());
      }
    }
#define SDL_EVENTS                                                      \
    EVENT(SDL_AUDIODEVICEADDED, audioDeviceAdded, adevice);             \
    EVENT(SDL_AUDIODEVICEREMOVED, audioDeviceRemoved, adevice);         \
    EVENT(SDL_CONTROLLERAXISMOTION, controllerAxisMotion, caxis);       \
    EVENT(SDL_CONTROLLERBUTTONDOWN, controllerButtonDown, cbutton);     \
    EVENT(SDL_CONTROLLERBUTTONUP, controllerButtonUp, cbutton);         \
    EVENT(SDL_CONTROLLERDEVICEADDED, controllerDeviceAdded, cdevice);   \
    EVENT(SDL_CONTROLLERDEVICEREMOVED, controllerDeviceRemoved, cdevice); \
    EVENT(SDL_CONTROLLERDEVICEREMAPPED, controllerDeviceRemapped, cdevice); \
    EVENT(SDL_DOLLARGESTURE, dollarGesture, dgesture);                  \
    EVENT(SDL_DOLLARRECORD, dollarRecord, dgesture);                    \
    EVENT(SDL_DROPFILE, dropFile, drop);                                \
    EVENT(SDL_FINGERMOTION, fingerMotion, tfinger);                     \
    EVENT(SDL_FINGERDOWN, fingerDown, tfinger);                         \
    EVENT(SDL_FINGERUP, fingerUp, tfinger);                             \
    EVENT(SDL_KEYDOWN, keyDown, key);                                   \
    EVENT(SDL_KEYUP, keyUp, key);                                       \
    EVENT(SDL_JOYAXISMOTION, joyAxisMotion, jaxis);                     \
    EVENT(SDL_JOYBALLMOTION, joyBallMotion, jball);                     \
    EVENT(SDL_JOYHATMOTION, joyHatMotion, jhat);                        \
    EVENT(SDL_JOYBUTTONDOWN, joyButtonDown, jbutton);                   \
    EVENT(SDL_JOYBUTTONUP, joyButtonUp, jbutton);                       \
    EVENT(SDL_JOYDEVICEADDED, joyDeviceAdded, jdevice);                 \
    EVENT(SDL_JOYDEVICEREMOVED, joyDeviceRemoved, jdevice);             \
    EVENT(SDL_MOUSEMOTION, mouseMotion, motion);                        \
    EVENT(SDL_MOUSEBUTTONDOWN, mouseButtonDown, button);                \
    EVENT(SDL_MOUSEBUTTONUP, mouseButtonUp, button);                    \
    EVENT(SDL_MOUSEWHEEL, mouseWheel, wheel);                           \
    EVENT(SDL_MULTIGESTURE, multiGesture, mgesture);                    \
    EVENT(SDL_QUIT, quit, quit);                                        \
    EVENT(SDL_SYSWMEVENT, sysWmEvent, syswm);                           \
    EVENT(SDL_TEXTEDITING, textEditing, edit);                          \
    EVENT(SDL_TEXTINPUT, textInput, text);                              \
    EVENT(SDL_USEREVENT, userEvent, user);                              \
    EVENT(SDL_WINDOWEVENT, windowEvent, window);

#define EVENT(x, y, z) std::function<void(const decltype(SDL_Event::z) &)> y
    SDL_EVENTS
#undef EVENT
    private:
    void handleEvent(const SDL_Event &e)
    {
      switch (e.type)
      {
#define EVENT(x, y, z)                          \
        case x:                                 \
          if (y)                                \
            y(e.z);                             \
          break;
        SDL_EVENTS
#undef EVENT
      default:
        break;
      }
    }
  };
}
<|endoftext|>


<commit_before>/* Copyright (c) 2013-2015 Jeffrey Pfau
 *
 * 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 "IOViewer.h"

#include "GameController.h"

#include <QFontDatabase>
#include <QGridLayout>
#include <QSpinBox>

extern "C" {
#include "gba/io.h"
}

using namespace QGBA;

QList<IOViewer::RegisterDescription> IOViewer::s_registers;

const QList<IOViewer::RegisterDescription>& IOViewer::registerDescriptions() {
	if (!s_registers.isEmpty()) {
		return s_registers;
	}
	// 0x04000000: DISPCNT
	s_registers.append({
		{ tr("Background mode"), 0, 3, {
			tr("Mode 0: 4 tile layers"),
			tr("Mode 1: 2 tile layers + 1 rotated/scaled tile layer"),
			tr("Mode 2: 2 rotated/scaled tile layers"),
			tr("Mode 3: Full 15-bit bitmap"),
			tr("Mode 4: Full 8-bit bitmap"),
			tr("Mode 5: Small 15-bit bitmap"),
			QString(),
			QString()
		} },
		{ tr("CGB Mode"), 3, 1, true },
		{ tr("Frame select"), 4 },
		{ tr("Unlocked HBlank"), 5 },
		{ tr("Linear OBJ tile mapping"), 6 },
		{ tr("Force blank screen"), 7 },
		{ tr("Enable background 0"), 8 },
		{ tr("Enable background 1"), 9 },
		{ tr("Enable background 2"), 10 },
		{ tr("Enable background 3"), 11 },
		{ tr("Enable OBJ"), 12 },
		{ tr("Enable Window 0"), 13 },
		{ tr("Enable Window 1"), 14 },
		{ tr("Enable OBJ Window"), 15 },
	});
	// 0x04000002: Green swap (undocumented and unimplemented)
	s_registers.append(RegisterDescription());
	// 0x04000004: DISPSTAT
	s_registers.append({
		{ tr("Currently in VBlank"), 0, 1, true },
		{ tr("Currently in HBlank"), 1, 1, true },
		{ tr("Currently in VCounter"), 2, 1, true },
		{ tr("Enable VBlank IRQ generation"), 3 },
		{ tr("Enable HBlank IRQ generation"), 4 },
		{ tr("Enable VCounter IRQ generation"), 5 },
		{ tr("VCounter scanline"), 8, 8 },
	});
	// 0x04000006: VCOUNT
	s_registers.append({
		{ tr("Current scanline"), 0, 8, true },
	});
	// 0x04000008: BG0CNT
	s_registers.append({
		{ tr("Priority"), 0, 2 },
		{ tr("Tile data base (* 16kB)"), 2, 2 },
		{ tr("Enable mosaic"), 6 },
		{ tr("Enable 256-color"), 7 },
		{ tr("Tile map base (* 2kB)"), 8, 5 },
		{ tr("Background dimensions"), 14, 2 },
	});
	// 0x0400000A: BG1CNT
	s_registers.append({
		{ tr("Priority"), 0, 2 },
		{ tr("Tile data base (* 16kB)"), 2, 2 },
		{ tr("Enable mosaic"), 6 },
		{ tr("Enable 256-color"), 7 },
		{ tr("Tile map base (* 2kB)"), 8, 5 },
		{ tr("Background dimensions"), 14, 2 },
	});
	// 0x0400000C: BG2CNT
	s_registers.append({
		{ tr("Priority"), 0, 2 },
		{ tr("Tile data base (* 16kB)"), 2, 2 },
		{ tr("Enable mosaic"), 6 },
		{ tr("Enable 256-color"), 7 },
		{ tr("Tile map base (* 2kB)"), 8, 5 },
		{ tr("Overflow wraps"), 13 },
		{ tr("Background dimensions"), 14, 2 },
	});
	// 0x0400000E: BG3CNT
	s_registers.append({
		{ tr("Priority"), 0, 2 },
		{ tr("Tile data base (* 16kB)"), 2, 2 },
		{ tr("Enable mosaic"), 6 },
		{ tr("Enable 256-color"), 7 },
		{ tr("Tile map base (* 2kB)"), 8, 5 },
		{ tr("Overflow wraps"), 13 },
		{ tr("Background dimensions"), 14, 2 },
	});
	// 0x04000010: BG0HOFS
	s_registers.append({
		{ tr("Horizontal offset"), 0, 9 },
	});
	// 0x04000012: BG0VOFS
	s_registers.append({
		{ tr("Vertical offset"), 0, 9 },
	});
	// 0x04000014: BG1HOFS
	s_registers.append({
		{ tr("Horizontal offset"), 0, 9 },
	});
	// 0x04000016: BG1VOFS
	s_registers.append({
		{ tr("Vertical offset"), 0, 9 },
	});
	// 0x04000018: BG2HOFS
	s_registers.append({
		{ tr("Horizontal offset"), 0, 9 },
	});
	// 0x0400001A: BG2VOFS
	s_registers.append({
		{ tr("Vertical offset"), 0, 9 },
	});
	// 0x0400001C: BG3HOFS
	s_registers.append({
		{ tr("Horizontal offset"), 0, 9 },
	});
	// 0x0400001E: BG3VOFS
	s_registers.append({
		{ tr("Vertical offset"), 0, 9 },
	});
	return s_registers;
}

IOViewer::IOViewer(GameController* controller, QWidget* parent)
	: QDialog(parent)
	, m_controller(controller)
{
	m_ui.setupUi(this);

	for (unsigned i = 0; i < REG_MAX >> 1; ++i) {
		const char* reg = GBAIORegisterNames[i];
		if (!reg) {
			continue;
		}
		m_ui.regSelect->addItem("0x0400" + QString("%1: %2").arg(i << 1, 4, 16, QChar('0')).toUpper().arg(reg), i << 1);
	}

	const QFont font = QFontDatabase::systemFont(QFontDatabase::FixedFont);
	m_ui.regValue->setFont(font);

	connect(m_ui.buttonBox, SIGNAL(clicked(QAbstractButton*)), this, SLOT(buttonPressed(QAbstractButton*)));
	connect(m_ui.buttonBox, SIGNAL(rejected()), this, SLOT(close()));
	connect(m_ui.regSelect, SIGNAL(currentIndexChanged(int)), this, SLOT(selectRegister()));

	m_b[0] = m_ui.b0;
	m_b[1] = m_ui.b1;
	m_b[2] = m_ui.b2;
	m_b[3] = m_ui.b3;
	m_b[4] = m_ui.b4;
	m_b[5] = m_ui.b5;
	m_b[6] = m_ui.b6;
	m_b[7] = m_ui.b7;
	m_b[8] = m_ui.b8;
	m_b[9] = m_ui.b9;
	m_b[10] = m_ui.bA;
	m_b[11] = m_ui.bB;
	m_b[12] = m_ui.bC;
	m_b[13] = m_ui.bD;
	m_b[14] = m_ui.bE;
	m_b[15] = m_ui.bF;

	for (int i = 0; i < 16; ++i) {
		connect(m_b[i], SIGNAL(toggled(bool)), this, SLOT(bitFlipped()));
	}

	selectRegister(0);
}

void IOViewer::updateRegister() {
	m_value = 0;
	uint16_t value = 0;
	m_controller->threadInterrupt();
	if (m_controller->isLoaded()) {
		value = GBAIORead(m_controller->thread()->gba, m_register);
	}
	m_controller->threadContinue();

	for (int i = 0; i < 16; ++i) {
		m_b[i]->setChecked(value & (1 << i) ? Qt::Checked : Qt::Unchecked);
	}
	m_value = value;
}

void IOViewer::bitFlipped() {
	m_value = 0;
	for (int i = 0; i < 16; ++i) {
		m_value |= m_b[i]->isChecked() << i;
	}
	m_ui.regValue->setText("0x" + QString("%1").arg(m_value, 4, 16, QChar('0')).toUpper());
}

void IOViewer::writeback() {
	m_controller->threadInterrupt();
	if (m_controller->isLoaded()) {
		GBAIOWrite(m_controller->thread()->gba, m_register, m_value);
	}
	m_controller->threadContinue();
	updateRegister();
}

void IOViewer::selectRegister(unsigned address) {
	m_register = address;
	QGridLayout* box = static_cast<QGridLayout*>(m_ui.regDescription->layout());
	if (box) {
		// I can't believe there isn't a real way to do this...
		while (!box->isEmpty()) {
			QLayoutItem* item = box->takeAt(0);
			if (item->widget()) {
				delete item->widget();
			}
			delete item;
		}
	} else {
		box = new QGridLayout;
	}
	if (registerDescriptions().count() > address >> 1) {
		// TODO: Remove the check when done filling in register information
		const RegisterDescription& description = registerDescriptions().at(address >> 1);
		int i = 0;
		for (const RegisterItem& ri : description) {
			QLabel* label = new QLabel(ri.description);
			box->addWidget(label, i, 0);
			if (ri.size == 1) {
				QCheckBox* check = new QCheckBox;
				check->setEnabled(!ri.readonly);
				box->addWidget(check, i, 1, Qt::AlignRight);
				connect(check, SIGNAL(toggled(bool)), m_b[ri.start], SLOT(setChecked(bool)));
				connect(m_b[ri.start], SIGNAL(toggled(bool)), check, SLOT(setChecked(bool)));
			} else if (ri.items.empty()) {
				QSpinBox* sbox = new QSpinBox;
				sbox->setEnabled(!ri.readonly);
				sbox->setMaximum((1 << ri.size) - 1);
				box->addWidget(sbox, i, 1, Qt::AlignRight);
				for (int b = 0; b < ri.size; ++b) {
					connect(sbox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), [this, b, &ri](int v) {
						m_b[ri.start + b]->setChecked(v & (1 << b));
					});
					auto connection = connect(m_b[ri.start + b], &QCheckBox::toggled, [sbox, this, b](bool t) {
						int v = sbox->value() & ~(1 << b);
						v |= t << b;
						bool signalsBlocked = sbox->blockSignals(true);
						sbox->setValue(v);
						sbox->blockSignals(signalsBlocked);
					});
					connect(sbox, &QObject::destroyed, [connection, this, b, &ri]() {
						m_b[ri.start + b]->disconnect(connection);
					});
				}
			} else {

			}
			++i;
		}
	}
	m_ui.regDescription->setLayout(box);
	updateRegister();
}

void IOViewer::selectRegister() {
	selectRegister(m_ui.regSelect->currentData().toUInt());
}

void IOViewer::buttonPressed(QAbstractButton* button) {
	switch (m_ui.buttonBox->standardButton(button)) {
	case QDialogButtonBox::Reset:
		updateRegister();
	 	break;
	case QDialogButtonBox::Apply:
	 	writeback();
	 	break;
	default:
		break;
	}
}
<commit_msg>Qt: I/O viewer option selection<commit_after>/* Copyright (c) 2013-2015 Jeffrey Pfau
 *
 * 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 "IOViewer.h"

#include "GameController.h"

#include <QFontDatabase>
#include <QGridLayout>
#include <QRadioButton>
#include <QSpinBox>

extern "C" {
#include "gba/io.h"
}

using namespace QGBA;

QList<IOViewer::RegisterDescription> IOViewer::s_registers;

const QList<IOViewer::RegisterDescription>& IOViewer::registerDescriptions() {
	if (!s_registers.isEmpty()) {
		return s_registers;
	}
	// 0x04000000: DISPCNT
	s_registers.append({
		{ tr("Background mode"), 0, 3, {
			tr("Mode 0: 4 tile layers"),
			tr("Mode 1: 2 tile layers + 1 rotated/scaled tile layer"),
			tr("Mode 2: 2 rotated/scaled tile layers"),
			tr("Mode 3: Full 15-bit bitmap"),
			tr("Mode 4: Full 8-bit bitmap"),
			tr("Mode 5: Small 15-bit bitmap"),
			QString(),
			QString()
		} },
		{ tr("CGB Mode"), 3, 1, true },
		{ tr("Frame select"), 4 },
		{ tr("Unlocked HBlank"), 5 },
		{ tr("Linear OBJ tile mapping"), 6 },
		{ tr("Force blank screen"), 7 },
		{ tr("Enable background 0"), 8 },
		{ tr("Enable background 1"), 9 },
		{ tr("Enable background 2"), 10 },
		{ tr("Enable background 3"), 11 },
		{ tr("Enable OBJ"), 12 },
		{ tr("Enable Window 0"), 13 },
		{ tr("Enable Window 1"), 14 },
		{ tr("Enable OBJ Window"), 15 },
	});
	// 0x04000002: Green swap (undocumented and unimplemented)
	s_registers.append(RegisterDescription());
	// 0x04000004: DISPSTAT
	s_registers.append({
		{ tr("Currently in VBlank"), 0, 1, true },
		{ tr("Currently in HBlank"), 1, 1, true },
		{ tr("Currently in VCounter"), 2, 1, true },
		{ tr("Enable VBlank IRQ generation"), 3 },
		{ tr("Enable HBlank IRQ generation"), 4 },
		{ tr("Enable VCounter IRQ generation"), 5 },
		{ tr("VCounter scanline"), 8, 8 },
	});
	// 0x04000006: VCOUNT
	s_registers.append({
		{ tr("Current scanline"), 0, 8, true },
	});
	// 0x04000008: BG0CNT
	s_registers.append({
		{ tr("Priority"), 0, 2 },
		{ tr("Tile data base (* 16kB)"), 2, 2 },
		{ tr("Enable mosaic"), 6 },
		{ tr("Enable 256-color"), 7 },
		{ tr("Tile map base (* 2kB)"), 8, 5 },
		{ tr("Background dimensions"), 14, 2 },
	});
	// 0x0400000A: BG1CNT
	s_registers.append({
		{ tr("Priority"), 0, 2 },
		{ tr("Tile data base (* 16kB)"), 2, 2 },
		{ tr("Enable mosaic"), 6 },
		{ tr("Enable 256-color"), 7 },
		{ tr("Tile map base (* 2kB)"), 8, 5 },
		{ tr("Background dimensions"), 14, 2 },
	});
	// 0x0400000C: BG2CNT
	s_registers.append({
		{ tr("Priority"), 0, 2 },
		{ tr("Tile data base (* 16kB)"), 2, 2 },
		{ tr("Enable mosaic"), 6 },
		{ tr("Enable 256-color"), 7 },
		{ tr("Tile map base (* 2kB)"), 8, 5 },
		{ tr("Overflow wraps"), 13 },
		{ tr("Background dimensions"), 14, 2 },
	});
	// 0x0400000E: BG3CNT
	s_registers.append({
		{ tr("Priority"), 0, 2 },
		{ tr("Tile data base (* 16kB)"), 2, 2 },
		{ tr("Enable mosaic"), 6 },
		{ tr("Enable 256-color"), 7 },
		{ tr("Tile map base (* 2kB)"), 8, 5 },
		{ tr("Overflow wraps"), 13 },
		{ tr("Background dimensions"), 14, 2 },
	});
	// 0x04000010: BG0HOFS
	s_registers.append({
		{ tr("Horizontal offset"), 0, 9 },
	});
	// 0x04000012: BG0VOFS
	s_registers.append({
		{ tr("Vertical offset"), 0, 9 },
	});
	// 0x04000014: BG1HOFS
	s_registers.append({
		{ tr("Horizontal offset"), 0, 9 },
	});
	// 0x04000016: BG1VOFS
	s_registers.append({
		{ tr("Vertical offset"), 0, 9 },
	});
	// 0x04000018: BG2HOFS
	s_registers.append({
		{ tr("Horizontal offset"), 0, 9 },
	});
	// 0x0400001A: BG2VOFS
	s_registers.append({
		{ tr("Vertical offset"), 0, 9 },
	});
	// 0x0400001C: BG3HOFS
	s_registers.append({
		{ tr("Horizontal offset"), 0, 9 },
	});
	// 0x0400001E: BG3VOFS
	s_registers.append({
		{ tr("Vertical offset"), 0, 9 },
	});
	return s_registers;
}

IOViewer::IOViewer(GameController* controller, QWidget* parent)
	: QDialog(parent)
	, m_controller(controller)
{
	m_ui.setupUi(this);

	for (unsigned i = 0; i < REG_MAX >> 1; ++i) {
		const char* reg = GBAIORegisterNames[i];
		if (!reg) {
			continue;
		}
		m_ui.regSelect->addItem("0x0400" + QString("%1: %2").arg(i << 1, 4, 16, QChar('0')).toUpper().arg(reg), i << 1);
	}

	const QFont font = QFontDatabase::systemFont(QFontDatabase::FixedFont);
	m_ui.regValue->setFont(font);

	connect(m_ui.buttonBox, SIGNAL(clicked(QAbstractButton*)), this, SLOT(buttonPressed(QAbstractButton*)));
	connect(m_ui.buttonBox, SIGNAL(rejected()), this, SLOT(close()));
	connect(m_ui.regSelect, SIGNAL(currentIndexChanged(int)), this, SLOT(selectRegister()));

	m_b[0] = m_ui.b0;
	m_b[1] = m_ui.b1;
	m_b[2] = m_ui.b2;
	m_b[3] = m_ui.b3;
	m_b[4] = m_ui.b4;
	m_b[5] = m_ui.b5;
	m_b[6] = m_ui.b6;
	m_b[7] = m_ui.b7;
	m_b[8] = m_ui.b8;
	m_b[9] = m_ui.b9;
	m_b[10] = m_ui.bA;
	m_b[11] = m_ui.bB;
	m_b[12] = m_ui.bC;
	m_b[13] = m_ui.bD;
	m_b[14] = m_ui.bE;
	m_b[15] = m_ui.bF;

	for (int i = 0; i < 16; ++i) {
		connect(m_b[i], SIGNAL(toggled(bool)), this, SLOT(bitFlipped()));
	}

	selectRegister(0);
}

void IOViewer::updateRegister() {
	m_value = 0;
	uint16_t value = 0;
	m_controller->threadInterrupt();
	if (m_controller->isLoaded()) {
		value = GBAIORead(m_controller->thread()->gba, m_register);
	}
	m_controller->threadContinue();

	for (int i = 0; i < 16; ++i) {
		m_b[i]->setChecked(value & (1 << i) ? Qt::Checked : Qt::Unchecked);
	}
	m_value = value;
}

void IOViewer::bitFlipped() {
	m_value = 0;
	for (int i = 0; i < 16; ++i) {
		m_value |= m_b[i]->isChecked() << i;
	}
	m_ui.regValue->setText("0x" + QString("%1").arg(m_value, 4, 16, QChar('0')).toUpper());
}

void IOViewer::writeback() {
	m_controller->threadInterrupt();
	if (m_controller->isLoaded()) {
		GBAIOWrite(m_controller->thread()->gba, m_register, m_value);
	}
	m_controller->threadContinue();
	updateRegister();
}

void IOViewer::selectRegister(unsigned address) {
	m_register = address;
	QGridLayout* box = static_cast<QGridLayout*>(m_ui.regDescription->layout());
	if (box) {
		// I can't believe there isn't a real way to do this...
		while (!box->isEmpty()) {
			QLayoutItem* item = box->takeAt(0);
			if (item->widget()) {
				delete item->widget();
			}
			delete item;
		}
	} else {
		box = new QGridLayout;
	}
	if (registerDescriptions().count() > address >> 1) {
		// TODO: Remove the check when done filling in register information
		const RegisterDescription& description = registerDescriptions().at(address >> 1);
		int i = 0;
		for (const RegisterItem& ri : description) {
			QLabel* label = new QLabel(ri.description);
			box->addWidget(label, i, 0);
			if (ri.size == 1) {
				QCheckBox* check = new QCheckBox;
				check->setEnabled(!ri.readonly);
				box->addWidget(check, i, 1, Qt::AlignRight);
				connect(check, SIGNAL(toggled(bool)), m_b[ri.start], SLOT(setChecked(bool)));
				connect(m_b[ri.start], SIGNAL(toggled(bool)), check, SLOT(setChecked(bool)));
			} else if (ri.items.empty()) {
				QSpinBox* sbox = new QSpinBox;
				sbox->setEnabled(!ri.readonly);
				sbox->setMaximum((1 << ri.size) - 1);
				box->addWidget(sbox, i, 1, Qt::AlignRight);
				for (int b = 0; b < ri.size; ++b) {
					connect(sbox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), [this, b, &ri](int v) {
						m_b[ri.start + b]->setChecked(v & (1 << b));
					});
					auto connection = connect(m_b[ri.start + b], &QCheckBox::toggled, [sbox, this, b](bool t) {
						int v = sbox->value() & ~(1 << b);
						v |= t << b;
						bool signalsBlocked = sbox->blockSignals(true);
						sbox->setValue(v);
						sbox->blockSignals(signalsBlocked);
					});
					connect(sbox, &QObject::destroyed, [connection, this, b, &ri]() {
						bool signalsBlocked = m_b[ri.start + b]->blockSignals(true);
						m_b[ri.start + b]->disconnect(connection);
						m_b[ri.start + b]->blockSignals(signalsBlocked);
					});
				}
			} else {
				QButtonGroup* group = new QButtonGroup(box);
				group->setExclusive(true);
				for (int o = 0; o < 1 << ri.size; ++o) {
					if (ri.items.at(o).isNull()) {
						continue;
					}
					++i;
					QRadioButton* button = new QRadioButton(ri.items.at(o));
					button->setEnabled(!ri.readonly);
					box->addWidget(button, i, 0, 1, 2, Qt::AlignLeft);
					group->addButton(button, o);
				}
				for (int b = 0; b < ri.size; ++b) {
					auto connection = connect(m_b[ri.start + b], &QCheckBox::toggled, [group, this, &ri](bool) {
						unsigned v = (m_value >> ri.start) & ((1 << ri.size) - 1);
						for (int i = 0; i < 1 << ri.size; ++i) {
							QAbstractButton* button = group->button(i);
							if (!button) {
								continue;
							}
							bool signalsBlocked = button->blockSignals(true);
							button->setChecked(i == v);
							button->blockSignals(signalsBlocked);
						}
					});
					connect(group, &QObject::destroyed, [connection, this, b, &ri]() {
						m_b[ri.start + b]->disconnect(connection);
					});
				}
				connect(group, static_cast<void (QButtonGroup::*)(int, bool)>(&QButtonGroup::buttonToggled), [this, &ri](int v) {
					for (int i = 0; i < ri.size; ++i) {
						bool signalsBlocked = m_b[ri.start + i]->blockSignals(true);
						m_b[ri.start + i]->setChecked(v & (1 << i));
						m_b[ri.start + i]->blockSignals(signalsBlocked);
					}
				});
			}
			++i;
		}
	}
	m_ui.regDescription->setLayout(box);
	updateRegister();
}

void IOViewer::selectRegister() {
	selectRegister(m_ui.regSelect->currentData().toUInt());
}

void IOViewer::buttonPressed(QAbstractButton* button) {
	switch (m_ui.buttonBox->standardButton(button)) {
	case QDialogButtonBox::Reset:
		updateRegister();
	 	break;
	case QDialogButtonBox::Apply:
	 	writeback();
	 	break;
	default:
		break;
	}
}
<|endoftext|>


<commit_before>// This file is a part of the IncludeOS unikernel - www.includeos.org
//
// Copyright 2015 Oslo and Akershus University College of Applied Sciences
// and Alfred Bratterud
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "acpi.hpp"
#include <kernel/syscalls.hpp>
#include <hw/ioport.hpp>
#include <debug>
#include <info>

extern "C" void reboot_os();

namespace x86 {

  struct RSDPDescriptor {
    char     Signature[8];
    uint8_t  Checksum;
    char     OEMID[6];
    uint8_t  Revision;
    uint32_t RsdtAddress;
  } __attribute__ ((packed));

  struct RSDPDescriptor20 {
    RSDPDescriptor rdsp10;

    uint32_t Length;
    uint64_t XsdtAddress;
    uint8_t  ExtendedChecksum;
    uint8_t  reserved[3];
  } __attribute__ ((packed));

  struct SDTHeader {
    char     Signature[4];
    uint32_t Length;
    uint8_t  Revision;
    uint8_t  Checksum;
    char     OEMID[6];
    char     OEMTableID[8];
    uint32_t OEMRevision;
    uint32_t CreatorID;
    uint32_t CreatorRevision;

    uint32_t sigint() const {
      return *(uint32_t*) Signature;
    }
  };

  struct MADTRecord {
    uint8_t type;
    uint8_t length;
    uint8_t data[0];
  };

  struct MADTHeader
  {
    SDTHeader  hdr;
    uint32_t   lapic_addr;
    uint32_t   flags; // 1 = dual 8259 PICs
    MADTRecord record[0];
  };

  struct FACPHeader
  {
    SDTHeader sdt;
    uint32_t  unneded1;
    uint32_t  DSDT;
    uint8_t   unneded2[48 - 44];
    uint32_t  SMI_CMD;
    uint8_t   ACPI_ENABLE;
    uint8_t   ACPI_DISABLE;
    uint8_t   unneded3[64 - 54];
    uint32_t  PM1a_CNT_BLK;
    uint32_t  PM1b_CNT_BLK;
    uint8_t   unneded4[89 - 72];
    uint8_t   PM1_CNT_LEN;
    uint8_t   unneded5[18];
    uint8_t   century;
  };

  struct AddressStructure
  {
    uint8_t  address_space_id; // 0 - system memory, 1 - system I/O
    uint8_t  register_bit_width;
    uint8_t  register_bit_offset;
    uint8_t  reserved;
    uint64_t address;
  };

  struct pci_vendor_t
  {
    uint16_t    ven_id;
    const char* ven_name;
  };

  struct HPET
  {
    uint8_t hardware_rev_id;
    uint8_t comparator_count :5;
    uint8_t counter_size     :1;
    uint8_t reserved         :1;
    uint8_t legacy_replacem  :1;
    pci_vendor_t pci_vendor_id;
    AddressStructure address;
    uint8_t hpet_number;
    uint16_t minimum_tick;
    uint8_t page_protection;
  } __attribute__((packed));

  uint64_t ACPI::time() {
    return 0;
  }

  void ACPI::begin(const void* addr)
  {
    auto* rdsp = (RSDPDescriptor20*) addr;

    INFO("ACPI", "Reading headers");
    INFO2("OEM: %.*s Rev. %u",
        6, rdsp->rdsp10.OEMID, rdsp->rdsp10.Revision);

    SDTHeader* rsdt;
    if (rdsp->XsdtAddress)
        rsdt = (SDTHeader*) (uintptr_t) rdsp->XsdtAddress;
    else
        rsdt = (SDTHeader*) (uintptr_t) rdsp->rdsp10.RsdtAddress;

    // verify Root SDT
    if (rsdt->Length < sizeof(SDTHeader)) {
      printf("ACPI: SDT verification failed: len=%u\n", rsdt->Length);
      panic("SDT had impossible length");
    }
    if (checksum((char*) rsdt, rsdt->Length) != 0)
    {
      printf("ACPI: SDT verification failed!");
      panic("SDT checksum failed");
    }

    // walk through system description table headers
    // remember the interesting ones, and count CPUs
    walk_sdts(rsdt);
  }

  constexpr uint32_t bake(char a, char b , char c, char d) {
    return a | (b << 8) | (c << 16) | (d << 24);
  }

  void ACPI::walk_sdts(SDTHeader* rsdt)
  {
    // find total number of SDTs
    int  total = (rsdt->Length - sizeof(SDTHeader)) / 4;
    // go past rsdt
    const char* addr = (const char*) rsdt;
    addr += sizeof(SDTHeader);

    // parse all tables
    constexpr uint32_t APIC_t = bake('A', 'P', 'I', 'C');
    constexpr uint32_t HPET_t = bake('H', 'P', 'E', 'T');
    constexpr uint32_t FACP_t = bake('F', 'A', 'C', 'P');

    while (total > 0)
    {
      // create SDT pointer from 32-bit address
      // NOTE: don't touch!
      auto* sdt = (SDTHeader*) (uintptr_t) (*(uint32_t*) addr);
      // find out which SDT it is
      switch (sdt->sigint()) {
      case APIC_t:
        debug("APIC found: P=%p L=%u\n", sdt, sdt->Length);
        walk_madt((char*) sdt);
        break;
      case HPET_t:
        debug("HPET found: P=%p L=%u\n", sdt, sdt->Length);
        this->hpet_base = (uintptr_t) addr + sizeof(SDTHeader);
        break;
      case FACP_t:
        debug("FACP found: P=%p L=%u\n", sdt, sdt->Length);
        walk_facp((char*) sdt);
        break;
      default:
        debug("Signature: %.*s (u=%u)\n", 4, sdt->Signature, sdt->sigint());
      }

      addr += 4; total--;
    }
    debug("Finished walking SDTs\n");
  }

  void ACPI::walk_madt(const char* addr)
  {
    auto* hdr = (MADTHeader*) addr;
    INFO("ACPI", "Reading APIC information");
    // the base address for APIC registers
    INFO2("LAPIC base: 0x%x  (flags: 0x%x)",
        hdr->lapic_addr, hdr->flags);
    this->apic_base = (uintptr_t) hdr->lapic_addr;

    // the length remaining after MADT header
    int len = hdr->hdr.Length - sizeof(MADTHeader);
    // start walking
    const char* ptr = (char*) hdr->record;

    while (len) {
      auto* rec = (MADTRecord*) ptr;
      switch (rec->type) {
      case 0:
        {
          auto& lapic = *(LAPIC*) rec;
          lapics.push_back(lapic);
          //INFO2("-> CPU %u ID %u  (flags=0x%x)",
          //    lapic.cpu, lapic.id, lapic.flags);
        }
        break;
      case 1:
        {
          auto& ioapic = *(IOAPIC*) rec;
          ioapics.push_back(ioapic);
          INFO2("I/O APIC %u   ADDR 0x%x  INTR 0x%x",
              ioapic.id, ioapic.addr_base, ioapic.intr_base);
        }
        break;
      case 2: // interrupt source override
        {
          auto& redirect = *(override_t*) rec;
          overrides.push_back(redirect);
          INFO2("IRQ redirect for bus %u from IRQ %u to VEC %u",
              redirect.bus_source, redirect.irq_source, redirect.global_intr);
        }
        break;
      case 4: // non-maskable interrupts
        {
          auto& nmi = *(nmi_t*) rec;
          nmis.push_back(nmi);
          //INFO2("NMI for CPU %u (flags %x) on LINT %u",
          //      nmi.cpu, nmi.flags, nmi.lint);
          break;
        }
      default:
        printf("Unrecognized ACPI MADT type: %u\n", rec->type);
      }
      // decrease length as we go
      len -= rec->length;
      // go to next entry
      ptr += rec->length;
    }
    INFO("SMP", "Found %u APs", (uint32_t) lapics.size());
  }

  void ACPI::walk_facp(const char* addr)
  {
    auto* facp = (FACPHeader*) addr;
    this->century = facp->century;

    auto  dsdt_addr = (uintptr_t) facp->DSDT;
    // verify DSDT
    constexpr uint32_t DSDT_t = bake('D', 'S', 'D', 'T');
    assert(*(uint32_t*) dsdt_addr == DSDT_t);

    /// big thanks to kaworu from OSdev.org forums for algo
    /// http://forum.osdev.org/viewtopic.php?t=16990
    char* S5Addr = (char*) dsdt_addr + 36; // skip header
    int dsdtLength = ((SDTHeader*) dsdt_addr)->Length;
    // some ting wong
    dsdtLength *= 2;
    while (dsdtLength-- > 0)
    {
      if (memcmp(S5Addr, "_S5_", 4) == 0)
           break;
      S5Addr++;
    }
    // check if \_S5 was found
    if (dsdtLength <= 0) {
      printf("WARNING: _S5 not present in ACPI\n");
      return;
    }

    // check for valid AML structure
    if ( ( *(S5Addr-1) == 0x08 || ( *(S5Addr-2) == 0x08 && *(S5Addr-1) == '\\') ) && *(S5Addr+4) == 0x12 )
    {
       S5Addr += 5;
       S5Addr += ((*S5Addr &0xC0)>>6) + 2;   // calculate PkgLength size

       if (*S5Addr == 0x0A)
          S5Addr++;   // skip byteprefix
       SLP_TYPa = *(S5Addr) << 10;
       S5Addr++;

       if (*S5Addr == 0x0A)
          S5Addr++;   // skip byteprefix
       SLP_TYPb = *(S5Addr)<<10;

       SMI_CMD = (uint32_t*) (uintptr_t) facp->SMI_CMD;

       ACPI_ENABLE = facp->ACPI_ENABLE;
       ACPI_DISABLE = facp->ACPI_DISABLE;

       PM1a_CNT = (uint32_t*) (uintptr_t) facp->PM1a_CNT_BLK;
       PM1b_CNT = (uint32_t*) (uintptr_t) facp->PM1b_CNT_BLK;

       PM1_CNT_LEN = facp->PM1_CNT_LEN;

       SLP_EN = 1<<13;
       SCI_EN = 1;

       debug("ACPI: Found shutdown information\n");
       return;

    } else {
       printf("WARNING: Failed to parse _S5 in ACPI\n");
    }
    // disable ACPI shutdown
    SCI_EN = 0;
  }

  uint8_t ACPI::checksum(const char* addr, size_t size) const noexcept
  {
    const char* end = addr + size;
    uint8_t sum = 0;
    while (addr < end) {
      sum += *addr; addr++;
    }
    return sum;
  }

  void ACPI::discover()
  {
    // "RSD PTR "
    const uint64_t sign = 0x2052545020445352;

    // guess at QEMU location of RDSP
    const auto* guess = (char*) 0xf68c0;
    if (*(uint64_t*) guess == sign) {
      if (checksum(guess, sizeof(RSDPDescriptor)) == 0)
      {
        debug("Found ACPI located at QEMU-guess (%p)\n", guess);
        begin(guess);
        return;
      }
    }

    // search in BIOS area (below 1mb)
    const auto* addr = (char*) 0x000e0000;
    const auto* end  = (char*) 0x000fffff;
    debug("Looking for ACPI at %p\n", addr);

    while (addr < end)
    {
      if (*(uint64_t*) addr == sign) {
        // verify checksum of potential RDSP
        if (checksum(addr, sizeof(RSDPDescriptor)) == 0)
        {
          debug("Found ACPI located at %p\n", addr);
          begin(addr);
          return;
        }
      }
      addr += 16;
    }

    panic("ACPI RDST-search failed\n");
  }

  void ACPI::reboot()
  {
    ::reboot_os();
  }

  void ACPI::acpi_shutdown() {
    // check if shutdown enabled
    if (SCI_EN == 1) {
      // write shutdown commands
      hw::outw((uintptr_t) PM1a_CNT, SLP_TYPa | SLP_EN);
      if (PM1b_CNT != 0)
        hw::outw((uintptr_t) PM1b_CNT, SLP_TYPb | SLP_EN);
      printf("*** ACPI shutdown failed\n");
    }
  }

  __attribute__((noreturn))
  void ACPI::shutdown()
  {
    asm("cli");

    // ACPI shutdown
    get().acpi_shutdown();

    // http://forum.osdev.org/viewtopic.php?t=16990
    hw::outw (0xB004, 0x2000);

    const char s[] = "Shutdown";
    const char *p;
    for (p = s; *p; p++)
      // magic code for bochs and qemu
      hw::outb (0x8900, *s);

    // VMWare poweroff when "gui.exitOnCLIHLT" is true
    printf("Shutdown failed :(\n");
    while (true) asm ("cli; hlt");
  }
}
<commit_msg>platform: Ignore nullptr STD entries in ACPI #1698<commit_after>// This file is a part of the IncludeOS unikernel - www.includeos.org
//
// Copyright 2015 Oslo and Akershus University College of Applied Sciences
// and Alfred Bratterud
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "acpi.hpp"
#include <kernel/syscalls.hpp>
#include <hw/ioport.hpp>
#include <debug>
#include <info>

extern "C" void reboot_os();

namespace x86 {

  struct RSDPDescriptor {
    char     Signature[8];
    uint8_t  Checksum;
    char     OEMID[6];
    uint8_t  Revision;
    uint32_t RsdtAddress;
  } __attribute__ ((packed));

  struct RSDPDescriptor20 {
    RSDPDescriptor rdsp10;

    uint32_t Length;
    uint64_t XsdtAddress;
    uint8_t  ExtendedChecksum;
    uint8_t  reserved[3];
  } __attribute__ ((packed));

  struct SDTHeader {
    char     Signature[4];
    uint32_t Length;
    uint8_t  Revision;
    uint8_t  Checksum;
    char     OEMID[6];
    char     OEMTableID[8];
    uint32_t OEMRevision;
    uint32_t CreatorID;
    uint32_t CreatorRevision;

    uint32_t sigint() const {
      return *(uint32_t*) Signature;
    }
  };

  struct MADTRecord {
    uint8_t type;
    uint8_t length;
    uint8_t data[0];
  };

  struct MADTHeader
  {
    SDTHeader  hdr;
    uint32_t   lapic_addr;
    uint32_t   flags; // 1 = dual 8259 PICs
    MADTRecord record[0];
  };

  struct FACPHeader
  {
    SDTHeader sdt;
    uint32_t  unneded1;
    uint32_t  DSDT;
    uint8_t   unneded2[48 - 44];
    uint32_t  SMI_CMD;
    uint8_t   ACPI_ENABLE;
    uint8_t   ACPI_DISABLE;
    uint8_t   unneded3[64 - 54];
    uint32_t  PM1a_CNT_BLK;
    uint32_t  PM1b_CNT_BLK;
    uint8_t   unneded4[89 - 72];
    uint8_t   PM1_CNT_LEN;
    uint8_t   unneded5[18];
    uint8_t   century;
  };

  struct AddressStructure
  {
    uint8_t  address_space_id; // 0 - system memory, 1 - system I/O
    uint8_t  register_bit_width;
    uint8_t  register_bit_offset;
    uint8_t  reserved;
    uint64_t address;
  };

  struct pci_vendor_t
  {
    uint16_t    ven_id;
    const char* ven_name;
  };

  struct HPET
  {
    uint8_t hardware_rev_id;
    uint8_t comparator_count :5;
    uint8_t counter_size     :1;
    uint8_t reserved         :1;
    uint8_t legacy_replacem  :1;
    pci_vendor_t pci_vendor_id;
    AddressStructure address;
    uint8_t hpet_number;
    uint16_t minimum_tick;
    uint8_t page_protection;
  } __attribute__((packed));

  uint64_t ACPI::time() {
    return 0;
  }

  void ACPI::begin(const void* addr)
  {
    auto* rdsp = (RSDPDescriptor20*) addr;

    INFO("ACPI", "Reading headers");
    INFO2("OEM: %.*s Rev. %u",
        6, rdsp->rdsp10.OEMID, rdsp->rdsp10.Revision);

    SDTHeader* rsdt;
    if (rdsp->XsdtAddress)
        rsdt = (SDTHeader*) (uintptr_t) rdsp->XsdtAddress;
    else
        rsdt = (SDTHeader*) (uintptr_t) rdsp->rdsp10.RsdtAddress;

    // verify Root SDT
    if (rsdt->Length < sizeof(SDTHeader)) {
      printf("ACPI: SDT verification failed: len=%u\n", rsdt->Length);
      panic("SDT had impossible length");
    }
    if (checksum((char*) rsdt, rsdt->Length) != 0)
    {
      printf("ACPI: SDT verification failed!");
      panic("SDT checksum failed");
    }

    // walk through system description table headers
    // remember the interesting ones, and count CPUs
    walk_sdts(rsdt);
  }

  constexpr uint32_t bake(char a, char b , char c, char d) {
    return a | (b << 8) | (c << 16) | (d << 24);
  }

  void ACPI::walk_sdts(SDTHeader* rsdt)
  {
    // find total number of SDTs
    int  total = (rsdt->Length - sizeof(SDTHeader)) / 4;
    // go past rsdt
    const char* addr = (const char*) rsdt;
    addr += sizeof(SDTHeader);

    // parse all tables
    constexpr uint32_t APIC_t = bake('A', 'P', 'I', 'C');
    constexpr uint32_t HPET_t = bake('H', 'P', 'E', 'T');
    constexpr uint32_t FACP_t = bake('F', 'A', 'C', 'P');

    while (total > 0)
    {
      // create SDT pointer from 32-bit address
      // NOTE: don't touch!
      auto* sdt = (SDTHeader*) (uintptr_t) (*(uint32_t*) addr);

      addr += 4; total--;

      // some entries seems to be null depending on hypervisor
      if(sdt == nullptr)
        continue;

      // find out which SDT it is
      switch (sdt->sigint()) {
      case APIC_t:
        debug("APIC found: P=%p L=%u\n", sdt, sdt->Length);
        walk_madt((char*) sdt);
        break;
      case HPET_t:
        debug("HPET found: P=%p L=%u\n", sdt, sdt->Length);
        this->hpet_base = (uintptr_t) addr + sizeof(SDTHeader);
        break;
      case FACP_t:
        debug("FACP found: P=%p L=%u\n", sdt, sdt->Length);
        walk_facp((char*) sdt);
        break;
      default:
        debug("Signature: %.*s (u=%u)\n", 4, sdt->Signature, sdt->sigint());
      }
    }
    debug("Finished walking SDTs\n");
  }

  void ACPI::walk_madt(const char* addr)
  {
    auto* hdr = (MADTHeader*) addr;
    INFO("ACPI", "Reading APIC information");
    // the base address for APIC registers
    INFO2("LAPIC base: 0x%x  (flags: 0x%x)",
        hdr->lapic_addr, hdr->flags);
    this->apic_base = (uintptr_t) hdr->lapic_addr;

    // the length remaining after MADT header
    int len = hdr->hdr.Length - sizeof(MADTHeader);
    // start walking
    const char* ptr = (char*) hdr->record;

    while (len) {
      auto* rec = (MADTRecord*) ptr;
      switch (rec->type) {
      case 0:
        {
          auto& lapic = *(LAPIC*) rec;
          lapics.push_back(lapic);
          //INFO2("-> CPU %u ID %u  (flags=0x%x)",
          //    lapic.cpu, lapic.id, lapic.flags);
        }
        break;
      case 1:
        {
          auto& ioapic = *(IOAPIC*) rec;
          ioapics.push_back(ioapic);
          INFO2("I/O APIC %u   ADDR 0x%x  INTR 0x%x",
              ioapic.id, ioapic.addr_base, ioapic.intr_base);
        }
        break;
      case 2: // interrupt source override
        {
          auto& redirect = *(override_t*) rec;
          overrides.push_back(redirect);
          INFO2("IRQ redirect for bus %u from IRQ %u to VEC %u",
              redirect.bus_source, redirect.irq_source, redirect.global_intr);
        }
        break;
      case 4: // non-maskable interrupts
        {
          auto& nmi = *(nmi_t*) rec;
          nmis.push_back(nmi);
          //INFO2("NMI for CPU %u (flags %x) on LINT %u",
          //      nmi.cpu, nmi.flags, nmi.lint);
          break;
        }
      default:
        printf("Unrecognized ACPI MADT type: %u\n", rec->type);
      }
      // decrease length as we go
      len -= rec->length;
      // go to next entry
      ptr += rec->length;
    }
    INFO("SMP", "Found %u APs", (uint32_t) lapics.size());
  }

  void ACPI::walk_facp(const char* addr)
  {
    auto* facp = (FACPHeader*) addr;
    this->century = facp->century;

    auto  dsdt_addr = (uintptr_t) facp->DSDT;
    // verify DSDT
    constexpr uint32_t DSDT_t = bake('D', 'S', 'D', 'T');
    assert(*(uint32_t*) dsdt_addr == DSDT_t);

    /// big thanks to kaworu from OSdev.org forums for algo
    /// http://forum.osdev.org/viewtopic.php?t=16990
    char* S5Addr = (char*) dsdt_addr + 36; // skip header
    int dsdtLength = ((SDTHeader*) dsdt_addr)->Length;
    // some ting wong
    dsdtLength *= 2;
    while (dsdtLength-- > 0)
    {
      if (memcmp(S5Addr, "_S5_", 4) == 0)
           break;
      S5Addr++;
    }
    // check if \_S5 was found
    if (dsdtLength <= 0) {
      printf("WARNING: _S5 not present in ACPI\n");
      return;
    }

    // check for valid AML structure
    if ( ( *(S5Addr-1) == 0x08 || ( *(S5Addr-2) == 0x08 && *(S5Addr-1) == '\\') ) && *(S5Addr+4) == 0x12 )
    {
       S5Addr += 5;
       S5Addr += ((*S5Addr &0xC0)>>6) + 2;   // calculate PkgLength size

       if (*S5Addr == 0x0A)
          S5Addr++;   // skip byteprefix
       SLP_TYPa = *(S5Addr) << 10;
       S5Addr++;

       if (*S5Addr == 0x0A)
          S5Addr++;   // skip byteprefix
       SLP_TYPb = *(S5Addr)<<10;

       SMI_CMD = (uint32_t*) (uintptr_t) facp->SMI_CMD;

       ACPI_ENABLE = facp->ACPI_ENABLE;
       ACPI_DISABLE = facp->ACPI_DISABLE;

       PM1a_CNT = (uint32_t*) (uintptr_t) facp->PM1a_CNT_BLK;
       PM1b_CNT = (uint32_t*) (uintptr_t) facp->PM1b_CNT_BLK;

       PM1_CNT_LEN = facp->PM1_CNT_LEN;

       SLP_EN = 1<<13;
       SCI_EN = 1;

       debug("ACPI: Found shutdown information\n");
       return;

    } else {
       printf("WARNING: Failed to parse _S5 in ACPI\n");
    }
    // disable ACPI shutdown
    SCI_EN = 0;
  }

  uint8_t ACPI::checksum(const char* addr, size_t size) const noexcept
  {
    const char* end = addr + size;
    uint8_t sum = 0;
    while (addr < end) {
      sum += *addr; addr++;
    }
    return sum;
  }

  void ACPI::discover()
  {
    // "RSD PTR "
    const uint64_t sign = 0x2052545020445352;

    // guess at QEMU location of RDSP
    const auto* guess = (char*) 0xf68c0;
    if (*(uint64_t*) guess == sign) {
      if (checksum(guess, sizeof(RSDPDescriptor)) == 0)
      {
        debug("Found ACPI located at QEMU-guess (%p)\n", guess);
        begin(guess);
        return;
      }
    }

    // search in BIOS area (below 1mb)
    const auto* addr = (char*) 0x000e0000;
    const auto* end  = (char*) 0x000fffff;
    debug("Looking for ACPI at %p\n", addr);

    while (addr < end)
    {
      if (*(uint64_t*) addr == sign) {
        // verify checksum of potential RDSP
        if (checksum(addr, sizeof(RSDPDescriptor)) == 0)
        {
          debug("Found ACPI located at %p\n", addr);
          begin(addr);
          return;
        }
      }
      addr += 16;
    }

    panic("ACPI RDST-search failed\n");
  }

  void ACPI::reboot()
  {
    ::reboot_os();
  }

  void ACPI::acpi_shutdown() {
    // check if shutdown enabled
    if (SCI_EN == 1) {
      // write shutdown commands
      hw::outw((uintptr_t) PM1a_CNT, SLP_TYPa | SLP_EN);
      if (PM1b_CNT != 0)
        hw::outw((uintptr_t) PM1b_CNT, SLP_TYPb | SLP_EN);
      printf("*** ACPI shutdown failed\n");
    }
  }

  __attribute__((noreturn))
  void ACPI::shutdown()
  {
    asm("cli");

    // ACPI shutdown
    get().acpi_shutdown();

    // http://forum.osdev.org/viewtopic.php?t=16990
    hw::outw (0xB004, 0x2000);

    const char s[] = "Shutdown";
    const char *p;
    for (p = s; *p; p++)
      // magic code for bochs and qemu
      hw::outb (0x8900, *s);

    // VMWare poweroff when "gui.exitOnCLIHLT" is true
    printf("Shutdown failed :(\n");
    while (true) asm ("cli; hlt");
  }
}
<|endoftext|>


<commit_before>// This file is part of gltfpack; see gltfpack.h for version/license details
#include "gltfpack.h"

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

#ifdef _WIN32
#define popen _popen
#define pclose _pclose
#endif

static const char* kMimeTypes[][2] =
{
	{ "image/jpeg", ".jpg" },
	{ "image/jpeg", ".jpeg" },
	{ "image/png", ".png" },
};

void analyzeImages(cgltf_data* data, std::vector<ImageInfo>& images)
{
	for (size_t i = 0; i < data->materials_count; ++i)
	{
		const cgltf_material& material = data->materials[i];

		if (material.has_pbr_metallic_roughness)
		{
			const cgltf_pbr_metallic_roughness& pbr = material.pbr_metallic_roughness;

			if (pbr.base_color_texture.texture && pbr.base_color_texture.texture->image)
				images[pbr.base_color_texture.texture->image - data->images].srgb = true;
		}

		if (material.has_pbr_specular_glossiness)
		{
			const cgltf_pbr_specular_glossiness& pbr = material.pbr_specular_glossiness;

			if (pbr.diffuse_texture.texture && pbr.diffuse_texture.texture->image)
				images[pbr.diffuse_texture.texture->image - data->images].srgb = true;
		}

		if (material.emissive_texture.texture && material.emissive_texture.texture->image)
			images[material.emissive_texture.texture->image - data->images].srgb = true;

		if (material.normal_texture.texture && material.normal_texture.texture->image)
			images[material.normal_texture.texture->image - data->images].normal_map = true;
	}
}

const char* inferMimeType(const char* path)
{
	const char* ext = strrchr(path, '.');
	if (!ext)
		return "";

	std::string extl = ext;
	for (size_t i = 0; i < extl.length(); ++i)
		extl[i] = char(tolower(extl[i]));

	for (size_t i = 0; i < sizeof(kMimeTypes) / sizeof(kMimeTypes[0]); ++i)
		if (extl == kMimeTypes[i][1])
			return kMimeTypes[i][0];

	return "";
}

static const char* mimeExtension(const char* mime_type)
{
	for (size_t i = 0; i < sizeof(kMimeTypes) / sizeof(kMimeTypes[0]); ++i)
		if (strcmp(kMimeTypes[i][0], mime_type) == 0)
			return kMimeTypes[i][1];

	return ".raw";
}

bool checkBasis()
{
#ifdef __EMSCRIPTEN__
	return false;
#else
	const char* basisu_path = getenv("BASISU_PATH");
	std::string cmd = basisu_path ? basisu_path : "basisu";

#ifdef _WIN32
	cmd += " 2>nul";
#else
	cmd += " 2>/dev/null";
#endif

	FILE* pipe = popen(cmd.c_str(), "r");
	if (!pipe)
		return false;

	char buf[15];
	size_t read = fread(buf, 1, sizeof(buf), pipe);
	pclose(pipe);

	return read == sizeof(buf) && memcmp(buf, "Basis Universal", sizeof(buf)) == 0;
#endif
}

bool encodeBasis(const std::string& data, const char* mime_type, std::string& result, bool normal_map, bool srgb, int quality, bool uastc)
{
	TempFile temp_input(mimeExtension(mime_type));
	TempFile temp_output(".basis");

	if (!writeFile(temp_input.path.c_str(), data))
		return false;

	const char* basisu_path = getenv("BASISU_PATH");
	std::string cmd = basisu_path ? basisu_path : "basisu";

	char ql[16];
	sprintf(ql, "%d", (quality * 255 + 50) / 100);

	cmd += " -q ";
	cmd += ql;

	cmd += " -mipmap";

	if (normal_map)
	{
		cmd += " -normal_map";
		// for optimal quality we should specify seperate_rg_to_color_alpha but this requires renderer awareness
	}
	else if (!srgb)
	{
		cmd += " -linear";
	}

	if (uastc)
	{
		cmd += " -uastc";
	}

	cmd += " -file ";
	cmd += temp_input.path;
	cmd += " -output_file ";
	cmd += temp_output.path;

#ifdef _WIN32
	cmd += " >nul";
#else
	cmd += " >/dev/null";
#endif

	int rc = system(cmd.c_str());

	return rc == 0 && readFile(temp_output.path.c_str(), result);
}
<commit_msg>gltfpack: Reformat<commit_after>// This file is part of gltfpack; see gltfpack.h for version/license details
#include "gltfpack.h"

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

#ifdef _WIN32
#define popen _popen
#define pclose _pclose
#endif

static const char* kMimeTypes[][2] = {
    {"image/jpeg", ".jpg"},
    {"image/jpeg", ".jpeg"},
    {"image/png", ".png"},
};

void analyzeImages(cgltf_data* data, std::vector<ImageInfo>& images)
{
	for (size_t i = 0; i < data->materials_count; ++i)
	{
		const cgltf_material& material = data->materials[i];

		if (material.has_pbr_metallic_roughness)
		{
			const cgltf_pbr_metallic_roughness& pbr = material.pbr_metallic_roughness;

			if (pbr.base_color_texture.texture && pbr.base_color_texture.texture->image)
				images[pbr.base_color_texture.texture->image - data->images].srgb = true;
		}

		if (material.has_pbr_specular_glossiness)
		{
			const cgltf_pbr_specular_glossiness& pbr = material.pbr_specular_glossiness;

			if (pbr.diffuse_texture.texture && pbr.diffuse_texture.texture->image)
				images[pbr.diffuse_texture.texture->image - data->images].srgb = true;
		}

		if (material.emissive_texture.texture && material.emissive_texture.texture->image)
			images[material.emissive_texture.texture->image - data->images].srgb = true;

		if (material.normal_texture.texture && material.normal_texture.texture->image)
			images[material.normal_texture.texture->image - data->images].normal_map = true;
	}
}

const char* inferMimeType(const char* path)
{
	const char* ext = strrchr(path, '.');
	if (!ext)
		return "";

	std::string extl = ext;
	for (size_t i = 0; i < extl.length(); ++i)
		extl[i] = char(tolower(extl[i]));

	for (size_t i = 0; i < sizeof(kMimeTypes) / sizeof(kMimeTypes[0]); ++i)
		if (extl == kMimeTypes[i][1])
			return kMimeTypes[i][0];

	return "";
}

static const char* mimeExtension(const char* mime_type)
{
	for (size_t i = 0; i < sizeof(kMimeTypes) / sizeof(kMimeTypes[0]); ++i)
		if (strcmp(kMimeTypes[i][0], mime_type) == 0)
			return kMimeTypes[i][1];

	return ".raw";
}

bool checkBasis()
{
#ifdef __EMSCRIPTEN__
	return false;
#else
	const char* basisu_path = getenv("BASISU_PATH");
	std::string cmd = basisu_path ? basisu_path : "basisu";

#ifdef _WIN32
	cmd += " 2>nul";
#else
	cmd += " 2>/dev/null";
#endif

	FILE* pipe = popen(cmd.c_str(), "r");
	if (!pipe)
		return false;

	char buf[15];
	size_t read = fread(buf, 1, sizeof(buf), pipe);
	pclose(pipe);

	return read == sizeof(buf) && memcmp(buf, "Basis Universal", sizeof(buf)) == 0;
#endif
}

bool encodeBasis(const std::string& data, const char* mime_type, std::string& result, bool normal_map, bool srgb, int quality, bool uastc)
{
	TempFile temp_input(mimeExtension(mime_type));
	TempFile temp_output(".basis");

	if (!writeFile(temp_input.path.c_str(), data))
		return false;

	const char* basisu_path = getenv("BASISU_PATH");
	std::string cmd = basisu_path ? basisu_path : "basisu";

	char ql[16];
	sprintf(ql, "%d", (quality * 255 + 50) / 100);

	cmd += " -q ";
	cmd += ql;

	cmd += " -mipmap";

	if (normal_map)
	{
		cmd += " -normal_map";
		// for optimal quality we should specify seperate_rg_to_color_alpha but this requires renderer awareness
	}
	else if (!srgb)
	{
		cmd += " -linear";
	}

	if (uastc)
	{
		cmd += " -uastc";
	}

	cmd += " -file ";
	cmd += temp_input.path;
	cmd += " -output_file ";
	cmd += temp_output.path;

#ifdef _WIN32
	cmd += " >nul";
#else
	cmd += " >/dev/null";
#endif

	int rc = system(cmd.c_str());

	return rc == 0 && readFile(temp_output.path.c_str(), result);
}
<|endoftext|>