<commit_before>#include "TileLoader.h"

#include <cmath>

#include <QtCore/QTime>
#include <QtCore/QVector>

#include "katlasdirs.h"
#include "TextureTile.h"

#include <QtCore/QDebug>

TileLoader::TileLoader( const QString& theme ){

	setMap( theme );
}

void TileLoader::setMap( const QString& theme ){
//	Initialize map theme.
	m_theme = theme;

	m_tile = new TextureTile( 0, 0, 0, m_theme );

//	We assume that all tiles have the same size. TODO: check to be safe
	m_tileWidth = m_tile->rawtile()->width();
	m_tileHeight = m_tile->rawtile()->height();

	delete m_tile;
}

void TileLoader::resetTilehash(){

	QHash<int, TextureTile*>::const_iterator it = m_tileHash.constBegin();
	while (it != m_tileHash.constEnd()) {
		m_tileHash.value(it.key())->setUsed( false );
		++it;
	}
}

void TileLoader::cleanupTilehash(){
//	Make sure that tiles which haven't been used during the last
//	rendering of the map at all get removed from the tile hash.

	QHashIterator<int, TextureTile*> it(m_tileHash);
	while (it.hasNext()) {
		it.next();
		if ((it.value())->used() == false){
//			qDebug("Removing " + QString::number(it.key()).toLatin1());
			delete m_tileHash.value(it.key());
			m_tileHash.remove(it.key());
		}
	}
}

void TileLoader::flush(){
//	Remove all m_tiles from m_tileHash
	QHash <int, TextureTile*>::const_iterator it;
	for( it = m_tileHash.begin(); it != m_tileHash.constEnd(); it++ )
		delete (*it);
	m_tileHash.clear();
}

TextureTile* TileLoader::loadTile( int tilx, int tily, int tileLevel ){
//	Choosing the correct m_tile via Lng/Lat info

	m_tileId =  (tilx *1000) + tily;

	// If the m_tile hasn't been loaded into the m_tileHash yet, then do so
	if (!m_tileHash.contains( m_tileId )){
		m_tile = new TextureTile(tilx, tily, tileLevel, m_theme);
		m_tileHash[m_tileId] = m_tile;
	}
	// otherwise pick the correct one from the hash
	else {
		m_tile=m_tileHash.value(m_tileId);
		if (!m_tile->used()){
			m_tile->setUsed(true);
			m_tileHash[m_tileId]=m_tile;
		}
	}

	return m_tile;
}

int TileLoader::levelToRow( int level ){
	return (int)pow( 2.0, (double)( level ) );
}

int TileLoader::levelToColumn( int level ){
	return (int)pow( 2.0, (double)( level + 1 ) );
}

int TileLoader::rowToLevel( int row ){
	return (int)( log( row ) / log( 2 ) );
}

int TileLoader::columnToLevel( int column ){
	return (int)( log( column / 2 ) / log( 2 ) );
}

int TileLoader::maxCompleteTileLevel( const QString& theme ){

	bool noerr = true;

	int tilelevel = -1;
	int trylevel = 0;

//	if ( m_bitmaplayer.type.toLower() == "bitmap" ){
	while ( noerr == true ){
		int nmaxit = TileLoader::levelToRow( trylevel );
		for ( int n=0; n < nmaxit; n++) {
			int mmaxit = TileLoader::levelToColumn( trylevel );
			for ( int m=0; m < mmaxit; m++){
				QString tilepath = KAtlasDirs::path( QString("maps/earth/%1/%2/%3/%3_%4.jpg").arg(theme).arg( trylevel ).arg( n, 4, 10, QChar('0') ).arg( m, 4, 10, QChar('0') ) );
//				qDebug() << tilepath;
				noerr = QFile::exists( tilepath );
				if ( noerr == false ) break;
			}
			if ( noerr == false ) break;
		}

		if ( noerr == true) tilelevel = trylevel;
		trylevel++;
	}

	if ( tilelevel == -1 ){
		qDebug("No Tiles Found!");
	}

	qDebug() << "Detected maximum complete tile level: " << tilelevel;

	return tilelevel;
}

int TileLoader::maxPartialTileLevel( const QString& theme ){

	QString tilepath = KAtlasDirs::path( QString("maps/earth/%1").arg(theme) );
	QStringList leveldirs = ( QDir( tilepath ) ).entryList( QDir::AllDirs | QDir::NoSymLinks | QDir::NoDotAndDotDot );

	int maxtilelevel = -1;

	QString str;
	bool ok = true;

	QStringList::const_iterator constIterator;
	for (constIterator = leveldirs.constBegin(); constIterator != leveldirs.constEnd();
		++constIterator){
		int value = (*constIterator).toInt( &ok, 10 );
//		qDebug() << "Value: " << value  << "Ok: " << ok;
		if ( ok && value > maxtilelevel ) maxtilelevel = value;
	}

	qDebug() << "Detected maximum tile level that contains data: " << maxtilelevel;

	return maxtilelevel;
}

bool TileLoader::baseTilesAvailable( const QString& theme ){

	bool noerr = true;

	int n = 0;

	// Check whether the two tiles from the lowest texture level are available

	for ( int m = 0; m < 2; ++m ){
		QString tilepath = KAtlasDirs::path( QString("maps/earth/%1/%2/%3/%3_%4.jpg").arg(theme).arg( 0 ).arg( 0, 4, 10, QChar('0') ).arg( m, 4, 10, QChar('0') ) );

		noerr = QFile::exists( tilepath );

		if ( noerr == false ) break;
	}

//	qDebug() << "Mandatory most basic tile level is fully available: " << noerr;

	return noerr;
}
<commit_msg>woops, forgot to add "flush()" to setMap while deleting setTexLevel during clean up<commit_after>#include "TileLoader.h"

#include <cmath>

#include <QtCore/QTime>
#include <QtCore/QVector>

#include "katlasdirs.h"
#include "TextureTile.h"

#include <QtCore/QDebug>

TileLoader::TileLoader( const QString& theme ){

	setMap( theme );
}

void TileLoader::setMap( const QString& theme ){
//	Initialize map theme.
        flush();

	m_theme = theme;

	m_tile = new TextureTile( 0, 0, 0, m_theme );

//	We assume that all tiles have the same size. TODO: check to be safe
	m_tileWidth = m_tile->rawtile()->width();
	m_tileHeight = m_tile->rawtile()->height();

	delete m_tile;
}

void TileLoader::resetTilehash(){

	QHash<int, TextureTile*>::const_iterator it = m_tileHash.constBegin();
	while (it != m_tileHash.constEnd()) {
		m_tileHash.value(it.key())->setUsed( false );
		++it;
	}
}

void TileLoader::cleanupTilehash(){
//	Make sure that tiles which haven't been used during the last
//	rendering of the map at all get removed from the tile hash.

	QHashIterator<int, TextureTile*> it(m_tileHash);
	while (it.hasNext()) {
		it.next();
		if ((it.value())->used() == false){
//			qDebug("Removing " + QString::number(it.key()).toLatin1());
			delete m_tileHash.value(it.key());
			m_tileHash.remove(it.key());
		}
	}
}

void TileLoader::flush(){
//	Remove all m_tiles from m_tileHash
	QHash <int, TextureTile*>::const_iterator it;
	for( it = m_tileHash.begin(); it != m_tileHash.constEnd(); it++ )
		delete (*it);
	m_tileHash.clear();
}

TextureTile* TileLoader::loadTile( int tilx, int tily, int tileLevel ){
//	Choosing the correct m_tile via Lng/Lat info

	m_tileId =  (tilx *1000) + tily;

	// If the m_tile hasn't been loaded into the m_tileHash yet, then do so
	if (!m_tileHash.contains( m_tileId )){
		m_tile = new TextureTile(tilx, tily, tileLevel, m_theme);
		m_tileHash[m_tileId] = m_tile;
	}
	// otherwise pick the correct one from the hash
	else {
		m_tile=m_tileHash.value(m_tileId);
		if (!m_tile->used()){
			m_tile->setUsed(true);
			m_tileHash[m_tileId]=m_tile;
		}
	}

	return m_tile;
}

int TileLoader::levelToRow( int level ){
	return (int)pow( 2.0, (double)( level ) );
}

int TileLoader::levelToColumn( int level ){
	return (int)pow( 2.0, (double)( level + 1 ) );
}

int TileLoader::rowToLevel( int row ){
	return (int)( log( row ) / log( 2 ) );
}

int TileLoader::columnToLevel( int column ){
	return (int)( log( column / 2 ) / log( 2 ) );
}

int TileLoader::maxCompleteTileLevel( const QString& theme ){

	bool noerr = true;

	int tilelevel = -1;
	int trylevel = 0;

//	if ( m_bitmaplayer.type.toLower() == "bitmap" ){
	while ( noerr == true ){
		int nmaxit = TileLoader::levelToRow( trylevel );
		for ( int n=0; n < nmaxit; n++) {
			int mmaxit = TileLoader::levelToColumn( trylevel );
			for ( int m=0; m < mmaxit; m++){
				QString tilepath = KAtlasDirs::path( QString("maps/earth/%1/%2/%3/%3_%4.jpg").arg(theme).arg( trylevel ).arg( n, 4, 10, QChar('0') ).arg( m, 4, 10, QChar('0') ) );
//				qDebug() << tilepath;
				noerr = QFile::exists( tilepath );
				if ( noerr == false ) break;
			}
			if ( noerr == false ) break;
		}

		if ( noerr == true) tilelevel = trylevel;
		trylevel++;
	}

	if ( tilelevel == -1 ){
		qDebug("No Tiles Found!");
	}

	qDebug() << "Detected maximum complete tile level: " << tilelevel;

	return tilelevel;
}

int TileLoader::maxPartialTileLevel( const QString& theme ){

	QString tilepath = KAtlasDirs::path( QString("maps/earth/%1").arg(theme) );
	QStringList leveldirs = ( QDir( tilepath ) ).entryList( QDir::AllDirs | QDir::NoSymLinks | QDir::NoDotAndDotDot );

	int maxtilelevel = -1;

	QString str;
	bool ok = true;

	QStringList::const_iterator constIterator;
	for (constIterator = leveldirs.constBegin(); constIterator != leveldirs.constEnd();
		++constIterator){
		int value = (*constIterator).toInt( &ok, 10 );
//		qDebug() << "Value: " << value  << "Ok: " << ok;
		if ( ok && value > maxtilelevel ) maxtilelevel = value;
	}

	qDebug() << "Detected maximum tile level that contains data: " << maxtilelevel;

	return maxtilelevel;
}

bool TileLoader::baseTilesAvailable( const QString& theme ){

	bool noerr = true;

	int n = 0;

	// Check whether the two tiles from the lowest texture level are available

	for ( int m = 0; m < 2; ++m ){
		QString tilepath = KAtlasDirs::path( QString("maps/earth/%1/%2/%3/%3_%4.jpg").arg(theme).arg( 0 ).arg( 0, 4, 10, QChar('0') ).arg( m, 4, 10, QChar('0') ) );

		noerr = QFile::exists( tilepath );

		if ( noerr == false ) break;
	}

//	qDebug() << "Mandatory most basic tile level is fully available: " << noerr;

	return noerr;
}
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: hyperdlg.cxx,v $
 *
 *  $Revision: 1.21 $
 *
 *  last change: $Author: rt $ $Date: 2007-04-26 07:34:00 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    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., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_svx.hxx"

// include ---------------------------------------------------------------

