<commit_before>#include "functions.hpp"
#include "ast.hpp"
#include "context.hpp"
#include "parser.hpp"
#include "constants.hpp"

#include <cmath>

#define ARG(argname, argtype) get_arg<argtype>(argname, env, sig, path, line)
#define BUILT_IN(name) Expression*\
name(Env& env, Context& ctx, Signature sig, const string& path, size_t line)

namespace Sass {

  Definition* make_native_function(Signature sig, Native_Function f, Context& ctx)
  {
    Parser sig_parser = Parser::from_c_str(sig, ctx, "[built-in function]", 0);
    sig_parser.lex<Prelexer::identifier>();
    string name(sig_parser.lexed);
    Parameters* params = sig_parser.parse_parameters();
    return new (ctx.mem) Definition("[built-in function]",
                                    0,
                                    sig,
                                    name,
                                    params,
                                    f);
  }

  namespace Functions {

    template <typename T>
    T* get_arg(const string& argname, Env& env, Signature sig, const string& path, size_t line)
    {
      // Minimal error handling -- the expectation is that built-ins will be written correctly!
      T* val = dynamic_cast<T*>(env[argname]);
      string msg("argument ");
      msg += argname;
      msg += " of ";
      msg += sig;
      msg += " must be a ";
      msg += T::type_name();
      if (!val) error(msg, path, line);
      return val;
    }

    ////////////////
    // RGB FUNCTIONS
    ////////////////

    Signature rgb_sig = "rgb($red, $green, $blue)";
    BUILT_IN(rgb)
    {
      return new (ctx.mem) Color(path,
                                 line,
                                 ARG("$red",   Number)->value(),
                                 ARG("$green", Number)->value(),
                                 ARG("$blue",  Number)->value());
    }

    Signature rgba_4_sig = "rgba($red, $green, $blue, $alpha)";
    BUILT_IN(rgba_4)
    {
      return new (ctx.mem) Color(path,
                                 line,
                                 ARG("$red",   Number)->value(),
                                 ARG("$green", Number)->value(),
                                 ARG("$blue",  Number)->value(),
                                 ARG("$alpha", Number)->value());
    }

    Signature rgba_2_sig = "rgba($color, $alpha)";
    BUILT_IN(rgba_2)
    {
      Color* c_arg = ARG("$color", Color);
      Color* new_c = new (ctx.mem) Color(*c_arg);
      new_c->a(ARG("$alpha", Number)->value());
      return new_c;
    }

    Signature red_sig = "red($color)";
    BUILT_IN(red)
    { return new (ctx.mem) Number(path, line, ARG("$color", Color)->r()); }

    Signature green_sig = "green($color)";
    BUILT_IN(green)
    { return new (ctx.mem) Number(path, line, ARG("$color", Color)->g()); }

    Signature blue_sig = "blue($color)";
    BUILT_IN(blue)
    { return new (ctx.mem) Number(path, line, ARG("$color", Color)->b()); }

    Signature mix_sig = "mix($color-1, $color-2, $weight: 50%)";
    BUILT_IN(mix)
    {
      Color*  color1 = ARG("$color-1", Color);
      Color*  color2 = ARG("$color-2", Color);
      Number* weight = ARG("$weight", Number);

      double p = weight->value()/100;
      double w = 2*p - 1;
      double a = color1->a() - color2->a();

      double w1 = (((w * a == -1) ? w : (w + a)/(1 + w*a)) + 1)/2.0;
      double w2 = 1 - w1;

      return new (ctx.mem) Color(path,
                                 line,
                                 std::floor(w1*color1->r() + w2*color2->r()),
                                 std::floor(w1*color1->g() + w2*color2->g()),
                                 std::floor(w1*color1->b() + w2*color2->b()),
                                 color1->a()*p + color2->a()*(1-p));
    }

    ////////////////
    // HSL FUNCTIONS
    ////////////////

    // RGB to HSL helper function
    struct HSL { double h; double s; double l; };
    HSL rgb_to_hsl(double r, double g, double b)
    {
      r /= 255.0; g /= 255.0; b /= 255.0;

      double max = std::max(r, std::max(g, b));
      double min = std::min(r, std::min(g, b));
      double del = max - min;

      double h = 0, s = 0, l = (max + min)/2;

      if (max == min) {
        h = s = 0; // achromatic
      }
      else {
        if (l < 0.5) s = del / (max + min);
        else         s = del / (2.0 - max - min);

        double dr = (((max - r)/6.0) + (del/2.0))/del;
        double dg = (((max - g)/6.0) + (del/2.0))/del;
        double db = (((max - b)/6.0) + (del/2.0))/del;

        if      (r == max) h = db - dg;
        else if (g == max) h = (1.0/3.0) + dr - db;
        else if (b == max) h = (2.0/3.0) + dg - dr;

        if      (h < 0) h += 1;
        else if (h > 1) h -= 1;
      }
      HSL hsl_struct;
      hsl_struct.h = static_cast<int>(h*360)%360;
      hsl_struct.s = s*100;
      hsl_struct.l = l*100;
      return hsl_struct;
    }

  }
}<commit_msg>Porting more functions.<commit_after>#include "functions.hpp"
#include "ast.hpp"
#include "context.hpp"
#include "parser.hpp"
#include "constants.hpp"

#include <cmath>
#include <sstream>

#define ARG(argname, argtype) get_arg<argtype>(argname, env, sig, path, line)
#define ARGR(argname, argtype, lo, hi) get_arg_r(argname, env, sig, path, line, lo, hi)
#define BUILT_IN(name) Expression*\
name(Env& env, Context& ctx, Signature sig, const string& path, size_t line)

namespace Sass {
  using std::stringstream;
  using std::endl;

  Definition* make_native_function(Signature sig, Native_Function f, Context& ctx)
  {
    Parser sig_parser = Parser::from_c_str(sig, ctx, "[built-in function]", 0);
    sig_parser.lex<Prelexer::identifier>();
    string name(sig_parser.lexed);
    Parameters* params = sig_parser.parse_parameters();
    return new (ctx.mem) Definition("[built-in function]",
                                    0,
                                    sig,
                                    name,
                                    params,
                                    f);
  }

  namespace Functions {

    template <typename T>
    T* get_arg(const string& argname, Env& env, Signature sig, const string& path, size_t line)
    {
      // Minimal error handling -- the expectation is that built-ins will be written correctly!
      T* val = dynamic_cast<T*>(env[argname]);
      if (!val) {
        string msg("argument ");
        msg += argname;
        msg += " of ";
        msg += sig;
        msg += " must be a ";
        msg += T::type_name();
        error(msg, path, line);
      }
      return val;
    }

    Number* get_arg_r(const string& argname, Env& env, Signature sig, const string& path, size_t line, double lo, double hi)
    {
      // Minimal error handling -- the expectation is that built-ins will be written correctly!
      Number* val = get_arg<Number>(argname, env, sig, path, line);
      double v = val->value();
      if (!(lo <= v && v <= hi)) {
        stringstream msg;
        msg << "argument " << argname << " of " << sig << " must be between ";
        msg << lo << " and " << hi;
        error(msg.str(), path, line);
      }
      return val;
    }

    ////////////////
    // RGB FUNCTIONS
    ////////////////

    Signature rgb_sig = "rgb($red, $green, $blue)";
    BUILT_IN(rgb)
    {
      return new (ctx.mem) Color(path,
                                 line,
                                 ARGR("$red",   Number, 0, 255)->value(),
                                 ARGR("$green", Number, 0, 255)->value(),
                                 ARGR("$blue",  Number, 0, 255)->value());
    }

    Signature rgba_4_sig = "rgba($red, $green, $blue, $alpha)";
    BUILT_IN(rgba_4)
    {
      return new (ctx.mem) Color(path,
                                 line,
                                 ARGR("$red",   Number, 0, 255)->value(),
                                 ARGR("$green", Number, 0, 255)->value(),
                                 ARGR("$blue",  Number, 0, 255)->value(),
                                 ARGR("$alpha", Number, 0, 1)->value());
    }

    Signature rgba_2_sig = "rgba($color, $alpha)";
    BUILT_IN(rgba_2)
    {
      Color* c_arg = ARG("$color", Color);
      Color* new_c = new (ctx.mem) Color(*c_arg);
      new_c->a(ARGR("$alpha", Number, 0, 1)->value());
      return new_c;
    }

    Signature red_sig = "red($color)";
    BUILT_IN(red)
    { return new (ctx.mem) Number(path, line, ARG("$color", Color)->r()); }

    Signature green_sig = "green($color)";
    BUILT_IN(green)
    { return new (ctx.mem) Number(path, line, ARG("$color", Color)->g()); }

    Signature blue_sig = "blue($color)";
    BUILT_IN(blue)
    { return new (ctx.mem) Number(path, line, ARG("$color", Color)->b()); }

    Signature mix_sig = "mix($color-1, $color-2, $weight: 50%)";
    BUILT_IN(mix)
    {
      Color*  color1 = ARG("$color-1", Color);
      Color*  color2 = ARG("$color-2", Color);
      Number* weight = ARGR("$weight", Number, 0, 100);

      double p = weight->value()/100;
      double w = 2*p - 1;
      double a = color1->a() - color2->a();

      double w1 = (((w * a == -1) ? w : (w + a)/(1 + w*a)) + 1)/2.0;
      double w2 = 1 - w1;

      return new (ctx.mem) Color(path,
                                 line,
                                 std::floor(w1*color1->r() + w2*color2->r()),
                                 std::floor(w1*color1->g() + w2*color2->g()),
                                 std::floor(w1*color1->b() + w2*color2->b()),
                                 color1->a()*p + color2->a()*(1-p));
    }

    ////////////////
    // HSL FUNCTIONS
    ////////////////

    // RGB to HSL helper function
    struct HSL { double h; double s; double l; };
    HSL rgb_to_hsl(double r, double g, double b)
    {
      r /= 255.0; g /= 255.0; b /= 255.0;

      double max = std::max(r, std::max(g, b));
      double min = std::min(r, std::min(g, b));
      double del = max - min;

      double h = 0, s = 0, l = (max + min)/2;

      if (max == min) {
        h = s = 0; // achromatic
      }
      else {
        if (l < 0.5) s = del / (max + min);
        else         s = del / (2.0 - max - min);

        double dr = (((max - r)/6.0) + (del/2.0))/del;
        double dg = (((max - g)/6.0) + (del/2.0))/del;
        double db = (((max - b)/6.0) + (del/2.0))/del;

        if      (r == max) h = db - dg;
        else if (g == max) h = (1.0/3.0) + dr - db;
        else if (b == max) h = (2.0/3.0) + dg - dr;

        if      (h < 0) h += 1;
        else if (h > 1) h -= 1;
      }
      HSL hsl_struct;
      hsl_struct.h = static_cast<int>(h*360)%360;
      hsl_struct.s = s*100;
      hsl_struct.l = l*100;
      return hsl_struct;
    }

    // hue to RGB helper function
    double h_to_rgb(double m1, double m2, double h) {
      if (h < 0) h += 1;
      if (h > 1) h -= 1;
      if (h*6.0 < 1) return m1 + (m2 - m1)*h*6;
      if (h*2.0 < 1) return m2;
      if (h*3.0 < 2) return m1 + (m2 - m1) * (2.0/3.0 - h)*6;
      return m1;
    }

    Color* hsla_impl(double h, double s, double l, double a, Context& ctx, const string& path, size_t line)
    {
      h = static_cast<double>(((static_cast<int>(h) % 360) + 360) % 360) / 360.0;
      s = (s < 0)   ? 0 :
          (s > 100) ? 100 :
          s;
      l = (l < 0)   ? 0 :
          (l > 100) ? 100 :
          l;
      s /= 100.0;
      l /= 100.0;

      double m2;
      if (l <= 0.5) m2 = l*(s+1.0);
      else m2 = l+s-l*s;
      double m1 = l*2-m2;
      // round the results -- consider moving this into the Color constructor
      double r = std::floor(h_to_rgb(m1, m2, h+1.0/3.0) * 255.0 + 0.5);
      double g = std::floor(h_to_rgb(m1, m2, h) * 255.0 + 0.5);
      double b = std::floor(h_to_rgb(m1, m2, h-1.0/3.0) * 255.0 + 0.5);

      return new (ctx.mem) Color(path, line, r, g, b, a);
    }

    Signature hsl_sig = "hsl($hue, $saturation, $lightness)";
    BUILT_IN(hsl)
    {
      return hsla_impl(ARG("$hue", Number)->value(),
                       ARGR("$saturation", Number, 0, 100)->value(),
                       ARGR("$lightness", Number, 0, 100)->value(),
                       1.0,
                       ctx,
                       path,
                       line);
    }

    Signature hsla_sig = "hsla($hue, $saturation, $lightness, $alpha)";
    BUILT_IN(hsla)
    {
      return hsla_impl(ARG("$hue", Number)->value(),
                       ARGR("$saturation", Number, 0, 100)->value(),
                       ARGR("$lightness", Number, 0, 100)->value(),
                       ARGR("$alpha", Number, 0, 1)->value(),
                       ctx,
                       path,
                       line);
    }

    Signature hue_sig = "hue($color)";
    BUILT_IN(hue)
    {
      Color* rgb_color = ARG("$color", Color);
      HSL hsl_color = rgb_to_hsl(rgb_color->r(),
                                 rgb_color->g(),
                                 rgb_color->b());
      return new (ctx.mem) Number(path, line, hsl_color.h, "deg");
    }

