<commit_before>/*
* this file is part of the oxygen gtk engine
* Copyright (c) 2010 Hugo Pereira Da Costa <hugo@oxygen-icons.org>
* Copyright (c) 2010 Ruslan Kabatsayev <b7.10110111@gmail.com>
*
* This  library is free  software; you can  redistribute it and/or
* modify it  under  the terms  of the  GNU Lesser  General  Public
* License  as published  by the Free  Software  Foundation; either
* version 2 of the License, or( at your option ) any later version.
*
* This library is distributed  in the hope that it will be useful,
* but  WITHOUT ANY WARRANTY; without even  the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License  along  with  this library;  if not,  write to  the Free
* Software Foundation, Inc., 51  Franklin St, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/

#include "oxygenwindowshadow.h"
#include "oxygencachekey.h"
#include "oxygencairoutils.h"
#include "oxygencolorutils.h"

namespace Oxygen
{

    //________________________________________________________________________________
    void WindowShadow::render(cairo_t* cr, gint x, gint y, gint w, gint h)
    {
        ColorUtils::Rgba background = settings().palette().color(Palette::Window);
        WindowShadowKey key;
        key.active=_wopt&WinDeco::Active;
        tileSet( background, key).render( cr, x, y, w, h, TileSet::Full );
    }

    //________________________________________________________________________________
    const TileSet& WindowShadow::tileSet(const ColorUtils::Rgba& color, WindowShadowKey key)
    {

        // check if tileset already in cache
        const TileSet& tileSet( helper().windowShadowCache().value(key) );
        if( tileSet.isValid() ) return tileSet;

        const double size( shadowSize() );
        return helper().windowShadowCache().insert(key, TileSet( shadowPixmap( color, key ), int(size), int(size), 1, 1 ) );

    }

    //________________________________________________________________________________
    Cairo::Surface WindowShadow::shadowPixmap(const ColorUtils::Rgba& color, const WindowShadowKey& key )
    {

        const bool active( key.active );
        ShadowConfiguration& shadowConfiguration( active ? activeShadowConfiguration_ : inactiveShadowConfiguration_ );

        static const double fixedSize=25.5;
        double size( shadowSize() );
        double shadowSize( shadowConfiguration.isEnabled() ? shadowConfiguration.shadowSize() : 0 );

        Cairo::Surface shadow( helper().createSurface( int(size*2), int(size*2) ) );
        Cairo::Context p(shadow);

        // some gradients rendering are different at bottom corners if client has no border
        bool hasBorder( true || _wopt&WinDeco::Shaded ); // TODO: true -> hasBorder

        if( shadowSize )
        {
            if( active && true ) // TODO: true -> useOxygenShadows
            {
                {
                    // inner (shark) gradient
                    const double gradientSize = std::min( shadowSize, (shadowSize+fixedSize)/2 );
                    const double hoffset = shadowConfiguration.horizontalOffset()*gradientSize/fixedSize;
                    const double voffset = shadowConfiguration.verticalOffset()*gradientSize/fixedSize;

                    Cairo::Pattern rg( cairo_pattern_create_radial( size+12.0*hoffset, size+12.0*voffset, gradientSize ) );
                    cairo_pattern_add_color_stop( rg, 1, ColorUtils::Rgba::transparent() );

                    // gaussian shadow is used
                    int nPoints( int( (10*gradientSize)/fixedSize ) );
                    Gaussian f( 0.85, 0.17 );
                    ColorUtils::Rgba c(shadowConfiguration.innerColor());
                    for( double i=0; i < nPoints; i++ )
                    {
                        double x = i/nPoints;
                        c.setAlpha( f(x) );
                        cairo_pattern_add_color_stop( rg, x, c );
                    }

                    cairo_set_source(p, rg);
                    GdkRectangle rect={0,0,int(size*2),int(size*2)};
                    renderGradient(p, rect, rg, hasBorder);
                }

                {
                    // outer (spread) gradient
                    const double gradientSize = shadowSize;
                    const double hoffset = shadowConfiguration.horizontalOffset()*gradientSize/fixedSize;
                    const double voffset = shadowConfiguration.verticalOffset()*gradientSize/fixedSize;

                    Cairo::Pattern rg( cairo_pattern_create_radial( size+12.0*hoffset, size+12.0*voffset, gradientSize ) );
                    cairo_pattern_add_color_stop( rg, 1, ColorUtils::Rgba::transparent() );

                    // gaussian shadow is used
                    int nPoints( int( (10*gradientSize)/fixedSize ) );
                    Gaussian f( 0.46, 0.34 );
                    ColorUtils::Rgba c(shadowConfiguration.outerColor());
                    for( double i=0; i < nPoints; i++ )
                    {
                        double x = i/nPoints;
                        c.setAlpha( f(x) );
                        cairo_pattern_add_color_stop( rg, x, c );
                    }

                    cairo_set_source(p, rg);
                    cairo_rectangle(p, 0,0,size*2,size*2);
                    cairo_fill(p);
                }
            } else {

                {
                    // inner (sharp) gradient
                    const double gradientSize = std::min( shadowSize, fixedSize );
                    const double hoffset = shadowConfiguration.horizontalOffset()*gradientSize/fixedSize;
                    const double voffset = shadowConfiguration.verticalOffset()*gradientSize/fixedSize;

                    Cairo::Pattern rg( cairo_pattern_create_radial( size+hoffset, size+voffset, gradientSize ) );
                    cairo_pattern_add_color_stop( rg, 1, ColorUtils::Rgba::transparent() );

                    // parabolic shadow is used
                    int nPoints( int( (10*gradientSize)/fixedSize ) );
                    Parabolic f( 1, 0.22 );
                    ColorUtils::Rgba c(shadowConfiguration.outerColor());
                    for( double i=0; i < nPoints; i++ )
                    {
                        double x = i/nPoints;
                        c.setAlpha( f(x) );
                        cairo_pattern_add_color_stop( rg, x, c );
                    }

                    cairo_set_source(p, rg);
                    GdkRectangle rect={0,0,int(size*2),int(size*2)};
                    renderGradient(p, rect, rg, hasBorder);
                }

                {
                    // mid gradient
                    const double gradientSize = std::min( shadowSize, (shadowSize+2*fixedSize)/3 );
                    const double hoffset = shadowConfiguration.horizontalOffset()*gradientSize/fixedSize;
                    const double voffset = shadowConfiguration.verticalOffset()*gradientSize/fixedSize;

                    Cairo::Pattern rg( cairo_pattern_create_radial( size+8.0*hoffset, size+8.0*voffset, gradientSize ) );
                    cairo_pattern_add_color_stop( rg, 1, ColorUtils::Rgba::transparent() );

                    // gaussian shadow is used
                    int nPoints( int( (10*gradientSize)/fixedSize ) );
                    Gaussian f( 0.54, 0.21 );
                    ColorUtils::Rgba c(shadowConfiguration.outerColor());
                    for( double i=0; i < nPoints; i++ )
                    {
                        double x = i/nPoints;
                        c.setAlpha( f(x) );
                        cairo_pattern_add_color_stop( rg, x, c );
                    }

                    cairo_set_source(p, rg);
                    cairo_rectangle(p, 0,0,size*2,size*2);
                    cairo_fill(p);
                }

                {
                    // outer (spread) gradient
                    const double gradientSize = shadowSize;
                    const double hoffset = shadowConfiguration.horizontalOffset()*gradientSize/fixedSize;
                    const double voffset = shadowConfiguration.verticalOffset()*gradientSize/fixedSize;

                    Cairo::Pattern rg( cairo_pattern_create_radial( size+20.0*hoffset, size+20.0*voffset, gradientSize ) );
                    cairo_pattern_add_color_stop( rg, 1, ColorUtils::Rgba::transparent() );

                    // gaussian shadow is used
                    int nPoints( int( (20*gradientSize)/fixedSize ) );
                    Gaussian f( 0.155, 0.445 );
                    ColorUtils::Rgba c(shadowConfiguration.outerColor());
                    for( double i=0; i < nPoints; i++ )
                    {
                        double x = i/nPoints;
                        c.setAlpha( f(x) );
                        cairo_pattern_add_color_stop( rg, x, c );
                    }

                    cairo_set_source(p, rg);
                    cairo_rectangle(p, 0,0,size*2,size*2);
                    cairo_fill(p);
                }
            }

            cairo_set_source_rgb(p,0,0,0);
            cairo_set_operator(p,CAIRO_OPERATOR_CLEAR);
            cairo_ellipse(p, size-3,size-3,6,6);
            cairo_fill(p);

        }

        return shadow;
    }

    //________________________________________________________________________________
    void WindowShadow::renderGradient( cairo_t* p, const GdkRectangle& rect, cairo_pattern_t* rg, bool hasBorder ) const
    {
        if( hasBorder )
        {
            cairo_set_source(p,rg);
            gdk_cairo_rectangle(p, &rect);
            cairo_fill(p);
            return;
        }

        double size( rect.width/2.0 );

        // get pattern definition
        double x0(0), y0(0), r0(0);
        double x1(0), y1(0), r1(0);
        const cairo_status_t status( cairo_pattern_get_radial_circles( rg, &x0, &y0, &r0, &x1, &y1, &r1 ) );
        assert( status == CAIRO_STATUS_SUCCESS );

        const double hoffset( x0 - size );
        const double voffset( y0 - size );
        const double radius( r1 );

        ColorStop::List stops( cairo_pattern_get_color_stops( rg ) );

        // draw ellipse for the upper rect
        {
            cairo_set_source( p, rg );
            cairo_rectangle( p, hoffset, voffset, 2*size-hoffset, size );
            cairo_fill( p );
        }

        // draw square gradients for the lower rect
        {
            // vertical lines
            Cairo::Pattern pattern( cairo_pattern_create_linear( hoffset, 0.0, 2*size+hoffset, 0.0 ) );
            for( unsigned int i = 0; i < stops.size(); ++i )
            {
                const ColorUtils::Rgba c( stops[i]._color );
                const double x( stops[i]._x * radius );
                cairo_pattern_add_color_stop( pattern, (size-x)/(2.0*size), c );
                cairo_pattern_add_color_stop( pattern, (size+x)/(2.0*size), c );
            }

            cairo_set_source( p, pattern );
            cairo_rectangle( p, hoffset, size+voffset, 2*size-hoffset, 4 );
            cairo_fill( p );
        }

        {

            // horizontal line
            Cairo::Pattern pattern( cairo_pattern_create_linear( 0, voffset, 0, 2*size+voffset ) );
            for( unsigned int i = 0; i < stops.size(); ++i )
            {
                const ColorUtils::Rgba c( stops[i]._color );
                const double x( stops[i]._x * radius );
                cairo_pattern_add_color_stop( pattern, (size+x)/(2.0*size), c );
            }

            cairo_set_source( p, pattern );
            cairo_rectangle( p, size-4+hoffset, size+voffset, 8, size );
            cairo_fill( p );

        }

        {

            // bottom-left corner
            Cairo::Pattern pattern( cairo_pattern_create_radial( size+hoffset-4, size+voffset+4, radius ) );
            for( unsigned int i = 0; i < stops.size(); ++i )
            {
                ColorUtils::Rgba c( stops[i]._color );
                double x( stops[i]._x -4.0/radius );
                if( x<0 )
                {
                    if( i < stops.size()-1 )
                    {
                        const double x1( stops[i+1]._x - 4.0/radius );
                        c = ColorUtils::mix( c, stops[i+1]._color, -x/(x1-x) );
                    }

                    x = 0;

                }

                cairo_pattern_add_color_stop( pattern, x, c );

            }

            cairo_set_source( p, pattern );
            cairo_rectangle( p, hoffset, size+voffset+4, size-4, size );
            cairo_fill( p );

        }

        {

            // bottom-right corner
            Cairo::Pattern pattern( cairo_pattern_create_radial( size+hoffset+4, size+voffset+4, radius ) );
            for( unsigned int i = 0; i < stops.size(); ++i )
            {
                ColorUtils::Rgba c( stops[i]._color );
                double x( stops[i]._x -4.0/radius );
                if( x<0 )
                {
                    if( i < stops.size()-1 )
                    {
                        const double x1( stops[i+1]._x - 4.0/radius );
                        c = ColorUtils::mix( c, stops[i+1]._color, -x/(x1-x) );
                    }

                    x = 0;

                }

                cairo_pattern_add_color_stop( pattern, x, c );

            }

            cairo_set_source( p, pattern );
            cairo_rectangle( p, size+hoffset+4, size+voffset+4, size-4, size );
            cairo_fill( p );

        }

    }
}
<commit_msg>use "key" values to set flags in shadowPixmap.<commit_after>/*
* this file is part of the oxygen gtk engine
* Copyright (c) 2010 Hugo Pereira Da Costa <hugo@oxygen-icons.org>
* Copyright (c) 2010 Ruslan Kabatsayev <b7.10110111@gmail.com>
*
* This  library is free  software; you can  redistribute it and/or
* modify it  under  the terms  of the  GNU Lesser  General  Public
* License  as published  by the Free  Software  Foundation; either
* version 2 of the License, or( at your option ) any later version.
*
* This library is distributed  in the hope that it will be useful,
* but  WITHOUT ANY WARRANTY; without even  the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License  along  with  this library;  if not,  write to  the Free
* Software Foundation, Inc., 51  Franklin St, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/

#include "oxygenwindowshadow.h"
#include "oxygencachekey.h"
#include "oxygencairoutils.h"
#include "oxygencolorutils.h"

namespace Oxygen
{

    //________________________________________________________________________________
    void WindowShadow::render(cairo_t* cr, gint x, gint y, gint w, gint h)
    {
        ColorUtils::Rgba background = settings().palette().color(Palette::Window);
        WindowShadowKey key;
        key.active=_wopt&WinDeco::Active;
        tileSet( background, key).render( cr, x, y, w, h, TileSet::Full );
    }

    //________________________________________________________________________________
    const TileSet& WindowShadow::tileSet(const ColorUtils::Rgba& color, WindowShadowKey key)
    {

        // check if tileset already in cache
        const TileSet& tileSet( helper().windowShadowCache().value(key) );
        if( tileSet.isValid() ) return tileSet;

        const double size( shadowSize() );
        return helper().windowShadowCache().insert(key, TileSet( shadowPixmap( color, key ), int(size), int(size), 1, 1 ) );

    }

    //________________________________________________________________________________
    Cairo::Surface WindowShadow::shadowPixmap(const ColorUtils::Rgba& color, const WindowShadowKey& key )
    {

        const bool active( key.active );
        ShadowConfiguration& shadowConfiguration( active ? activeShadowConfiguration_ : inactiveShadowConfiguration_ );

        static const double fixedSize=25.5;
        double size( shadowSize() );
        double shadowSize( shadowConfiguration.isEnabled() ? shadowConfiguration.shadowSize() : 0 );

        Cairo::Surface shadow( helper().createSurface( int(size*2), int(size*2) ) );
        Cairo::Context p(shadow);

        // some gradients rendering are different at bottom corners if client has no border
        // TODO: true -> hasBorder
        bool hasBorder( key.hasBorder );

        if( shadowSize )
        {

            if( active )
            {
                {
                    // inner (shark) gradient
                    const double gradientSize = std::min( shadowSize, (shadowSize+fixedSize)/2 );
                    const double hoffset = shadowConfiguration.horizontalOffset()*gradientSize/fixedSize;
                    const double voffset = shadowConfiguration.verticalOffset()*gradientSize/fixedSize;

                    Cairo::Pattern rg( cairo_pattern_create_radial( size+12.0*hoffset, size+12.0*voffset, gradientSize ) );
                    cairo_pattern_add_color_stop( rg, 1, ColorUtils::Rgba::transparent() );

                    // gaussian shadow is used
                    int nPoints( int( (10*gradientSize)/fixedSize ) );
                    Gaussian f( 0.85, 0.17 );
                    ColorUtils::Rgba c(shadowConfiguration.innerColor());
                    for( double i=0; i < nPoints; i++ )
                    {
                        double x = i/nPoints;
                        c.setAlpha( f(x) );
                        cairo_pattern_add_color_stop( rg, x, c );
                    }

                    cairo_set_source(p, rg);
                    GdkRectangle rect={0,0,int(size*2),int(size*2)};
                    renderGradient(p, rect, rg, hasBorder);
                }

                {
                    // outer (spread) gradient
                    const double gradientSize = shadowSize;
                    const double hoffset = shadowConfiguration.horizontalOffset()*gradientSize/fixedSize;
                    const double voffset = shadowConfiguration.verticalOffset()*gradientSize/fixedSize;

                    Cairo::Pattern rg( cairo_pattern_create_radial( size+12.0*hoffset, size+12.0*voffset, gradientSize ) );
                    cairo_pattern_add_color_stop( rg, 1, ColorUtils::Rgba::transparent() );

                    // gaussian shadow is used
                    int nPoints( int( (10*gradientSize)/fixedSize ) );
                    Gaussian f( 0.46, 0.34 );
                    ColorUtils::Rgba c(shadowConfiguration.outerColor());
                    for( double i=0; i < nPoints; i++ )
                    {
                        double x = i/nPoints;
                        c.setAlpha( f(x) );
                        cairo_pattern_add_color_stop( rg, x, c );
                    }

                    cairo_set_source(p, rg);
                    cairo_rectangle(p, 0,0,size*2,size*2);
                    cairo_fill(p);
                }
            } else {

                {
                    // inner (sharp) gradient
                    const double gradientSize = std::min( shadowSize, fixedSize );
                    const double hoffset = shadowConfiguration.horizontalOffset()*gradientSize/fixedSize;
                    const double voffset = shadowConfiguration.verticalOffset()*gradientSize/fixedSize;

                    Cairo::Pattern rg( cairo_pattern_create_radial( size+hoffset, size+voffset, gradientSize ) );
                    cairo_pattern_add_color_stop( rg, 1, ColorUtils::Rgba::transparent() );

                    // parabolic shadow is used
                    int nPoints( int( (10*gradientSize)/fixedSize ) );
                    Parabolic f( 1, 0.22 );
                    ColorUtils::Rgba c(shadowConfiguration.outerColor());
                    for( double i=0; i < nPoints; i++ )
                    {
                        double x = i/nPoints;
                        c.setAlpha( f(x) );
                        cairo_pattern_add_color_stop( rg, x, c );
                    }

                    cairo_set_source(p, rg);
                    GdkRectangle rect={0,0,int(size*2),int(size*2)};
                    renderGradient(p, rect, rg, hasBorder);
                }

                {
                    // mid gradient
                    const double gradientSize = std::min( shadowSize, (shadowSize+2*fixedSize)/3 );
                    const double hoffset = shadowConfiguration.horizontalOffset()*gradientSize/fixedSize;
                    const double voffset = shadowConfiguration.verticalOffset()*gradientSize/fixedSize;

                    Cairo::Pattern rg( cairo_pattern_create_radial( size+8.0*hoffset, size+8.0*voffset, gradientSize ) );
                    cairo_pattern_add_color_stop( rg, 1, ColorUtils::Rgba::transparent() );

                    // gaussian shadow is used
                    int nPoints( int( (10*gradientSize)/fixedSize ) );
                    Gaussian f( 0.54, 0.21 );
                    ColorUtils::Rgba c(shadowConfiguration.outerColor());
                    for( double i=0; i < nPoints; i++ )
                    {
                        double x = i/nPoints;
                        c.setAlpha( f(x) );
                        cairo_pattern_add_color_stop( rg, x, c );
                    }

                    cairo_set_source(p, rg);
                    cairo_rectangle(p, 0,0,size*2,size*2);
                    cairo_fill(p);
                }

                {
                    // outer (spread) gradient
                    const double gradientSize = shadowSize;
                    const double hoffset = shadowConfiguration.horizontalOffset()*gradientSize/fixedSize;
                    const double voffset = shadowConfiguration.verticalOffset()*gradientSize/fixedSize;

                    Cairo::Pattern rg( cairo_pattern_create_radial( size+20.0*hoffset, size+20.0*voffset, gradientSize ) );
                    cairo_pattern_add_color_stop( rg, 1, ColorUtils::Rgba::transparent() );

                    // gaussian shadow is used
                    int nPoints( int( (20*gradientSize)/fixedSize ) );
                    Gaussian f( 0.155, 0.445 );
                    ColorUtils::Rgba c(shadowConfiguration.outerColor());
                    for( double i=0; i < nPoints; i++ )
                    {
                        double x = i/nPoints;
                        c.setAlpha( f(x) );
                        cairo_pattern_add_color_stop( rg, x, c );
                    }

                    cairo_set_source(p, rg);
                    cairo_rectangle(p, 0,0,size*2,size*2);
                    cairo_fill(p);
                }
            }

            cairo_set_source_rgb(p,0,0,0);
            cairo_set_operator(p,CAIRO_OPERATOR_CLEAR);
            cairo_ellipse(p, size-3,size-3,6,6);
            cairo_fill(p);

        }

        return shadow;
    }

    //________________________________________________________________________________
    void WindowShadow::renderGradient( cairo_t* p, const GdkRectangle& rect, cairo_pattern_t* rg, bool hasBorder ) const
    {
        if( hasBorder )
        {
            cairo_set_source(p,rg);
            gdk_cairo_rectangle(p, &rect);
            cairo_fill(p);
            return;
        }

        double size( rect.width/2.0 );

        // get pattern definition
        double x0(0), y0(0), r0(0);
        double x1(0), y1(0), r1(0);
        const cairo_status_t status( cairo_pattern_get_radial_circles( rg, &x0, &y0, &r0, &x1, &y1, &r1 ) );
        assert( status == CAIRO_STATUS_SUCCESS );

        const double hoffset( x0 - size );
        const double voffset( y0 - size );
        const double radius( r1 );

        ColorStop::List stops( cairo_pattern_get_color_stops( rg ) );

        // draw ellipse for the upper rect
        {
            cairo_set_source( p, rg );
            cairo_rectangle( p, hoffset, voffset, 2*size-hoffset, size );
            cairo_fill( p );
        }

        // draw square gradients for the lower rect
        {
            // vertical lines
            Cairo::Pattern pattern( cairo_pattern_create_linear( hoffset, 0.0, 2*size+hoffset, 0.0 ) );
            for( unsigned int i = 0; i < stops.size(); ++i )
            {
                const ColorUtils::Rgba c( stops[i]._color );
                const double x( stops[i]._x * radius );
                cairo_pattern_add_color_stop( pattern, (size-x)/(2.0*size), c );
                cairo_pattern_add_color_stop( pattern, (size+x)/(2.0*size), c );
            }

            cairo_set_source( p, pattern );
            cairo_rectangle( p, hoffset, size+voffset, 2*size-hoffset, 4 );
            cairo_fill( p );
        }

        {

            // horizontal line
            Cairo::Pattern pattern( cairo_pattern_create_linear( 0, voffset, 0, 2*size+voffset ) );
            for( unsigned int i = 0; i < stops.size(); ++i )
            {
                const ColorUtils::Rgba c( stops[i]._color );
                const double x( stops[i]._x * radius );
                cairo_pattern_add_color_stop( pattern, (size+x)/(2.0*size), c );
            }

            cairo_set_source( p, pattern );
            cairo_rectangle( p, size-4+hoffset, size+voffset, 8, size );
            cairo_fill( p );

        }

        {

            // bottom-left corner
            Cairo::Pattern pattern( cairo_pattern_create_radial( size+hoffset-4, size+voffset+4, radius ) );
            for( unsigned int i = 0; i < stops.size(); ++i )
            {
                ColorUtils::Rgba c( stops[i]._color );
                double x( stops[i]._x -4.0/radius );
                if( x<0 )
                {
                    if( i < stops.size()-1 )
                    {
                        const double x1( stops[i+1]._x - 4.0/radius );
                        c = ColorUtils::mix( c, stops[i+1]._color, -x/(x1-x) );
                    }

                    x = 0;

                }

                cairo_pattern_add_color_stop( pattern, x, c );

            }

            cairo_set_source( p, pattern );
            cairo_rectangle( p, hoffset, size+voffset+4, size-4, size );
            cairo_fill( p );

        }

        {

            // bottom-right corner
            Cairo::Pattern pattern( cairo_pattern_create_radial( size+hoffset+4, size+voffset+4, radius ) );
            for( unsigned int i = 0; i < stops.size(); ++i )
            {
                ColorUtils::Rgba c( stops[i]._color );
                double x( stops[i]._x -4.0/radius );
                if( x<0 )
                {
                    if( i < stops.size()-1 )
                    {
                        const double x1( stops[i+1]._x - 4.0/radius );
                        c = ColorUtils::mix( c, stops[i+1]._color, -x/(x1-x) );
                    }

                    x = 0;

                }

                cairo_pattern_add_color_stop( pattern, x, c );

            }

            cairo_set_source( p, pattern );
            cairo_rectangle( p, size+hoffset+4, size+voffset+4, size-4, size );
            cairo_fill( p );

        }

    }
}
<|endoftext|>


<commit_before>/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include "pathOrderOptimizer.h"
#include "utils/logoutput.h"
#include "utils/BucketGrid2D.h"

#define INLINE static inline

namespace cura {

/**
*
*/
void PathOrderOptimizer::optimize()
{
    bool picked[polygons.size()];
    memset(picked, false, sizeof(bool) * polygons.size());/// initialized as falses

    for (PolygonRef poly : polygons) /// find closest point to initial starting point within each polygon +initialize picked
    {
        int best = -1;
        float bestDist = std::numeric_limits<float>::infinity();
        for (unsigned int point_idx = 0; point_idx < poly.size(); point_idx++) /// get closest point in polygon
        {
            float dist = vSize2f(poly[point_idx] - startPoint);
            if (dist < bestDist)
            {
                best = point_idx;
                bestDist = dist;
            }
        }
        polyStart.push_back(best);
        //picked.push_back(false); /// initialize all picked values as false

        assert(poly.size() != 2);
    }

    Point prev_point = startPoint;
    for (unsigned int poly_order_idx = 0; poly_order_idx < polygons.size(); poly_order_idx++) /// actual path order optimizer
    {
        int best_poly_idx = -1;
        float bestDist = std::numeric_limits<float>::infinity();

        for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++)
        {
            if (picked[poly_idx] || polygons[poly_idx].size() < 1) /// skip single-point-polygons
            {
                continue;
            }

            assert (polygons[poly_idx].size() != 2);

            float dist = vSize2f(polygons[poly_idx][polyStart[poly_idx]] - prev_point);
            if (dist < bestDist)
            {
                best_poly_idx = poly_idx;
                bestDist = dist;
            }

        }

        if (best_poly_idx > -1) /// should always be true; we should have been able to identify the best next polygon
        {
            assert(polygons[best_poly_idx].size() != 2);

            prev_point = polygons[best_poly_idx][polyStart[best_poly_idx]];

            picked[best_poly_idx] = true;
            polyOrder.push_back(best_poly_idx);
        }
        else
        {
            logError("Failed to find next closest polygon.\n");
        }
    }

    prev_point = startPoint;
    for (unsigned int poly_idx = 0; poly_idx < polyOrder.size(); poly_idx++) /// decide final starting points in each polygon
    {
        int ordered_poly_idx = polyOrder[poly_idx];
        int point_idx = getPolyStart(prev_point, ordered_poly_idx);
        polyStart[poly_idx] = point_idx;
        prev_point = polygons[ordered_poly_idx][point_idx];

    }
}