#ifndef _SV_SETTINGS_HXX
#include <vcl/settings.hxx>
#endif
#ifndef INCLUDED_SVTOOLS_VIEWOPTIONS_HXX
#include <svtools/viewoptions.hxx>
#endif
#ifndef _CUI_TAB_HYPERLINK_HXX //CHINA001
#include "cuihyperdlg.hxx"  //CHINA001
#endif   //CHINA001
#include "hyperdlg.hxx"
#include "svxdlg.hxx" //CHINA001

#include <sfx2/app.hxx>

#include "hyperdlg.hrc"

//########################################################################
//#                                                                      #
//# Childwindow-Wrapper-Class                                            #
//#                                                                      #
//########################################################################

SFX_IMPL_CHILDWINDOW(SvxHlinkDlgWrapper, SID_HYPERLINK_DIALOG)

// -----------------------------------------------------------------------

struct MyStruct
{
    ::com::sun::star::uno::Reference< ::com::sun::star::frame::XFrame > xFrame;
    SfxChildWinFactory* pFact;
    sal_Bool                bHideNotDelete;
    sal_Bool                bVisible;
    sal_Bool                bHideAtToggle;
    SfxModule*          pContextModule;
    SfxWorkWindow*      pWorkWin;
};

SvxHlinkDlgWrapper::SvxHlinkDlgWrapper( Window* _pParent, USHORT nId,
                                                SfxBindings* pBindings,
                                                SfxChildWinInfo* pInfo ) :
    SfxChildWindow( _pParent, nId ),

    mpDlg( NULL )

{
    //CHINA001 pWindow = new SvxHpLinkDlg( _pParent, pBindings );
    SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
    DBG_ASSERT(pFact, "Dialogdiet fail!");//CHINA001
    mpDlg = pFact->CreateSvxHpLinkDlg( _pParent, pBindings, SID_HYPERLINK_DIALOG );
    DBG_ASSERT(mpDlg, "Dialogdiet fail!");//CHINA001
    pWindow = mpDlg->GetWindow();
    ((MyStruct*)pImp)->bVisible = FALSE;

    if ( pInfo->aSize.Width() != 0 && pInfo->aSize.Height() != 0 )
    {
        Size aParentSize( SFX_APP()->GetTopWindow()->GetSizePixel() );
        Size aDlgSize ( GetSizePixel () );

        if( aParentSize.Width() < pInfo->aPos.X() )
            pInfo->aPos.setX( aParentSize.Width()-aDlgSize.Width() < long(0.1*aParentSize.Width()) ?
                              long(0.1*aParentSize.Width()) : aParentSize.Width()-aDlgSize.Width() );
        if( aParentSize.Height() < pInfo->aPos. Y() )
            pInfo->aPos.setY( aParentSize.Height()-aDlgSize.Height() < long(0.1*aParentSize.Height()) ?
                              long(0.1*aParentSize.Height()) : aParentSize.Height()-aDlgSize.Height() );

        pWindow->SetPosPixel( pInfo->aPos );
    }

    eChildAlignment = SFX_ALIGN_NOALIGNMENT;

    SetHideNotDelete( TRUE );
}

SfxChildWinInfo SvxHlinkDlgWrapper::GetInfo() const
{
    return SfxChildWindow::GetInfo();
}

sal_Bool SvxHlinkDlgWrapper::QueryClose()
{
    return mpDlg ? mpDlg->QueryClose() : sal_True;
}

<commit_msg>INTEGRATION: CWS vgbugs07 (1.21.50); FILE MERGED 2007/06/04 13:26:19 vg 1.21.50.1: #i76605# Remove -I .../inc/module hack introduced by hedaburemove01<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: hyperdlg.cxx,v $
 *
 *  $Revision: 1.22 $
 *
 *  last change: $Author: hr $ $Date: 2007-06-27 17:12:06 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    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., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_svx.hxx"

// include ---------------------------------------------------------------

#ifndef _SV_SETTINGS_HXX
#include <vcl/settings.hxx>
#endif
#ifndef INCLUDED_SVTOOLS_VIEWOPTIONS_HXX
#include <svtools/viewoptions.hxx>
#endif
#ifndef _CUI_TAB_HYPERLINK_HXX //CHINA001
#include "cuihyperdlg.hxx"  //CHINA001
#endif   //CHINA001
#include "hyperdlg.hxx"
#include <svx/svxdlg.hxx> //CHINA001

#include <sfx2/app.hxx>

#include "hyperdlg.hrc"

//########################################################################
//#                                                                      #
//# Childwindow-Wrapper-Class                                            #
//#                                                                      #
//########################################################################

SFX_IMPL_CHILDWINDOW(SvxHlinkDlgWrapper, SID_HYPERLINK_DIALOG)

// -----------------------------------------------------------------------

struct MyStruct
{
    ::com::sun::star::uno::Reference< ::com::sun::star::frame::XFrame > xFrame;
    SfxChildWinFactory* pFact;
    sal_Bool                bHideNotDelete;
    sal_Bool                bVisible;
    sal_Bool                bHideAtToggle;
    SfxModule*          pContextModule;
    SfxWorkWindow*      pWorkWin;
};

SvxHlinkDlgWrapper::SvxHlinkDlgWrapper( Window* _pParent, USHORT nId,
                                                SfxBindings* pBindings,
                                                SfxChildWinInfo* pInfo ) :
    SfxChildWindow( _pParent, nId ),

    mpDlg( NULL )

{
    //CHINA001 pWindow = new SvxHpLinkDlg( _pParent, pBindings );
    SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
    DBG_ASSERT(pFact, "Dialogdiet fail!");//CHINA001
    mpDlg = pFact->CreateSvxHpLinkDlg( _pParent, pBindings, SID_HYPERLINK_DIALOG );
    DBG_ASSERT(mpDlg, "Dialogdiet fail!");//CHINA001
    pWindow = mpDlg->GetWindow();
    ((MyStruct*)pImp)->bVisible = FALSE;

    if ( pInfo->aSize.Width() != 0 && pInfo->aSize.Height() != 0 )
    {
        Size aParentSize( SFX_APP()->GetTopWindow()->GetSizePixel() );
        Size aDlgSize ( GetSizePixel () );

        if( aParentSize.Width() < pInfo->aPos.X() )
            pInfo->aPos.setX( aParentSize.Width()-aDlgSize.Width() < long(0.1*aParentSize.Width()) ?
                              long(0.1*aParentSize.Width()) : aParentSize.Width()-aDlgSize.Width() );
        if( aParentSize.Height() < pInfo->aPos. Y() )
            pInfo->aPos.setY( aParentSize.Height()-aDlgSize.Height() < long(0.1*aParentSize.Height()) ?
                              long(0.1*aParentSize.Height()) : aParentSize.Height()-aDlgSize.Height() );

        pWindow->SetPosPixel( pInfo->aPos );
    }

    eChildAlignment = SFX_ALIGN_NOALIGNMENT;

    SetHideNotDelete( TRUE );
}

SfxChildWinInfo SvxHlinkDlgWrapper::GetInfo() const
{
    return SfxChildWindow::GetInfo();
}

sal_Bool SvxHlinkDlgWrapper::QueryClose()
{
    return mpDlg ? mpDlg->QueryClose() : sal_True;
}

<|endoftext|>


<commit_before>//Copyright (c) 2017 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.

#include "infill.h"
#include "LayerPlan.h"
#include "sliceDataStorage.h"
#include "TopSurface.h"

namespace cura
{

TopSurface::TopSurface()
{
    //Do nothing. Areas stays empty.
}

void TopSurface::setAreasFromMeshAndLayerNumber(SliceMeshStorage& mesh, size_t layer_number)
{
    //The top surface is all parts of the mesh where there's no mesh above it, so find the layer above it first.
    Polygons mesh_above;
    if (layer_number < mesh.layers.size() - 1)
    {
        mesh_above = mesh.layers[layer_number + 1].getOutlines();
    } //If this is the top-most layer, mesh_above stays empty.

    areas = mesh.layers[layer_number].getOutlines().difference(mesh_above);
}

bool TopSurface::ironing(const SliceMeshStorage& mesh, const GCodePathConfig& line_config, LayerPlan& layer) const
{
    if (areas.empty())
    {
        return false; //Nothing to do.
    }
    //Generate the lines to cover the surface.
    const EFillMethod pattern = mesh.settings.get<EFillMethod>("ironing_pattern");
    const bool zig_zaggify_infill = pattern == EFillMethod::ZIG_ZAG;
    constexpr bool connect_polygons = false; // midway connections can make the surface less smooth
    const coord_t line_spacing = mesh.settings.get<coord_t>("ironing_line_spacing");
    const coord_t line_width = line_config.getLineWidth();
    const std::vector<AngleDegrees>& top_most_skin_angles = (mesh.settings.get<size_t>("roofing_layer_count") > 0) ? mesh.roofing_angles : mesh.skin_angles;
    assert(top_most_skin_angles.size() > 0);
    const AngleDegrees direction = top_most_skin_angles[layer.getLayerNr() % top_most_skin_angles.size()] + AngleDegrees(90.0); //Always perpendicular to the skin lines.
    constexpr coord_t infill_overlap = 0;
    constexpr int infill_multiplier = 1;
    constexpr coord_t shift = 0;
    const Ratio ironing_flow = mesh.settings.get<Ratio>("ironing_flow");

    coord_t ironing_inset = -mesh.settings.get<coord_t>("ironing_inset");
    if (pattern == EFillMethod::ZIG_ZAG && ironing_inset == 0)
    {
        //Compensate for the outline_offset decrease that takes place when using the infill generator to generate ironing with the zigzag pattern
        const Ratio width_scale = (float)mesh.settings.get<coord_t>("layer_height") / mesh.settings.get<coord_t>("infill_sparse_thickness");
        ironing_inset += width_scale * line_width / 2;
        //Align the edge of the ironing line with the edge of the outer wall
        ironing_inset -= ironing_flow * line_width / 2;
    }
    else if (pattern == EFillMethod::CONCENTRIC)
    {
        //Counteract the outline_offset increase that takes place when using the infill generator to generate ironing with the concentric pattern
        ironing_inset += line_spacing - line_width / 2;
        //Align the edge of the ironing line with the edge of the outer wall
        ironing_inset -= ironing_flow * line_width / 2;
    }
    const coord_t outline_offset = ironing_inset;

    Infill infill_generator(pattern, zig_zaggify_infill, connect_polygons, areas, outline_offset, line_width, line_spacing, infill_overlap, infill_multiplier, direction, layer.z - 10, shift);
    Polygons ironing_polygons;
    Polygons ironing_lines;
    infill_generator.generate(ironing_polygons, ironing_lines);

    if (ironing_polygons.empty() && ironing_lines.empty())
    {
        return false; //Nothing to do.
    }

    layer.mode_skip_agressive_merge = true;

    bool added = false;
    if (!ironing_polygons.empty())
    {
        constexpr bool force_comb_retract = false;
        layer.addTravel(ironing_polygons[0][0], force_comb_retract);
        layer.addPolygonsByOptimizer(ironing_polygons, line_config, nullptr, ZSeamConfig());
        added = true;
    }

    if (!ironing_lines.empty())
    {
        if (pattern == EFillMethod::LINES || pattern == EFillMethod::ZIG_ZAG)
        {
            //Move to a corner of the area that is perpendicular to the ironing lines, to reduce the number of seams.
            const AABB bounding_box(areas);
            PointMatrix rotate(-direction + 90);
            const Point center = bounding_box.getMiddle();
            const Point far_away = rotate.apply(Point(0, vSize(bounding_box.max - center) * 100)); //Some direction very far away in the direction perpendicular to the ironing lines, relative to the centre.
            //Two options to start, both perpendicular to the ironing lines. Which is closer?
            const Point front_side = PolygonUtils::findNearestVert(center + far_away, areas).p();
            const Point back_side = PolygonUtils::findNearestVert(center - far_away, areas).p();
            if (vSize2(layer.getLastPlannedPositionOrStartingPosition() - front_side) < vSize2(layer.getLastPlannedPositionOrStartingPosition() - back_side))
            {
                layer.addTravel(front_side);
            }
            else
            {
                layer.addTravel(back_side);
            }
        }

        layer.addLinesByOptimizer(ironing_lines, line_config, SpaceFillType::PolyLines);
        added = true;
    }

    layer.mode_skip_agressive_merge = false;
    return added;
}

}
<commit_msg>Apply corrections in zigzag ironing pattern even if inset != 0<commit_after>//Copyright (c) 2017 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.