    Signature saturation_sig = "saturation($color)";
    BUILT_IN(saturation)
    {
      Color* rgb_color = ARG("$color", Color);
      HSL hsl_color = rgb_to_hsl(rgb_color->r(),
                                 rgb_color->g(),
                                 rgb_color->b());
      return new (ctx.mem) Number(path, line, hsl_color.s, "%");
    }

    Signature lightness_sig = "lightness($color)";
    BUILT_IN(lightness)
    {
      Color* rgb_color = ARG("$color", Color);
      HSL hsl_color = rgb_to_hsl(rgb_color->r(),
                                 rgb_color->g(),
                                 rgb_color->b());
      return new (ctx.mem) Number(path, line, hsl_color.l, "%");
    }

    Signature adjust_hue_sig = "adjust-hue($color, $degrees)";
    BUILT_IN(adjust_hue)
    {
      Color* rgb_color = ARG("$color", Color);
      Number* degrees = ARG("$degrees", Number);
      HSL hsl_color = rgb_to_hsl(rgb_color->r(),
                                 rgb_color->g(),
                                 rgb_color->b());
      return hsla_impl(hsl_color.h + degrees->value(),
                       hsl_color.s,
                       hsl_color.l,
                       rgb_color->a(),
                       ctx,
                       path,
                       line);
    }

    Signature lighten_sig = "lighten($color, $amount)";
    BUILT_IN(lighten)
    {
      Color* rgb_color = ARG("$color", Color);
      Number* amount = ARGR("$amount", Number, 0, 100);
      HSL hsl_color = rgb_to_hsl(rgb_color->r(),
                                 rgb_color->g(),
                                 rgb_color->b());
      return hsla_impl(hsl_color.h,
                       hsl_color.s,
                       hsl_color.l + amount->value(),
                       rgb_color->a(),
                       ctx,
                       path,
                       line);
    }
  }
}<|endoftext|>


<commit_before>#include "util/bitmap.h"

#include "gui/context-box.h"
#include "util/font.h"

using namespace std;
using namespace Gui;

ContextItem::ContextItem(){
}
ContextItem::~ContextItem(){
}

ContextBox::ContextBox():
current(0),
fadeState(NotActive),
fontWidth(0),
fontHeight(0),
fadeSpeed(12),
fadeAlpha(0){
}
ContextBox::ContextBox( const ContextBox & copy ):
current(0),
fadeState(NotActive){
    this->context = copy.context;
    this->font = copy.font;
    this->fontWidth = copy.fontWidth;
    this->fontHeight = copy.fontHeight;
    this->fadeSpeed = copy.fadeSpeed;
    this->fadeAlpha = copy.fadeAlpha;
}
ContextBox::~ContextBox(){
}
ContextBox & ContextBox::operator=( const ContextBox & copy){
    this->current = 0;
    this->fadeState = NotActive;
    this->context = copy.context;
    this->font = copy.font;
    this->fontWidth = copy.fontWidth;
    this->fontHeight = copy.fontHeight;
    this->fadeSpeed = copy.fadeSpeed;
    this->fadeAlpha = copy.fadeAlpha;
    return *this;
}

void ContextBox::act(){
    // do fade
    doFade();
}

void ContextBox::render(const Bitmap & work){
    board.render(work);
    drawText(work);
}

void ContextBox::next(){
    if (fadeState != Active){
	return;
    }
}
void ContextBox::previous(){
    if (fadeState != Active){
	return;
    }
}
void ContextBox::adjustLeft(){
}
void ContextBox::adjustRight(){
}
void ContextBox::open(){
    // Set the fade stuff
    fadeState = FadeInBox;
    board.position = position;
    board.position.width = board.position.height = 0;
    board.position.x = position.x+(position.width/2);
    board.position.y = position.y+(position.height/2);
    fadeAlpha = 0;
}

void ContextBox::close(){
    fadeState = FadeOutText;
}

void ContextBox::doFade(){
    switch ( fadeState ){
	case FadeInBox: {
	    if (board.position.x > position.x){
		board.position.x -= fadeSpeed;
	    } else if ( board.position.x < position.x ){
		board.position.x = position.x;
	    }

	    if (board.position.y > position.y){
		board.position.y -= fadeSpeed;
	    } else if (board.position.y < position.y){
		board.position.y = position.y;
	    }

	    if (board.position.width < position.width){
		board.position.width += (fadeSpeed*2);
	    } else if (board.position.width > position.width){
		board.position.width = position.width;
	    }

	    if (board.position.height < position.height){
		board.position.height += (fadeSpeed*2);
	    } else if (board.position.height > position.height){
		board.position.height = position.height;
	    }

	    if (board.position == position){
		fadeState = FadeInText;
	    }

	    break;
	}
	case FadeInText: {
	    if (fadeAlpha < 255){
		fadeAlpha += (fadeSpeed+2);
	    }

	    if (fadeAlpha >= 255){
		fadeAlpha = 255;
		fadeState = Active;
	    }
	    break;
	}
	case FadeOutText: {
	    if (fadeAlpha > 0){
		fadeAlpha -= (fadeSpeed+2);
	    }

	    if (fadeAlpha <= 0){
		fadeAlpha = 0;
		fadeState = FadeOutBox;
	    }
	    break;
	}
	case FadeOutBox: {
	    const int positionX = position.x+(position.width/2);
	    const int positionY = position.y+(position.height/2);
	    if (board.position.x < positionX){
		board.position.x += fadeSpeed;
	    } else if ( board.position.x > positionX ){
		board.position.x = positionX;
	    }

	    if (board.position.y < positionY){
		board.position.y += fadeSpeed;
	    } else if (board.position.y < positionY){
		board.position.y = positionY;
	    }

	    if (board.position.width > 0){
		board.position.width -= (fadeSpeed*2);
	    } else if (board.position.width < 0){
		board.position.width = 0;
	    }

	    if (board.position.height > 0){
		board.position.height -= (fadeSpeed*2);
	    } else if (board.position.height < 0){
		board.position.height = 0;
	    }

	    if (board.position == RectArea()){
		fadeState = NotActive;
	    }

	    break;
	}
	case Active:
	case NotActive:
	default:
	    break;
    }
}

void ContextBox::calculateText(){
    //const total =
    renderable.clear();
    if (context->size() == 1){
	renderable.push_back(context->front());
	return;
    }
    //if (current
}

void ContextBox::drawText(const Bitmap & bmp){
    const Font & vFont = Font::getFont(font, fontWidth, fontHeight);
    const double spacing = 1.3;
    bmp.setClipRect(position.x+2, position.y+2, position.getX2()-2, position.getY2()-2);
    bmp.setClipRect(0, 0, bmp.getWidth(), bmp.getHeight());
}
<commit_msg>Calculate renderable text.<commit_after>#include "util/bitmap.h"

#include "gui/context-box.h"
#include "util/font.h"

using namespace std;
using namespace Gui;

ContextItem::ContextItem(){
}
ContextItem::~ContextItem(){
}

ContextBox::ContextBox():
current(0),
fadeState(NotActive),
fontWidth(0),
fontHeight(0),
fadeSpeed(12),
fadeAlpha(0){
}
ContextBox::ContextBox( const ContextBox & copy ):
current(0),
fadeState(NotActive){
    this->context = copy.context;
    this->font = copy.font;
    this->fontWidth = copy.fontWidth;
    this->fontHeight = copy.fontHeight;
    this->fadeSpeed = copy.fadeSpeed;
    this->fadeAlpha = copy.fadeAlpha;
}
ContextBox::~ContextBox(){
}
ContextBox & ContextBox::operator=( const ContextBox & copy){
    this->current = 0;
    this->fadeState = NotActive;
    this->context = copy.context;
    this->font = copy.font;
    this->fontWidth = copy.fontWidth;
    this->fontHeight = copy.fontHeight;
    this->fadeSpeed = copy.fadeSpeed;
    this->fadeAlpha = copy.fadeAlpha;
    return *this;
}

void ContextBox::act(){
    // do fade
    doFade();
}

void ContextBox::render(const Bitmap & work){
    board.render(work);
    drawText(work);
}

void ContextBox::next(){
    if (fadeState != Active){
	return;
    }
}
void ContextBox::previous(){
    if (fadeState != Active){
	return;
    }
}
void ContextBox::adjustLeft(){
}
void ContextBox::adjustRight(){
}
void ContextBox::open(){
    // Set the fade stuff
    fadeState = FadeInBox;
    board.position = position;
    board.position.width = board.position.height = 0;
    board.position.x = position.x+(position.width/2);
    board.position.y = position.y+(position.height/2);
    fadeAlpha = 0;
}

void ContextBox::close(){
    fadeState = FadeOutText;
}

void ContextBox::doFade(){
    switch ( fadeState ){
	case FadeInBox: {
	    if (board.position.x > position.x){
		board.position.x -= fadeSpeed;
	    } else if ( board.position.x < position.x ){
		board.position.x = position.x;
	    }

	    if (board.position.y > position.y){
		board.position.y -= fadeSpeed;
	    } else if (board.position.y < position.y){
		board.position.y = position.y;
	    }

	    if (board.position.width < position.width){
		board.position.width += (fadeSpeed*2);
	    } else if (board.position.width > position.width){
		board.position.width = position.width;
	    }

	    if (board.position.height < position.height){
		board.position.height += (fadeSpeed*2);
	    } else if (board.position.height > position.height){
		board.position.height = position.height;
	    }

	    if (board.position == position){
		fadeState = FadeInText;
	    }

	    break;
	}
	case FadeInText: {
	    if (fadeAlpha < 255){
		fadeAlpha += (fadeSpeed+2);
	    }

	    if (fadeAlpha >= 255){
		fadeAlpha = 255;
		fadeState = Active;
	    }
	    break;
	}
	case FadeOutText: {
	    if (fadeAlpha > 0){
		fadeAlpha -= (fadeSpeed+2);
	    }

	    if (fadeAlpha <= 0){
		fadeAlpha = 0;
		fadeState = FadeOutBox;
	    }
	    break;
	}
	case FadeOutBox: {
	    const int positionX = position.x+(position.width/2);
	    const int positionY = position.y+(position.height/2);
	    if (board.position.x < positionX){
		board.position.x += fadeSpeed;
	    } else if ( board.position.x > positionX ){
		board.position.x = positionX;
	    }

	    if (board.position.y < positionY){
		board.position.y += fadeSpeed;
	    } else if (board.position.y < positionY){
		board.position.y = positionY;
	    }

	    if (board.position.width > 0){
		board.position.width -= (fadeSpeed*2);
	    } else if (board.position.width < 0){
		board.position.width = 0;
	    }

	    if (board.position.height > 0){
		board.position.height -= (fadeSpeed*2);
	    } else if (board.position.height < 0){
		board.position.height = 0;
	    }

	    if (board.position == RectArea()){
		fadeState = NotActive;
	    }

	    break;
	}
	case Active:
	case NotActive:
	default:
	    break;
    }
}

void ContextBox::calculateText(){
    renderable.clear();
    if (context->empty()){
        return;
    }
    renderable.push_back((*context)[current]);
    if (context->size() == 1){
	    return;
    }
    const Font & vFont = Font::getFont(font, fontWidth, fontHeight);
    const double spacing = 1.3;
    const int displayTotal = (int)(position.height/vFont.getHeight()) + 2;

    unsigned int position = current + 1;
    for (int i = 0; i < (int)(displayTotal/2); ++i){
        if (position == context->size()){
            position = 0;
        }
        renderable.push_back((*context)[position]);
        position++;
    }
    position = current - 1;
    for (int i = 0; i < (int)(displayTotal/2); ++i){
        if (position < 0){
            position = context->size()-1;
        }
        renderable.insert(renderable.begin(), (*context)[position]);
        position--;
    }

}

void ContextBox::drawText(const Bitmap & bmp){
    const Font & vFont = Font::getFont(font, fontWidth, fontHeight);
    const double spacing = 1.3;
    bmp.setClipRect(position.x+2, position.y+2, position.getX2()-2, position.getY2()-2);
    bmp.setClipRect(0, 0, bmp.getWidth(), bmp.getHeight());
}
<|endoftext|>


<commit_before>
#pragma once

#include <proxc/version.hpp>

////////////////////////////////////////////////////////////////////////////////
// Detect the compiler
////////////////////////////////////////////////////////////////////////////////

#if defined(_MSC_VER) && !defined(__clang__) // MSVC

#   pragma message("Warning: the native Microsoft compiler is not supported due to lack of proper C++14 support.")

#elif defined(__clang__) && defined(_MSC_VER) // Clang-cl (Clang for Windows)

#   define PROXC_CONFIG_CLANG PROXC_CONFIG_VERSION( \
        __clang_major__, __clang_minor__, __clang_patchlevel__)

#   if PROXC_CONFIG_CLANG < PROXC_CONFIG_VERSION(3, 5, 0)
#       warning "Versions of Clang prior to 3.5.0 are not supported for lack of proper C++14 support."
#   endif

#elif defined(__clang__) && defined(__apple_build_version__) // Apple's Clang

#   if __apple_build_version__ >= 6000051
#       define PROXC_CONFIG_CLANG PROXC_CONFIG_VERSION(3, 6, 0)
#   else
#       warning "Versions of Apple's Clang prior to the one shipped with XCode is not supported."
#   endif