int PathOrderOptimizer::getPolyStart(Point prev_point, int poly_idx)
{
    switch (type)
    {
        case EZSeamType::BACK:      return getFarthestPointInPolygon(poly_idx);
        case EZSeamType::RANDOM:    return getRandomPointInPolygon(poly_idx);
        case EZSeamType::SHORTEST:  return getClosestPointInPolygon(prev_point, poly_idx);
        default:                    return getClosestPointInPolygon(prev_point, poly_idx);
    }
}

int PathOrderOptimizer::getClosestPointInPolygon(Point prev_point, int poly_idx)
{
    PolygonRef poly = polygons[poly_idx];
    int best_point_idx = -1;
    float bestDist = std::numeric_limits<float>::infinity();
    bool orientation = poly.orientation();
    for (unsigned int point_idx = 0; point_idx < poly.size(); point_idx++)
    {
        float dist = vSize2f(poly[point_idx] - prev_point);
        Point n0 = normal(poly[(point_idx-1+poly.size())%poly.size()] - poly[point_idx], 2000);
        Point n1 = normal(poly[point_idx] - poly[(point_idx + 1) % poly.size()], 2000);
        float dot_score = dot(n0, n1) - dot(crossZ(n0), n1); /// prefer binnenbocht
        if (orientation)
            dot_score = -dot_score;
        if (dist + dot_score < bestDist)
        {
            best_point_idx = point_idx;
            bestDist = dist;
        }
    }
    return best_point_idx;
}