#include "infill.h"
#include "LayerPlan.h"
#include "sliceDataStorage.h"
#include "TopSurface.h"

namespace cura
{

TopSurface::TopSurface()
{
    //Do nothing. Areas stays empty.
}

void TopSurface::setAreasFromMeshAndLayerNumber(SliceMeshStorage& mesh, size_t layer_number)
{
    //The top surface is all parts of the mesh where there's no mesh above it, so find the layer above it first.
    Polygons mesh_above;
    if (layer_number < mesh.layers.size() - 1)
    {
        mesh_above = mesh.layers[layer_number + 1].getOutlines();
    } //If this is the top-most layer, mesh_above stays empty.

    areas = mesh.layers[layer_number].getOutlines().difference(mesh_above);
}

bool TopSurface::ironing(const SliceMeshStorage& mesh, const GCodePathConfig& line_config, LayerPlan& layer) const
{
    if (areas.empty())
    {
        return false; //Nothing to do.
    }
    //Generate the lines to cover the surface.
    const EFillMethod pattern = mesh.settings.get<EFillMethod>("ironing_pattern");
    const bool zig_zaggify_infill = pattern == EFillMethod::ZIG_ZAG;
    constexpr bool connect_polygons = false; // midway connections can make the surface less smooth
    const coord_t line_spacing = mesh.settings.get<coord_t>("ironing_line_spacing");
    const coord_t line_width = line_config.getLineWidth();
    const std::vector<AngleDegrees>& top_most_skin_angles = (mesh.settings.get<size_t>("roofing_layer_count") > 0) ? mesh.roofing_angles : mesh.skin_angles;
    assert(top_most_skin_angles.size() > 0);
    const AngleDegrees direction = top_most_skin_angles[layer.getLayerNr() % top_most_skin_angles.size()] + AngleDegrees(90.0); //Always perpendicular to the skin lines.
    constexpr coord_t infill_overlap = 0;
    constexpr int infill_multiplier = 1;
    constexpr coord_t shift = 0;
    const Ratio ironing_flow = mesh.settings.get<Ratio>("ironing_flow");

    coord_t ironing_inset = -mesh.settings.get<coord_t>("ironing_inset");
    if (pattern == EFillMethod::ZIG_ZAG)
    {
        //Compensate for the outline_offset decrease that takes place when using the infill generator to generate ironing with the zigzag pattern
        const Ratio width_scale = (float)mesh.settings.get<coord_t>("layer_height") / mesh.settings.get<coord_t>("infill_sparse_thickness");
        ironing_inset += width_scale * line_width / 2;
        //Align the edge of the ironing line with the edge of the outer wall
        ironing_inset -= ironing_flow * line_width / 2;
    }
    else if (pattern == EFillMethod::CONCENTRIC)
    {
        //Counteract the outline_offset increase that takes place when using the infill generator to generate ironing with the concentric pattern
        ironing_inset += line_spacing - line_width / 2;
        //Align the edge of the ironing line with the edge of the outer wall
        ironing_inset -= ironing_flow * line_width / 2;
    }
    const coord_t outline_offset = ironing_inset;

    Infill infill_generator(pattern, zig_zaggify_infill, connect_polygons, areas, outline_offset, line_width, line_spacing, infill_overlap, infill_multiplier, direction, layer.z - 10, shift);
    Polygons ironing_polygons;
    Polygons ironing_lines;
    infill_generator.generate(ironing_polygons, ironing_lines);

    if (ironing_polygons.empty() && ironing_lines.empty())
    {
        return false; //Nothing to do.
    }

    layer.mode_skip_agressive_merge = true;

    bool added = false;
    if (!ironing_polygons.empty())
    {
        constexpr bool force_comb_retract = false;
        layer.addTravel(ironing_polygons[0][0], force_comb_retract);
        layer.addPolygonsByOptimizer(ironing_polygons, line_config, nullptr, ZSeamConfig());
        added = true;
    }

    if (!ironing_lines.empty())
    {
        if (pattern == EFillMethod::LINES || pattern == EFillMethod::ZIG_ZAG)
        {
            //Move to a corner of the area that is perpendicular to the ironing lines, to reduce the number of seams.
            const AABB bounding_box(areas);
            PointMatrix rotate(-direction + 90);
            const Point center = bounding_box.getMiddle();
            const Point far_away = rotate.apply(Point(0, vSize(bounding_box.max - center) * 100)); //Some direction very far away in the direction perpendicular to the ironing lines, relative to the centre.
            //Two options to start, both perpendicular to the ironing lines. Which is closer?
            const Point front_side = PolygonUtils::findNearestVert(center + far_away, areas).p();
            const Point back_side = PolygonUtils::findNearestVert(center - far_away, areas).p();
            if (vSize2(layer.getLastPlannedPositionOrStartingPosition() - front_side) < vSize2(layer.getLastPlannedPositionOrStartingPosition() - back_side))
            {
                layer.addTravel(front_side);
            }
            else
            {
                layer.addTravel(back_side);
            }
        }

        layer.addLinesByOptimizer(ironing_lines, line_config, SpaceFillType::PolyLines);
        added = true;
    }

    layer.mode_skip_agressive_merge = false;
    return added;
}

}
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: transfrm.hxx,v $
 *
 *  $Revision: 1.6 $
 *
 *  last change: $Author: hr $ $Date: 2006-06-19 15:36:19 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    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., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/
#ifndef _SVX_TRANSFRM_HXX
#define _SVX_TRANSFRM_HXX

// include ---------------------------------------------------------------

#include "dlgctrl.hxx"

#ifndef _FIXED_HXX //autogen
#include <vcl/fixed.hxx>
#endif
class SdrView;

/*************************************************************************
|*
|* Transform-Tab-Dialog
|*
\************************************************************************/

/** put this into the nAnchorTypes parameter of the SvxTransformTabDialog c'tor
    to disable the size controls */
const USHORT SVX_OBJ_NORESIZE = 0x0100;

/** put this into the nAnchorTypes parameter of the SvxTransformTabDialog c'tor
    to disable the protect controls */
const USHORT SVX_OBJ_NOPROTECT = 0x0200;

class SvxTransformTabDialog : public SfxTabDialog
{
private:
    const SdrView*      pView;

    USHORT              nAnchorCtrls;
    Link                aValidateLink;

    virtual void        PageCreated( USHORT nId, SfxTabPage &rPage );

public:

            SvxTransformTabDialog( Window* pParent, const SfxItemSet* pAttr,
                            const SdrView* pView,
                            USHORT nAnchorTypes = 0);
            ~SvxTransformTabDialog();

            //link for the Writer to validate positions
            void SetValidateFramePosLink( const Link& rLink );
};

/*************************************************************************
|*
|* position and size tab page
|*
\************************************************************************/

class SvxPositionSizeTabPage : public SvxTabPage
{
private:
    // position
    FixedLine           maFlPosition;
    FixedText           maFtPosX;
    MetricField         maMtrPosX;
    FixedText           maFtPosY;
    MetricField         maMtrPosY;
    FixedText           maFtPosReference;
    SvxRectCtl          maCtlPos;

    // size
    FixedLine           maFlSize;
    FixedText           maFtWidth;
    MetricField         maMtrWidth;
    FixedText           maFtHeight;
    MetricField         maMtrHeight;
    CheckBox            maCbxScale;
    FixedText           maFtSizeReference;
    SvxRectCtl          maCtlSize;

    // protect
    FixedLine           maFlProtect;
    TriStateBox         maTsbPosProtect;
    TriStateBox         maTsbSizeProtect;

    // adjust
    FixedLine           maFlAdjust;
    TriStateBox         maTsbAutoGrowWidth;
    TriStateBox         maTsbAutoGrowHeight;

    FixedLine           maFlDivider;

private:
    const SfxItemSet&   mrOutAttrs;

    const SdrView*      mpView;
    Rectangle           maRect;
    Rectangle           maWorkArea;

    Point               maAnchorPos;
    SfxMapUnit          mePoolUnit;
    FieldUnit           meDlgUnit;
    MapUnit             meMapUnit;
    TriState            mnProtectSizeState;
    bool                mbPageDisabled;
    bool                mbProtectDisabled;
    bool                mbSizeDisabled;

    // frome size
    UINT32              mlOldWidth;
    UINT32              mlOldHeight;
    RECT_POINT          meRP;

    //------------------------------------
#if _SOLAR__PRIVATE
    DECL_LINK( ChangePosProtectHdl, void * );
    DECL_LINK( ChangeSizeProtectHdl, void * );
    DECL_LINK( ChangePosXHdl, void * );
    DECL_LINK( ChangePosYHdl, void * );
//  DECL_LINK( SetAnchorHdl, ListBox * );
//  DECL_LINK( SetOrientHdl, ListBox * );

    void SetMinMaxPosition();
    void GetTopLeftPosition( long& rX, long& rY, const Rectangle& rRect );
#endif

#if _SOLAR__PRIVATE
    DECL_LINK( ChangeWidthHdl, void * );
    DECL_LINK( ChangeHeightHdl, void * );
    DECL_LINK( ClickSizeProtectHdl, void * );
    DECL_LINK( ClickAutoHdl, void * );

    void        DisableSizeControls();
    void        SetMaxSize( Rectangle aRect );
    Rectangle   GetRect();
#endif

public:
    SvxPositionSizeTabPage( Window* pParent, const SfxItemSet& rInAttrs  );

    static SfxTabPage* Create( Window*, const SfxItemSet& );
    static USHORT*     GetRanges();

    virtual BOOL FillItemSet( SfxItemSet& );
    virtual void Reset( const SfxItemSet & );

    virtual void ActivatePage( const SfxItemSet& rSet );
    virtual int  DeactivatePage( SfxItemSet* pSet );

    virtual void PointChanged( Window* pWindow, RECT_POINT eRP );

    void         Construct();
    void         SetView( const SdrView* pSdrView ) { mpView = pSdrView; }

//  void         ShowAnchorCtrls(USHORT nAnchorCtrls); // Writer-spezifische Controls anzeigen
    virtual void FillUserData();

    void        DisableResize();
    void        DisableProtect();
};

/*************************************************************************
|*
|* Drehwinkel-Tab-Page
|*
\************************************************************************/
class SvxAngleTabPage : public SvxTabPage
{
private:
    FixedLine           aFlPosition;
    FixedText           aFtPosX;
    MetricField         aMtrPosX;
    FixedText           aFtPosY;
    MetricField         aMtrPosY;
    FixedText           aFtPosPresets;
    SvxRectCtl          aCtlRect;