#elif defined(__clang__) // Genuine Clang

#   define PROXC_CONFIG_CLANG PROXC_CONFIG_VERSION( \
        __clang_major__, __clang_minor__, __clang_patchlevel__)

#   if PROXC_CONFIG_CLANG < PROXC_CONFIG_VERSION(3, 5, 0)
#       warning "Versions of Clang prior to 3.5.0 are not supported for lack of proper C++14 support."
#   endif

#elif defined(__GNUC__) // GCC

#   define PROXC_CONFIC_GCC PROXC_CONFIG_VERSION(\
        __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)

#   if PROXC_CONFIC_GCC < PROXC_CONFIG_VERSION(5, 0, 0)
#       warning "Versions of GCC prior to 5.0.0 are not supported for lack of proper C++14 support."
#   endif

#else

#   warning "Your compiler was not detected properly and might not work for ProXC."

#endif

////////////////////////////////////////////////////////////////////////////////
// Detect the compiler for general C++14 capabilities
////////////////////////////////////////////////////////////////////////////////

#if (__cplusplus < 201400)
#   if defined(_MSC_VER)
#       pragma message("Warning: Your compiler doesn't provide C++14 or higher capabilities. Try adding the compiler flag 'std=c++14' or '-std=c++1y'.")
#   else
#       warning "Your compiler doesn't provde C++14 or higher capabilities. Try adding the compiler flag 'std=c++14' or 'std=c++1y'"
#   endif
#endif

////////////////////////////////////////////////////////////////////////////////
// Detect the standard library
////////////////////////////////////////////////////////////////////////////////

#include <cstddef>

#if defined(_LIBCPP_VERSION)

#   define PROXC_CONFIG_LIBCPP PROXC_CONFIG_VERSION( \
        ((_LIBCPP_VERSION) / 1000) % 10, 0, (_LIBCPP_VERSION) % 1000)

#   if PROXC_CONFIG_LIBCPP < PROXC_CONFIG_VERSION(1, 0, 101)
#       warning "Versions of libc++ prior to the one shipped with Clang 3.5.0 are not supported for lack of full C++14 support."
#   endif

#elif defined(__GLIBCXX__)

#   if __GLIBCXX__ < 20150422 // --> the libstdc++ shipped with GCC 5.1.0
#       warning "Versions of libstdc++ prior to the one shipped with GCC 5.1.0 are not supported for lack of full C++14 support."
#   endif

#   define PROXC_CONFIG_LIBSTDCXX

#elif defined(_MSC_VER)

#   define PROXC_CONFIG_LIBMSVCCXX

#else

#   warning "Your standard library was not detected properly and might not work for ProXC."

#endif

////////////////////////////////////////////////////////////////////////////////
// Namespace macros
////////////////////////////////////////////////////////////////////////////////

#define PROXC_NAMESPACE_BEGIN namespace proxc {

#define PROXC_NAMESPACE_END }

<commit_msg>Added 32/64-bit configuration.<commit_after>
#pragma once

#include <proxc/version.hpp>

////////////////////////////////////////////////////////////////////////////////
// Detect the compiler
////////////////////////////////////////////////////////////////////////////////

#if defined(_MSC_VER) && !defined(__clang__) // MSVC

#   pragma message("Warning: the native Microsoft compiler is not supported due to lack of proper C++14 support.")

#elif defined(__clang__) && defined(_MSC_VER) // Clang-cl (Clang for Windows)

#   define PROXC_CONFIG_CLANG PROXC_CONFIG_VERSION( \
        __clang_major__, __clang_minor__, __clang_patchlevel__)

#   if PROXC_CONFIG_CLANG < PROXC_CONFIG_VERSION(3, 5, 0)
#       warning "Versions of Clang prior to 3.5.0 are not supported for lack of proper C++14 support."
#   endif

#elif defined(__clang__) && defined(__apple_build_version__) // Apple's Clang

#   if __apple_build_version__ >= 6000051
#       define PROXC_CONFIG_CLANG PROXC_CONFIG_VERSION(3, 6, 0)
#   else
#       warning "Versions of Apple's Clang prior to the one shipped with XCode is not supported."
#   endif

#elif defined(__clang__) // Genuine Clang

#   define PROXC_CONFIG_CLANG PROXC_CONFIG_VERSION( \
        __clang_major__, __clang_minor__, __clang_patchlevel__)

#   if PROXC_CONFIG_CLANG < PROXC_CONFIG_VERSION(3, 5, 0)
#       warning "Versions of Clang prior to 3.5.0 are not supported for lack of proper C++14 support."
#   endif

#elif defined(__GNUC__) // GCC

#   define PROXC_CONFIC_GCC PROXC_CONFIG_VERSION(\
        __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)

#   if PROXC_CONFIC_GCC < PROXC_CONFIG_VERSION(5, 0, 0)
#       warning "Versions of GCC prior to 5.0.0 are not supported for lack of proper C++14 support."
#   endif

#else

#   warning "Your compiler was not detected properly and might not work for ProXC."

#endif

////////////////////////////////////////////////////////////////////////////////
// Detect the compiler for general C++14 capabilities
////////////////////////////////////////////////////////////////////////////////

#if (__cplusplus < 201400)
#   if defined(_MSC_VER)
#       pragma message("Warning: Your compiler doesn't provide C++14 or higher capabilities. Try adding the compiler flag 'std=c++14' or '-std=c++1y'.")
#   else
#       warning "Your compiler doesn't provde C++14 or higher capabilities. Try adding the compiler flag 'std=c++14' or 'std=c++1y'"
#   endif
#endif

////////////////////////////////////////////////////////////////////////////////
// Detect the standard library
////////////////////////////////////////////////////////////////////////////////

#include <cstddef>

#if defined(_LIBCPP_VERSION)

#   define PROXC_CONFIG_LIBCPP PROXC_CONFIG_VERSION( \
        ((_LIBCPP_VERSION) / 1000) % 10, 0, (_LIBCPP_VERSION) % 1000)

#   if PROXC_CONFIG_LIBCPP < PROXC_CONFIG_VERSION(1, 0, 101)
#       warning "Versions of libc++ prior to the one shipped with Clang 3.5.0 are not supported for lack of full C++14 support."
#   endif

#elif defined(__GLIBCXX__)

#   if __GLIBCXX__ < 20150422 // --> the libstdc++ shipped with GCC 5.1.0
#       warning "Versions of libstdc++ prior to the one shipped with GCC 5.1.0 are not supported for lack of full C++14 support."
#   endif

#   define PROXC_CONFIG_LIBSTDCXX

#elif defined(_MSC_VER)

#   define PROXC_CONFIG_LIBMSVCCXX

#else

#   warning "Your standard library was not detected properly and might not work for ProXC."

#endif

////////////////////////////////////////////////////////////////////////////////
// Detect 32- or 64-bit architecture
////////////////////////////////////////////////////////////////////////////////

#ifndef PROXC_ARCH

#if defined(__powerpc__) || defined(__ppc__) || defined(__PPC__) /* PowerPC */

#   if defined(__powerpc64__) || defined(__ppc64__) || defined(__PPC64__) || \
       defined(__64BIT__)     || defined(_LP64)     || defined(__LP64__) /* PowerPC 64-bit */

#       define PROXC_ARCH 64

#   else /* PowerPC 32-bit */

#       define PROXC_ARCH 32

#   endif

#elif defined(__x86_64__) || defined(_M_X64) /* x86 64-bit */

#   define PROXC_ARCH 64

#elif defined(__i386) || defined(_M_IX86) /* x86 32-bit */

#   define PROXC_ARCH 32

#elif defined(_WIN32) || defined(_WIN64) /* Windows */

#   if defined(_WIN64) /* Windows 64-bit */

#       define PROXC_ARCH 64

#   else /* Windows 32-bit */

#       define PROXC_ARCH 32

#   endif

#else /* Unknown architecture */

#   define PROXC_ARCH 64
#   warning "Architecture was neither correctly determined nor specified. Defaults to 64-bit. This can be set with -DPROXC_ARCH={32/64}, for 32-bit or 64-bit respectively."

#endif

#else

static_assert(((PROXC_ARCH) == 32) || ((PROXC_ARCH) == 64), "Architecture specified by PROXC_ARCH only supports 32 or 64 value.");

#endif // PROXC_ARCH

////////////////////////////////////////////////////////////////////////////////
// Namespace macros
////////////////////////////////////////////////////////////////////////////////

#define PROXC_NAMESPACE_BEGIN namespace proxc {

#define PROXC_NAMESPACE_END }

<|endoftext|>


<commit_before>/*
Copyright (c) 2017 InversePalindrome
Memento Mori - MenuState.cpp
InversePalindrome.com
*/

#include "MenuState.hpp"
#include "StateMachine.hpp"

#include <SFGUI/Image.hpp>
#include <Thor/Math/Distributions.hpp>
#include <Thor/Particles/Emitters.hpp>
#include <Thor/Particles/Affectors.hpp>
#include <Thor/Animations/FadeAnimation.hpp>
#include <Thor/Animations/FrameAnimation.hpp>

MenuState::MenuState(StateMachine& stateMachine, SharedData& sharedData) :
	State(stateMachine, sharedData),
	background(sharedData.textures[Textures::ID::MenuBackground]),
	attackingSkeleton(sharedData.textures[Textures::ID::DecoAttackingSkeleton]),
	jumpingSkeleton(sharedData.textures[Textures::ID::DecoJumpingSkeleton]),
	playButton(sfg::Button::Create()),
	settingsButton(sfg::Button::Create()),
	leaderboardButton(sfg::Button::Create()),
	attackAnimator(),
	jumpAnimator(),
	particleSystem(),
	animationCountdown(sf::seconds(3.f))
{
	background.setScale(static_cast<float>(sharedData.window.getSize().x) / background.getLocalBounds().width,
		static_cast<float>(sharedData.window.getSize().y) / background.getLocalBounds().height);

	attackingSkeleton.setPosition(sf::Vector2f(60.f, 700.f));
	jumpingSkeleton.setPosition(sf::Vector2f(2000.f, 700.f));

	playButton->SetImage(sfg::Image::Create(sharedData.images[Images::ID::PlayButton]));
	playButton->SetPosition(sf::Vector2f(1120.f, 1000.f));
	playButton->GetSignal(sfg::Widget::OnLeftClick).Connect([this] { transitionToGame(); });

	settingsButton->SetImage(sfg::Image::Create(sharedData.images[Images::ID::SettingsButton]));
	settingsButton->SetPosition(sf::Vector2f(720.f, 1000.f));
	settingsButton->GetSignal(sfg::Widget::OnLeftClick).Connect([this] { transitionToSettings(); });

	leaderboardButton->SetImage(sfg::Image::Create(sharedData.images[Images::ID::LeaderboardButton]));
	leaderboardButton->SetPosition(sf::Vector2f(1520.f, 1000.f));
	leaderboardButton->GetSignal(sfg::Widget::OnLeftClick).Connect([this] { transitionToLeaderboard(); });

	thor::UniversalEmitter emitter;
	emitter.setEmissionRate(2.f);
	emitter.setParticleLifetime(thor::Distributions::uniform(sf::seconds(3.f), sf::seconds(8.f)));
	emitter.setParticlePosition(thor::Distributions::rect(sf::Vector2f(sharedData.window.getSize().x / 2.f,sharedData.window.getSize().y / 2.f - 400.f),
		sf::Vector2f(sharedData.window.getSize().x / 2.f, sharedData.window.getSize().y / 2.f - 400.f)));

	thor::FrameAnimation attackAnimation;
	attackAnimation.addFrame(1.f, sf::IntRect(0u, 0u, 518u, 512u));
	attackAnimation.addFrame(1.f, sf::IntRect(519u, 0u, 518u, 512u));
	attackAnimation.addFrame(1.f, sf::IntRect(1038u, 0u, 518u, 512u));
	attackAnimation.addFrame(1.f, sf::IntRect(0u, 513u, 518u, 512u));
	attackAnimation.addFrame(1.f, sf::IntRect(519u, 513u, 518u, 512u));
	attackAnimation.addFrame(1.f, sf::IntRect(1038u, 513u, 518u, 512u));
	attackAnimation.addFrame(1.f, sf::IntRect(0u, 0u, 518u, 512u));

	thor::FrameAnimation jumpAnimation;
	jumpAnimation.addFrame(1.f, sf::IntRect(0u, 0u, 446u, 544u));
	jumpAnimation.addFrame(1.f, sf::IntRect(447u, 0u, 446u, 544u));
	jumpAnimation.addFrame(1.f, sf::IntRect(894u, 0u, 446u, 544u));
	jumpAnimation.addFrame(1.f, sf::IntRect(1341u, 0u, 446u, 544u));
	jumpAnimation.addFrame(1.f, sf::IntRect(0u, 545u, 446u, 544u));
	jumpAnimation.addFrame(1.f, sf::IntRect(447u, 545u, 446u, 544u));
	jumpAnimation.addFrame(1.f, sf::IntRect(894u, 545u, 446u, 544u));
	jumpAnimation.addFrame(1.f, sf::IntRect(0u, 0u, 446u, 544u));

	attackAnimator.addAnimation("Attack", attackAnimation, sf::seconds(0.6f));
	attackAnimator.playAnimation("Attack");

	jumpAnimator.addAnimation("Jump", jumpAnimation, sf::seconds(0.6f));
	jumpAnimator.playAnimation("Jump");

	particleSystem.setTexture(sharedData.textures[Textures::ID::StarParticle]);
	particleSystem.addEmitter(emitter);
	particleSystem.addAffector(thor::AnimationAffector(thor::FadeAnimation(0.3f, 0.3f)));

	sharedData.hud.SetProperty("Button", "BackgroundColor", sf::Color::Transparent);
	sharedData.hud.SetProperty("Button", "BorderColor", sf::Color::Transparent);
	sharedData.hud.SetProperty("Button:PRELIGHT", "BackgroundColor", sf::Color::Transparent);

	sharedData.hud.Add(playButton);
	sharedData.hud.Add(settingsButton);
	sharedData.hud.Add(leaderboardButton);

	sharedData.soundManager.playMusic("SpookyMusic.wav", true);
}