int PathOrderOptimizer::getRandomPointInPolygon(int poly_idx)
{
    return rand() % polygons[poly_idx].size();
}

int PathOrderOptimizer::getFarthestPointInPolygon(int poly_idx)
{
    PolygonRef poly = polygons[poly_idx];
    int best_point_idx = -1;
    float best_y = std::numeric_limits<float>::min();
    for(unsigned int point_idx=0 ; point_idx<poly.size() ; point_idx++)
    {
        if (poly[point_idx].Y > best_y)
        {
            best_point_idx = point_idx;
            best_y = poly[point_idx].Y;
        }
    }
    return best_point_idx;
}

/**
*
*/
void LineOrderOptimizer::optimize()
{
    int gridSize = 5000; // the size of the cells in the hash grid.
    BucketGrid2D<unsigned int> line_bucket_grid(gridSize);
    bool picked[polygons.size()];
    memset(picked, false, sizeof(bool) * polygons.size());/// initialized as falses

    for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++) /// find closest point to initial starting point within each polygon +initialize picked
    {
        int best_point_idx = -1;
        float best_point_dist = std::numeric_limits<float>::infinity();
        PolygonRef poly = polygons[poly_idx];
        for (unsigned int point_idx = 0; point_idx < poly.size(); point_idx++) /// get closest point from polygon
        {
            float dist = vSize2f(poly[point_idx] - startPoint);
            if (dist < best_point_dist)
            {
                best_point_idx = point_idx;
                best_point_dist = dist;
            }
        }
        polyStart.push_back(best_point_idx);

        assert(poly.size() == 2);

        line_bucket_grid.insert(poly[0], poly_idx);
        line_bucket_grid.insert(poly[1], poly_idx);

    }

    Point incommingPerpundicularNormal(0, 0);
    Point prev_point = startPoint;
    for (unsigned int order_idx = 0; order_idx < polygons.size(); order_idx++) /// actual path order optimizer
    {
        int best_line_idx = -1;
        float bestDist = std::numeric_limits<float>::infinity();

        for(unsigned int close_line_poly_idx :  line_bucket_grid.findNearbyObjects(prev_point)) /// check if single-line-polygon is close to last point
        {
            if (picked[close_line_poly_idx] || polygons[close_line_poly_idx].size() < 1)
            {
                continue;
            }

            checkIfLineIsBest(close_line_poly_idx, best_line_idx, bestDist, prev_point, incommingPerpundicularNormal);
        }

        if (best_line_idx == -1) /// if single-line-polygon hasn't been found yet
        {
            for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++)
            {
                if (picked[poly_idx] || polygons[poly_idx].size() < 1) /// skip single-point-polygons
                {
                    continue;
                }
                assert(polygons[poly_idx].size() == 2);

                checkIfLineIsBest(poly_idx, best_line_idx, bestDist, prev_point, incommingPerpundicularNormal);

            }
        }

        if (best_line_idx > -1) /// should always be true; we should have been able to identify the best next polygon
        {
            assert(polygons[best_line_idx].size() == 2);

            int endIdx = polyStart[best_line_idx] * -1 + 1; /// 1 -> 0 , 0 -> 1
            prev_point = polygons[best_line_idx][endIdx];
            incommingPerpundicularNormal = crossZ(normal(polygons[best_line_idx][endIdx] - polygons[best_line_idx][polyStart[best_line_idx]], 1000));

            picked[best_line_idx] = true;
            polyOrder.push_back(best_line_idx);
        }
        else
        {
            logError("Failed to find next closest line.\n");
        }
    }

    prev_point = startPoint;
    for (int poly_idx : polyOrder)
    {
        PolygonRef poly = polygons[poly_idx];
        int best = -1;
        float bestDist = std::numeric_limits<float>::infinity();
        bool orientation = poly.orientation();
        for (unsigned int point_idx = 0; point_idx < poly.size(); point_idx++)
        {
            float dist = vSize2f(polygons[poly_idx][point_idx] - prev_point);
            Point n0 = normal(poly[(point_idx+poly.size()-1)%poly.size()] - poly[point_idx], 2000);
            Point n1 = normal(poly[point_idx] - poly[(point_idx + 1) % poly.size()], 2000);
            float dot_score = dot(n0, n1) - dot(crossZ(n0), n1);
            if (orientation)
                dot_score = -dot_score;
            if (dist + dot_score < bestDist)
            {
                best = point_idx;
                bestDist = dist + dot_score;
            }
        }

        polyStart[poly_idx] = best;
        assert(poly.size() == 2);
        prev_point = poly[best *-1 + 1]; /// 1 -> 0 , 0 -> 1

    }
}