    FixedLine           aFlAngle;
    FixedText           aFtAngle;
    MetricField         aMtrAngle;
    FixedText           aFtAnglePresets;
    SvxRectCtl          aCtlAngle;

    const SfxItemSet&   rOutAttrs;

    const SdrView*      pView;
    Rectangle           aRect;

    Point               aAnchorPos;
    SfxMapUnit          ePoolUnit;
    FieldUnit           eDlgUnit;
    MapUnit             eMapUnit;
    //------------------------------------
#if _SOLAR__PRIVATE
    DECL_LINK( ModifiedHdl, void * );
#endif
public:
         SvxAngleTabPage( Window* pParent, const SfxItemSet& rInAttrs  );

    static SfxTabPage* Create( Window*, const SfxItemSet& );
    static USHORT*     GetRanges();

    virtual BOOL FillItemSet( SfxItemSet& );
    virtual void Reset( const SfxItemSet & );

    virtual void ActivatePage( const SfxItemSet& rSet );
    virtual int  DeactivatePage( SfxItemSet* pSet );

    virtual void PointChanged( Window* pWindow, RECT_POINT eRP );

    void         Construct();
    void         SetView( const SdrView* pSdrView ) { pView = pSdrView; }
};

/*************************************************************************
|*
|* Schraegstellen/Eckenradius-Tab-Page
|*
\************************************************************************/
class SvxSlantTabPage : public SvxTabPage
{
private:
    FixedLine           aFlRadius;
    FixedText           aFtRadius;
    MetricField         aMtrRadius;
    //TriStateBox           aTsbVertical;
    FixedLine           aFlAngle;
    FixedText           aFtAngle;
    MetricField         aMtrAngle;
    //SvxRectCtl            aCtlAngle;

    const SfxItemSet&   rOutAttrs;

    const SdrView*      pView;
    Rectangle           aRect;

    SfxMapUnit          ePoolUnit;
    FieldUnit           eDlgUnit;
    MapUnit             eMapUnit;
    //------------------------------------
public:
         SvxSlantTabPage( Window* pParent, const SfxItemSet& rInAttrs  );

    static SfxTabPage* Create( Window*, const SfxItemSet& );
    static USHORT*     GetRanges();

    virtual BOOL FillItemSet( SfxItemSet& );
    virtual void Reset( const SfxItemSet & );

    virtual void ActivatePage( const SfxItemSet& rSet );
    virtual int  DeactivatePage( SfxItemSet* pSet );

    virtual void PointChanged( Window* pWindow, RECT_POINT eRP );

    void         Construct();
    void         SetView( const SdrView* pSdrView ) { pView = pSdrView; }
};

#endif // _SVX_TRANSFRM_HXX

<commit_msg>INTEGRATION: CWS sb59 (1.6.62); FILE MERGED 2006/08/03 13:51:38 cl 1.6.62.1: removed compiler warnings<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: transfrm.hxx,v $
 *
 *  $Revision: 1.7 $
 *
 *  last change: $Author: obo $ $Date: 2006-10-12 12:32:12 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    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., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/
#ifndef _SVX_TRANSFRM_HXX
#define _SVX_TRANSFRM_HXX

// include ---------------------------------------------------------------

#include "dlgctrl.hxx"

#ifndef _FIXED_HXX //autogen
#include <vcl/fixed.hxx>
#endif
class SdrView;

/*************************************************************************
|*
|* Transform-Tab-Dialog
|*
\************************************************************************/

/** put this into the nAnchorTypes parameter of the SvxTransformTabDialog c'tor
    to disable the size controls */
const USHORT SVX_OBJ_NORESIZE = 0x0100;

/** put this into the nAnchorTypes parameter of the SvxTransformTabDialog c'tor
    to disable the protect controls */
const USHORT SVX_OBJ_NOPROTECT = 0x0200;

class SvxTransformTabDialog : public SfxTabDialog
{
private:
    const SdrView*      pView;

    USHORT              nAnchorCtrls;
    Link                aValidateLink;

    virtual void        PageCreated( USHORT nId, SfxTabPage &rPage );

public:

            SvxTransformTabDialog( Window* pParent, const SfxItemSet* pAttr,
                            const SdrView* pView,
                            USHORT nAnchorTypes = 0);
            ~SvxTransformTabDialog();

            //link for the Writer to validate positions
            void SetValidateFramePosLink( const Link& rLink );
};

/*************************************************************************
|*
|* position and size tab page
|*
\************************************************************************/

class SvxPositionSizeTabPage : public SvxTabPage
{
    using TabPage::ActivatePage;
    using TabPage::DeactivatePage;

private:
    // position
    FixedLine           maFlPosition;
    FixedText           maFtPosX;
    MetricField         maMtrPosX;
    FixedText           maFtPosY;
    MetricField         maMtrPosY;
    FixedText           maFtPosReference;
    SvxRectCtl          maCtlPos;

    // size
    FixedLine           maFlSize;
    FixedText           maFtWidth;
    MetricField         maMtrWidth;
    FixedText           maFtHeight;
    MetricField         maMtrHeight;
    CheckBox            maCbxScale;
    FixedText           maFtSizeReference;
    SvxRectCtl          maCtlSize;

    // protect
    FixedLine           maFlProtect;
    TriStateBox         maTsbPosProtect;
    TriStateBox         maTsbSizeProtect;

    // adjust
    FixedLine           maFlAdjust;
    TriStateBox         maTsbAutoGrowWidth;
    TriStateBox         maTsbAutoGrowHeight;

    FixedLine           maFlDivider;

private:
    const SfxItemSet&   mrOutAttrs;

    const SdrView*      mpView;
    Rectangle           maRect;
    Rectangle           maWorkArea;

    Point               maAnchorPos;
    SfxMapUnit          mePoolUnit;
    FieldUnit           meDlgUnit;
    MapUnit             meMapUnit;
    TriState            mnProtectSizeState;
    bool                mbPageDisabled;
    bool                mbProtectDisabled;
    bool                mbSizeDisabled;

    // frome size
    UINT32              mlOldWidth;
    UINT32              mlOldHeight;
    RECT_POINT          meRP;

    //------------------------------------
#if _SOLAR__PRIVATE
    DECL_LINK( ChangePosProtectHdl, void * );
    DECL_LINK( ChangeSizeProtectHdl, void * );
    DECL_LINK( ChangePosXHdl, void * );
    DECL_LINK( ChangePosYHdl, void * );
//  DECL_LINK( SetAnchorHdl, ListBox * );
//  DECL_LINK( SetOrientHdl, ListBox * );

    void SetMinMaxPosition();
    void GetTopLeftPosition( long& rX, long& rY, const Rectangle& rRect );
#endif

#if _SOLAR__PRIVATE
    DECL_LINK( ChangeWidthHdl, void * );
    DECL_LINK( ChangeHeightHdl, void * );
    DECL_LINK( ClickSizeProtectHdl, void * );
    DECL_LINK( ClickAutoHdl, void * );

    void        DisableSizeControls();
    void        SetMaxSize( Rectangle aRect );
    Rectangle   GetRect();
#endif

public:
    SvxPositionSizeTabPage( Window* pParent, const SfxItemSet& rInAttrs  );

    static SfxTabPage* Create( Window*, const SfxItemSet& );
    static USHORT*     GetRanges();

    virtual BOOL FillItemSet( SfxItemSet& );
    virtual void Reset( const SfxItemSet & );

    virtual void ActivatePage( const SfxItemSet& rSet );
    virtual int  DeactivatePage( SfxItemSet* pSet );

    virtual void PointChanged( Window* pWindow, RECT_POINT eRP );

    void         Construct();
    void         SetView( const SdrView* pSdrView ) { mpView = pSdrView; }

//  void         ShowAnchorCtrls(USHORT nAnchorCtrls); // Writer-spezifische Controls anzeigen
    virtual void FillUserData();

    void        DisableResize();
    void        DisableProtect();
};

/*************************************************************************
|*
|* Drehwinkel-Tab-Page
|*
\************************************************************************/
class SvxAngleTabPage : public SvxTabPage
{
    using TabPage::ActivatePage;
    using TabPage::DeactivatePage;

private:
    FixedLine           aFlPosition;
    FixedText           aFtPosX;
    MetricField         aMtrPosX;
    FixedText           aFtPosY;
    MetricField         aMtrPosY;
    FixedText           aFtPosPresets;
    SvxRectCtl          aCtlRect;

    FixedLine           aFlAngle;
    FixedText           aFtAngle;
    MetricField         aMtrAngle;
    FixedText           aFtAnglePresets;
    SvxRectCtl          aCtlAngle;

    const SfxItemSet&   rOutAttrs;

    const SdrView*      pView;
    Rectangle           aRect;

    Point               aAnchorPos;
    SfxMapUnit          ePoolUnit;
    FieldUnit           eDlgUnit;
    MapUnit             eMapUnit;
    //------------------------------------
#if _SOLAR__PRIVATE
    DECL_LINK( ModifiedHdl, void * );
#endif
public:
         SvxAngleTabPage( Window* pParent, const SfxItemSet& rInAttrs  );

    static SfxTabPage* Create( Window*, const SfxItemSet& );
    static USHORT*     GetRanges();

    virtual BOOL FillItemSet( SfxItemSet& );
    virtual void Reset( const SfxItemSet & );

    virtual void ActivatePage( const SfxItemSet& rSet );
    virtual int  DeactivatePage( SfxItemSet* pSet );

    virtual void PointChanged( Window* pWindow, RECT_POINT eRP );

    void         Construct();
    void         SetView( const SdrView* pSdrView ) { pView = pSdrView; }
};

/*************************************************************************
|*
|* Schraegstellen/Eckenradius-Tab-Page
|*
\************************************************************************/
class SvxSlantTabPage : public SvxTabPage
{
    using TabPage::ActivatePage;
    using TabPage::DeactivatePage;

private:
    FixedLine           aFlRadius;
    FixedText           aFtRadius;
    MetricField         aMtrRadius;
    //TriStateBox           aTsbVertical;
    FixedLine           aFlAngle;
    FixedText           aFtAngle;
    MetricField         aMtrAngle;
    //SvxRectCtl            aCtlAngle;

    const SfxItemSet&   rOutAttrs;

    const SdrView*      pView;
    Rectangle           aRect;

    SfxMapUnit          ePoolUnit;
    FieldUnit           eDlgUnit;
    MapUnit             eMapUnit;
    //------------------------------------
public:
         SvxSlantTabPage( Window* pParent, const SfxItemSet& rInAttrs  );

    static SfxTabPage* Create( Window*, const SfxItemSet& );
    static USHORT*     GetRanges();

    virtual BOOL FillItemSet( SfxItemSet& );
    virtual void Reset( const SfxItemSet & );

    virtual void ActivatePage( const SfxItemSet& rSet );
    virtual int  DeactivatePage( SfxItemSet* pSet );

    virtual void PointChanged( Window* pWindow, RECT_POINT eRP );

    void         Construct();
    void         SetView( const SdrView* pSdrView ) { pView = pSdrView; }
};

#endif // _SVX_TRANSFRM_HXX

<|endoftext|>


<commit_before>/* ---------------------------------------------------------------------------
** This software is in the public domain, furnished "as is", without technical
** support, and with no warranty, express or implied, as to its usefulness for
** any purpose.
**
** V4l2Device.cpp
**
** -------------------------------------------------------------------------*/