void MenuState::handleEvent(const sf::Event& event)
{

}

void MenuState::update(sf::Time deltaTime)
{
	this->playButton->Show(true);
	this->settingsButton->Show(true);
	this->leaderboardButton->Show(true);

	this->animationCountdown -= deltaTime;

	this->attackAnimator.update(deltaTime);
	this->attackAnimator.animate(this->attackingSkeleton);

	if(!this->attackAnimator.isPlayingAnimation() && this->animationCountdown.asSeconds() <= 0.f)
	{
		this->attackAnimator.playAnimation("Attack");
		this->animationCountdown = sf::seconds(2.f);
	}

	this->jumpAnimator.update(deltaTime);
	this->jumpAnimator.animate(this->jumpingSkeleton);

	if (!this->jumpAnimator.isPlayingAnimation() && this->animationCountdown.asSeconds() <= 1.5f)
	{
		this->jumpAnimator.playAnimation("Jump");
	}

	this->particleSystem.update(deltaTime);
}

void MenuState::draw()
{
	this->sharedData.window.draw(this->background);
	this->sharedData.window.draw(this->attackingSkeleton);
	this->sharedData.window.draw(this->jumpingSkeleton);
	this->sharedData.window.draw(this->particleSystem);
}

bool MenuState::isTransparent()
{
	return false;
}

void MenuState::transitionToGame()
{
	this->playButton->Show(false);
	this->settingsButton->Show(false);
	this->leaderboardButton->Show(false);

	this->sharedData.soundManager.stopAllMusic();

	this->stateMachine.popState();
	this->stateMachine.pushState(StateMachine::StateID::Game);
}

void MenuState::transitionToSettings()
{
	this->playButton->Show(false);
	this->settingsButton->Show(false);
	this->leaderboardButton->Show(false);

	this->stateMachine.pushState(StateMachine::StateID::Settings);
}

void MenuState::transitionToLeaderboard()
{
	this->playButton->Show(false);
	this->settingsButton->Show(false);
	this->leaderboardButton->Show(false);

	this->stateMachine.pushState(StateMachine::StateID::Leaderboard);
}<commit_msg>Delete MenuState.cpp<commit_after><|endoftext|>


<commit_before>/** \copyright
 * Copyright (c) 2012, Stuart W Baker
 * 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.
 *
 * 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 HOLDER 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.
 *
 * \file OS.hxx
 * This file represents a C++ language abstraction of common operating
 * system calls.
 *
 * @author Stuart W. Baker
 * @date 28 May 2012
 */

#ifndef _os_hxx_
#define _os_hxx_

#include <endian.h>

#include "utils/macros.h"
#include "os/os.h"

/** This class provides a threading API.
 */
class OSThread
{
public:
    /** Create a thread.
     * @param name name of thread, NULL for an auto generated name
     * @param priority priority of created thread, 0 means default,
     *        lower numbers means lower priority, higher numbers mean higher
     *        priority
     * @param stack_size size in bytes of the created thread's stack
     * @param start_routine entry point of the thread
     * @param arg entry parameter to the thread
     */
    OSThread(const char *name, int priority, size_t stack_size,
             void *(*start_routine)(void*), void *arg)
    {
        os_thread_create(&handle, name, priority, stack_size, start_routine, arg);
    }

    /** Creates a thread via inheritance. The user must overload the entry()
     * function and call the start() method whenever convenient. */
    OSThread()
    {
    }

   /** Starts the thread.  This call can be used when OSThread is inherited and
    * there is a virtual entry point.
    * @param name name of thread, NULL for an auto generated name
    * @param priority priority of created thread, 0 means default,
    *        lower numbers means lower priority, higher numbers mean higher
    *        priority
    * @param stack_size size in bytes of the created thread's stack
    */
    void start(const char *name, int priority, size_t stack_size)
    {
        os_thread_create(&handle, name, priority, stack_size, start, this);
    }

    /** Default destructor. */
    ~OSThread()
    {
    }

protected:
    /** User entry point for the created thread.
     * @return exit status
     */
    virtual void *entry() {
        HASSERT(0 && "forgot to overload OSThread entry point. This thread "
                     "would do nothing.");
        return NULL;
    }

private:
    /** Starting point for a new thread.
     * @param arg pointer to an OSThread instance
     * @return exit status
     */
    static void *start(void *arg)
    {
        OSThread *thread = (OSThread*)arg;
        return thread->entry();
    }

    DISALLOW_COPY_AND_ASSIGN(OSThread);

    /** Private thread handle. */
    os_thread_t handle;
};

/** This class provides support for one time initialization.
 */
class OSThreadOnce
{
public:
    /** One time intialization constructor.
     * @param routine method to call once
     */
    OSThreadOnce(void (*routine)(void))
        : handle(OS_THREAD_ONCE_INIT),
          routine(routine)
    {
    }

    /** call one time intialization routine
     * @return 0 upon success
     */
    int once(void)
    {
        return os_thread_once(&handle, routine);
    }

    /** Default Destructor */
    ~OSThreadOnce()
    {
    }

private:
    DISALLOW_COPY_AND_ASSIGN(OSThreadOnce);

    /** Private once handle. */
    os_thread_once_t handle;

    /** One time initialization routine */
    void (*routine)(void);
};

/** This class provides a timer API.
 * The return value of the callback determines the behavior of how the timer is
 * rearmed.  @ref OS_TIMER_NONE indicates that the timer is not restarted,
 * OS_TIMER_RESTART restarts the timer with the period of the last timeout,
 * OS_TIMER_DELETE deletes the timer, and all other values indicate the restart
 * period in nanoseconds.
 */
class OSTimer
{
public:
    /** Create a new timer.
     * @param callback callback associated with timer
     * @param data1 data to pass along with callback
     * @param data2 data to pass along with callback
     */
    OSTimer(long long (*callback)(void*, void*), void *data1, void *data2)
    {
        handle = os_timer_create(callback, data1, data2);
    }

    /** Delete a timer.
     */
    ~OSTimer()
    {
        os_timer_delete(handle);
    }

    /** Start a timer.
     * @param period period in nanoseconds before expiration
     */
    void start(long long period)
    {
        os_timer_start(handle, period);
    }

    /** Delete a timer.
     */
    void stop()
    {
        os_timer_stop(handle);
    }

private:
    /** Default constructor.
     */
    OSTimer();

    DISALLOW_COPY_AND_ASSIGN(OSTimer);

    /** Private timer handle. */
    os_timer_t handle;
};

/** This class provides a counting semaphore API.
 */
class OSSem
{
public:
    /** Initialize a Semaphore.
     * @param value initial count
     */
    OSSem(unsigned int value = 0)
    {
        os_sem_init(&handle, value);
    }

    ~OSSem()
    {
        os_sem_destroy(&handle);
    }

    /** Post (increment) a semaphore.
     */
    void post()
    {
        os_sem_post(&handle);
    }

#if defined (__FreeRTOS__)
    void post_from_isr()
    {
        os_sem_post_from_isr(&handle);
    }
#endif

    /** Wait on (decrement) a semaphore.
     */
    void wait()
    {
        os_sem_wait(&handle);
    }

    /** Wait on (decrement) a semaphore with timeout condition.
     * @param timeout timeout in nanoseconds, else OS_WAIT_FOREVER to wait forever
     * @return 0 upon success, else -1 with errno set to indicate error
     */
    int timedwait(long long timeout)
    {
        return os_sem_timedwait(&handle, timeout);
    }

private:
    DISALLOW_COPY_AND_ASSIGN(OSSem);

    /** Private semaphore handle. */
    os_sem_t handle;
};

/** This class provides a simple message queue.
 */
class OSMQ
{
public:

    /** Constructor.
     * @param length number of items the queue can hold
     * @param item_size size of each item in bytes in the queue
     */
    OSMQ(size_t length, size_t item_size)
        : handle(os_mq_create(length, item_size))
    {
        HASSERT(handle != NULL);
    }

    /** Destructor.
     */
    ~OSMQ()
    {
        /** @todo (Stuart Baker) need a way to destroy a message queue */
    }

    /** Blocking send of a message to a queue.
     * @param data message to copy into queue
     */
    void send(const void *data)
    {
        os_mq_send(handle, data);
    }

    /** Send a message to a queue with a timeout.
     * @param data message to copy into queue
     * @param timeout time in nanoseconds to wait for queue to be able to accept message
     * @return OS_MQ_NONE on success, OS_MQ_TIMEDOUT on timeout
     */
    int timedsend(const void *data, long long timeout)
    {
        return os_mq_timedsend(handle, data, timeout);
    }

    /** Blocking receive a message from a queue.
     * @param data location to copy message from the queue
     */
    void receive(void *data)
    {
        os_mq_receive(handle, data);
    }

    /** Receive a message from a queue.
     * @param data location to copy message from the queue
     * @param timeout time in nanoseconds to wait for queue to have a message available
     * @return OS_MQ_NONE on success, OS_MQ_TIMEDOUT on timeout
     */
    int timedreceive(void *data, long long timeout)
    {
        return os_mq_timedreceive(handle, data, timeout);
    }

    /** Send of a message to a queue from ISR context.
     * @param data message to copy into queue
     * @param woken is the task woken up
     * @return OS_MQ_NONE on success, else OS_MQ_FULL
     */
    int send_from_isr(const void *data, int *woken)
    {
        return os_mq_send_from_isr(handle, data, woken);
    }

    /** Receive a message from a queue from ISR context.
     * @param data location to copy message from the queue
     * @param woken is the task woken up
     * @return OS_MQ_NONE on success, else OS_MQ_FULL
     */
    int receive_from_isr(void *data, int *woken)
    {
        return os_mq_receive_from_isr(handle, data, woken);
    }

    /** Return the number of messages pending in the queue.
     * @return number of messages in the queue
     */
    int num_pending()
    {
        return os_mq_num_pending(handle);
    }

    /** Return the number of messages pending in the queue from ISR context.
     * @return number of messages in the queue
     */
    int pending_from_isr()
    {
        return os_mq_num_pending_from_isr(handle);
    }

    /** Check if a queue is full from ISR context.
     * @return non-zero if the queue is full.
     */
    int is_full_from_isr()
    {
        return os_mq_is_full_from_isr(handle);
    }

private:
    /** Default Constructor.
     */
    OSMQ();

    /** Private message queue handle */
    os_mq_t handle;

    DISALLOW_COPY_AND_ASSIGN(OSMQ);
};

/** This class provides a mutex API.
 */
class OSMutex
{
public:
    /** Initialize a mutex.
     * @param recursive false creates a normal mutex, true creates a recursive mutex
     */
    OSMutex(bool recursive = false)
    {
        if (recursive)
        {
            os_recursive_mutex_init(&handle);
        }
        else
        {
            os_mutex_init(&handle);
        }
    }

    /** Lock a mutex.
     */
    void lock()
    {
        os_mutex_lock(&handle);
    }

    /** Unlock a mutex.
     */
    void unlock()
    {
        os_mutex_unlock(&handle);
    }

    /** Destructor */
    ~OSMutex()
    {
        os_mutex_destroy(&handle);
    }

private:
    DISALLOW_COPY_AND_ASSIGN(OSMutex);

    friend class OSMutexLock;
    /** Private mutex handle. */
    os_mutex_t handle;
};

/**
 * Class to allow convenient locking and unlocking of mutexes in a C context.
 * The mutex will be automatically unlocked when the context is left, even if
 * there are multiple return, break or continue statements.
 *
 * Usage:
 *
 * void foo()
 * {
 *   // ...non-critical-section code here...
 *   {
 *     OSMutexLock locker(&mutex_);
 *     // ...critical section here...
 *     if (error) return;
 *     // ...more critical code here...
 *   }
 *   // ...at this point the mutex is unlocked...
 *   // ...more non-critical-section code here...
 * }
 *
 */
class OSMutexLock
{
public:
    OSMutexLock(OSMutex* mutex)
        : mutex_(&mutex->handle)
    {
        os_mutex_lock(mutex_);
    }

    OSMutexLock(os_mutex_t* mutex)
        : mutex_(mutex)
    {
        os_mutex_lock(mutex_);
    }

    ~OSMutexLock()
    {
        os_mutex_unlock(mutex_);
    }
private:
    DISALLOW_COPY_AND_ASSIGN(OSMutexLock);

    os_mutex_t* mutex_;
};