inline void LineOrderOptimizer::checkIfLineIsBest(unsigned int i_line_polygon, int& best, float& bestDist, Point& prev_point, Point& incommingPerpundicularNormal)
{
    { /// check distance to first point on line (0)
        float dist = vSize2f(polygons[i_line_polygon][0] - prev_point);
        dist += abs(dot(incommingPerpundicularNormal, normal(polygons[i_line_polygon][1] - polygons[i_line_polygon][0], 1000))) * 0.0001f; /// penalize sharp corners
        if (dist < bestDist)
        {
            best = i_line_polygon;
            bestDist = dist;
            polyStart[i_line_polygon] = 0;
        }
    }
    { /// check distance to second point on line (1)
        float dist = vSize2f(polygons[i_line_polygon][1] - prev_point);
        dist += abs(dot(incommingPerpundicularNormal, normal(polygons[i_line_polygon][0] - polygons[i_line_polygon][1], 1000) )) * 0.0001f; /// penalize sharp corners
        if (dist < bestDist)
        {
            best = i_line_polygon;
            bestDist = dist;
            polyStart[i_line_polygon] = 1;
        }
    }
}

}//namespace cura
<commit_msg>refactor: more cleanup of pathOrderOptimizer (CURA-1170)<commit_after>/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include "pathOrderOptimizer.h"
#include "utils/logoutput.h"
#include "utils/BucketGrid2D.h"

#define INLINE static inline

namespace cura {

/**
*
*/
void PathOrderOptimizer::optimize()
{
    bool picked[polygons.size()];
    memset(picked, false, sizeof(bool) * polygons.size());/// initialized as falses

    for (PolygonRef poly : polygons) /// find closest point to initial starting point within each polygon +initialize picked
    {
        int best = -1;
        float bestDist = std::numeric_limits<float>::infinity();
        for (unsigned int point_idx = 0; point_idx < poly.size(); point_idx++) /// get closest point in polygon
        {
            float dist = vSize2f(poly[point_idx] - startPoint);
            if (dist < bestDist)
            {
                best = point_idx;
                bestDist = dist;
            }
        }
        polyStart.push_back(best);
        //picked.push_back(false); /// initialize all picked values as false

        assert(poly.size() != 2);
    }

    Point prev_point = startPoint;
    for (unsigned int poly_order_idx = 0; poly_order_idx < polygons.size(); poly_order_idx++) /// actual path order optimizer
    {
        int best_poly_idx = -1;
        float bestDist = std::numeric_limits<float>::infinity();

        for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++)
        {
            if (picked[poly_idx] || polygons[poly_idx].size() < 1) /// skip single-point-polygons
            {
                continue;
            }

            assert (polygons[poly_idx].size() != 2);

            float dist = vSize2f(polygons[poly_idx][polyStart[poly_idx]] - prev_point);
            if (dist < bestDist)
            {
                best_poly_idx = poly_idx;
                bestDist = dist;
            }

        }

        if (best_poly_idx > -1) /// should always be true; we should have been able to identify the best next polygon
        {
            assert(polygons[best_poly_idx].size() != 2);

            prev_point = polygons[best_poly_idx][polyStart[best_poly_idx]];

            picked[best_poly_idx] = true;
            polyOrder.push_back(best_poly_idx);
        }
        else
        {
            logError("Failed to find next closest polygon.\n");
        }
    }

    prev_point = startPoint;
    for (unsigned int poly_idx = 0; poly_idx < polyOrder.size(); poly_idx++) /// decide final starting points in each polygon
    {
        int ordered_poly_idx = polyOrder[poly_idx];
        int point_idx = getPolyStart(prev_point, ordered_poly_idx);
        polyStart[poly_idx] = point_idx;
        prev_point = polygons[ordered_poly_idx][point_idx];

    }
}

int PathOrderOptimizer::getPolyStart(Point prev_point, int poly_idx)
{
    switch (type)
    {
        case EZSeamType::BACK:      return getFarthestPointInPolygon(poly_idx);
        case EZSeamType::RANDOM:    return getRandomPointInPolygon(poly_idx);
        case EZSeamType::SHORTEST:  return getClosestPointInPolygon(prev_point, poly_idx);
        default:                    return getClosestPointInPolygon(prev_point, poly_idx);
    }
}

int PathOrderOptimizer::getClosestPointInPolygon(Point prev_point, int poly_idx)
{
    PolygonRef poly = polygons[poly_idx];
    int best_point_idx = -1;
    float bestDist = std::numeric_limits<float>::infinity();
    bool orientation = poly.orientation();
    Point p0 = poly.back();
    for (unsigned int point_idx = 0; point_idx < poly.size(); point_idx++)
    {
        Point& p1 = poly[point_idx];
        Point& p2 = poly[(point_idx + 1) % poly.size()];
        float dist = vSize2f(p1 - prev_point);
        Point n0 = normal(p0 - p1, 2000);
        Point n1 = normal(p1 - p2, 2000);
        float dot_score = dot(n0, n1) - dot(crossZ(n0), n1); /// prefer binnenbocht
        if (orientation)
        {
            dot_score = -dot_score;
        }
        if (dist + dot_score < bestDist)
        {
            best_point_idx = point_idx;
            bestDist = dist;
        }
        p0 = p1;
    }
    return best_point_idx;
}

int PathOrderOptimizer::getRandomPointInPolygon(int poly_idx)
{
    return rand() % polygons[poly_idx].size();
}

int PathOrderOptimizer::getFarthestPointInPolygon(int poly_idx)
{
    PolygonRef poly = polygons[poly_idx];
    int best_point_idx = -1;
    float best_y = std::numeric_limits<float>::min();
    for(unsigned int point_idx=0 ; point_idx<poly.size() ; point_idx++)
    {
        if (poly[point_idx].Y > best_y)
        {
            best_point_idx = point_idx;
            best_y = poly[point_idx].Y;
        }
    }
    return best_point_idx;
}