#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <fcntl.h>

// libv4l2
#include <linux/videodev2.h>

#include "logger.h"

#include "V4l2Device.h"

// -----------------------------------------
//    V4L2Device
// -----------------------------------------
V4l2Device::V4l2Device(const V4L2DeviceParameters&  params, v4l2_buf_type deviceType) : m_params(params), m_fd(-1), m_deviceType(deviceType), m_bufferSize(0), m_format(0)
{
}

V4l2Device::~V4l2Device()
{
	this->close();
}

void V4l2Device::close()
{
	if (m_fd != -1)
		::close(m_fd);

	m_fd = -1;
}

// query current format
void V4l2Device::queryFormat()
{
	struct v4l2_format     fmt;
	memset(&fmt,0,sizeof(fmt));
	fmt.type  = m_deviceType;
	if (0 == ioctl(m_fd,VIDIOC_G_FMT,&fmt))
	{
		m_format     = fmt.fmt.pix.pixelformat;
		m_width      = fmt.fmt.pix.width;
		m_height     = fmt.fmt.pix.height;
		m_bufferSize = fmt.fmt.pix.sizeimage;
	}
}

// intialize the V4L2 connection
bool V4l2Device::init(unsigned int mandatoryCapabilities)
{
        struct stat sb;
        if ( (stat(m_params.m_devName.c_str(), &sb)==0) && ((sb.st_mode & S_IFMT) == S_IFCHR) )
        {
		if (initdevice(m_params.m_devName.c_str(), mandatoryCapabilities) == -1)
		{
			LOG(ERROR) << "Cannot init device:" << m_params.m_devName;
		}
	}
	else
	{
                // open a normal file
                m_fd = open(m_params.m_devName.c_str(), O_WRONLY | O_CREAT | O_TRUNC, S_IRWXU);
	}
	return (m_fd!=-1);
}

// intialize the V4L2 device
int V4l2Device::initdevice(const char *dev_name, unsigned int mandatoryCapabilities)
{
	m_fd = open(dev_name,  O_RDWR | O_NONBLOCK);
	if (m_fd < 0)
	{
		LOG(ERROR) << "Cannot open device:" << m_params.m_devName << " " << strerror(errno);
		this->close();
		return -1;
	}
	if (checkCapabilities(m_fd,mandatoryCapabilities) !=0)
	{
		this->close();
		return -1;
	}
	if (configureFormat(m_fd) !=0)
	{
		this->close();
		return -1;
	}
	if (configureParam(m_fd) !=0)
	{
		this->close();
		return -1;
	}

	return m_fd;
}