/** This catches programming errors where you declare a mutex locker object
 *  without a name like this:
 *
 *  void foo()
 *  {
 *    OSMutexLock(&mutex_);
 *    // ...critical section here...
 *  }

 *  The above is valid C++, which creates a temporary OSMutexLock object, locks
 *  it, then immediately destructs the object, unlocking the mutex in the same
 *  line it was created. Thus the critical sections goes unprotected
 *  unintentionally.
 *
 *  The correct code is
 *
 *  void foo()
 *  {
 *    OSMutexLock locker(&mutex_);
 *    // ...critical section here...
 *  }
 *
 */
#define OSMutexLock(l) int error_omitted_mutex_lock_variable[-1]

/** Allows for OS abstracted access to time.
 */
class OSTime
{
public:
    /** Get the monotonic time since the system started.
     * @return time in nanoseconds since system start
     */
    static long long get_monotonic(void)
    {
        return os_get_time_monotonic();
    }

private:
    DISALLOW_COPY_AND_ASSIGN(OSTime);

    /* Private default constructor prevents instantiating this class. */
    OSTime();

    /** Default destructor. */
    ~OSTime();
};

#endif /* _os_hxx_ */
<commit_msg>Add support in OSThread to detect whether the start function needs to be invoked or we created the thread in the constructor.<commit_after>/** \copyright
 * Copyright (c) 2012, Stuart W Baker
 * 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.
 *
 * 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 HOLDER 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.
 *
 * \file OS.hxx
 * This file represents a C++ language abstraction of common operating
 * system calls.
 *
 * @author Stuart W. Baker
 * @date 28 May 2012
 */

#ifndef _os_hxx_
#define _os_hxx_

#include <endian.h>

#include "utils/macros.h"
#include "os/os.h"

/** This class provides a threading API.
 */
class OSThread
{
public:
    /** Create a thread.
     * @param name name of thread, NULL for an auto generated name
     * @param priority priority of created thread, 0 means default,
     *        lower numbers means lower priority, higher numbers mean higher
     *        priority
     * @param stack_size size in bytes of the created thread's stack
     * @param start_routine entry point of the thread
     * @param arg entry parameter to the thread
     */
    OSThread(const char *name, int priority, size_t stack_size,
             void *(*start_routine)(void*), void *arg)
    {
        os_thread_create(&handle, name, priority, stack_size, start_routine, arg);
    }

    /** Creates a thread via inheritance. The user must overload the entry()
     * function and call the start() method whenever convenient. */
    OSThread()
        : handle(0)
    {
    }

   /** Starts the thread.  This call can be used when OSThread is inherited and
    * there is a virtual entry point.
    * @param name name of thread, NULL for an auto generated name
    * @param priority priority of created thread, 0 means default,
    *        lower numbers means lower priority, higher numbers mean higher
    *        priority
    * @param stack_size size in bytes of the created thread's stack
    */
    void start(const char *name, int priority, size_t stack_size)
    {
        os_thread_create(&handle, name, priority, stack_size, start, this);
    }

    /** Default destructor. */
    ~OSThread()
    {
    }

    /** Returns true if a thread was already created. */
    bool is_created()
    {
        return handle != 0;
    }

protected:
    /** User entry point for the created thread.
     * @return exit status
     */
    virtual void *entry() {
        HASSERT(0 && "forgot to overload OSThread entry point. This thread "
                     "would do nothing.");
        return NULL;
    }

private:
    /** Starting point for a new thread.
     * @param arg pointer to an OSThread instance
     * @return exit status
     */
    static void *start(void *arg)
    {
        OSThread *thread = (OSThread*)arg;
        return thread->entry();
    }

    DISALLOW_COPY_AND_ASSIGN(OSThread);

    /** Private thread handle. */
    os_thread_t handle;
};

/** This class provides support for one time initialization.
 */
class OSThreadOnce
{
public:
    /** One time intialization constructor.
     * @param routine method to call once
     */
    OSThreadOnce(void (*routine)(void))
        : handle(OS_THREAD_ONCE_INIT),
          routine(routine)
    {
    }

    /** call one time intialization routine
     * @return 0 upon success
     */
    int once(void)
    {
        return os_thread_once(&handle, routine);
    }

    /** Default Destructor */
    ~OSThreadOnce()
    {
    }

private:
    DISALLOW_COPY_AND_ASSIGN(OSThreadOnce);

    /** Private once handle. */
    os_thread_once_t handle;

    /** One time initialization routine */
    void (*routine)(void);
};

/** This class provides a timer API.
 * The return value of the callback determines the behavior of how the timer is
 * rearmed.  @ref OS_TIMER_NONE indicates that the timer is not restarted,
 * OS_TIMER_RESTART restarts the timer with the period of the last timeout,
 * OS_TIMER_DELETE deletes the timer, and all other values indicate the restart
 * period in nanoseconds.
 */
class OSTimer
{
public:
    /** Create a new timer.
     * @param callback callback associated with timer
     * @param data1 data to pass along with callback
     * @param data2 data to pass along with callback
     */
    OSTimer(long long (*callback)(void*, void*), void *data1, void *data2)
    {
        handle = os_timer_create(callback, data1, data2);
    }

    /** Delete a timer.
     */
    ~OSTimer()
    {
        os_timer_delete(handle);
    }

    /** Start a timer.
     * @param period period in nanoseconds before expiration
     */
    void start(long long period)
    {
        os_timer_start(handle, period);
    }

    /** Delete a timer.
     */
    void stop()
    {
        os_timer_stop(handle);
    }

private:
    /** Default constructor.
     */
    OSTimer();

    DISALLOW_COPY_AND_ASSIGN(OSTimer);

    /** Private timer handle. */
    os_timer_t handle;
};

/** This class provides a counting semaphore API.
 */
class OSSem
{
public:
    /** Initialize a Semaphore.
     * @param value initial count
     */
    OSSem(unsigned int value = 0)
    {
        os_sem_init(&handle, value);
    }

    ~OSSem()
    {
        os_sem_destroy(&handle);
    }

    /** Post (increment) a semaphore.
     */
    void post()
    {
        os_sem_post(&handle);
    }

#if defined (__FreeRTOS__)
    void post_from_isr()
    {
        os_sem_post_from_isr(&handle);
    }
#endif

    /** Wait on (decrement) a semaphore.
     */
    void wait()
    {
        os_sem_wait(&handle);
    }

    /** Wait on (decrement) a semaphore with timeout condition.
     * @param timeout timeout in nanoseconds, else OS_WAIT_FOREVER to wait forever
     * @return 0 upon success, else -1 with errno set to indicate error
     */
    int timedwait(long long timeout)
    {
        return os_sem_timedwait(&handle, timeout);
    }

private:
    DISALLOW_COPY_AND_ASSIGN(OSSem);

    /** Private semaphore handle. */
    os_sem_t handle;
};

/** This class provides a simple message queue.
 */
class OSMQ
{
public:

    /** Constructor.
     * @param length number of items the queue can hold
     * @param item_size size of each item in bytes in the queue
     */
    OSMQ(size_t length, size_t item_size)
        : handle(os_mq_create(length, item_size))
    {
        HASSERT(handle != NULL);
    }

    /** Destructor.
     */
    ~OSMQ()
    {
        /** @todo (Stuart Baker) need a way to destroy a message queue */
    }

    /** Blocking send of a message to a queue.
     * @param data message to copy into queue
     */
    void send(const void *data)
    {
        os_mq_send(handle, data);
    }

    /** Send a message to a queue with a timeout.
     * @param data message to copy into queue
     * @param timeout time in nanoseconds to wait for queue to be able to accept message
     * @return OS_MQ_NONE on success, OS_MQ_TIMEDOUT on timeout
     */
    int timedsend(const void *data, long long timeout)
    {
        return os_mq_timedsend(handle, data, timeout);
    }

    /** Blocking receive a message from a queue.
     * @param data location to copy message from the queue
     */
    void receive(void *data)
    {
        os_mq_receive(handle, data);
    }

    /** Receive a message from a queue.
     * @param data location to copy message from the queue
     * @param timeout time in nanoseconds to wait for queue to have a message available
     * @return OS_MQ_NONE on success, OS_MQ_TIMEDOUT on timeout
     */
    int timedreceive(void *data, long long timeout)
    {
        return os_mq_timedreceive(handle, data, timeout);
    }

    /** Send of a message to a queue from ISR context.
     * @param data message to copy into queue
     * @param woken is the task woken up
     * @return OS_MQ_NONE on success, else OS_MQ_FULL
     */
    int send_from_isr(const void *data, int *woken)
    {
        return os_mq_send_from_isr(handle, data, woken);
    }

    /** Receive a message from a queue from ISR context.
     * @param data location to copy message from the queue
     * @param woken is the task woken up
     * @return OS_MQ_NONE on success, else OS_MQ_FULL
     */
    int receive_from_isr(void *data, int *woken)
    {
        return os_mq_receive_from_isr(handle, data, woken);
    }

    /** Return the number of messages pending in the queue.
     * @return number of messages in the queue
     */
    int num_pending()
    {
        return os_mq_num_pending(handle);
    }

    /** Return the number of messages pending in the queue from ISR context.
     * @return number of messages in the queue
     */
    int pending_from_isr()
    {
        return os_mq_num_pending_from_isr(handle);
    }

    /** Check if a queue is full from ISR context.
     * @return non-zero if the queue is full.
     */
    int is_full_from_isr()
    {
        return os_mq_is_full_from_isr(handle);
    }

private:
    /** Default Constructor.
     */
    OSMQ();

    /** Private message queue handle */
    os_mq_t handle;

    DISALLOW_COPY_AND_ASSIGN(OSMQ);
};

/** This class provides a mutex API.
 */
class OSMutex
{
public:
    /** Initialize a mutex.
     * @param recursive false creates a normal mutex, true creates a recursive mutex
     */
    OSMutex(bool recursive = false)
    {
        if (recursive)
        {
            os_recursive_mutex_init(&handle);
        }
        else
        {
            os_mutex_init(&handle);
        }
    }

    /** Lock a mutex.
     */
    void lock()
    {
        os_mutex_lock(&handle);
    }

    /** Unlock a mutex.
     */
    void unlock()
    {
        os_mutex_unlock(&handle);
    }

    /** Destructor */
    ~OSMutex()
    {
        os_mutex_destroy(&handle);
    }

private:
    DISALLOW_COPY_AND_ASSIGN(OSMutex);

    friend class OSMutexLock;
    /** Private mutex handle. */
    os_mutex_t handle;
};

/**
 * Class to allow convenient locking and unlocking of mutexes in a C context.
 * The mutex will be automatically unlocked when the context is left, even if
 * there are multiple return, break or continue statements.
 *
 * Usage:
 *
 * void foo()
 * {
 *   // ...non-critical-section code here...
 *   {
 *     OSMutexLock locker(&mutex_);
 *     // ...critical section here...
 *     if (error) return;
 *     // ...more critical code here...
 *   }
 *   // ...at this point the mutex is unlocked...
 *   // ...more non-critical-section code here...
 * }
 *
 */
class OSMutexLock
{
public:
    OSMutexLock(OSMutex* mutex)
        : mutex_(&mutex->handle)
    {
        os_mutex_lock(mutex_);
    }

    OSMutexLock(os_mutex_t* mutex)
        : mutex_(mutex)
    {
        os_mutex_lock(mutex_);
    }

    ~OSMutexLock()
    {
        os_mutex_unlock(mutex_);
    }
private:
    DISALLOW_COPY_AND_ASSIGN(OSMutexLock);

    os_mutex_t* mutex_;
};

/** This catches programming errors where you declare a mutex locker object
 *  without a name like this:
 *
 *  void foo()
 *  {
 *    OSMutexLock(&mutex_);
 *    // ...critical section here...
 *  }

 *  The above is valid C++, which creates a temporary OSMutexLock object, locks
 *  it, then immediately destructs the object, unlocking the mutex in the same
 *  line it was created. Thus the critical sections goes unprotected
 *  unintentionally.
 *
 *  The correct code is
 *
 *  void foo()
 *  {
 *    OSMutexLock locker(&mutex_);
 *    // ...critical section here...
 *  }
 *
 */
#define OSMutexLock(l) int error_omitted_mutex_lock_variable[-1]

/** Allows for OS abstracted access to time.
 */
class OSTime
{
public:
    /** Get the monotonic time since the system started.
     * @return time in nanoseconds since system start
     */
    static long long get_monotonic(void)
    {
        return os_get_time_monotonic();
    }

private:
    DISALLOW_COPY_AND_ASSIGN(OSTime);

    /* Private default constructor prevents instantiating this class. */
    OSTime();

    /** Default destructor. */
    ~OSTime();
};

#endif /* _os_hxx_ */
<|endoftext|>


<commit_before>/*
 * Software License Agreement (BSD License)
 *
 *  Copyright (c) 2010, Willow Garage, 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 Willow Garage, 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.
 *
 * $Id$
 *
 */

#include <gtest/gtest.h>
#include <pcl/common/common.h>
#include <pcl/common/distances.h>
#include <pcl/common/eigen.h>
#include <pcl/point_types.h>
#include <pcl/point_cloud.h>