/**
*
*/
void LineOrderOptimizer::optimize()
{
    int gridSize = 5000; // the size of the cells in the hash grid.
    BucketGrid2D<unsigned int> line_bucket_grid(gridSize);
    bool picked[polygons.size()];
    memset(picked, false, sizeof(bool) * polygons.size());/// initialized as falses

    for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++) /// find closest point to initial starting point within each polygon +initialize picked
    {
        int best_point_idx = -1;
        float best_point_dist = std::numeric_limits<float>::infinity();
        PolygonRef poly = polygons[poly_idx];
        for (unsigned int point_idx = 0; point_idx < poly.size(); point_idx++) /// get closest point from polygon
        {
            float dist = vSize2f(poly[point_idx] - startPoint);
            if (dist < best_point_dist)
            {
                best_point_idx = point_idx;
                best_point_dist = dist;
            }
        }
        polyStart.push_back(best_point_idx);

        assert(poly.size() == 2);

        line_bucket_grid.insert(poly[0], poly_idx);
        line_bucket_grid.insert(poly[1], poly_idx);

    }

    Point incommingPerpundicularNormal(0, 0);
    Point prev_point = startPoint;
    for (unsigned int order_idx = 0; order_idx < polygons.size(); order_idx++) /// actual path order optimizer
    {
        int best_line_idx = -1;
        float bestDist = std::numeric_limits<float>::infinity();

        for(unsigned int close_line_poly_idx :  line_bucket_grid.findNearbyObjects(prev_point)) /// check if single-line-polygon is close to last point
        {
            if (picked[close_line_poly_idx] || polygons[close_line_poly_idx].size() < 1)
            {
                continue;
            }

            checkIfLineIsBest(close_line_poly_idx, best_line_idx, bestDist, prev_point, incommingPerpundicularNormal);
        }

        if (best_line_idx == -1) /// if single-line-polygon hasn't been found yet
        {
            for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++)
            {
                if (picked[poly_idx] || polygons[poly_idx].size() < 1) /// skip single-point-polygons
                {
                    continue;
                }
                assert(polygons[poly_idx].size() == 2);

                checkIfLineIsBest(poly_idx, best_line_idx, bestDist, prev_point, incommingPerpundicularNormal);

            }
        }

        if (best_line_idx > -1) /// should always be true; we should have been able to identify the best next polygon
        {
            assert(polygons[best_line_idx].size() == 2);

            int endIdx = polyStart[best_line_idx] * -1 + 1; /// 1 -> 0 , 0 -> 1
            prev_point = polygons[best_line_idx][endIdx];
            incommingPerpundicularNormal = crossZ(normal(polygons[best_line_idx][endIdx] - polygons[best_line_idx][polyStart[best_line_idx]], 1000));

            picked[best_line_idx] = true;
            polyOrder.push_back(best_line_idx);
        }
        else
        {
            logError("Failed to find next closest line.\n");
        }
    }

    prev_point = startPoint;
    for (int poly_idx : polyOrder)
    {
        PolygonRef poly = polygons[poly_idx];
        int best = -1;
        float bestDist = std::numeric_limits<float>::infinity();
        bool orientation = poly.orientation();
        for (unsigned int point_idx = 0; point_idx < poly.size(); point_idx++)
        {
            float dist = vSize2f(polygons[poly_idx][point_idx] - prev_point);
            Point n0 = normal(poly[(point_idx+poly.size()-1)%poly.size()] - poly[point_idx], 2000);
            Point n1 = normal(poly[point_idx] - poly[(point_idx + 1) % poly.size()], 2000);
            float dot_score = dot(n0, n1) - dot(crossZ(n0), n1);
            if (orientation)
                dot_score = -dot_score;
            if (dist + dot_score < bestDist)
            {
                best = point_idx;
                bestDist = dist + dot_score;
            }
        }

        polyStart[poly_idx] = best;
        assert(poly.size() == 2);
        prev_point = poly[best *-1 + 1]; /// 1 -> 0 , 0 -> 1

    }
}

inline void LineOrderOptimizer::checkIfLineIsBest(unsigned int poly_idx, int& best, float& best_dist, Point& prev_point, Point& incomming_perpundicular_normal)
{
    { /// check distance to first point on line (0)
        float dist = vSize2f(polygons[poly_idx][0] - prev_point);
        dist += abs(dot(incomming_perpundicular_normal, normal(polygons[poly_idx][1] - polygons[poly_idx][0], 1000))) * 0.0001f; /// penalize sharp corners
        if (dist < best_dist)
        {
            best = poly_idx;
            best_dist = dist;
            polyStart[poly_idx] = 0;
        }
    }
    { /// check distance to second point on line (1)
        float dist = vSize2f(polygons[poly_idx][1] - prev_point);
        dist += abs(dot(incomming_perpundicular_normal, normal(polygons[poly_idx][0] - polygons[poly_idx][1], 1000) )) * 0.0001f; /// penalize sharp corners
        if (dist < best_dist)
        {
            best = poly_idx;
            best_dist = dist;
            polyStart[poly_idx] = 1;
        }
    }
}

}//namespace cura
<|endoftext|>


<commit_before>#include "interface/usb.h"
#include "util/bytebuffer.h"
#include "util/log.h"
#include "gpio.h"

#ifdef CHIPKIT

#define USB_VBUS_ANALOG_INPUT A0

#endif

#define USB_HANDLE_MAX_WAIT_COUNT 35000

namespace gpio = openxc::gpio;

using openxc::util::log::debug;
using openxc::interface::usb::UsbDevice;
using openxc::interface::usb::UsbEndpoint;
using openxc::interface::usb::UsbEndpointDirection;
using openxc::gpio::GPIO_DIRECTION_INPUT;
using openxc::util::bytebuffer::processQueue;

// This is a reference to the last packet read
extern volatile CTRL_TRF_SETUP SetupPkt;
extern UsbDevice USB_DEVICE;
extern bool handleControlRequest(uint8_t);

boolean usbCallback(USB_EVENT event, void *pdata, word size) {
    // initial connection up to configure will be handled by the default
    // callback routine.
    USB_DEVICE.device.DefaultCBEventHandler(event, pdata, size);

    // TODO why can't we use USB_EVENT as the type anymore? rejects
    // EVENT_EP0_REQUEST as a switch case
    switch((int)event) {
    case EVENT_CONFIGURED:
        debug("USB Configured");
        USB_DEVICE.configured = true;

        for(int i = 0; i < ENDPOINT_COUNT; i++) {
            UsbEndpoint* endpoint = &USB_DEVICE.endpoints[i];
            if(endpoint->direction == UsbEndpointDirection::USB_ENDPOINT_DIRECTION_OUT) {
                USB_DEVICE.device.EnableEndpoint(endpoint->address,
                        USB_OUT_ENABLED|USB_HANDSHAKE_ENABLED|USB_DISALLOW_SETUP);
            } else {
                USB_DEVICE.device.EnableEndpoint(endpoint->address,
                        USB_IN_ENABLED|USB_HANDSHAKE_ENABLED|USB_DISALLOW_SETUP);
            }
        }
        break;

    case EVENT_EP0_REQUEST:
        handleControlRequest(SetupPkt.bRequest);
        break;

    default:
        break;
    }
    return true;
}

void openxc::interface::usb::sendControlMessage(uint8_t* data, uint8_t length) {
    USB_DEVICE.device.EP0SendRAMPtr(data, length, USB_EP0_INCLUDE_ZERO);
}

bool vbusEnabled() {
#ifdef USB_VBUS_ANALOG_INPUT
    return analogRead(USB_VBUS_ANALOG_INPUT) > 100;
#else
    return true;
#endif
}

bool waitForHandle(UsbDevice* usbDevice, UsbEndpoint* endpoint) {
    int i = 0;
    while(usbDevice->configured &&
            usbDevice->device.HandleBusy(endpoint->deviceToHostHandle)) {
        ++i;
        if(i > USB_HANDLE_MAX_WAIT_COUNT) {
            // The reason we want to exit this loop early is that if USB is
            // attached and configured, but the host isn't sending an IN
            // requests, we will block here forever. As it is, it still slows
            // down UART transfers quite a bit, so setting configured = false
            // ASAP is important.

            // This can get really noisy when running but I want to leave it in
            // because it' useful to enable when debugging.
            // debug("USB most likely not connected or at least not requesting "
                    // "IN transfers - bailing out of handle waiting");
            return false;
        }
    }
    return true;
}

void openxc::interface::usb::processSendQueue(UsbDevice* usbDevice) {
    if(usbDevice->configured && !vbusEnabled()) {
        debug("USB no longer detected - marking unconfigured");
        usbDevice->configured = false;
        return;
    }

    for(int i = 0; i < ENDPOINT_COUNT; i++) {
        UsbEndpoint* endpoint = &usbDevice->endpoints[i];

        // Don't touch usbDevice->sendBuffer if there's still a pending transfer
        if(!waitForHandle(usbDevice, endpoint)) {
            return;
        }

        while(usbDevice->configured &&
                !QUEUE_EMPTY(uint8_t, &endpoint->queue)) {
            int byteCount = 0;
            while(!QUEUE_EMPTY(uint8_t, &endpoint->queue) &&
                    byteCount < USB_SEND_BUFFER_SIZE) {
                endpoint->sendBuffer[byteCount++] = QUEUE_POP(uint8_t,
                        &endpoint->queue);
            }

            int nextByteIndex = 0;
            while(nextByteIndex < byteCount) {
                // Make sure the USB write is 100% complete before messing with this
                // buffer after we copy the message into it - the Microchip library
                // doesn't copy the data to its own internal buffer. See #171 for
                // background on this issue.
                // TODO instead of dropping, replace POP above with a SNAPSHOT
                // and POP off only exactly how many bytes were sent after the
                // fact.
                // TODO in order for this not to fail too often I had to increase
                // the USB_HANDLE_MAX_WAIT_COUNT. that may be OK since now we have
                // VBUS detection.
                if(!waitForHandle(usbDevice, endpoint)) {
                    debug("USB not responding in a timely fashion, dropped data");
                    return;
                }

                int bytesToTransfer = min(MAX_USB_PACKET_SIZE_BYTES,
                        byteCount - nextByteIndex);
                endpoint->deviceToHostHandle = usbDevice->device.GenWrite(
                        endpoint->address,
                        &endpoint->sendBuffer[nextByteIndex], bytesToTransfer);
                nextByteIndex += bytesToTransfer;
            }
        }
    }
}

void openxc::interface::usb::initialize(UsbDevice* usbDevice) {
    usb::initializeCommon(usbDevice);
    usbDevice->device = USBDevice(usbCallback);
    usbDevice->device.InitializeSystem(false);
#ifdef USB_VBUS_ANALOG_INPUT
    gpio::setDirection(0, USB_VBUS_ANALOG_INPUT, GPIO_DIRECTION_INPUT);
#endif
    debug("Done.");
}