// check needed V4L2 capabilities
int V4l2Device::checkCapabilities(int fd, unsigned int mandatoryCapabilities)
{
	struct v4l2_capability cap;
	memset(&(cap), 0, sizeof(cap));
	if (-1 == ioctl(fd, VIDIOC_QUERYCAP, &cap))
	{
		LOG(ERROR) << "Cannot get capabilities for device:" << m_params.m_devName << " " << strerror(errno);
		return -1;
	}
	LOG(NOTICE) << "driver:" << cap.driver << " capabilities:" << std::hex << cap.capabilities <<  " mandatory:" << mandatoryCapabilities << std::dec;

	if ((cap.capabilities & V4L2_CAP_VIDEO_OUTPUT))  LOG(NOTICE) << m_params.m_devName << " support output";
	if ((cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) LOG(NOTICE) << m_params.m_devName << " support capture";

	if ((cap.capabilities & V4L2_CAP_READWRITE))     LOG(NOTICE) << m_params.m_devName << " support read/write";
	if ((cap.capabilities & V4L2_CAP_STREAMING))     LOG(NOTICE) << m_params.m_devName << " support streaming";

	if ((cap.capabilities & V4L2_CAP_TIMEPERFRAME))  LOG(NOTICE) << m_params.m_devName << " support timeperframe";

	if ( (cap.capabilities & mandatoryCapabilities) != mandatoryCapabilities )
	{
		LOG(ERROR) << "Mandatory capability not available for device:" << m_params.m_devName;
		return -1;
	}

	return 0;
}

std::string fourcc(unsigned int format)
{
	char formatArray[] = { (char)(format&0xff), (char)((format>>8)&0xff), (char)((format>>16)&0xff), (char)((format>>24)&0xff), 0 };
	return std::string(formatArray, strlen(formatArray));
}

// configure capture format
int V4l2Device::configureFormat(int fd)
{
	// get current configuration
	this->queryFormat();
	LOG(NOTICE) << m_params.m_devName << ":" << fourcc(m_format) << " size:" << m_width << "x" << m_height;

	unsigned int width = m_width;
	unsigned int height = m_height;
	if (m_params.m_width != 0)  {
		width= m_params.m_width;
	}
	if (m_params.m_height != 0)  {
		height= m_params.m_height;
	}
	if  (m_params.m_formatList.size()==0)  {
		m_params.m_formatList.push_back(m_format);
	}

	// try to set format, widht, height
	std::list<unsigned int>::iterator it;
	for (it = m_params.m_formatList.begin(); it != m_params.m_formatList.end(); ++it) {
		unsigned int format = *it;
		if ( (format == m_format) && (width == m_width) && (height == m_height) ) {
			// format is the current one
			return 0;
		}
		else if (this->configureFormat(fd, format, width, height)==0) {
			// format has been set
			return 0;
		}
	}
	return -1;
}

// configure capture format
int V4l2Device::configureFormat(int fd, unsigned int format, unsigned int width, unsigned int height)
{
	struct v4l2_format   fmt;
	memset(&(fmt), 0, sizeof(fmt));
	fmt.type                = m_deviceType;
	fmt.fmt.pix.width       = width;
	fmt.fmt.pix.height      = height;
	fmt.fmt.pix.pixelformat = format;
	fmt.fmt.pix.field       = V4L2_FIELD_ANY;

	if (ioctl(fd, VIDIOC_S_FMT, &fmt) == -1)
	{
		LOG(ERROR) << "Cannot set format for device:" << m_params.m_devName << " " << strerror(errno);
		return -1;
	}
	if (fmt.fmt.pix.pixelformat != format)
	{
		LOG(ERROR) << "Cannot set pixelformat to:" << fourcc(format) << " format is:" << fourcc(fmt.fmt.pix.pixelformat);
		return -1;
	}
	if ((fmt.fmt.pix.width != width) || (fmt.fmt.pix.height != height))
	{
		LOG(WARN) << "Cannot set size width:" << fmt.fmt.pix.width << " height:" << fmt.fmt.pix.height;
	}

	m_format     = fmt.fmt.pix.pixelformat;
	m_width      = fmt.fmt.pix.width;
	m_height     = fmt.fmt.pix.height;
	m_bufferSize = fmt.fmt.pix.sizeimage;

	LOG(NOTICE) << m_params.m_devName << ":" << fourcc(m_format) << " size:" << m_params.m_width << "x" << m_params.m_height << " bufferSize:" << m_bufferSize;

	return 0;
}

// configure capture FPS
int V4l2Device::configureParam(int fd)
{
	if (m_params.m_fps!=0)
	{
		struct v4l2_streamparm   param;
		memset(&(param), 0, sizeof(param));
		param.type = m_deviceType;
		param.parm.capture.timeperframe.numerator = 1;
		param.parm.capture.timeperframe.denominator = m_params.m_fps;

		if (ioctl(fd, VIDIOC_S_PARM, &param) == -1)
		{
			LOG(WARN) << "Cannot set param for device:" << m_params.m_devName << " " << strerror(errno);
		}

		LOG(NOTICE) << "fps:" << param.parm.capture.timeperframe.numerator << "/" << param.parm.capture.timeperframe.denominator;
		LOG(NOTICE) << "nbBuffer:" << param.parm.capture.readbuffers;
	}

	return 0;
}

<commit_msg>fix log of width & height<commit_after>/* ---------------------------------------------------------------------------
** This software is in the public domain, furnished "as is", without technical
** support, and with no warranty, express or implied, as to its usefulness for
** any purpose.
**
** V4l2Device.cpp
**
** -------------------------------------------------------------------------*/

#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <fcntl.h>

// libv4l2
#include <linux/videodev2.h>

#include "logger.h"

#include "V4l2Device.h"

// -----------------------------------------
//    V4L2Device
// -----------------------------------------
V4l2Device::V4l2Device(const V4L2DeviceParameters&  params, v4l2_buf_type deviceType) : m_params(params), m_fd(-1), m_deviceType(deviceType), m_bufferSize(0), m_format(0)
{
}

V4l2Device::~V4l2Device()
{
	this->close();
}

void V4l2Device::close()
{
	if (m_fd != -1)
		::close(m_fd);

	m_fd = -1;
}

// query current format
void V4l2Device::queryFormat()
{
	struct v4l2_format     fmt;
	memset(&fmt,0,sizeof(fmt));
	fmt.type  = m_deviceType;
	if (0 == ioctl(m_fd,VIDIOC_G_FMT,&fmt))
	{
		m_format     = fmt.fmt.pix.pixelformat;
		m_width      = fmt.fmt.pix.width;
		m_height     = fmt.fmt.pix.height;
		m_bufferSize = fmt.fmt.pix.sizeimage;
	}
}

// intialize the V4L2 connection
bool V4l2Device::init(unsigned int mandatoryCapabilities)
{
        struct stat sb;
        if ( (stat(m_params.m_devName.c_str(), &sb)==0) && ((sb.st_mode & S_IFMT) == S_IFCHR) )
        {
		if (initdevice(m_params.m_devName.c_str(), mandatoryCapabilities) == -1)
		{
			LOG(ERROR) << "Cannot init device:" << m_params.m_devName;
		}
	}
	else
	{
                // open a normal file
                m_fd = open(m_params.m_devName.c_str(), O_WRONLY | O_CREAT | O_TRUNC, S_IRWXU);
	}
	return (m_fd!=-1);
}

// intialize the V4L2 device
int V4l2Device::initdevice(const char *dev_name, unsigned int mandatoryCapabilities)
{
	m_fd = open(dev_name,  O_RDWR | O_NONBLOCK);
	if (m_fd < 0)
	{
		LOG(ERROR) << "Cannot open device:" << m_params.m_devName << " " << strerror(errno);
		this->close();
		return -1;
	}
	if (checkCapabilities(m_fd,mandatoryCapabilities) !=0)
	{
		this->close();
		return -1;
	}
	if (configureFormat(m_fd) !=0)
	{
		this->close();
		return -1;
	}
	if (configureParam(m_fd) !=0)
	{
		this->close();
		return -1;
	}

	return m_fd;
}

// check needed V4L2 capabilities
int V4l2Device::checkCapabilities(int fd, unsigned int mandatoryCapabilities)
{
	struct v4l2_capability cap;
	memset(&(cap), 0, sizeof(cap));
	if (-1 == ioctl(fd, VIDIOC_QUERYCAP, &cap))
	{
		LOG(ERROR) << "Cannot get capabilities for device:" << m_params.m_devName << " " << strerror(errno);
		return -1;
	}
	LOG(NOTICE) << "driver:" << cap.driver << " capabilities:" << std::hex << cap.capabilities <<  " mandatory:" << mandatoryCapabilities << std::dec;

	if ((cap.capabilities & V4L2_CAP_VIDEO_OUTPUT))  LOG(NOTICE) << m_params.m_devName << " support output";
	if ((cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) LOG(NOTICE) << m_params.m_devName << " support capture";

	if ((cap.capabilities & V4L2_CAP_READWRITE))     LOG(NOTICE) << m_params.m_devName << " support read/write";
	if ((cap.capabilities & V4L2_CAP_STREAMING))     LOG(NOTICE) << m_params.m_devName << " support streaming";

	if ((cap.capabilities & V4L2_CAP_TIMEPERFRAME))  LOG(NOTICE) << m_params.m_devName << " support timeperframe";

	if ( (cap.capabilities & mandatoryCapabilities) != mandatoryCapabilities )
	{
		LOG(ERROR) << "Mandatory capability not available for device:" << m_params.m_devName;
		return -1;
	}

	return 0;
}

std::string fourcc(unsigned int format)
{
	char formatArray[] = { (char)(format&0xff), (char)((format>>8)&0xff), (char)((format>>16)&0xff), (char)((format>>24)&0xff), 0 };
	return std::string(formatArray, strlen(formatArray));
}

// configure capture format
int V4l2Device::configureFormat(int fd)
{
	// get current configuration
	this->queryFormat();
	LOG(NOTICE) << m_params.m_devName << ":" << fourcc(m_format) << " size:" << m_width << "x" << m_height;

	unsigned int width = m_width;
	unsigned int height = m_height;
	if (m_params.m_width != 0)  {
		width= m_params.m_width;
	}
	if (m_params.m_height != 0)  {
		height= m_params.m_height;
	}
	if  (m_params.m_formatList.size()==0)  {
		m_params.m_formatList.push_back(m_format);
	}

	// try to set format, widht, height
	std::list<unsigned int>::iterator it;
	for (it = m_params.m_formatList.begin(); it != m_params.m_formatList.end(); ++it) {
		unsigned int format = *it;
		if ( (format == m_format) && (width == m_width) && (height == m_height) ) {
			// format is the current one
			return 0;
		}
		else if (this->configureFormat(fd, format, width, height)==0) {
			// format has been set
			return 0;
		}
	}
	return -1;
}

// configure capture format
int V4l2Device::configureFormat(int fd, unsigned int format, unsigned int width, unsigned int height)
{
	struct v4l2_format   fmt;
	memset(&(fmt), 0, sizeof(fmt));
	fmt.type                = m_deviceType;
	fmt.fmt.pix.width       = width;
	fmt.fmt.pix.height      = height;
	fmt.fmt.pix.pixelformat = format;
	fmt.fmt.pix.field       = V4L2_FIELD_ANY;

	if (ioctl(fd, VIDIOC_S_FMT, &fmt) == -1)
	{
		LOG(ERROR) << "Cannot set format for device:" << m_params.m_devName << " " << strerror(errno);
		return -1;
	}
	if (fmt.fmt.pix.pixelformat != format)
	{
		LOG(ERROR) << "Cannot set pixelformat to:" << fourcc(format) << " format is:" << fourcc(fmt.fmt.pix.pixelformat);
		return -1;
	}
	if ((fmt.fmt.pix.width != width) || (fmt.fmt.pix.height != height))
	{
		LOG(WARN) << "Cannot set size width:" << fmt.fmt.pix.width << " height:" << fmt.fmt.pix.height;
	}

	m_format     = fmt.fmt.pix.pixelformat;
	m_width      = fmt.fmt.pix.width;
	m_height     = fmt.fmt.pix.height;
	m_bufferSize = fmt.fmt.pix.sizeimage;

	LOG(NOTICE) << m_params.m_devName << ":" << fourcc(m_format) << " size:" << m_width << "x" << m_height << " bufferSize:" << m_bufferSize;

	return 0;
}

// configure capture FPS
int V4l2Device::configureParam(int fd)
{
	if (m_params.m_fps!=0)
	{
		struct v4l2_streamparm   param;
		memset(&(param), 0, sizeof(param));
		param.type = m_deviceType;
		param.parm.capture.timeperframe.numerator = 1;
		param.parm.capture.timeperframe.denominator = m_params.m_fps;

		if (ioctl(fd, VIDIOC_S_PARM, &param) == -1)
		{
			LOG(WARN) << "Cannot set param for device:" << m_params.m_devName << " " << strerror(errno);
		}

		LOG(NOTICE) << "fps:" << param.parm.capture.timeperframe.numerator << "/" << param.parm.capture.timeperframe.denominator;
		LOG(NOTICE) << "nbBuffer:" << param.parm.capture.readbuffers;
	}

	return 0;
}

<|endoftext|>


<commit_before>/*
 * Copyright 2007-2017 Content Management AG
 * All rights reserved.
 *
 * author: Max Kellermann <mk@cm4all.com>
 *
 * 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
 * FOUNDATION 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 "http_server/http_server.hxx"
#include "http_server/Request.hxx"
#include "http_server/Handler.hxx"
#include "http_client.hxx"
#include "http_headers.hxx"
#include "HttpResponseHandler.hxx"
#include "lease.hxx"
#include "direct.hxx"
#include "PInstance.hxx"
#include "pool/pool.hxx"
#include "istream/UnusedPtr.hxx"
#include "istream/HeadIstream.hxx"
#include "istream/BlockIstream.hxx"
#include "istream/istream_catch.hxx"
#include "istream/Sink.hxx"
#include "fb_pool.hxx"
#include "fs/FilteredSocket.hxx"
#include "net/UniqueSocketDescriptor.hxx"
#include "system/Error.hxx"
#include "util/Cancellable.hxx"
#include "util/PrintException.hxx"

#include <functional>

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

class Server final
    : HttpServerConnectionHandler, Lease, BufferedSocketHandler
{
    struct pool *pool;

    HttpServerConnection *connection = nullptr;

    std::function<void(HttpServerRequest &request,
                       CancellablePointer &cancel_ptr)> request_handler;

    FilteredSocket client_fs;

    bool client_fs_released = false;

public:
    Server(struct pool &_pool, EventLoop &event_loop);

    ~Server() noexcept {
        CheckCloseConnection();
    }

    struct pool &GetPool() noexcept {
        return *pool;
    }

    template<typename T>
    void SetRequestHandler(T &&handler) noexcept {
        request_handler = std::forward<T>(handler);
    }

    void CloseConnection() noexcept {
        http_server_connection_close(connection);
        connection = nullptr;
    }

    void CheckCloseConnection() noexcept {
        if (connection != nullptr)
            CloseConnection();
    }

    void SendRequest(http_method_t method, const char *uri,
                     HttpHeaders &&headers,
                     UnusedIstreamPtr body, bool expect_100,
                     HttpResponseHandler &handler,
                     CancellablePointer &cancel_ptr) noexcept {
        http_client_request(*pool, client_fs, *this,
                            "foo",
                            method, uri, std::move(headers),
                            std::move(body), expect_100,
                            handler, cancel_ptr);
    }

    void CloseClientSocket() noexcept {
        if (client_fs.IsValid() && client_fs.IsConnected()) {
            client_fs.Close();
            client_fs.Destroy();
        }
    }

private:
    /* virtual methods from class HttpServerConnectionHandler */
    void HandleHttpRequest(HttpServerRequest &request,
                           CancellablePointer &cancel_ptr) noexcept override;

    void LogHttpRequest(HttpServerRequest &,
                        http_status_t, int64_t,
                        uint64_t, uint64_t) noexcept override {}

    void HttpConnectionError(std::exception_ptr e) noexcept override;
    void HttpConnectionClosed() noexcept override;

    /* virtual methods from class Lease */
    void ReleaseLease(bool reuse) noexcept override {
        client_fs_released = true;

        if (reuse && client_fs.IsValid() && client_fs.IsConnected()) {
            client_fs.Reinit(Event::Duration(-1), Event::Duration(-1), *this);
            client_fs.UnscheduleWrite();
        } else {
            CloseClientSocket();
        }
    }

    /* virtual methods from class BufferedSocketHandler */
    BufferedResult OnBufferedData() override {
        fprintf(stderr, "unexpected data in idle TCP connection");
        CloseClientSocket();
        return BufferedResult::CLOSED;
    }

    bool OnBufferedClosed() noexcept override {
        CloseClientSocket();
        return false;
    }

    gcc_noreturn
    bool OnBufferedWrite() override {
        /* should never be reached because we never schedule
           writing */
        gcc_unreachable();
    }

    void OnBufferedError(std::exception_ptr e) noexcept override {
        PrintException(e);
        CloseClientSocket();
    }
};

class Client final : HttpResponseHandler, IstreamSink {
    CancellablePointer client_cancel_ptr;

    std::exception_ptr response_error;
    std::string response_body;
    http_status_t status{};

    bool response_eof = false;

public:
    void SendRequest(Server &server,
                     http_method_t method, const char *uri,
                     HttpHeaders &&headers,
                     UnusedIstreamPtr body, bool expect_100=false) noexcept {
        server.SendRequest(method, uri, std::move(headers),
                             std::move(body), expect_100,
                             *this, client_cancel_ptr);
    }

    bool IsClientDone() const noexcept {
        return response_error || response_eof;
    }

    void RethrowResponseError() const {
        if (response_error)
            std::rethrow_exception(response_error);
    }

private:
    /* virtual methods from class HttpResponseHandler */
    void OnHttpResponse(http_status_t _status, StringMap &&headers,
                        UnusedIstreamPtr body) noexcept override {
        status = _status;

        (void)headers;

        IstreamSink::SetInput(std::move(body));
        input.Read();
    }

    void OnHttpError(std::exception_ptr ep) noexcept override {
        response_error = std::move(ep);
    }

    /* virtual methods from class IstreamHandler */

    size_t OnData(const void *data, size_t length) override {
        response_body.append((const char *)data, length);
        return length;
    }

    void OnEof() noexcept override {
        IstreamSink::ClearInput();
        response_eof = true;
    }

    void OnError(std::exception_ptr ep) noexcept override {
        IstreamSink::ClearInput();
        response_error = std::move(ep);
    }
};

Server::Server(struct pool &_pool, EventLoop &event_loop)
    :pool(pool_new_libc(&_pool, "catch")),
     client_fs(event_loop)
{
    UniqueSocketDescriptor client_socket, server_socket;
    if (!UniqueSocketDescriptor::CreateSocketPair(AF_LOCAL, SOCK_STREAM, 0,
                                                  client_socket, server_socket))
        throw MakeErrno("socketpair() failed");

    connection = http_server_connection_new(pool, event_loop,
                                            std::move(server_socket),
                                            FdType::FD_SOCKET,
                                            nullptr,
                                            nullptr, nullptr,
                                            true, *this);

    client_fs.InitDummy(client_socket.Release(), FdType::FD_SOCKET);

    pool_unref(pool);
}

static std::exception_ptr
catch_callback(std::exception_ptr ep, gcc_unused void *ctx) noexcept
{
    PrintException(ep);
    return {};
}

void
Server::HandleHttpRequest(HttpServerRequest &request,
                            CancellablePointer &cancel_ptr) noexcept
{
    request_handler(request, cancel_ptr);
}

void
Server::HttpConnectionError(std::exception_ptr e) noexcept
{
    connection = nullptr;

    PrintException(e);
}

void
Server::HttpConnectionClosed() noexcept
{
    connection = nullptr;
}

static void
test_catch(EventLoop &event_loop, struct pool *_pool)
{
    Server server(*_pool, event_loop);

    server.SetRequestHandler([&server](HttpServerRequest &request, CancellablePointer &) noexcept {
        http_server_response(&request, HTTP_STATUS_OK, HttpHeaders(request.pool),
                             istream_catch_new(&request.pool,
                                               std::move(request.body),
                                               catch_callback, nullptr));
        server.CloseConnection();
    });

    Client client;

    client.SendRequest(server,
                       HTTP_METHOD_POST, "/", HttpHeaders(server.GetPool()),
                       istream_head_new(server.GetPool(),
                                        istream_block_new(server.GetPool()),
                                        1024, true));

    while (!client.IsClientDone())
        event_loop.LoopOnce();

    server.CloseClientSocket();
    client.RethrowResponseError();

    event_loop.Dispatch();
}

int
main(int argc, char **argv) noexcept
try {
    (void)argc;
    (void)argv;

    direct_global_init();
    const ScopeFbPoolInit fb_pool_init;
    PInstance instance;

    test_catch(instance.event_loop, instance.root_pool);
} catch (...) {
    PrintException(std::current_exception());
    return EXIT_FAILURE;
}
<commit_msg>test/t_http_server: auto-close the HTTP client socket<commit_after>/*
 * Copyright 2007-2017 Content Management AG
 * All rights reserved.
 *
 * author: Max Kellermann <mk@cm4all.com>
 *
 * 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
 * FOUNDATION 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 "http_server/http_server.hxx"
#include "http_server/Request.hxx"
#include "http_server/Handler.hxx"
#include "http_client.hxx"
#include "http_headers.hxx"
#include "HttpResponseHandler.hxx"
#include "lease.hxx"
#include "direct.hxx"
#include "PInstance.hxx"
#include "pool/pool.hxx"
#include "istream/UnusedPtr.hxx"
#include "istream/HeadIstream.hxx"
#include "istream/BlockIstream.hxx"
#include "istream/istream_catch.hxx"
#include "istream/Sink.hxx"
#include "fb_pool.hxx"
#include "fs/FilteredSocket.hxx"
#include "net/UniqueSocketDescriptor.hxx"
#include "system/Error.hxx"
#include "util/Cancellable.hxx"
#include "util/PrintException.hxx"

#include <functional>

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

class Server final
    : HttpServerConnectionHandler, Lease, BufferedSocketHandler
{
    struct pool *pool;

    HttpServerConnection *connection = nullptr;

    std::function<void(HttpServerRequest &request,
                       CancellablePointer &cancel_ptr)> request_handler;

    FilteredSocket client_fs;

    bool client_fs_released = false;

public:
    Server(struct pool &_pool, EventLoop &event_loop);

    ~Server() noexcept {
        CloseClientSocket();
        CheckCloseConnection();
    }

    struct pool &GetPool() noexcept {
        return *pool;
    }

    template<typename T>
    void SetRequestHandler(T &&handler) noexcept {
        request_handler = std::forward<T>(handler);
    }

    void CloseConnection() noexcept {
        http_server_connection_close(connection);
        connection = nullptr;
    }

    void CheckCloseConnection() noexcept {
        if (connection != nullptr)
            CloseConnection();
    }

    void SendRequest(http_method_t method, const char *uri,
                     HttpHeaders &&headers,
                     UnusedIstreamPtr body, bool expect_100,
                     HttpResponseHandler &handler,
                     CancellablePointer &cancel_ptr) noexcept {
        http_client_request(*pool, client_fs, *this,
                            "foo",
                            method, uri, std::move(headers),
                            std::move(body), expect_100,
                            handler, cancel_ptr);
    }

    void CloseClientSocket() noexcept {
        if (client_fs.IsValid() && client_fs.IsConnected()) {
            client_fs.Close();
            client_fs.Destroy();
        }
    }

private:
    /* virtual methods from class HttpServerConnectionHandler */
    void HandleHttpRequest(HttpServerRequest &request,
                           CancellablePointer &cancel_ptr) noexcept override;

    void LogHttpRequest(HttpServerRequest &,
                        http_status_t, int64_t,
                        uint64_t, uint64_t) noexcept override {}

    void HttpConnectionError(std::exception_ptr e) noexcept override;
    void HttpConnectionClosed() noexcept override;

    /* virtual methods from class Lease */
    void ReleaseLease(bool reuse) noexcept override {
        client_fs_released = true;

        if (reuse && client_fs.IsValid() && client_fs.IsConnected()) {
            client_fs.Reinit(Event::Duration(-1), Event::Duration(-1), *this);
            client_fs.UnscheduleWrite();
        } else {
            CloseClientSocket();
        }
    }

    /* virtual methods from class BufferedSocketHandler */
    BufferedResult OnBufferedData() override {
        fprintf(stderr, "unexpected data in idle TCP connection");
        CloseClientSocket();
        return BufferedResult::CLOSED;
    }

    bool OnBufferedClosed() noexcept override {
        CloseClientSocket();
        return false;
    }

    gcc_noreturn
    bool OnBufferedWrite() override {
        /* should never be reached because we never schedule
           writing */
        gcc_unreachable();
    }

    void OnBufferedError(std::exception_ptr e) noexcept override {
        PrintException(e);
        CloseClientSocket();
    }
};