using namespace pcl;

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
TEST (PCL, PointXYZRGB)
{
  PointXYZRGB p;

  uint8_t r = 127, g = 64, b = 254;
  uint32_t rgb = ((uint32_t)r << 16 | (uint32_t)g << 8 | (uint32_t)b);
  p.rgb = *reinterpret_cast<float*>(&rgb);

  rgb = *reinterpret_cast<int*>(&p.rgb);
  uint8_t rr = (rgb >> 16) & 0x0000ff;
  uint8_t gg = (rgb >> 8)  & 0x0000ff;
  uint8_t bb = (rgb)       & 0x0000ff;

  EXPECT_EQ (r, rr);
  EXPECT_EQ (g, gg);
  EXPECT_EQ (b, bb);
  EXPECT_EQ (rr, 127);
  EXPECT_EQ (gg, 64);
  EXPECT_EQ (bb, 254);

  p.r = 0; p.g = 127; p.b = 0;
  rgb = *reinterpret_cast<int*>(&p.rgb);
  rr = (rgb >> 16) & 0x0000ff;
  gg = (rgb >> 8)  & 0x0000ff;
  bb = (rgb)       & 0x0000ff;

  EXPECT_EQ (rr, 0);
  EXPECT_EQ (gg, 127);
  EXPECT_EQ (bb, 0);
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
TEST (PCL, PointXYZRGBNormal)
{
  PointXYZRGBNormal p;

  uint8_t r = 127, g = 64, b = 254;
  uint32_t rgb = ((uint32_t)r << 16 | (uint32_t)g << 8 | (uint32_t)b);
  p.rgb = *reinterpret_cast<float*>(&rgb);

  rgb = *reinterpret_cast<int*>(&p.rgb);
  uint8_t rr = (rgb >> 16) & 0x0000ff;
  uint8_t gg = (rgb >> 8)  & 0x0000ff;
  uint8_t bb = (rgb)       & 0x0000ff;

  EXPECT_EQ (r, rr);
  EXPECT_EQ (g, gg);
  EXPECT_EQ (b, bb);
  EXPECT_EQ (rr, 127);
  EXPECT_EQ (gg, 64);
  EXPECT_EQ (bb, 254);

  p.r = 0; p.g = 127; p.b = 0;
  rgb = *reinterpret_cast<int*>(&p.rgb);
  rr = (rgb >> 16) & 0x0000ff;
  gg = (rgb >> 8)  & 0x0000ff;
  bb = (rgb)       & 0x0000ff;

  EXPECT_EQ (rr, 0);
  EXPECT_EQ (gg, 127);
  EXPECT_EQ (bb, 0);
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
TEST (PCL, Common)
{
  PointXYZ p1, p2, p3;
  p1.x = 1; p1.y = p1.z = 0;
  p2.y = 1; p2.x = p2.z = 0;
  p3.z = 1; p3.x = p3.y = 0;
  double radius = getCircumcircleRadius (p1, p2, p3);
  EXPECT_NEAR (radius, 0.816497, 1e-4);

  Eigen::Vector4f pt (1,0,0,0), line_pt (0,0,0,0), line_dir (1,1,0,0);
  double point2line_disance = sqrt (sqrPointToLineDistance (pt, line_pt, line_dir));
  EXPECT_NEAR (point2line_disance, sqrt(2.0)/2, 1e-4);
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
TEST (PCL, Eigen)
{
  Eigen::Matrix3f mat, vec;
  mat << 0.000536227, -1.56178e-05, -9.47391e-05, -1.56178e-05, 0.000297322, -0.000148785, -9.47391e-05, -0.000148785, 9.7827e-05;
  Eigen::Vector3f val;

  eigen33 (mat, vec, val);

  EXPECT_NEAR (fabs (vec (0, 0)), 0.168841, 1e-4); EXPECT_NEAR (fabs (vec (0, 1)), 0.161623, 1e-4); EXPECT_NEAR (fabs (vec (0, 2)), 0.972302, 1e-4);
  EXPECT_NEAR (fabs (vec (1, 0)), 0.451632, 1e-4); EXPECT_NEAR (fabs (vec (1, 1)), 0.889498, 1e-4); EXPECT_NEAR (fabs (vec (1, 2)), 0.0694328, 1e-4);
  EXPECT_NEAR (fabs (vec (2, 0)), 0.876082, 1e-4); EXPECT_NEAR (fabs (vec (2, 1)), 0.4274,   1e-4); EXPECT_NEAR (fabs (vec (2, 2)), 0.223178, 1e-4);

  EXPECT_NEAR (val (0), 2.86806e-06, 1e-4); EXPECT_NEAR (val (1), 0.00037165, 1e-4); EXPECT_NEAR (val (2), 0.000556858, 1e-4);

  Eigen::SelfAdjointEigenSolver<Eigen::Matrix3f> eig (mat);

  EXPECT_NEAR (eig.eigenvectors () (0, 0), -0.168841, 1e-4); EXPECT_NEAR (eig.eigenvectors () (0, 1),  0.161623, 1e-4); EXPECT_NEAR (eig.eigenvectors () (0, 2),  0.972302, 1e-4);
  EXPECT_NEAR (eig.eigenvectors () (1, 0), -0.451632, 1e-4); EXPECT_NEAR (eig.eigenvectors () (1, 1), -0.889498, 1e-4); EXPECT_NEAR (eig.eigenvectors () (1, 2),  0.0694328, 1e-4);
  EXPECT_NEAR (eig.eigenvectors () (2, 0), -0.876083, 1e-4); EXPECT_NEAR (eig.eigenvectors () (2, 1),  0.4274,   1e-4); EXPECT_NEAR (eig.eigenvectors () (2, 2), -0.223178, 1e-4);

  EXPECT_NEAR (eig.eigenvalues () (0), 2.86806e-06, 1e-4); EXPECT_NEAR (eig.eigenvalues () (1), 0.00037165, 1e-4); EXPECT_NEAR (eig.eigenvalues () (2), 0.000556858, 1e-4);

  Eigen::Vector3f eivals = mat.selfadjointView<Eigen::Lower>().eigenvalues ();

  EXPECT_NEAR (eivals (0), 2.86806e-06, 1e-4); EXPECT_NEAR (eivals (1), 0.00037165, 1e-4); EXPECT_NEAR (eivals (2), 0.000556858, 1e-4);

}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
TEST (PCL, PointCloud)
{
  PointCloud<PointXYZ> cloud;
  cloud.width = 640;
  cloud.height = 480;

  EXPECT_EQ (cloud.isOrganized (), true);

  cloud.height = 1;
  EXPECT_EQ (cloud.isOrganized (), false);

  cloud.width = 10;
  for (uint32_t i = 0; i < cloud.width*cloud.height; ++i)
    cloud.points.push_back (PointXYZ (3*i+0,3*i+1,3*i+2));

  Eigen::MatrixXf mat_xyz1 = cloud.getMatrixXfMap();
  EXPECT_EQ (mat_xyz1.rows (), 4);
  EXPECT_EQ (mat_xyz1.cols (), cloud.width);
  EXPECT_EQ (mat_xyz1 (0,0), 0);
  EXPECT_EQ (mat_xyz1 (2,cloud.width-1), 3*cloud.width-1);

  Eigen::MatrixXf mat_xyz = cloud.getMatrixXfMap(3,4,0);
  EXPECT_EQ (mat_xyz.rows (), 3);
  EXPECT_EQ (mat_xyz.cols (), cloud.width);
  EXPECT_EQ (mat_xyz (0,0), 0);
  EXPECT_EQ (mat_xyz (2,cloud.width-1), 3*cloud.width-1);

#ifdef NDEBUG
  Eigen::MatrixXf mat_yz = cloud.getMatrixXfMap(2,4,1);
  EXPECT_EQ (mat_yz.rows (), 2);
  EXPECT_EQ (mat_yz.cols (), cloud.width);
  EXPECT_EQ (mat_yz (0,0), 1);
  EXPECT_EQ (mat_yz (1,cloud.width-1), 3*cloud.width-1);
#endif

  cloud.clear ();
  EXPECT_EQ (cloud.width, 0);
  EXPECT_EQ (cloud.height, 0);

  cloud.width = 640;
  cloud.height = 480;

  cloud.insert (cloud.end (), PointXYZ (1, 1, 1));
  EXPECT_EQ (cloud.isOrganized (), false);
  EXPECT_EQ (cloud.width, 1);

  cloud.insert (cloud.end (), 5, PointXYZ (1, 1, 1));
  EXPECT_EQ (cloud.isOrganized (), false);
  EXPECT_EQ (cloud.width, 6);

  cloud.erase (cloud.end ());
  EXPECT_EQ (cloud.isOrganized (), false);
  EXPECT_EQ (cloud.width, 5);

  cloud.erase (cloud.begin (), cloud.end ());
  EXPECT_EQ (cloud.isOrganized (), false);
  EXPECT_EQ (cloud.width, 0);
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
TEST (PCL, PointTypes)
{
  EXPECT_EQ (sizeof (PointXYZ), 16);
  EXPECT_EQ (__alignof (PointXYZ), 16);
  EXPECT_EQ (sizeof (PointXYZI), 32);
  EXPECT_EQ (__alignof (PointXYZI), 16);
  EXPECT_EQ (sizeof (PointXYZRGB), 32);
  EXPECT_EQ (__alignof (PointXYZRGB), 16);
  EXPECT_EQ (sizeof (PointXYZRGBA), 32);
  EXPECT_EQ (__alignof (PointXYZRGBA), 16);
  EXPECT_EQ (sizeof (Normal), 32);
  EXPECT_EQ (__alignof (Normal), 16);
  EXPECT_EQ (sizeof (PointNormal), 48);
  EXPECT_EQ (__alignof (PointNormal), 16);
  EXPECT_EQ (sizeof (PointXYZRGBNormal), 48);
  EXPECT_EQ (__alignof (PointXYZRGBNormal), 16);
}

/* ---[ */
int
main (int argc, char** argv)
{
  testing::InitGoogleTest (&argc, argv);
  return (RUN_ALL_TESTS ());
}
/* ]--- */
<commit_msg>erase the last item<commit_after>/*
 * Software License Agreement (BSD License)
 *
 *  Copyright (c) 2010, Willow Garage, 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 Willow Garage, 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.
 *
 * $Id$
 *
 */

#include <gtest/gtest.h>
#include <pcl/common/common.h>
#include <pcl/common/distances.h>
#include <pcl/common/eigen.h>
#include <pcl/point_types.h>
#include <pcl/point_cloud.h>

using namespace pcl;

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
TEST (PCL, PointXYZRGB)
{
  PointXYZRGB p;

  uint8_t r = 127, g = 64, b = 254;
  uint32_t rgb = ((uint32_t)r << 16 | (uint32_t)g << 8 | (uint32_t)b);
  p.rgb = *reinterpret_cast<float*>(&rgb);

  rgb = *reinterpret_cast<int*>(&p.rgb);
  uint8_t rr = (rgb >> 16) & 0x0000ff;
  uint8_t gg = (rgb >> 8)  & 0x0000ff;
  uint8_t bb = (rgb)       & 0x0000ff;

  EXPECT_EQ (r, rr);
  EXPECT_EQ (g, gg);
  EXPECT_EQ (b, bb);
  EXPECT_EQ (rr, 127);
  EXPECT_EQ (gg, 64);
  EXPECT_EQ (bb, 254);

  p.r = 0; p.g = 127; p.b = 0;
  rgb = *reinterpret_cast<int*>(&p.rgb);
  rr = (rgb >> 16) & 0x0000ff;
  gg = (rgb >> 8)  & 0x0000ff;
  bb = (rgb)       & 0x0000ff;

  EXPECT_EQ (rr, 0);
  EXPECT_EQ (gg, 127);
  EXPECT_EQ (bb, 0);
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
TEST (PCL, PointXYZRGBNormal)
{
  PointXYZRGBNormal p;

  uint8_t r = 127, g = 64, b = 254;
  uint32_t rgb = ((uint32_t)r << 16 | (uint32_t)g << 8 | (uint32_t)b);
  p.rgb = *reinterpret_cast<float*>(&rgb);

  rgb = *reinterpret_cast<int*>(&p.rgb);
  uint8_t rr = (rgb >> 16) & 0x0000ff;
  uint8_t gg = (rgb >> 8)  & 0x0000ff;
  uint8_t bb = (rgb)       & 0x0000ff;

  EXPECT_EQ (r, rr);
  EXPECT_EQ (g, gg);
  EXPECT_EQ (b, bb);
  EXPECT_EQ (rr, 127);
  EXPECT_EQ (gg, 64);
  EXPECT_EQ (bb, 254);

  p.r = 0; p.g = 127; p.b = 0;
  rgb = *reinterpret_cast<int*>(&p.rgb);
  rr = (rgb >> 16) & 0x0000ff;
  gg = (rgb >> 8)  & 0x0000ff;
  bb = (rgb)       & 0x0000ff;

  EXPECT_EQ (rr, 0);
  EXPECT_EQ (gg, 127);
  EXPECT_EQ (bb, 0);
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
TEST (PCL, Common)
{
  PointXYZ p1, p2, p3;
  p1.x = 1; p1.y = p1.z = 0;
  p2.y = 1; p2.x = p2.z = 0;
  p3.z = 1; p3.x = p3.y = 0;
  double radius = getCircumcircleRadius (p1, p2, p3);
  EXPECT_NEAR (radius, 0.816497, 1e-4);

  Eigen::Vector4f pt (1,0,0,0), line_pt (0,0,0,0), line_dir (1,1,0,0);
  double point2line_disance = sqrt (sqrPointToLineDistance (pt, line_pt, line_dir));
  EXPECT_NEAR (point2line_disance, sqrt(2.0)/2, 1e-4);
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
TEST (PCL, Eigen)
{
  Eigen::Matrix3f mat, vec;
  mat << 0.000536227, -1.56178e-05, -9.47391e-05, -1.56178e-05, 0.000297322, -0.000148785, -9.47391e-05, -0.000148785, 9.7827e-05;
  Eigen::Vector3f val;

  eigen33 (mat, vec, val);

  EXPECT_NEAR (fabs (vec (0, 0)), 0.168841, 1e-4); EXPECT_NEAR (fabs (vec (0, 1)), 0.161623, 1e-4); EXPECT_NEAR (fabs (vec (0, 2)), 0.972302, 1e-4);
  EXPECT_NEAR (fabs (vec (1, 0)), 0.451632, 1e-4); EXPECT_NEAR (fabs (vec (1, 1)), 0.889498, 1e-4); EXPECT_NEAR (fabs (vec (1, 2)), 0.0694328, 1e-4);
  EXPECT_NEAR (fabs (vec (2, 0)), 0.876082, 1e-4); EXPECT_NEAR (fabs (vec (2, 1)), 0.4274,   1e-4); EXPECT_NEAR (fabs (vec (2, 2)), 0.223178, 1e-4);

  EXPECT_NEAR (val (0), 2.86806e-06, 1e-4); EXPECT_NEAR (val (1), 0.00037165, 1e-4); EXPECT_NEAR (val (2), 0.000556858, 1e-4);

  Eigen::SelfAdjointEigenSolver<Eigen::Matrix3f> eig (mat);

  EXPECT_NEAR (eig.eigenvectors () (0, 0), -0.168841, 1e-4); EXPECT_NEAR (eig.eigenvectors () (0, 1),  0.161623, 1e-4); EXPECT_NEAR (eig.eigenvectors () (0, 2),  0.972302, 1e-4);
  EXPECT_NEAR (eig.eigenvectors () (1, 0), -0.451632, 1e-4); EXPECT_NEAR (eig.eigenvectors () (1, 1), -0.889498, 1e-4); EXPECT_NEAR (eig.eigenvectors () (1, 2),  0.0694328, 1e-4);
  EXPECT_NEAR (eig.eigenvectors () (2, 0), -0.876083, 1e-4); EXPECT_NEAR (eig.eigenvectors () (2, 1),  0.4274,   1e-4); EXPECT_NEAR (eig.eigenvectors () (2, 2), -0.223178, 1e-4);

  EXPECT_NEAR (eig.eigenvalues () (0), 2.86806e-06, 1e-4); EXPECT_NEAR (eig.eigenvalues () (1), 0.00037165, 1e-4); EXPECT_NEAR (eig.eigenvalues () (2), 0.000556858, 1e-4);

  Eigen::Vector3f eivals = mat.selfadjointView<Eigen::Lower>().eigenvalues ();

  EXPECT_NEAR (eivals (0), 2.86806e-06, 1e-4); EXPECT_NEAR (eivals (1), 0.00037165, 1e-4); EXPECT_NEAR (eivals (2), 0.000556858, 1e-4);

}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
TEST (PCL, PointCloud)
{
  PointCloud<PointXYZ> cloud;
  cloud.width = 640;
  cloud.height = 480;

  EXPECT_EQ (cloud.isOrganized (), true);

  cloud.height = 1;
  EXPECT_EQ (cloud.isOrganized (), false);

  cloud.width = 10;
  for (uint32_t i = 0; i < cloud.width*cloud.height; ++i)
    cloud.points.push_back (PointXYZ (3*i+0,3*i+1,3*i+2));

  Eigen::MatrixXf mat_xyz1 = cloud.getMatrixXfMap();
  EXPECT_EQ (mat_xyz1.rows (), 4);
  EXPECT_EQ (mat_xyz1.cols (), cloud.width);
  EXPECT_EQ (mat_xyz1 (0,0), 0);
  EXPECT_EQ (mat_xyz1 (2,cloud.width-1), 3*cloud.width-1);

  Eigen::MatrixXf mat_xyz = cloud.getMatrixXfMap(3,4,0);
  EXPECT_EQ (mat_xyz.rows (), 3);
  EXPECT_EQ (mat_xyz.cols (), cloud.width);
  EXPECT_EQ (mat_xyz (0,0), 0);
  EXPECT_EQ (mat_xyz (2,cloud.width-1), 3*cloud.width-1);

#ifdef NDEBUG
  Eigen::MatrixXf mat_yz = cloud.getMatrixXfMap(2,4,1);
  EXPECT_EQ (mat_yz.rows (), 2);
  EXPECT_EQ (mat_yz.cols (), cloud.width);
  EXPECT_EQ (mat_yz (0,0), 1);
  EXPECT_EQ (mat_yz (1,cloud.width-1), 3*cloud.width-1);
#endif

  cloud.clear ();
  EXPECT_EQ (cloud.width, 0);
  EXPECT_EQ (cloud.height, 0);

  cloud.width = 640;
  cloud.height = 480;

  cloud.insert (cloud.end (), PointXYZ (1, 1, 1));
  EXPECT_EQ (cloud.isOrganized (), false);
  EXPECT_EQ (cloud.width, 1);

  cloud.insert (cloud.end (), 5, PointXYZ (1, 1, 1));
  EXPECT_EQ (cloud.isOrganized (), false);
  EXPECT_EQ (cloud.width, 6);

  cloud.erase (cloud.end () - 1);
  EXPECT_EQ (cloud.isOrganized (), false);
  EXPECT_EQ (cloud.width, 5);

  cloud.erase (cloud.begin (), cloud.end ());
  EXPECT_EQ (cloud.isOrganized (), false);
  EXPECT_EQ (cloud.width, 0);
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
TEST (PCL, PointTypes)
{
  EXPECT_EQ (sizeof (PointXYZ), 16);
  EXPECT_EQ (__alignof (PointXYZ), 16);
  EXPECT_EQ (sizeof (PointXYZI), 32);
  EXPECT_EQ (__alignof (PointXYZI), 16);
  EXPECT_EQ (sizeof (PointXYZRGB), 32);
  EXPECT_EQ (__alignof (PointXYZRGB), 16);
  EXPECT_EQ (sizeof (PointXYZRGBA), 32);
  EXPECT_EQ (__alignof (PointXYZRGBA), 16);
  EXPECT_EQ (sizeof (Normal), 32);
  EXPECT_EQ (__alignof (Normal), 16);
  EXPECT_EQ (sizeof (PointNormal), 48);
  EXPECT_EQ (__alignof (PointNormal), 16);
  EXPECT_EQ (sizeof (PointXYZRGBNormal), 48);
  EXPECT_EQ (__alignof (PointXYZRGBNormal), 16);
}

/* ---[ */
int
main (int argc, char** argv)
{
  testing::InitGoogleTest (&argc, argv);
  return (RUN_ALL_TESTS ());
}
/* ]--- */
<|endoftext|>


<commit_before>#include <condition_variable>
#include <locale.h>
#include <mutex>
#include <ncursesw/ncurses.h>
#include <painterface.h>
#include <queue>
#include <signal.h>
#include <string>
#include <thread>

struct UpdateData
{
	bool redrawAll;
	UpdateData() = default;
	UpdateData(bool redrawAll) { this->redrawAll = redrawAll; }
};

#define DECAY_STEP 0.04
#define MAX_VOL 1.5
#define SYM_VOLBAR L'\u25ae' //▮

// GLOBAL VARIABLES
bool     running         = true;
unsigned selectedInput   = 0;
uint8_t  selectedChannel = 0;
std::map<uint32_t, double> lastPeaks;
std::mutex screenMutex;

std::map<uint32_t, uint32_t> mapMonitorLines;

// sync main and callback threads
std::mutex              updMutex;
std::condition_variable cv;

std::queue<UpdateData> updateDataQ;

void signal_update(bool all)
{
	{
		std::lock_guard<std::mutex> lk(updMutex);
		updateDataQ.push(UpdateData(all));
	}
	cv.notify_one();
}

void generateMeter(int y, int x, int width, const double pct, const double maxvol)
{
	int segments = width - 2;
	if (segments <= 0)
		return;
	int filled = pct / maxvol * (double)segments;

	std::wstring meter = L"[]";

	meter.insert(1, filled, SYM_VOLBAR);
	meter.insert(filled + 1, segments - filled, ' ');
	mvaddwstr(y, x, meter.c_str());
}

void updatesinks(PAInterface *interface)
{
	interface->modifyLock();
	//avoid concurrent drawing
	std::lock_guard<std::mutex> lg(screenMutex);

	if (selectedInput > interface->getInputInfo().size() - 1)
	{
		selectedInput   = interface->getInputInfo().size() - 1;
		selectedChannel = 0;
	}

	clear();

	unsigned y     = 1;
	unsigned index = 0;

	for (iter_inputinfo_t it = interface->getInputInfo().begin(); it != interface->getInputInfo().end(); it++)
	{
		std::string &appname = it->second.m_Appname;
		pa_volume_t  avgvol  = it->second.getAverageVolume();
		double       dB      = pa_sw_volume_to_dB(avgvol);
		double       vol     = avgvol / (double)PA_VOLUME_NORM;

		bool isSelInput = index++ == selectedInput;

		if (it->second.m_ChannelsLocked)
		{
			generateMeter(y, 32, COLS - 33, vol, MAX_VOL);

			std::string descstring = "%.2fdB (%.2f)";
			if (isSelInput)
				descstring.insert(0, "▶ ");
			mvprintw(y++, 1, descstring.c_str(), dB, vol);
		}
		else
		{
			for (uint32_t chan = 0; chan < it->second.m_PAVolume.channels; chan++)
			{
				bool        isSelChannel = isSelInput && chan == selectedChannel;
				std::string channame     = pa_channel_position_to_pretty_string(it->second.m_PAChannelMap.map[chan]);
				double      cdB          = pa_sw_volume_to_dB(it->second.m_PAVolume.values[chan]);
				double      cvol         = it->second.m_PAVolume.values[chan] / (double)PA_VOLUME_NORM;
				generateMeter(y, 32, COLS - 33, cvol, MAX_VOL);
				std::string descstring = "%.*s  %.2fdB (%.2f)";
				if (isSelChannel)
					descstring.insert(0, "▶ ");

				mvprintw(y++, 1, descstring.c_str(), isSelChannel ? 13 : 15, channame.c_str(), cdB, cvol);
			}
		}

		double peak = it->second.m_Peak;

		mapMonitorLines[it->first] = y;
		generateMeter(y++, 1, COLS - 2, peak, 1.0);

		//mark selected input with arrow in front of name
		if (isSelInput)
			attron(A_STANDOUT);

		mvprintw(y++, 1, appname.c_str());
		if (isSelInput)
			attroff(A_STANDOUT);
		bool muted = it->second.m_Mute || avgvol == PA_VOLUME_MUTED;
		printw(" %s %s", muted ? "🔇" : "", it->second.m_ChannelsLocked ? "🔒" : "");

		//append sinkname
		int      px = 0, py = 0;
		unsigned space = 0;
		getyx(stdscr, py, px);
		space = COLS - px - 3;

		std::string sinkname = interface->getSinkInfo()[it->second.m_Sink].m_Name;
		if (space < sinkname.size())
		{
			sinkname = sinkname.substr(0, space - 3);
			sinkname.append("...");
			space = 0;
		}
		else
		{
			space -= sinkname.size();
		}
		mvprintw(py, px + space, sinkname.c_str());

		y += 1;
	}

	interface->modifyUnlock();
	refresh();
}

void updateMonitors(PAInterface *interface)
{
	std::lock_guard<std::mutex> lg(screenMutex);
	interface->modifyLock();
	for (iter_inputinfo_t it = interface->getInputInfo().begin(); it != interface->getInputInfo().end(); it++)
	{
		uint32_t y = mapMonitorLines[it->first];
		generateMeter(y, 1, COLS - 2, it->second.m_Peak, 1.0);
	}
	interface->modifyUnlock();
	refresh();
}

inline iter_inputinfo_t get_selected_input_iter(PAInterface *interface)
{
	return std::next(interface->getInputInfo().begin(), selectedInput);
}

void change_volume(double pctDelta, PAInterface *interface)
{
	iter_inputinfo_t it = get_selected_input_iter(interface);
	interface->addVolume(it->first, it->second.m_ChannelsLocked ? -1 : selectedChannel, pctDelta);
}

void change_sink(bool increment, PAInterface *interface)
{
	iter_inputinfo_t iit = get_selected_input_iter(interface);
	iter_sinkinfo_t  sit = interface->getSinkInfo().find(iit->second.m_Sink);

	if (increment)
		sit++;
	else
	{
		if (sit == interface->getSinkInfo().begin())
			sit = std::next(sit, interface->getSinkInfo().size() - 1);
		else
			sit--;
	}

	if (sit == interface->getSinkInfo().end())
		sit = interface->getSinkInfo().begin();

	interface->setInputSink(iit->first, sit->first);
}

void mute_input(PAInterface *interface)
{
	iter_inputinfo_t it = get_selected_input_iter(interface);
	interface->setMute(it->first, !it->second.m_Mute);
}

void selectNext(PAInterface *interface, bool channelLevel = true)
{
	if (selectedInput < interface->getInputInfo().size())
	{
		if (channelLevel)
		{
			iter_inputinfo_t it = get_selected_input_iter(interface);
			if (!it->second.m_ChannelsLocked && selectedChannel < it->second.m_PAVolume.channels - 1)
			{
				selectedChannel++;
				return;
			}
		}
		selectedInput++;
		if (selectedInput >= interface->getInputInfo().size())
			selectedInput = interface->getInputInfo().size() - 1;
		else
			selectedChannel = 0;
	}
}

void selectPrev(PAInterface *interface, bool channelLevel = true)
{
	if (selectedInput < interface->getInputInfo().size())
	{
		if (channelLevel)
		{
			iter_inputinfo_t it = get_selected_input_iter(interface);
			if (!it->second.m_ChannelsLocked && selectedChannel > 0)
			{
				selectedChannel--;
				return;
			}
		}
		if (selectedInput > 0)
			selectedInput--;
		else if (!get_selected_input_iter(interface)->second.m_ChannelsLocked)
		{
			selectedChannel = get_selected_input_iter(interface)->second.m_PAVolume.channels - 1;
		}
	}
}

void toggleChannelLock(PAInterface *interface)
{
	iter_inputinfo_t it         = get_selected_input_iter(interface);
	it->second.m_ChannelsLocked = !it->second.m_ChannelsLocked;
}

void inputThread(PAInterface *interface)
{
	while (running)
	{
		int ch = getch();
		switch (ch)
		{
		case 'h':
			change_volume(-0.05, interface);
			break;
		case 'H':
			change_volume(-0.15, interface);
			break;
		case 'l':
			change_volume(0.05, interface);
			break;
		case 'L':
			change_volume(0.15, interface);
			break;
		case 'j':
			selectNext(interface);
			signal_update(true);
			break;
		case 'J':
			selectNext(interface, false);
			signal_update(true);
			break;
		case 'k':
			selectPrev(interface);
			signal_update(true);
			break;
		case 'K':
			selectPrev(interface, false);
			signal_update(true);
			break;
		case 's':
			change_sink(true, interface);
			break;
		case 'S':
			change_sink(false, interface);
			break;
		case 'm':
			mute_input(interface);
			break;
		case 'c':
			toggleChannelLock(interface);
			signal_update(true);
			break;
		case 'q':
			running = false;
			signal_update(false);
			break;
		default:
			break;
		}
	}
}

void pai_subscription(PAInterface *interface, pai_subscription_type_t type)
{
	bool updAll = type & (PAI_SUBSCRIPTION_MASK_INPUT | PAI_SUBSCRIPTION_MASK_SINK);
	signal_update(updAll);
}

void sig_handle_resize(int s)
{
	endwin();
	refresh();
	signal_update(true);
}

int main(int argc, char **argv)
{
	setlocale(LC_ALL, "");
	initscr();
	curs_set(0); // make cursor invisible
	noecho();

	signal(SIGWINCH, sig_handle_resize);

	PAInterface pai("pamix");
	pai.subscribe(pai_subscription);

	std::thread inputT(inputThread, &pai);
	inputT.detach();

	signal_update(true);
	while (running)
	{
		std::unique_lock<std::mutex> lk(updMutex);
		cv.wait(lk, [] { return !updateDataQ.empty(); });

		if (updateDataQ.front().redrawAll)
		{
			updatesinks(&pai);
		}
		else
		{
			updateMonitors(&pai);
		}
		updateDataQ.pop();
	}
	endwin();
	return 0;
}
<commit_msg>fixed issue that made volumebars overflow and crash<commit_after>#include <condition_variable>
#include <locale.h>
#include <mutex>
#include <ncursesw/ncurses.h>
#include <painterface.h>
#include <queue>
#include <signal.h>
#include <string>
#include <thread>

struct UpdateData
{
	bool redrawAll;
	UpdateData() = default;
	UpdateData(bool redrawAll) { this->redrawAll = redrawAll; }
};

#define DECAY_STEP 0.04
#define MAX_VOL 1.5
#define SYM_VOLBAR L'\u25ae' //▮

// GLOBAL VARIABLES
bool     running         = true;
unsigned selectedInput   = 0;
uint8_t  selectedChannel = 0;
std::map<uint32_t, double> lastPeaks;
std::mutex screenMutex;

std::map<uint32_t, uint32_t> mapMonitorLines;

// sync main and callback threads
std::mutex              updMutex;
std::condition_variable cv;

std::queue<UpdateData> updateDataQ;

void signal_update(bool all)
{
	{
		std::lock_guard<std::mutex> lk(updMutex);
		updateDataQ.push(UpdateData(all));
	}
	cv.notify_one();
}

void generateMeter(int y, int x, int width, const double pct, const double maxvol)
{
	int segments = width - 2;
	if (segments <= 0)
		return;
	int filled = pct / maxvol * (double)segments;
	if (filled > segments)
		filled = segments;

	std::wstring meter = L"[]";

	meter.insert(1, filled, SYM_VOLBAR);
	meter.insert(filled + 1, segments - filled, ' ');
	mvaddwstr(y, x, meter.c_str());
}

void updatesinks(PAInterface *interface)
{
	interface->modifyLock();
	//avoid concurrent drawing
	std::lock_guard<std::mutex> lg(screenMutex);

	if (selectedInput > interface->getInputInfo().size() - 1)
	{
		selectedInput   = interface->getInputInfo().size() - 1;
		selectedChannel = 0;
	}

	clear();

	unsigned y     = 1;
	unsigned index = 0;

	for (iter_inputinfo_t it = interface->getInputInfo().begin(); it != interface->getInputInfo().end(); it++)
	{
		std::string &appname = it->second.m_Appname;
		pa_volume_t  avgvol  = it->second.getAverageVolume();
		double       dB      = pa_sw_volume_to_dB(avgvol);
		double       vol     = avgvol / (double)PA_VOLUME_NORM;

		bool isSelInput = index++ == selectedInput;

		if (it->second.m_ChannelsLocked)
		{
			generateMeter(y, 32, COLS - 33, vol, MAX_VOL);

			std::string descstring = "%.2fdB (%.2f)";
			if (isSelInput)
				descstring.insert(0, "▶ ");
			mvprintw(y++, 1, descstring.c_str(), dB, vol);
		}
		else
		{
			for (uint32_t chan = 0; chan < it->second.m_PAVolume.channels; chan++)
			{
				bool        isSelChannel = isSelInput && chan == selectedChannel;
				std::string channame     = pa_channel_position_to_pretty_string(it->second.m_PAChannelMap.map[chan]);
				double      cdB          = pa_sw_volume_to_dB(it->second.m_PAVolume.values[chan]);
				double      cvol         = it->second.m_PAVolume.values[chan] / (double)PA_VOLUME_NORM;
				generateMeter(y, 32, COLS - 33, cvol, MAX_VOL);
				std::string descstring = "%.*s  %.2fdB (%.2f)";
				if (isSelChannel)
					descstring.insert(0, "▶ ");

				mvprintw(y++, 1, descstring.c_str(), isSelChannel ? 13 : 15, channame.c_str(), cdB, cvol);
			}
		}

		double peak = it->second.m_Peak;

		mapMonitorLines[it->first] = y;
		generateMeter(y++, 1, COLS - 2, peak, 1.0);

		//mark selected input with arrow in front of name
		if (isSelInput)
			attron(A_STANDOUT);

		mvprintw(y++, 1, appname.c_str());
		if (isSelInput)
			attroff(A_STANDOUT);
		bool muted = it->second.m_Mute || avgvol == PA_VOLUME_MUTED;
		printw(" %s %s", muted ? "🔇" : "", it->second.m_ChannelsLocked ? "🔒" : "");

		//append sinkname
		int      px = 0, py = 0;
		unsigned space = 0;
		getyx(stdscr, py, px);
		space = COLS - px - 3;

		std::string sinkname = interface->getSinkInfo()[it->second.m_Sink].m_Name;
		if (space < sinkname.size())
		{
			sinkname = sinkname.substr(0, space - 3);
			sinkname.append("...");
			space = 0;
		}
		else
		{
			space -= sinkname.size();
		}
		mvprintw(py, px + space, sinkname.c_str());

		y += 1;
	}

	interface->modifyUnlock();
	refresh();
}

void updateMonitors(PAInterface *interface)
{
	std::lock_guard<std::mutex> lg(screenMutex);
	interface->modifyLock();
	for (iter_inputinfo_t it = interface->getInputInfo().begin(); it != interface->getInputInfo().end(); it++)
	{
		uint32_t y = mapMonitorLines[it->first];
		generateMeter(y, 1, COLS - 2, it->second.m_Peak, 1.0);
	}
	interface->modifyUnlock();
	refresh();
}

inline iter_inputinfo_t get_selected_input_iter(PAInterface *interface)
{
	return std::next(interface->getInputInfo().begin(), selectedInput);
}

void change_volume(double pctDelta, PAInterface *interface)
{
	iter_inputinfo_t it = get_selected_input_iter(interface);
	interface->addVolume(it->first, it->second.m_ChannelsLocked ? -1 : selectedChannel, pctDelta);
}

void change_sink(bool increment, PAInterface *interface)
{
	iter_inputinfo_t iit = get_selected_input_iter(interface);
	iter_sinkinfo_t  sit = interface->getSinkInfo().find(iit->second.m_Sink);

	if (increment)
		sit++;
	else
	{
		if (sit == interface->getSinkInfo().begin())
			sit = std::next(sit, interface->getSinkInfo().size() - 1);
		else
			sit--;
	}

	if (sit == interface->getSinkInfo().end())
		sit = interface->getSinkInfo().begin();

	interface->setInputSink(iit->first, sit->first);
}

void mute_input(PAInterface *interface)
{
	iter_inputinfo_t it = get_selected_input_iter(interface);
	interface->setMute(it->first, !it->second.m_Mute);
}

void selectNext(PAInterface *interface, bool channelLevel = true)
{
	if (selectedInput < interface->getInputInfo().size())
	{
		if (channelLevel)
		{
			iter_inputinfo_t it = get_selected_input_iter(interface);
			if (!it->second.m_ChannelsLocked && selectedChannel < it->second.m_PAVolume.channels - 1)
			{
				selectedChannel++;
				return;
			}
		}
		selectedInput++;
		if (selectedInput >= interface->getInputInfo().size())
			selectedInput = interface->getInputInfo().size() - 1;
		else
			selectedChannel = 0;
	}
}

void selectPrev(PAInterface *interface, bool channelLevel = true)
{
	if (selectedInput < interface->getInputInfo().size())
	{
		if (channelLevel)
		{
			iter_inputinfo_t it = get_selected_input_iter(interface);
			if (!it->second.m_ChannelsLocked && selectedChannel > 0)
			{
				selectedChannel--;
				return;
			}
		}
		if (selectedInput > 0)
			selectedInput--;
		else if (!get_selected_input_iter(interface)->second.m_ChannelsLocked)
		{
			selectedChannel = get_selected_input_iter(interface)->second.m_PAVolume.channels - 1;
		}
	}
}

void toggleChannelLock(PAInterface *interface)
{
	iter_inputinfo_t it         = get_selected_input_iter(interface);
	it->second.m_ChannelsLocked = !it->second.m_ChannelsLocked;
}

void inputThread(PAInterface *interface)
{
	while (running)
	{
		int ch = getch();
		switch (ch)
		{
		case 'h':
			change_volume(-0.05, interface);
			break;
		case 'H':
			change_volume(-0.15, interface);
			break;
		case 'l':
			change_volume(0.05, interface);
			break;
		case 'L':
			change_volume(0.15, interface);
			break;
		case 'j':
			selectNext(interface);
			signal_update(true);
			break;
		case 'J':
			selectNext(interface, false);
			signal_update(true);
			break;
		case 'k':
			selectPrev(interface);
			signal_update(true);
			break;
		case 'K':
			selectPrev(interface, false);
			signal_update(true);
			break;
		case 's':
			change_sink(true, interface);
			break;
		case 'S':
			change_sink(false, interface);
			break;
		case 'm':
			mute_input(interface);
			break;
		case 'c':
			toggleChannelLock(interface);
			signal_update(true);
			break;
		case 'q':
			running = false;
			signal_update(false);
			break;
		default:
			break;
		}
	}
}

void pai_subscription(PAInterface *interface, pai_subscription_type_t type)
{
	bool updAll = type & (PAI_SUBSCRIPTION_MASK_INPUT | PAI_SUBSCRIPTION_MASK_SINK);
	signal_update(updAll);
}

void sig_handle_resize(int s)
{
	endwin();
	refresh();
	signal_update(true);
}

int main(int argc, char **argv)
{
	setlocale(LC_ALL, "");
	initscr();
	curs_set(0); // make cursor invisible
	noecho();

	signal(SIGWINCH, sig_handle_resize);

	PAInterface pai("pamix");
	pai.subscribe(pai_subscription);

	std::thread inputT(inputThread, &pai);
	inputT.detach();

	signal_update(true);
	while (running)
	{
		std::unique_lock<std::mutex> lk(updMutex);
		cv.wait(lk, [] { return !updateDataQ.empty(); });

		if (updateDataQ.front().redrawAll)
		{
			updatesinks(&pai);
		}
		else
		{
			updateMonitors(&pai);
		}
		updateDataQ.pop();
	}
	endwin();
	return 0;
}
<|endoftext|>