void openxc::interface::usb::deinitialize(UsbDevice* usbDevice) {
    // disable USB (notifies stack we are disabling)
    USBModuleDisable();

    // power off the USB peripheral
    // TODO could not find a ready-made function or macro
    // in the USB library to actually turn off the module.
    // USBModuleDisable() is close to what we want, but it
    // sets the ON bit to 1 for some reason.
    // so, easy solution is just go right to the control register, for now
    U1PWRCCLR = (1 << 0);
}

/* Private: Arm the given endpoint for a read from the device to host.
 *
 * This also puts a NUL char in the beginning of the buffer so you don't get
 * confused that it's still a valid message.
 *
 * device - the CAN USB device to arm the endpoint on
 * endpoint - the endpoint to arm.
 * buffer - the destination buffer for the next OUT transfer.
 */
void armForRead(UsbDevice* usbDevice, UsbEndpoint* endpoint) {
    endpoint->receiveBuffer[0] = 0;
    endpoint->hostToDeviceHandle = usbDevice->device.GenRead(
            endpoint->address, (uint8_t*)endpoint->receiveBuffer,
            endpoint->size);
}

void openxc::interface::usb::read(UsbDevice* device, UsbEndpoint* endpoint,
        bool (*callback)(uint8_t*)) {
    if(!device->device.HandleBusy(endpoint->hostToDeviceHandle)) {
        if(endpoint->receiveBuffer[0] != '\0') {
            for(int i = 0; i < endpoint->size; i++) {
                if(!QUEUE_PUSH(uint8_t, &endpoint->queue,
                            endpoint->receiveBuffer[i])) {
                    debug("Dropped write from host -- queue is full");
                }
            }
            processQueue(&endpoint->queue, callback);
        }
        armForRead(device, endpoint);
    }
}
<commit_msg>Remove a TODO resolved by Makefile improvements.<commit_after>#include "interface/usb.h"
#include "util/bytebuffer.h"
#include "util/log.h"
#include "gpio.h"

#ifdef CHIPKIT

#define USB_VBUS_ANALOG_INPUT A0

#endif

#define USB_HANDLE_MAX_WAIT_COUNT 35000

namespace gpio = openxc::gpio;

using openxc::util::log::debug;
using openxc::interface::usb::UsbDevice;
using openxc::interface::usb::UsbEndpoint;
using openxc::interface::usb::UsbEndpointDirection;
using openxc::gpio::GPIO_DIRECTION_INPUT;
using openxc::util::bytebuffer::processQueue;

// This is a reference to the last packet read
extern volatile CTRL_TRF_SETUP SetupPkt;
extern UsbDevice USB_DEVICE;
extern bool handleControlRequest(uint8_t);

boolean usbCallback(USB_EVENT event, void *pdata, word size) {
    // initial connection up to configure will be handled by the default
    // callback routine.
    USB_DEVICE.device.DefaultCBEventHandler(event, pdata, size);

    switch(event) {
    case EVENT_CONFIGURED:
        debug("USB Configured");
        USB_DEVICE.configured = true;

        for(int i = 0; i < ENDPOINT_COUNT; i++) {
            UsbEndpoint* endpoint = &USB_DEVICE.endpoints[i];
            if(endpoint->direction == UsbEndpointDirection::USB_ENDPOINT_DIRECTION_OUT) {
                USB_DEVICE.device.EnableEndpoint(endpoint->address,
                        USB_OUT_ENABLED|USB_HANDSHAKE_ENABLED|USB_DISALLOW_SETUP);
            } else {
                USB_DEVICE.device.EnableEndpoint(endpoint->address,
                        USB_IN_ENABLED|USB_HANDSHAKE_ENABLED|USB_DISALLOW_SETUP);
            }
        }
        break;

    case EVENT_EP0_REQUEST:
        handleControlRequest(SetupPkt.bRequest);
        break;

    default:
        break;
    }
    return true;
}

void openxc::interface::usb::sendControlMessage(uint8_t* data, uint8_t length) {
    USB_DEVICE.device.EP0SendRAMPtr(data, length, USB_EP0_INCLUDE_ZERO);
}

bool vbusEnabled() {
#ifdef USB_VBUS_ANALOG_INPUT
    return analogRead(USB_VBUS_ANALOG_INPUT) > 100;
#else
    return true;
#endif
}

bool waitForHandle(UsbDevice* usbDevice, UsbEndpoint* endpoint) {
    int i = 0;
    while(usbDevice->configured &&
            usbDevice->device.HandleBusy(endpoint->deviceToHostHandle)) {
        ++i;
        if(i > USB_HANDLE_MAX_WAIT_COUNT) {
            // The reason we want to exit this loop early is that if USB is
            // attached and configured, but the host isn't sending an IN
            // requests, we will block here forever. As it is, it still slows
            // down UART transfers quite a bit, so setting configured = false
            // ASAP is important.

            // This can get really noisy when running but I want to leave it in
            // because it' useful to enable when debugging.
            // debug("USB most likely not connected or at least not requesting "
                    // "IN transfers - bailing out of handle waiting");
            return false;
        }
    }
    return true;
}

void openxc::interface::usb::processSendQueue(UsbDevice* usbDevice) {
    if(usbDevice->configured && !vbusEnabled()) {
        debug("USB no longer detected - marking unconfigured");
        usbDevice->configured = false;
        return;
    }

    for(int i = 0; i < ENDPOINT_COUNT; i++) {
        UsbEndpoint* endpoint = &usbDevice->endpoints[i];

        // Don't touch usbDevice->sendBuffer if there's still a pending transfer
        if(!waitForHandle(usbDevice, endpoint)) {
            return;
        }

        while(usbDevice->configured &&
                !QUEUE_EMPTY(uint8_t, &endpoint->queue)) {
            int byteCount = 0;
            while(!QUEUE_EMPTY(uint8_t, &endpoint->queue) &&
                    byteCount < USB_SEND_BUFFER_SIZE) {
                endpoint->sendBuffer[byteCount++] = QUEUE_POP(uint8_t,
                        &endpoint->queue);
            }

            int nextByteIndex = 0;
            while(nextByteIndex < byteCount) {
                // Make sure the USB write is 100% complete before messing with this
                // buffer after we copy the message into it - the Microchip library
                // doesn't copy the data to its own internal buffer. See #171 for
                // background on this issue.
                // TODO instead of dropping, replace POP above with a SNAPSHOT
                // and POP off only exactly how many bytes were sent after the
                // fact.
                // TODO in order for this not to fail too often I had to increase
                // the USB_HANDLE_MAX_WAIT_COUNT. that may be OK since now we have
                // VBUS detection.
                if(!waitForHandle(usbDevice, endpoint)) {
                    debug("USB not responding in a timely fashion, dropped data");
                    return;
                }

                int bytesToTransfer = min(MAX_USB_PACKET_SIZE_BYTES,
                        byteCount - nextByteIndex);
                endpoint->deviceToHostHandle = usbDevice->device.GenWrite(
                        endpoint->address,
                        &endpoint->sendBuffer[nextByteIndex], bytesToTransfer);
                nextByteIndex += bytesToTransfer;
            }
        }
    }
}

void openxc::interface::usb::initialize(UsbDevice* usbDevice) {
    usb::initializeCommon(usbDevice);
    usbDevice->device = USBDevice(usbCallback);
    usbDevice->device.InitializeSystem(false);
#ifdef USB_VBUS_ANALOG_INPUT
    gpio::setDirection(0, USB_VBUS_ANALOG_INPUT, GPIO_DIRECTION_INPUT);
#endif
    debug("Done.");
}

void openxc::interface::usb::deinitialize(UsbDevice* usbDevice) {
    // disable USB (notifies stack we are disabling)
    USBModuleDisable();

    // power off the USB peripheral
    // TODO could not find a ready-made function or macro
    // in the USB library to actually turn off the module.
    // USBModuleDisable() is close to what we want, but it
    // sets the ON bit to 1 for some reason.
    // so, easy solution is just go right to the control register, for now
    U1PWRCCLR = (1 << 0);
}

/* Private: Arm the given endpoint for a read from the device to host.
 *
 * This also puts a NUL char in the beginning of the buffer so you don't get
 * confused that it's still a valid message.
 *
 * device - the CAN USB device to arm the endpoint on
 * endpoint - the endpoint to arm.
 * buffer - the destination buffer for the next OUT transfer.
 */
void armForRead(UsbDevice* usbDevice, UsbEndpoint* endpoint) {
    endpoint->receiveBuffer[0] = 0;
    endpoint->hostToDeviceHandle = usbDevice->device.GenRead(
            endpoint->address, (uint8_t*)endpoint->receiveBuffer,
            endpoint->size);
}

void openxc::interface::usb::read(UsbDevice* device, UsbEndpoint* endpoint,
        bool (*callback)(uint8_t*)) {
    if(!device->device.HandleBusy(endpoint->hostToDeviceHandle)) {
        if(endpoint->receiveBuffer[0] != '\0') {
            for(int i = 0; i < endpoint->size; i++) {
                if(!QUEUE_PUSH(uint8_t, &endpoint->queue,
                            endpoint->receiveBuffer[i])) {
                    debug("Dropped write from host -- queue is full");
                }
            }
            processQueue(&endpoint->queue, callback);
        }
        armForRead(device, endpoint);
    }
}
<|endoftext|>


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

//#define DEBUG
#define MYINFO(X,...) INFO("Kernel", X, ##__VA_ARGS__)

#include <boot/multiboot.h>
#include <hw/cmos.hpp>
#include <kernel/os.hpp>
#include <kernel/events.hpp>
#include <kprint>
#include <service>
#include <statman>
#include <cstdio>
#include <cinttypes>

//#define ENABLE_PROFILERS
#ifdef ENABLE_PROFILERS
#include <profile>
#define PROFILE(name)  ScopedProfiler __CONCAT(sp, __COUNTER__){name};
#else
#define PROFILE(name) /* name */
#endif