class Client final : HttpResponseHandler, IstreamSink {
    CancellablePointer client_cancel_ptr;

    std::exception_ptr response_error;
    std::string response_body;
    http_status_t status{};

    bool response_eof = false;

public:
    void SendRequest(Server &server,
                     http_method_t method, const char *uri,
                     HttpHeaders &&headers,
                     UnusedIstreamPtr body, bool expect_100=false) noexcept {
        server.SendRequest(method, uri, std::move(headers),
                             std::move(body), expect_100,
                             *this, client_cancel_ptr);
    }

    bool IsClientDone() const noexcept {
        return response_error || response_eof;
    }

    void RethrowResponseError() const {
        if (response_error)
            std::rethrow_exception(response_error);
    }

private:
    /* virtual methods from class HttpResponseHandler */
    void OnHttpResponse(http_status_t _status, StringMap &&headers,
                        UnusedIstreamPtr body) noexcept override {
        status = _status;

        (void)headers;

        IstreamSink::SetInput(std::move(body));
        input.Read();
    }

    void OnHttpError(std::exception_ptr ep) noexcept override {
        response_error = std::move(ep);
    }

    /* virtual methods from class IstreamHandler */

    size_t OnData(const void *data, size_t length) override {
        response_body.append((const char *)data, length);
        return length;
    }

    void OnEof() noexcept override {
        IstreamSink::ClearInput();
        response_eof = true;
    }

    void OnError(std::exception_ptr ep) noexcept override {
        IstreamSink::ClearInput();
        response_error = std::move(ep);
    }
};

Server::Server(struct pool &_pool, EventLoop &event_loop)
    :pool(pool_new_libc(&_pool, "catch")),
     client_fs(event_loop)
{
    UniqueSocketDescriptor client_socket, server_socket;
    if (!UniqueSocketDescriptor::CreateSocketPair(AF_LOCAL, SOCK_STREAM, 0,
                                                  client_socket, server_socket))
        throw MakeErrno("socketpair() failed");

    connection = http_server_connection_new(pool, event_loop,
                                            std::move(server_socket),
                                            FdType::FD_SOCKET,
                                            nullptr,
                                            nullptr, nullptr,
                                            true, *this);

    client_fs.InitDummy(client_socket.Release(), FdType::FD_SOCKET);

    pool_unref(pool);
}

static std::exception_ptr
catch_callback(std::exception_ptr ep, gcc_unused void *ctx) noexcept
{
    PrintException(ep);
    return {};
}

void
Server::HandleHttpRequest(HttpServerRequest &request,
                            CancellablePointer &cancel_ptr) noexcept
{
    request_handler(request, cancel_ptr);
}

void
Server::HttpConnectionError(std::exception_ptr e) noexcept
{
    connection = nullptr;

    PrintException(e);
}

void
Server::HttpConnectionClosed() noexcept
{
    connection = nullptr;
}

static void
test_catch(EventLoop &event_loop, struct pool *_pool)
{
    Server server(*_pool, event_loop);

    server.SetRequestHandler([&server](HttpServerRequest &request, CancellablePointer &) noexcept {
        http_server_response(&request, HTTP_STATUS_OK, HttpHeaders(request.pool),
                             istream_catch_new(&request.pool,
                                               std::move(request.body),
                                               catch_callback, nullptr));
        server.CloseConnection();
    });

    Client client;

    client.SendRequest(server,
                       HTTP_METHOD_POST, "/", HttpHeaders(server.GetPool()),
                       istream_head_new(server.GetPool(),
                                        istream_block_new(server.GetPool()),
                                        1024, true));

    while (!client.IsClientDone())
        event_loop.LoopOnce();

    server.CloseClientSocket();
    client.RethrowResponseError();

    event_loop.Dispatch();
}

int
main(int argc, char **argv) noexcept
try {
    (void)argc;
    (void)argv;

    direct_global_init();
    const ScopeFbPoolInit fb_pool_init;
    PInstance instance;

    test_catch(instance.event_loop, instance.root_pool);
} catch (...) {
    PrintException(std::current_exception());
    return EXIT_FAILURE;
}
<|endoftext|>


<commit_before>#include "http_server/http_server.hxx"
#include "http_server/Request.hxx"
#include "http_server/Handler.hxx"
#include "http_headers.hxx"
#include "direct.hxx"
#include "pool.hxx"
#include "istream/istream.hxx"
#include "istream/istream_block.hxx"
#include "istream/istream_cat.hxx"
#include "istream/istream_catch.hxx"
#include "istream/istream_socketpair.hxx"
#include "istream/istream_string.hxx"
#include "istream/sink_null.hxx"
#include "fb_pool.hxx"

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

static GError *
catch_callback(GError *error, gcc_unused void *ctx)
{
    fprintf(stderr, "%s\n", error->message);
    g_error_free(error);
    return nullptr;
}

static void
catch_close_request(struct http_server_request *request, void *ctx,
                    struct async_operation_ref *async_ref gcc_unused)
{
    (void)ctx;

    http_server_response(request, HTTP_STATUS_OK, HttpHeaders(),
                         istream_catch_new(request->pool, request->body,
                                           catch_callback, nullptr));
    http_server_connection_close(request->connection);
}

static void
catch_close_error(GError *error, void *ctx)
{
    (void)ctx;

    g_printerr("%s\n", error->message);
    g_error_free(error);
}

static void
catch_close_free(void *ctx)
{
    (void)ctx;
}

static const struct http_server_connection_handler catch_close_handler = {
    .request = catch_close_request,
    .error = catch_close_error,
    .free = catch_close_free,
};

static void
test_catch(struct pool *pool)
{
    int fd;
    struct http_server_connection *connection;

    pool = pool_new_libc(pool, "catch");

    struct istream *request =
        istream_cat_new(pool,
                        istream_string_new(pool,
                                           "POST / HTTP/1.1\r\nContent-Length: 1024\r\n\r\nfoo"),
                        istream_block_new(*pool),
                        nullptr);
    struct istream *sock = istream_socketpair_new(pool, request, &fd);
    sink_null_new(sock);

    http_server_connection_new(pool, fd, FdType::FD_SOCKET, nullptr, nullptr,
                               nullptr, nullptr,
                               true, &catch_close_handler, nullptr,
                               &connection);
    pool_unref(pool);

    event_dispatch();
}