extern "C" void* get_cpu_esp();
extern "C" void __libc_init_array();
extern uintptr_t heap_begin;
extern uintptr_t heap_end;
extern uintptr_t _start;
extern uintptr_t _end;
extern uintptr_t _ELF_START_;
extern uintptr_t _TEXT_START_;
extern uintptr_t _LOAD_START_;
extern uintptr_t _ELF_END_;

struct alignas(SMP_ALIGN) OS_CPU {
  uint64_t cycles_hlt = 0;
};
static SMP_ARRAY<OS_CPU> os_per_cpu;

int64_t OS::micros_since_boot() noexcept {
  return cycles_since_boot() / cpu_freq().count();
}

RTC::timestamp_t OS::boot_timestamp()
{
  return RTC::boot_timestamp();
}

RTC::timestamp_t OS::uptime()
{
  return RTC::time_since_boot();
}

uint64_t OS::cycles_asleep() noexcept {
  return PER_CPU(os_per_cpu).cycles_hlt;
}
uint64_t OS::micros_asleep() noexcept {
  return PER_CPU(os_per_cpu).cycles_hlt / cpu_freq().count();
}

__attribute__((noinline))
void OS::halt()
{
  uint64_t cycles_before = __arch_cpu_cycles();
  asm volatile("hlt");

  // add a global symbol here so we can quickly discard
  // event loop from stack sampling
  asm volatile(
  ".global _irq_cb_return_location;\n"
  "_irq_cb_return_location:" );

  // Count sleep cycles
  PER_CPU(os_per_cpu).cycles_hlt += __arch_cpu_cycles() - cycles_before;
}

void OS::default_stdout(const char* str, const size_t len)
{
  __serial_print(str, len);
}

void OS::start(uint32_t boot_magic, uint32_t boot_addr)
{
  OS::cmdline = Service::binary_name();
  // Initialize stdout handlers
  OS::add_stdout(&OS::default_stdout);

  PROFILE("OS::start");
  // Print a fancy header
  CAPTION("#include<os> // Literally");

  void* esp = get_cpu_esp();
  MYINFO("Stack: %p", esp);
  MYINFO("Boot magic: 0x%x, addr: 0x%x", boot_magic, boot_addr);

  /// STATMAN ///
  PROFILE("Statman");
  /// initialize on page 7, 3 pages in size
  Statman::get().init(0x6000, 0x3000);

  // Call global ctors
  PROFILE("Global constructors");
  __libc_init_array();

  // BOOT METHOD //
  PROFILE("Multiboot / legacy");
  OS::memory_end_ = 0;
  // Detect memory limits etc. depending on boot type
  if (boot_magic == MULTIBOOT_BOOTLOADER_MAGIC) {
    OS::multiboot(boot_addr);
  } else {

    if (is_softreset_magic(boot_magic) && boot_addr != 0)
        OS::resume_softreset(boot_addr);

    OS::legacy_boot();
  }
  Expects(OS::memory_end_ != 0);
  // Give the rest of physical memory to heap
  OS::heap_max_ = OS::memory_end_;

  PROFILE("Memory map");
  // Assign memory ranges used by the kernel
  auto& memmap = memory_map();
  MYINFO("Assigning fixed memory ranges (Memory map)");

  memmap.assign_range({0x6000, 0x8fff, "Statman", "Statistics"});
#if defined(ARCH_x86_64)
  memmap.assign_range({0x100000, 0x8fffff, "Pagetables", "System page tables"});
  memmap.assign_range({0x900000, 0x9fffff, "Stack", "System main stack"});
#elif defined(ARCH_i686)
  memmap.assign_range({0xA000, 0x9fbff, "Stack", "System main stack"});
#endif
  memmap.assign_range({(uintptr_t)&_LOAD_START_, (uintptr_t)&_end - 1,
        "ELF", "Your service binary including OS"});

  Expects(::heap_begin and heap_max_);
  // @note for security we don't want to expose this
  memmap.assign_range({(uintptr_t)&_end, ::heap_begin - 1,
        "Pre-heap", "Heap randomization area"});

  uintptr_t span_max = std::numeric_limits<std::ptrdiff_t>::max();
  uintptr_t heap_range_max_ = std::min(span_max, heap_max_);

  MYINFO("Assigning heap");
  memmap.assign_range({::heap_begin, heap_range_max_,
        "Heap", "Dynamic memory", heap_usage });

  MYINFO("Printing memory map");
  for (const auto &i : memmap)
    INFO2("* %s",i.second.to_string().c_str());

  PROFILE("Platform init");
  extern void __platform_init();
  __platform_init();

  PROFILE("RTC init");
  // Realtime/monotonic clock
  RTC::init();
}

void OS::event_loop()
{
  Events::get(0).process_events();
  do {
    OS::halt();
    Events::get(0).process_events();
  } while (power_);

  MYINFO("Stopping service");
  Service::stop();

  MYINFO("Powering off");
  extern void __arch_poweroff();
  __arch_poweroff();
}

void OS::legacy_boot()
{
  // Fetch CMOS memory info (unfortunately this is maximally 10^16 kb)
  auto mem = hw::CMOS::meminfo();
  //uintptr_t low_memory_size = mem.base.total * 1024;
  INFO2("* Low memory: %i Kib", mem.base.total);

  uintptr_t high_memory_size = mem.extended.total * 1024;
  INFO2("* High memory (from cmos): %i Kib", mem.extended.total);

  auto& memmap = memory_map();

  // No guarantees without multiboot, but we assume standard memory layout
  memmap.assign_range({0x0009FC00, 0x0009FFFF,
        "EBDA", "Extended BIOS data area"});
  memmap.assign_range({0x000A0000, 0x000FFFFF,
        "VGA/ROM", "Memory mapped video memory"});

  // @note : since the maximum size of a span is unsigned (ptrdiff_t) we may need more than one
  uintptr_t addr_max = std::numeric_limits<std::size_t>::max();
  uintptr_t span_max = std::numeric_limits<std::ptrdiff_t>::max();

  uintptr_t unavail_start = 0x100000 + high_memory_size;
  size_t interval = std::min(span_max, addr_max - unavail_start) - 1;
  uintptr_t unavail_end = unavail_start + interval;

  while (unavail_end < addr_max){
    INFO2("* Unavailable memory: 0x%" PRIxPTR" - 0x%" PRIxPTR, unavail_start, unavail_end);
    memmap.assign_range({unavail_start, unavail_end,
          "N/A", "Reserved / outside physical range" });
    unavail_start = unavail_end + 1;
    interval = std::min(span_max, addr_max - unavail_start);
    // Increment might wrapped around
    if (unavail_start > unavail_end + interval or unavail_start + interval == addr_max){
      INFO2("* Last chunk of memory: 0x%" PRIxPTR" - 0x%" PRIxPTR, unavail_start, addr_max);
      memmap.assign_range({unavail_start, addr_max,
            "N/A", "Reserved / outside physical range" });
      break;
    }

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

//#define DEBUG
#define MYINFO(X,...) INFO("Kernel", X, ##__VA_ARGS__)

#include <boot/multiboot.h>
#include <hw/cmos.hpp>
#include <kernel/os.hpp>
#include <kernel/events.hpp>
#include <kprint>
#include <service>
#include <statman>
#include <cstdio>
#include <cinttypes>

//#define ENABLE_PROFILERS
#ifdef ENABLE_PROFILERS
#include <profile>
#define PROFILE(name)  ScopedProfiler __CONCAT(sp, __COUNTER__){name};
#else
#define PROFILE(name) /* name */
#endif

extern "C" void* get_cpu_esp();
extern "C" void __libc_init_array();
extern uintptr_t heap_begin;
extern uintptr_t heap_end;
extern uintptr_t _start;
extern uintptr_t _end;
extern uintptr_t _ELF_START_;
extern uintptr_t _TEXT_START_;
extern uintptr_t _LOAD_START_;
extern uintptr_t _ELF_END_;

struct alignas(SMP_ALIGN) OS_CPU {
  uint64_t cycles_hlt = 0;
};
static SMP_ARRAY<OS_CPU> os_per_cpu;

int64_t OS::micros_since_boot() noexcept {
  return cycles_since_boot() / cpu_freq().count();
}

RTC::timestamp_t OS::boot_timestamp()
{
  return RTC::boot_timestamp();
}

RTC::timestamp_t OS::uptime()
{
  return RTC::time_since_boot();
}

uint64_t OS::cycles_asleep() noexcept {
  return PER_CPU(os_per_cpu).cycles_hlt;
}
uint64_t OS::micros_asleep() noexcept {
  return PER_CPU(os_per_cpu).cycles_hlt / cpu_freq().count();
}

__attribute__((noinline))
void OS::halt()
{
  uint64_t cycles_before = __arch_cpu_cycles();
  asm volatile("hlt");

  // add a global symbol here so we can quickly discard
  // event loop from stack sampling
  asm volatile(
  ".global _irq_cb_return_location;\n"
  "_irq_cb_return_location:" );

  // Count sleep cycles
  PER_CPU(os_per_cpu).cycles_hlt += __arch_cpu_cycles() - cycles_before;
}

void OS::default_stdout(const char* str, const size_t len)
{
  __serial_print(str, len);
}

void OS::start(uint32_t boot_magic, uint32_t boot_addr)
{
  OS::cmdline = Service::binary_name();
  // Initialize stdout handlers
  OS::add_stdout(&OS::default_stdout);

  PROFILE("OS::start");
  // Print a fancy header
  CAPTION("#include<os> // Literally");

  MYINFO("Stack: %p", get_cpu_esp());
  MYINFO("Boot magic: 0x%x, addr: 0x%x", boot_magic, boot_addr);

  /// STATMAN ///
  PROFILE("Statman");
  /// initialize on page 7, 3 pages in size
  Statman::get().init(0x6000, 0x3000);

  // Call global ctors
  PROFILE("Global constructors");
  __libc_init_array();

  // BOOT METHOD //
  PROFILE("Multiboot / legacy");
  OS::memory_end_ = 0;
  // Detect memory limits etc. depending on boot type
  if (boot_magic == MULTIBOOT_BOOTLOADER_MAGIC) {
    OS::multiboot(boot_addr);
  } else {

    if (is_softreset_magic(boot_magic) && boot_addr != 0)
        OS::resume_softreset(boot_addr);

    OS::legacy_boot();
  }
  assert(OS::memory_end_ != 0);
  // Give the rest of physical memory to heap
  OS::heap_max_ = OS::memory_end_;

  PROFILE("Memory map");
  // Assign memory ranges used by the kernel
  auto& memmap = memory_map();
  MYINFO("Assigning fixed memory ranges (Memory map)");

  memmap.assign_range({0x6000, 0x8fff, "Statman", "Statistics"});
#if defined(ARCH_x86_64)
  memmap.assign_range({0x100000, 0x8fffff, "Pagetables", "System page tables"});
  memmap.assign_range({0x900000, 0x9fffff, "Stack", "System main stack"});
#elif defined(ARCH_i686)
  memmap.assign_range({0xA000, 0x9fbff, "Stack", "System main stack"});
#endif
  memmap.assign_range({(uintptr_t)&_LOAD_START_, (uintptr_t)&_end - 1,
        "ELF", "Your service binary including OS"});

  assert(::heap_begin != 0x0 and OS::heap_max_ != 0x0);
  // @note for security we don't want to expose this
  memmap.assign_range({(uintptr_t)&_end, ::heap_begin - 1,
        "Pre-heap", "Heap randomization area"});

  uintptr_t span_max = std::numeric_limits<std::ptrdiff_t>::max();
  uintptr_t heap_range_max_ = std::min(span_max, OS::heap_max_-1);

  MYINFO("Assigning heap");
  memmap.assign_range({::heap_begin, heap_range_max_,
        "Heap", "Dynamic memory", heap_usage });

  MYINFO("Printing memory map");
  for (const auto &i : memmap)
    INFO2("* %s",i.second.to_string().c_str());

  PROFILE("Platform init");
  extern void __platform_init();
  __platform_init();

  PROFILE("RTC init");
  // Realtime/monotonic clock
  RTC::init();
}

void OS::event_loop()
{
  Events::get(0).process_events();
  do {
    OS::halt();
    Events::get(0).process_events();
  } while (power_);

  MYINFO("Stopping service");
  Service::stop();

  MYINFO("Powering off");
  extern void __arch_poweroff();
  __arch_poweroff();
}

void OS::legacy_boot()
{
  // Fetch CMOS memory info (unfortunately this is maximally 10^16 kb)
  auto mem = hw::CMOS::meminfo();
  if (OS::memory_end_ == 0)
  {
    //uintptr_t low_memory_size = mem.base.total * 1024;
    INFO2("* Low memory: %i Kib", mem.base.total);

    uintptr_t high_memory_size = mem.extended.total * 1024;
    INFO2("* High memory (from cmos): %i Kib", mem.extended.total);
    OS::memory_end_ = 0x100000 + high_memory_size;
  }

  auto& memmap = memory_map();
  // No guarantees without multiboot, but we assume standard memory layout
  memmap.assign_range({0x0009FC00, 0x0009FFFF,
        "EBDA", "Extended BIOS data area"});
  memmap.assign_range({0x000A0000, 0x000FFFFF,
        "VGA/ROM", "Memory mapped video memory"});

  // @note : since the maximum size of a span is unsigned (ptrdiff_t) we may need more than one
  uintptr_t addr_max = std::numeric_limits<std::size_t>::max();
  uintptr_t span_max = std::numeric_limits<std::ptrdiff_t>::max();

  uintptr_t unavail_start = OS::memory_end_;
  size_t interval = std::min(span_max, addr_max - unavail_start) - 1;
  uintptr_t unavail_end = unavail_start + interval;

  while (unavail_end < addr_max)
  {
    INFO2("* Unavailable memory: 0x%" PRIxPTR" - 0x%" PRIxPTR, unavail_start, unavail_end);
    memmap.assign_range({unavail_start, unavail_end,
          "N/A", "Reserved / outside physical range" });
    unavail_start = unavail_end + 1;
    interval = std::min(span_max, addr_max - unavail_start);
    // Increment might wrapped around
    if (unavail_start > unavail_end + interval or unavail_start + interval == addr_max){
      INFO2("* Last chunk of memory: 0x%" PRIxPTR" - 0x%" PRIxPTR, unavail_start, addr_max);
      memmap.assign_range({unavail_start, addr_max,
            "N/A", "Reserved / outside physical range" });
      break;
    }

    unavail_end += interval;
  }
}
<|endoftext|>


<commit_before><commit_msg>Fix comment.<commit_after><|endoftext|>


<commit_before>/*

Copyright (c) 2006, Arvid Norberg & Daniel Wallin
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 the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#include "libtorrent/pch.hpp"

#include <libtorrent/kademlia/closest_nodes.hpp>
#include <libtorrent/kademlia/routing_table.hpp>
#include <libtorrent/kademlia/rpc_manager.hpp>
#include "libtorrent/assert.hpp"

namespace libtorrent { namespace dht
{

using asio::ip::udp;

closest_nodes_observer::~closest_nodes_observer()
{
	if (m_algorithm) m_algorithm->failed(m_self, true);
}

void closest_nodes_observer::reply(msg const& in)
{
	if (!m_algorithm)
	{
		TORRENT_ASSERT(false);
		return;
	}

	if (!in.nodes.empty())
	{
		for (msg::nodes_t::const_iterator i = in.nodes.begin()
			, end(in.nodes.end()); i != end; ++i)
		{
			m_algorithm->traverse(i->id, i->addr);
		}
	}
	m_algorithm->finished(m_self);
	m_algorithm = 0;
}

void closest_nodes_observer::timeout()
{
	if (!m_algorithm) return;
	m_algorithm->failed(m_self);
	m_algorithm = 0;
}

closest_nodes::closest_nodes(
	node_id target
	, int branch_factor
	, int max_results
	, routing_table& table
	, rpc_manager& rpc
	, done_callback const& callback
)
	: traversal_algorithm(
		target
		, branch_factor
		, max_results
		, table
		, rpc
		, table.begin()
		, table.end()
	)
	, m_done_callback(callback)
{
	boost::intrusive_ptr<closest_nodes> self(this);
	add_requests();
}

void closest_nodes::invoke(node_id const& id, udp::endpoint addr)
{
	TORRENT_ASSERT(m_rpc.allocation_size() >= sizeof(closest_nodes_observer));
	observer_ptr o(new (m_rpc.allocator().malloc()) closest_nodes_observer(this, id, m_target));
#ifndef NDEBUG
	o->m_in_constructor = false;
#endif
	m_rpc.invoke(messages::find_node, addr, o);
}

void closest_nodes::done()
{
	std::vector<node_entry> results;
	int num_results = m_max_results;
	for (std::vector<result>::iterator i = m_results.begin()
		, end(m_results.end()); i != end && num_results >= 0; ++i)
	{
		if (i->flags & result::no_id) continue;
		if ((i->flags & result::queried) == 0) continue;
		results.push_back(node_entry(i->id, i->addr));
		--num_results;
	}
	m_done_callback(results);
}

void closest_nodes::initiate(
	node_id target
	, int branch_factor
	, int max_results
	, routing_table& table
	, rpc_manager& rpc
	, done_callback const& callback
)
{
	new closest_nodes(target, branch_factor, max_results, table, rpc, callback);
}

} } // namespace libtorrent::dht

<commit_msg>minor dht fix<commit_after>/*

Copyright (c) 2006, Arvid Norberg & Daniel Wallin
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 the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#include "libtorrent/pch.hpp"

#include <libtorrent/kademlia/closest_nodes.hpp>
#include <libtorrent/kademlia/routing_table.hpp>
#include <libtorrent/kademlia/rpc_manager.hpp>
#include "libtorrent/assert.hpp"

namespace libtorrent { namespace dht
{

using asio::ip::udp;

closest_nodes_observer::~closest_nodes_observer()
{
	if (m_algorithm) m_algorithm->failed(m_self, true);
}

void closest_nodes_observer::reply(msg const& in)
{
	if (!m_algorithm)
	{
		TORRENT_ASSERT(false);
		return;
	}

	if (!in.nodes.empty())
	{
		for (msg::nodes_t::const_iterator i = in.nodes.begin()
			, end(in.nodes.end()); i != end; ++i)
		{
			m_algorithm->traverse(i->id, i->addr);
		}
	}
	m_algorithm->finished(m_self);
	m_algorithm = 0;
}

void closest_nodes_observer::timeout()
{
	if (!m_algorithm) return;
	m_algorithm->failed(m_self);
	m_algorithm = 0;
}

closest_nodes::closest_nodes(
	node_id target
	, int branch_factor
	, int max_results
	, routing_table& table
	, rpc_manager& rpc
	, done_callback const& callback
)
	: traversal_algorithm(
		target
		, branch_factor
		, max_results
		, table
		, rpc
		, table.begin()
		, table.end()
	)
	, m_done_callback(callback)
{
	boost::intrusive_ptr<closest_nodes> self(this);
	add_requests();
}

void closest_nodes::invoke(node_id const& id, udp::endpoint addr)
{
	TORRENT_ASSERT(m_rpc.allocation_size() >= sizeof(closest_nodes_observer));
	observer_ptr o(new (m_rpc.allocator().malloc()) closest_nodes_observer(this, id, m_target));
#ifndef NDEBUG
	o->m_in_constructor = false;
#endif
	m_rpc.invoke(messages::find_node, addr, o);
}

void closest_nodes::done()
{
	std::vector<node_entry> results;
	int num_results = m_max_results;
	for (std::vector<result>::iterator i = m_results.begin()
		, end(m_results.end()); i != end && num_results > 0; ++i)
	{
		if (i->flags & result::no_id) continue;
		if ((i->flags & result::queried) == 0) continue;
		results.push_back(node_entry(i->id, i->addr));
		--num_results;
	}
	m_done_callback(results);
}

void closest_nodes::initiate(
	node_id target
	, int branch_factor
	, int max_results
	, routing_table& table
	, rpc_manager& rpc
	, done_callback const& callback
)
{
	new closest_nodes(target, branch_factor, max_results, table, rpc, callback);
}

} } // namespace libtorrent::dht

<|endoftext|>