int main(int argc, char **argv) {
    struct event_base *event_base;
    struct pool *pool;

    (void)argc;
    (void)argv;

    direct_global_init();
    event_base = event_init();
    fb_pool_init(false);

    pool = pool_new_libc(nullptr, "root");

    test_catch(pool);

    pool_unref(pool);
    pool_commit();
    pool_recycler_clear();

    fb_pool_deinit();
    event_base_free(event_base);
    direct_global_deinit();
}
<commit_msg>test/t_http_server: initialise "log" attribute<commit_after>#include "http_server/http_server.hxx"
#include "http_server/Request.hxx"
#include "http_server/Handler.hxx"
#include "http_headers.hxx"
#include "direct.hxx"
#include "pool.hxx"
#include "istream/istream.hxx"
#include "istream/istream_block.hxx"
#include "istream/istream_cat.hxx"
#include "istream/istream_catch.hxx"
#include "istream/istream_socketpair.hxx"
#include "istream/istream_string.hxx"
#include "istream/sink_null.hxx"
#include "fb_pool.hxx"

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

static GError *
catch_callback(GError *error, gcc_unused void *ctx)
{
    fprintf(stderr, "%s\n", error->message);
    g_error_free(error);
    return nullptr;
}

static void
catch_close_request(struct http_server_request *request, void *ctx,
                    struct async_operation_ref *async_ref gcc_unused)
{
    (void)ctx;

    http_server_response(request, HTTP_STATUS_OK, HttpHeaders(),
                         istream_catch_new(request->pool, request->body,
                                           catch_callback, nullptr));
    http_server_connection_close(request->connection);
}

static void
catch_close_error(GError *error, void *ctx)
{
    (void)ctx;

    g_printerr("%s\n", error->message);
    g_error_free(error);
}

static void
catch_close_free(void *ctx)
{
    (void)ctx;
}

static const struct http_server_connection_handler catch_close_handler = {
    .request = catch_close_request,
    .log = nullptr,
    .error = catch_close_error,
    .free = catch_close_free,
};

static void
test_catch(struct pool *pool)
{
    int fd;
    struct http_server_connection *connection;

    pool = pool_new_libc(pool, "catch");

    struct istream *request =
        istream_cat_new(pool,
                        istream_string_new(pool,
                                           "POST / HTTP/1.1\r\nContent-Length: 1024\r\n\r\nfoo"),
                        istream_block_new(*pool),
                        nullptr);
    struct istream *sock = istream_socketpair_new(pool, request, &fd);
    sink_null_new(sock);

    http_server_connection_new(pool, fd, FdType::FD_SOCKET, nullptr, nullptr,
                               nullptr, nullptr,
                               true, &catch_close_handler, nullptr,
                               &connection);
    pool_unref(pool);

    event_dispatch();
}

int main(int argc, char **argv) {
    struct event_base *event_base;
    struct pool *pool;

    (void)argc;
    (void)argv;

    direct_global_init();
    event_base = event_init();
    fb_pool_init(false);

    pool = pool_new_libc(nullptr, "root");

    test_catch(pool);

    pool_unref(pool);
    pool_commit();
    pool_recycler_clear();

    fb_pool_deinit();
    event_base_free(event_base);
    direct_global_deinit();
}
<|endoftext|>


<commit_before>#include "istream/istream_tee.hxx"
#include "istream/istream_delayed.hxx"
#include "istream/istream_string.hxx"
#include "istream/istream.hxx"
#include "istream/sink_close.hxx"
#include "istream/sink_gstring.hxx"
#include "async.hxx"
#include "event/Event.hxx"

#include <glib.h>

#include <string.h>

struct Context {
    GString *value = nullptr;

    bool eof = false, aborted = false;
};

struct BlockContext final : Context, IstreamHandler {
    /* istream handler */

    size_t OnData(gcc_unused const void *data, gcc_unused size_t length) override {
        // block
        return 0;
    }

    void OnEof() override {
        eof = true;
    }

    void OnError(GError *error) override {
        g_error_free(error);
        aborted = true;
    }
};

/*
 * tests
 *
 */

static void
buffer_callback(GString *value, GError *error, void *_ctx)
{
    auto *ctx = (Context *)_ctx;

    assert(value != nullptr);
    assert(error == nullptr);

    ctx->value = value;
}

static void
test_block1(struct pool *pool)
{
    BlockContext ctx;
    struct async_operation_ref async_ref;

    Istream *delayed = istream_delayed_new(pool);
    Istream *tee = istream_tee_new(*pool, *delayed, false, false);
    Istream *second = &istream_tee_second(*tee);

    tee->SetHandler(ctx);

    sink_gstring_new(*pool, *second, buffer_callback, (Context *)&ctx, async_ref);
    assert(ctx.value == nullptr);

    /* the input (istream_delayed) blocks */
    second->Read();
    assert(ctx.value == nullptr);

    /* feed data into input */
    istream_delayed_set(*delayed, *istream_string_new(pool, "foo"));
    assert(ctx.value == nullptr);

    /* the first output (block_istream_handler) blocks */
    second->Read();
    assert(ctx.value == nullptr);

    /* close the blocking output, this should release the "tee"
       object and restart reading (into the second output) */
    assert(!ctx.aborted && !ctx.eof);
    istream_free(&tee);
    assert(!ctx.aborted && !ctx.eof);
    assert(ctx.value != nullptr);
    assert(strcmp(ctx.value->str, "foo") == 0);
    g_string_free(ctx.value, true);
}

static void
test_close_data(struct pool *pool)
{
    Context ctx;
    struct async_operation_ref async_ref;

    Istream *tee =
        istream_tee_new(*pool, *istream_string_new(pool, "foo"), false, false);

    sink_close_new(*pool, *tee);
    Istream *second = &istream_tee_second(*tee);

    sink_gstring_new(*pool, *second, buffer_callback, &ctx, async_ref);
    assert(ctx.value == nullptr);

    second->Read();

    /* at this point, sink_close has closed itself, and istream_tee
       should have passed the data to the sink_gstring */

    assert(ctx.value != nullptr);
    assert(strcmp(ctx.value->str, "foo") == 0);
    g_string_free(ctx.value, true);
}

/**
 * Close the second output after data has been consumed only by the
 * first output.  This verifies that istream_tee's "skip" attribute is
 * obeyed properly.
 */
static void
test_close_skipped(struct pool *pool)
{
    Context ctx;
    struct async_operation_ref async_ref;

    Istream *input = istream_string_new(pool, "foo");
    Istream *tee = istream_tee_new(*pool, *input, false, false);
    sink_gstring_new(*pool, *tee, buffer_callback, &ctx, async_ref);

    Istream *second = &istream_tee_second(*tee);
    sink_close_new(*pool, *second);

    assert(ctx.value == nullptr);

    input->Read();

    assert(ctx.value != nullptr);
    assert(strcmp(ctx.value->str, "foo") == 0);
    g_string_free(ctx.value, true);
}

/*
 * main
 *
 */

int main(int argc, char **argv) {
    struct pool *root_pool;

    (void)argc;
    (void)argv;

    EventBase event_base;
    root_pool = pool_new_libc(nullptr, "root");

    /* run test suite */

    test_block1(root_pool);
    test_close_data(root_pool);
    test_close_skipped(root_pool);

    /* cleanup */

    pool_unref(root_pool);
    pool_commit();

    pool_recycler_clear();
}
<commit_msg>test/t_istream_tee: add child pools<commit_after>#include "istream/istream_tee.hxx"
#include "istream/istream_delayed.hxx"
#include "istream/istream_string.hxx"
#include "istream/istream.hxx"
#include "istream/sink_close.hxx"
#include "istream/sink_gstring.hxx"
#include "async.hxx"
#include "event/Event.hxx"

#include <glib.h>

#include <string.h>

struct Context {
    GString *value = nullptr;

    bool eof = false, aborted = false;
};

struct BlockContext final : Context, IstreamHandler {
    /* istream handler */

    size_t OnData(gcc_unused const void *data, gcc_unused size_t length) override {
        // block
        return 0;
    }

    void OnEof() override {
        eof = true;
    }

    void OnError(GError *error) override {
        g_error_free(error);
        aborted = true;
    }
};

/*
 * tests
 *
 */

static void
buffer_callback(GString *value, GError *error, void *_ctx)
{
    auto *ctx = (Context *)_ctx;

    assert(value != nullptr);
    assert(error == nullptr);

    ctx->value = value;
}

static void
test_block1(struct pool *pool)
{
    BlockContext ctx;
    struct async_operation_ref async_ref;

    pool = pool_new_libc(nullptr, "test");

    Istream *delayed = istream_delayed_new(pool);
    Istream *tee = istream_tee_new(*pool, *delayed, false, false);
    Istream *second = &istream_tee_second(*tee);

    tee->SetHandler(ctx);

    sink_gstring_new(*pool, *second, buffer_callback, (Context *)&ctx, async_ref);
    assert(ctx.value == nullptr);

    pool_unref(pool);

    /* the input (istream_delayed) blocks */
    second->Read();
    assert(ctx.value == nullptr);

    /* feed data into input */
    istream_delayed_set(*delayed, *istream_string_new(pool, "foo"));
    assert(ctx.value == nullptr);

    /* the first output (block_istream_handler) blocks */
    second->Read();
    assert(ctx.value == nullptr);

    /* close the blocking output, this should release the "tee"
       object and restart reading (into the second output) */
    assert(!ctx.aborted && !ctx.eof);
    istream_free(&tee);
    assert(!ctx.aborted && !ctx.eof);
    assert(ctx.value != nullptr);
    assert(strcmp(ctx.value->str, "foo") == 0);
    g_string_free(ctx.value, true);

    pool_commit();
}

static void
test_close_data(struct pool *pool)
{
    Context ctx;
    struct async_operation_ref async_ref;

    pool = pool_new_libc(nullptr, "test");
    Istream *tee =
        istream_tee_new(*pool, *istream_string_new(pool, "foo"), false, false);

    sink_close_new(*pool, *tee);
    Istream *second = &istream_tee_second(*tee);

    sink_gstring_new(*pool, *second, buffer_callback, &ctx, async_ref);
    assert(ctx.value == nullptr);

    pool_unref(pool);

    second->Read();

    /* at this point, sink_close has closed itself, and istream_tee
       should have passed the data to the sink_gstring */

    assert(ctx.value != nullptr);
    assert(strcmp(ctx.value->str, "foo") == 0);
    g_string_free(ctx.value, true);

    pool_commit();
}

/**
 * Close the second output after data has been consumed only by the
 * first output.  This verifies that istream_tee's "skip" attribute is
 * obeyed properly.
 */
static void
test_close_skipped(struct pool *pool)
{
    Context ctx;
    struct async_operation_ref async_ref;

    pool = pool_new_libc(nullptr, "test");
    Istream *input = istream_string_new(pool, "foo");
    Istream *tee = istream_tee_new(*pool, *input, false, false);
    sink_gstring_new(*pool, *tee, buffer_callback, &ctx, async_ref);

    Istream *second = &istream_tee_second(*tee);
    sink_close_new(*pool, *second);
    pool_unref(pool);

    assert(ctx.value == nullptr);

    input->Read();

    assert(ctx.value != nullptr);
    assert(strcmp(ctx.value->str, "foo") == 0);
    g_string_free(ctx.value, true);

    pool_commit();
}

/*
 * main
 *
 */

int main(int argc, char **argv) {
    struct pool *root_pool;

    (void)argc;
    (void)argv;

    EventBase event_base;
    root_pool = pool_new_libc(nullptr, "root");

    /* run test suite */

    test_block1(root_pool);
    test_close_data(root_pool);
    test_close_skipped(root_pool);

    /* cleanup */

    pool_unref(root_pool);
    pool_commit();

    pool_recycler_clear();
}
<|endoftext|>