<commit_before>/*
 OFX FrameHold plugin.

 Copyright (C) 2015 INRIA
 Author: Frederic Devernay <frederic.devernay@inria.fr>

 Redistribution and use in source and binary forms, with or without modification,
 are permitted provided that the following conditions are met:

 Redistributions of source code must retain the above copyright notice, this
 list of conditions and the following disclaimer.

 Redistributions in binary form must reproduce the above copyright notice, this
 list of conditions and the following disclaimer in the documentation and/or
 other materials provided with the distribution.

 Neither the name of the {organization} nor the names of its
 contributors may be used to endorse or promote products derived from
 this software without specific prior written permission.

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

 INRIA
 Domaine de Voluceau
 Rocquencourt - B.P. 105
 78153 Le Chesnay Cedex - France

 The skeleton for this source file is from:
 OFX Basic Example plugin, a plugin that illustrates the use of the OFX Support library.

 Copyright (C) 2004-2005 The Open Effects Association Ltd
 Author Bruno Nicoletti bruno@thefoundry.co.uk

 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are met:

 * Redistributions of source code must retain the above copyright notice,
 this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright notice,
 this list of conditions and the following disclaimer in the documentation
 and/or other materials provided with the distribution.
 * Neither the name The Open Effects Association Ltd, nor the names of its
 contributors may be used to endorse or promote products derived from this
 software without specific prior written permission.

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

 The Open Effects Association Ltd
 1 Wardour St
 London W1D 6PA
 England

 */

#include "FrameHold.h"

#include <cmath>
#include <stdio.h> // for snprintf & _snprintf
#ifdef _WINDOWS
#include <windows.h>
#define snprintf _snprintf
#endif

#include "ofxsProcessing.H"
#include "ofxsMacros.h"
#include "ofxNatron.h"

#define kPluginName "FrameHoldOFX"
#define kPluginGrouping "Time"
#define kPluginDescription "Hold a given frame for the input clip indefinitely, or use a subsample of the input frames and hold them for several frames."
#define kPluginIdentifier "net.sf.openfx.FrameHold"
#define kPluginVersionMajor 1 // Incrementing this number means that you have broken backwards compatibility of the plug-in.
#define kPluginVersionMinor 0 // Increment this when you have fixed a bug or made it faster.

#define kSupportsTiles 1
#define kSupportsMultiResolution 1
#define kSupportsRenderScale 1
#define kSupportsMultipleClipPARs false
#define kSupportsMultipleClipDepths false
#define kRenderThreadSafety eRenderFullySafe

#define kParamFirstFrame "firstFrame"
#define kParamFirstFrameLabel "First Frame"
#define kParamFirstFrameHint "Reference input frame (the frame to hold if increment is 0)."
#define kParamIncrement "increment"
#define kParamIncrementLabel "Increment"
#define kParamIncrementHint "If increment is 0, only the \"firstFrame\" will be held. If it is positive, every multiple of \"increment\" plus \"firstFrame\" will be held for \"increment\" frames afterwards (before if it is negative)."

namespace OFX {
    extern ImageEffectHostDescription gHostDescription;
}

////////////////////////////////////////////////////////////////////////////////
/** @brief The plugin that does our work */
class FrameHoldPlugin : public OFX::ImageEffect
{
public:
    /** @brief ctor */
    FrameHoldPlugin(OfxImageEffectHandle handle)
    : ImageEffect(handle)
    , _dstClip(0)
    , _srcClip(0)
    , _firstFrame(0)
    , _increment(0)
    , _sublabel(0)
    {
        _dstClip = fetchClip(kOfxImageEffectOutputClipName);
        assert(_dstClip && (_dstClip->getPixelComponents() == OFX::ePixelComponentAlpha || _dstClip->getPixelComponents() == OFX::ePixelComponentRGB || _dstClip->getPixelComponents() == OFX::ePixelComponentRGBA));
        _srcClip = fetchClip(kOfxImageEffectSimpleSourceClipName);
        assert(_srcClip && (_srcClip->getPixelComponents() == OFX::ePixelComponentAlpha || _srcClip->getPixelComponents() == OFX::ePixelComponentRGB || _srcClip->getPixelComponents() == OFX::ePixelComponentRGBA));

        _firstFrame = fetchIntParam(kParamFirstFrame);
        _increment = fetchIntParam(kParamIncrement);
        _sublabel = fetchStringParam(kNatronOfxParamStringSublabelName);
        assert(_firstFrame && _increment);
    }

private:
    /* Override the render */
    virtual void render(const OFX::RenderArguments &args) OVERRIDE FINAL;

    /** Override the get frames needed action */
    virtual void getFramesNeeded(const OFX::FramesNeededArguments &args, OFX::FramesNeededSetter &frames) OVERRIDE FINAL;

    /* override is identity */
    virtual bool isIdentity(const OFX::IsIdentityArguments &args, OFX::Clip * &identityClip, double &identityTime) OVERRIDE FINAL;

    virtual void changedParam(const OFX::InstanceChangedArgs &args, const std::string &paramName) OVERRIDE FINAL;

private:
    double getSourceTime(double time) const;

    void updateSublabel();

private:
    // do not need to delete these, the ImageEffect is managing them for us
    OFX::Clip *_dstClip;            /**< @brief Mandated output clips */
    OFX::Clip *_srcClip;            /**< @brief Mandated input clips */

    OFX::IntParam  *_firstFrame;
    OFX::IntParam  *_increment;
    OFX::StringParam *_sublabel;
};

////////////////////////////////////////////////////////////////////////////////
/** @brief render for the filter */

////////////////////////////////////////////////////////////////////////////////
// basic plugin render function, just a skelington to instantiate templates from

// figure the frame we should be retiming from
double
FrameHoldPlugin::getSourceTime(double t) const
{
    int firstFrame, increment;
    _firstFrame->getValueAtTime(t, firstFrame);
    _increment->getValueAtTime(t, increment);

    if (increment == 0) {
        return firstFrame;
    }

    return firstFrame + increment * std::floor((t - firstFrame)/increment);
}

void
FrameHoldPlugin::updateSublabel()
{
    char label[80];

    int firstFrame, increment;
    _firstFrame->getValue(firstFrame);
    _increment->getValue(increment);
    if (increment == 0) {
        snprintf(label, sizeof(label), "frame %d", firstFrame);
    } else {
        snprintf(label, sizeof(label), "frame %d+n*%d", firstFrame, increment);
    }
    _sublabel->setValue(label);
}

void
FrameHoldPlugin::getFramesNeeded(const OFX::FramesNeededArguments &args,
                                  OFX::FramesNeededSetter &frames)
{
    double sourceTime = getSourceTime(args.time);
    OfxRangeD range;
    range.min = sourceTime;
    range.max = sourceTime;
    frames.setFramesNeeded(*_srcClip, range);
}

// the overridden render function
void
FrameHoldPlugin::render(const OFX::RenderArguments &/*args*/)
{
    // do nothing as this should never be called as isIdentity should always be trapped
}

// overridden is identity
bool
FrameHoldPlugin::isIdentity(const OFX::IsIdentityArguments &args, OFX::Clip * &identityClip, double &identityTime)
{
    identityClip = _srcClip;
    identityTime = getSourceTime(args.time);
    return true;
}

void
FrameHoldPlugin::changedParam(const OFX::InstanceChangedArgs &args, const std::string &paramName)
{
    if ((paramName == kParamFirstFrame || paramName == kParamIncrement) && args.reason == OFX::eChangeUserEdit) {
        updateSublabel();
    }
}

using namespace OFX;

mDeclarePluginFactory(FrameHoldPluginFactory, ;, {});

void FrameHoldPluginFactory::load()
{
    // we can't be used on hosts that don't perfrom temporal clip access
    if (!gHostDescription.temporalClipAccess) {
        throw OFX::Exception::HostInadequate("Need random temporal image access to work");
    }
}

/** @brief The basic describe function, passed a plugin descriptor */
void FrameHoldPluginFactory::describe(OFX::ImageEffectDescriptor &desc)
{
    // basic labels
    desc.setLabel(kPluginName);
    desc.setPluginGrouping(kPluginGrouping);
    desc.setPluginDescription(kPluginDescription);

    // Say we are a filer context
    desc.addSupportedContext(OFX::eContextFilter);
    desc.addSupportedContext(OFX::eContextGeneral);

    // Add supported pixel depths
    desc.addSupportedBitDepth(eBitDepthUByte);
    desc.addSupportedBitDepth(eBitDepthUShort);
    desc.addSupportedBitDepth(eBitDepthFloat);

    // set a few flags
    desc.setSingleInstance(false);
    desc.setHostFrameThreading(false);
    desc.setSupportsMultiResolution(kSupportsMultiResolution);
    desc.setSupportsTiles(kSupportsTiles);
    desc.setTemporalClipAccess(true); // say we will be doing random time access on clips
    desc.setRenderTwiceAlways(false);
    desc.setSupportsMultipleClipPARs(kSupportsMultipleClipPARs);
    desc.setSupportsMultipleClipDepths(kSupportsMultipleClipDepths);
    desc.setRenderThreadSafety(kRenderThreadSafety);
    // we can't be used on hosts that don't perfrom temporal clip access
    if (!gHostDescription.temporalClipAccess) {
        throw OFX::Exception::HostInadequate("Need random temporal image access to work");
    }
}

/** @brief The describe in context function, passed a plugin descriptor and a context */
void FrameHoldPluginFactory::describeInContext(OFX::ImageEffectDescriptor &desc, ContextEnum /*context*/)
{
    // we are a transition, so define the sourceTo input clip
    ClipDescriptor *srcClip = desc.defineClip(kOfxImageEffectSimpleSourceClipName);
    srcClip->addSupportedComponent(ePixelComponentRGBA);
    srcClip->addSupportedComponent(ePixelComponentRGB);
    srcClip->addSupportedComponent(ePixelComponentAlpha);
    srcClip->setTemporalClipAccess(true); // say we will be doing random time access on this clip
    srcClip->setSupportsTiles(kSupportsTiles);

    // create the mandated output clip
    ClipDescriptor *dstClip = desc.defineClip(kOfxImageEffectOutputClipName);
    dstClip->addSupportedComponent(ePixelComponentRGBA);
    dstClip->addSupportedComponent(ePixelComponentRGB);
    dstClip->addSupportedComponent(ePixelComponentAlpha);
    dstClip->setSupportsTiles(kSupportsTiles);

    // make some pages and to things in
    PageParamDescriptor *page = desc.definePageParam("Controls");

    // firstFrame
    {
        IntParamDescriptor *param = desc.defineIntParam(kParamFirstFrame);
        param->setLabel(kParamFirstFrameLabel);
        param->setHint(kParamFirstFrameHint);
        param->setDefault(0);
        // keep default range (INT_MIN..INT_MAX)
        // no display range
        // param->setDisplayRange(0, 0);
        param->setAnimates(true);
        if (page) {
            page->addChild(*param);
        }
    }

    // increment
    {
        IntParamDescriptor *param = desc.defineIntParam(kParamIncrement);
        param->setLabel(kParamIncrementLabel);
        param->setHint(kParamIncrementHint);
        param->setDefault(0);
        param->setAnimates(true);
        if (page) {
            page->addChild(*param);
        }
    }

    // sublabel
    {
        StringParamDescriptor* param = desc.defineStringParam(kNatronOfxParamStringSublabelName);
        param->setIsSecret(true); // always secret
        param->setEnabled(false);
        param->setIsPersistant(true);
        param->setEvaluateOnChange(false);
        param->setDefault("frame 0");
        if (page) {
            page->addChild(*param);
        }
    }
}

/** @brief The create instance function, the plugin must return an object derived from the \ref OFX::ImageEffect class */
ImageEffect* FrameHoldPluginFactory::createInstance(OfxImageEffectHandle handle, ContextEnum /*context*/)
{
    return new FrameHoldPlugin(handle);
}

void getFrameHoldPluginID(OFX::PluginFactoryArray &ids)
{
    static FrameHoldPluginFactory p(kPluginIdentifier, kPluginVersionMajor, kPluginVersionMinor);
    ids.push_back(&p);
}
<commit_msg>FrameHost: fixlet<commit_after>/*
 OFX FrameHold plugin.

 Copyright (C) 2015 INRIA
 Author: Frederic Devernay <frederic.devernay@inria.fr>

 Redistribution and use in source and binary forms, with or without modification,
 are permitted provided that the following conditions are met:

 Redistributions of source code must retain the above copyright notice, this
 list of conditions and the following disclaimer.

 Redistributions in binary form must reproduce the above copyright notice, this
 list of conditions and the following disclaimer in the documentation and/or
 other materials provided with the distribution.

 Neither the name of the {organization} nor the names of its
 contributors may be used to endorse or promote products derived from
 this software without specific prior written permission.

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

 INRIA
 Domaine de Voluceau
 Rocquencourt - B.P. 105
 78153 Le Chesnay Cedex - France

 The skeleton for this source file is from:
 OFX Basic Example plugin, a plugin that illustrates the use of the OFX Support library.

 Copyright (C) 2004-2005 The Open Effects Association Ltd
 Author Bruno Nicoletti bruno@thefoundry.co.uk

 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are met:

 * Redistributions of source code must retain the above copyright notice,
 this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright notice,
 this list of conditions and the following disclaimer in the documentation
 and/or other materials provided with the distribution.
 * Neither the name The Open Effects Association Ltd, nor the names of its
 contributors may be used to endorse or promote products derived from this
 software without specific prior written permission.

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

 The Open Effects Association Ltd
 1 Wardour St
 London W1D 6PA
 England

 */

#include "FrameHold.h"

#include <cmath>
#include <stdio.h> // for snprintf & _snprintf
#ifdef _WINDOWS
#include <windows.h>
#define snprintf _snprintf
#endif

#include "ofxsProcessing.H"
#include "ofxsMacros.h"
#include "ofxNatron.h"

#define kPluginName "FrameHoldOFX"
#define kPluginGrouping "Time"
#define kPluginDescription "Hold a given frame for the input clip indefinitely, or use a subsample of the input frames and hold them for several frames."
#define kPluginIdentifier "net.sf.openfx.FrameHold"
#define kPluginVersionMajor 1 // Incrementing this number means that you have broken backwards compatibility of the plug-in.
#define kPluginVersionMinor 0 // Increment this when you have fixed a bug or made it faster.

#define kSupportsTiles 1
#define kSupportsMultiResolution 1
#define kSupportsRenderScale 1
#define kSupportsMultipleClipPARs false
#define kSupportsMultipleClipDepths false
#define kRenderThreadSafety eRenderFullySafe

#define kParamFirstFrame "firstFrame"
#define kParamFirstFrameLabel "First Frame"
#define kParamFirstFrameHint "Reference input frame (the frame to hold if increment is 0)."
#define kParamIncrement "increment"
#define kParamIncrementLabel "Increment"
#define kParamIncrementHint "If increment is 0, only the \"firstFrame\" will be held. If it is positive, every multiple of \"increment\" plus \"firstFrame\" will be held for \"increment\" frames afterwards (before if it is negative)."

namespace OFX {
    extern ImageEffectHostDescription gHostDescription;
}

////////////////////////////////////////////////////////////////////////////////
/** @brief The plugin that does our work */
class FrameHoldPlugin : public OFX::ImageEffect
{
public:
    /** @brief ctor */
    FrameHoldPlugin(OfxImageEffectHandle handle)
    : ImageEffect(handle)
    , _dstClip(0)
    , _srcClip(0)
    , _firstFrame(0)
    , _increment(0)
    , _sublabel(0)
    {
        _dstClip = fetchClip(kOfxImageEffectOutputClipName);
        assert(_dstClip && (_dstClip->getPixelComponents() == OFX::ePixelComponentAlpha || _dstClip->getPixelComponents() == OFX::ePixelComponentRGB || _dstClip->getPixelComponents() == OFX::ePixelComponentRGBA));
        _srcClip = fetchClip(kOfxImageEffectSimpleSourceClipName);
        assert(_srcClip && (_srcClip->getPixelComponents() == OFX::ePixelComponentAlpha || _srcClip->getPixelComponents() == OFX::ePixelComponentRGB || _srcClip->getPixelComponents() == OFX::ePixelComponentRGBA));

        _firstFrame = fetchIntParam(kParamFirstFrame);
        _increment = fetchIntParam(kParamIncrement);
        _sublabel = fetchStringParam(kNatronOfxParamStringSublabelName);
        assert(_firstFrame && _increment && _sublabel);
    }

private:
    /* Override the render */
    virtual void render(const OFX::RenderArguments &args) OVERRIDE FINAL;

    /** Override the get frames needed action */
    virtual void getFramesNeeded(const OFX::FramesNeededArguments &args, OFX::FramesNeededSetter &frames) OVERRIDE FINAL;

    /* override is identity */
    virtual bool isIdentity(const OFX::IsIdentityArguments &args, OFX::Clip * &identityClip, double &identityTime) OVERRIDE FINAL;

    virtual void changedParam(const OFX::InstanceChangedArgs &args, const std::string &paramName) OVERRIDE FINAL;

private:
    double getSourceTime(double time) const;

    void updateSublabel();

private:
    // do not need to delete these, the ImageEffect is managing them for us
    OFX::Clip *_dstClip;            /**< @brief Mandated output clips */
    OFX::Clip *_srcClip;            /**< @brief Mandated input clips */

    OFX::IntParam  *_firstFrame;
    OFX::IntParam  *_increment;
    OFX::StringParam *_sublabel;
};

////////////////////////////////////////////////////////////////////////////////
/** @brief render for the filter */

////////////////////////////////////////////////////////////////////////////////
// basic plugin render function, just a skelington to instantiate templates from

// figure the frame we should be retiming from
double
FrameHoldPlugin::getSourceTime(double t) const
{
    int firstFrame, increment;
    _firstFrame->getValueAtTime(t, firstFrame);
    _increment->getValueAtTime(t, increment);

    if (increment == 0) {
        return firstFrame;
    }

    return firstFrame + increment * std::floor((t - firstFrame)/increment);
}

void
FrameHoldPlugin::updateSublabel()
{
    char label[80];

    int firstFrame, increment;
    _firstFrame->getValue(firstFrame);
    _increment->getValue(increment);
    if (increment == 0) {
        snprintf(label, sizeof(label), "frame %d", firstFrame);
    } else {
        snprintf(label, sizeof(label), "frame %d+n*%d", firstFrame, increment);
    }
    _sublabel->setValue(label);
}

void
FrameHoldPlugin::getFramesNeeded(const OFX::FramesNeededArguments &args,
                                  OFX::FramesNeededSetter &frames)
{
    double sourceTime = getSourceTime(args.time);
    OfxRangeD range;
    range.min = sourceTime;
    range.max = sourceTime;
    frames.setFramesNeeded(*_srcClip, range);
}

// the overridden render function
void
FrameHoldPlugin::render(const OFX::RenderArguments &/*args*/)
{
    // do nothing as this should never be called as isIdentity should always be trapped
}

// overridden is identity
bool
FrameHoldPlugin::isIdentity(const OFX::IsIdentityArguments &args, OFX::Clip * &identityClip, double &identityTime)
{
    identityClip = _srcClip;
    identityTime = getSourceTime(args.time);
    return true;
}

void
FrameHoldPlugin::changedParam(const OFX::InstanceChangedArgs &args, const std::string &paramName)
{
    if ((paramName == kParamFirstFrame || paramName == kParamIncrement) && args.reason == OFX::eChangeUserEdit) {
        updateSublabel();
    }
}

using namespace OFX;

mDeclarePluginFactory(FrameHoldPluginFactory, ;, {});

void FrameHoldPluginFactory::load()
{
    // we can't be used on hosts that don't perfrom temporal clip access
    if (!gHostDescription.temporalClipAccess) {
        throw OFX::Exception::HostInadequate("Need random temporal image access to work");
    }
}

/** @brief The basic describe function, passed a plugin descriptor */
void FrameHoldPluginFactory::describe(OFX::ImageEffectDescriptor &desc)
{
    // basic labels
    desc.setLabel(kPluginName);
    desc.setPluginGrouping(kPluginGrouping);
    desc.setPluginDescription(kPluginDescription);

    // Say we are a filer context
    desc.addSupportedContext(OFX::eContextFilter);
    desc.addSupportedContext(OFX::eContextGeneral);

    // Add supported pixel depths
    desc.addSupportedBitDepth(eBitDepthUByte);
    desc.addSupportedBitDepth(eBitDepthUShort);
    desc.addSupportedBitDepth(eBitDepthFloat);

    // set a few flags
    desc.setSingleInstance(false);
    desc.setHostFrameThreading(false);
    desc.setSupportsMultiResolution(kSupportsMultiResolution);
    desc.setSupportsTiles(kSupportsTiles);
    desc.setTemporalClipAccess(true); // say we will be doing random time access on clips
    desc.setRenderTwiceAlways(false);
    desc.setSupportsMultipleClipPARs(kSupportsMultipleClipPARs);
    desc.setSupportsMultipleClipDepths(kSupportsMultipleClipDepths);
    desc.setRenderThreadSafety(kRenderThreadSafety);
    // we can't be used on hosts that don't perfrom temporal clip access
    if (!gHostDescription.temporalClipAccess) {
        throw OFX::Exception::HostInadequate("Need random temporal image access to work");
    }
}

/** @brief The describe in context function, passed a plugin descriptor and a context */
void FrameHoldPluginFactory::describeInContext(OFX::ImageEffectDescriptor &desc, ContextEnum /*context*/)
{
    // we are a transition, so define the sourceTo input clip
    ClipDescriptor *srcClip = desc.defineClip(kOfxImageEffectSimpleSourceClipName);
    srcClip->addSupportedComponent(ePixelComponentRGBA);
    srcClip->addSupportedComponent(ePixelComponentRGB);
    srcClip->addSupportedComponent(ePixelComponentAlpha);
    srcClip->setTemporalClipAccess(true); // say we will be doing random time access on this clip
    srcClip->setSupportsTiles(kSupportsTiles);

    // create the mandated output clip
    ClipDescriptor *dstClip = desc.defineClip(kOfxImageEffectOutputClipName);
    dstClip->addSupportedComponent(ePixelComponentRGBA);
    dstClip->addSupportedComponent(ePixelComponentRGB);
    dstClip->addSupportedComponent(ePixelComponentAlpha);
    dstClip->setSupportsTiles(kSupportsTiles);

    // make some pages and to things in
    PageParamDescriptor *page = desc.definePageParam("Controls");

    // firstFrame
    {
        IntParamDescriptor *param = desc.defineIntParam(kParamFirstFrame);
        param->setLabel(kParamFirstFrameLabel);
        param->setHint(kParamFirstFrameHint);
        param->setDefault(0);
        // keep default range (INT_MIN..INT_MAX)
        // no display range
        // param->setDisplayRange(0, 0);
        param->setAnimates(true);
        if (page) {
            page->addChild(*param);
        }
    }

    // increment
    {
        IntParamDescriptor *param = desc.defineIntParam(kParamIncrement);
        param->setLabel(kParamIncrementLabel);
        param->setHint(kParamIncrementHint);
        param->setDefault(0);
        param->setAnimates(true);
        if (page) {
            page->addChild(*param);
        }
    }

    // sublabel
    {
        StringParamDescriptor* param = desc.defineStringParam(kNatronOfxParamStringSublabelName);
        param->setIsSecret(true); // always secret
        param->setEnabled(false);
        param->setIsPersistant(true);
        param->setEvaluateOnChange(false);
        param->setDefault("frame 0");
        if (page) {
            page->addChild(*param);
        }
    }
}

/** @brief The create instance function, the plugin must return an object derived from the \ref OFX::ImageEffect class */
ImageEffect* FrameHoldPluginFactory::createInstance(OfxImageEffectHandle handle, ContextEnum /*context*/)
{
    return new FrameHoldPlugin(handle);
}

void getFrameHoldPluginID(OFX::PluginFactoryArray &ids)
{
    static FrameHoldPluginFactory p(kPluginIdentifier, kPluginVersionMajor, kPluginVersionMinor);
    ids.push_back(&p);
}
<|endoftext|>


<commit_before>/*
 Copyright (c) 2011 Aaron Drew
 All rights reserved.

 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions
 are met:
 1. Redistributions of source code must retain the above copyright
    notice, this list of conditions and the following disclaimer.
 2. 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.
 3. Neither the name of the copyright holders nor the names of its
    contributors may be used to endorse or promote products derived from
    this software without specific prior written permission.

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

#include <errno.h>
#include <fcntl.h>
#include <sys/epoll.h>

#include <glog/logging.h>

namespace epoll_threadpool {

using std::tr1::function;
using std::tr1::bind;

TcpSocket::TcpSocket(EventManager* em, int fd)
    : _internal(new Internal(em, fd)) {
}

TcpSocket::~TcpSocket() {
  _internal->_disconnectCallback = NULL;
  _internal->disconnect();
}

TcpSocket::Internal::Internal(EventManager* em, int fd)
    : _em(em), _fd(fd), _isStarted(false) {
  pthread_mutex_init(&_mutex, 0);
  fcntl(_fd, F_SETFL, O_NONBLOCK);
}
TcpSocket::Internal::~Internal() {
  disconnect();
  pthread_mutex_destroy(&_mutex);
}

void TcpSocket::start() {
  _internal->start();
}

void TcpSocket::Internal::start() {
  pthread_mutex_lock(&_mutex);
  if (!_isStarted) {
    _isStarted = true;
    if (_fd > 0) {
      _em->watchFd(_fd, EventManager::EM_READ,
          bind(&TcpSocket::Internal::onReceive, shared_from_this()));
      _em->watchFd(_fd, EventManager::EM_WRITE,
          bind(&TcpSocket::Internal::onCanSend, shared_from_this()));

      // We trigger the receive handler to handle the case where we got
      // disconnected before start() completed.
      _em->enqueue(
          bind(&TcpSocket::Internal::onReceive, shared_from_this()));
      _em->enqueue(
          bind(&TcpSocket::Internal::onCanSend, shared_from_this()));
    } else {
      DLOG(INFO) << "disconnected in start()";
      if (_disconnectCallback) {
        _em->enqueue(_disconnectCallback);
        _disconnectCallback = NULL;
      }
    }
  }
  pthread_mutex_unlock(&_mutex);
}

shared_ptr<TcpSocket> TcpSocket::connect(
    EventManager* em, string host, uint16_t port) {
  struct sockaddr_in sa;
  int fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);

  if (fd == -1) {
    LOG(ERROR) << "can not create socket";
    return shared_ptr<TcpSocket>();
  }

  memset(&sa, 0, sizeof(sa));
  sa.sin_family = AF_INET;
  sa.sin_port = htons(port);
  if (inet_pton(AF_INET, host.c_str(), &sa.sin_addr) <= 0) {
    LOG(ERROR) << "Failed to resolve address: " << host;
    close(fd);
    return shared_ptr<TcpSocket>();
  }

  if (::connect(fd, (struct sockaddr*)&sa, sizeof(sa)) == -1) {
    close(fd);
    return shared_ptr<TcpSocket>();
  }

  return shared_ptr<TcpSocket>(new TcpSocket(em, fd));
}

void TcpSocket::write(IOBuffer* data) {
  _internal->write(data);
}

void TcpSocket::disconnect() {
  _internal->disconnect();
}

bool TcpSocket::isDisconnected() const {
  return _internal->_fd == -1;
}

void TcpSocket::setReceiveCallback(function<void(IOBuffer*)> callback) {
  pthread_mutex_lock(&_internal->_mutex);
  _internal->_recvCallback = callback;
  pthread_mutex_unlock(&_internal->_mutex);
}

void TcpSocket::setDisconnectCallback(function<void()> callback) {
  pthread_mutex_lock(&_internal->_mutex);
  _internal->_disconnectCallback = callback;
  pthread_mutex_unlock(&_internal->_mutex);
}

void TcpSocket::Internal::write(IOBuffer* data) {
  pthread_mutex_lock(&_mutex);
  bool wasBufferEmpty = (_sendBuffer.size() == 0);
  _sendBuffer.append(data);
  if (_fd >= 0 && _isStarted && wasBufferEmpty) {
    _em->watchFd(_fd, EventManager::EM_WRITE,
      bind(&TcpSocket::Internal::onCanSend, shared_from_this()));
  }
  pthread_mutex_unlock(&_mutex);
}

void TcpSocket::Internal::disconnect() {
  pthread_mutex_lock(&_mutex);
  if (_fd > 0) {
    _em->removeFd(_fd, EventManager::EM_READ);
    _em->removeFd(_fd, EventManager::EM_WRITE);
    _em->removeFd(_fd, EventManager::EM_ERROR);
    ::shutdown(_fd, SHUT_RDWR);
    ::close(_fd);
    _fd = -1;
    if (_disconnectCallback) {
      _em->enqueue(_disconnectCallback);
      _disconnectCallback = NULL;
    }
  }
  pthread_mutex_unlock(&_mutex);
}

void TcpSocket::Internal::onReceive() {
  pthread_mutex_lock(&_mutex);
  while (_fd > 0) {
    vector<char>* buf = new vector<char>();
    buf->resize(4096);

    int r = ::read(_fd, &(*buf)[0], buf->size());
    if (r > 0) {
      buf->resize(r);
      _recvBuffer.append(buf);
      if (_recvCallback) {
        _recvCallback(&_recvBuffer);
      }
    } else {
      if (r < 0) {
        delete buf;
        if (errno != EAGAIN) {
          LOG(INFO) << "Read error. (" << errno << "). Disconnecting fd " << _fd;
          _em->enqueue(bind(
              &TcpSocket::Internal::disconnect, shared_from_this()));
        }
      } else {
        delete buf;
        _em->enqueue(bind(
            &TcpSocket::Internal::disconnect, shared_from_this()));
      }
      break;
    }
  }
  pthread_mutex_unlock(&_mutex);
}

void TcpSocket::Internal::onCanSend() {
  const int kMaxSendSize = 4096;
  pthread_mutex_lock(&_mutex);
  if (_fd > 0) {
    while (_sendBuffer.size()) {
      int sz = (_sendBuffer.size() > kMaxSendSize) ?
                   kMaxSendSize : _sendBuffer.size();
      const char* buf = _sendBuffer.pulldown(sz);
      if (buf) {
        int r = ::write(_fd, buf, sz);
        if (r > 0) {
          _sendBuffer.consume(r);
        } else if (r < 0) {
          if (errno != EAGAIN) {
            LOG(INFO) << "Write error. (" << errno << "). Disconnecting fd " << _fd;
            _em->enqueue(bind(
                &TcpSocket::Internal::disconnect, shared_from_this()));
          }
          pthread_mutex_unlock(&_mutex);
          return;
        } else {
          _em->enqueue(bind(
              &TcpSocket::Internal::disconnect, shared_from_this()));
        }
      }
    }
    _em->removeFd(_fd, EventManager::EM_WRITE);
  }
  pthread_mutex_unlock(&_mutex);
}

TcpListenSocket::TcpListenSocket(EventManager* em, int fd)
    : _internal(new Internal(em, fd)) {
  em->watchFd(fd, EventManager::EM_READ, bind(
      &TcpListenSocket::Internal::onAccept, _internal));
}

TcpListenSocket::~TcpListenSocket() {
  _internal->shutdown();
}

TcpListenSocket::Internal::Internal(EventManager* em, int fd)
    : _em(em), _fd(fd) {
  pthread_mutex_init(&_mutex, 0);
  fcntl(_fd, F_SETFL, O_NONBLOCK);
}

TcpListenSocket::Internal::~Internal() {
  pthread_mutex_destroy(&_mutex);
}

void TcpListenSocket::Internal::shutdown() {
  pthread_mutex_lock(&_mutex);
  _em->removeFd(_fd, EventManager::EM_READ);
  ::shutdown(_fd, SHUT_RDWR);
  ::close(_fd);
  _fd = -1;
  pthread_mutex_unlock(&_mutex);
}

shared_ptr<TcpListenSocket> TcpListenSocket::create(
    EventManager* em, uint16_t port) {
  struct sockaddr_in sa;
  int fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);

  if (fd == -1) {
    LOG(ERROR) << "can not create socket";
    return shared_ptr<TcpListenSocket>();
  }

  memset(&sa, 0, sizeof(sa));
  sa.sin_family = AF_INET;
  sa.sin_port = htons(port);
  sa.sin_addr.s_addr = INADDR_ANY;

  if (::bind(fd, (struct sockaddr *)&sa, sizeof(sa)) == -1) {
    LOG(ERROR) << "error bind failed";
    close(fd);
    return shared_ptr<TcpListenSocket>();
  }

  if (::listen(fd, 5) == -1) {
    LOG(ERROR) << "error listen failed";
    close(fd);
    return shared_ptr<TcpListenSocket>();
  }

  return shared_ptr<TcpListenSocket>(new TcpListenSocket(em, fd));
}

void TcpListenSocket::setAcceptCallback(
    function<void(shared_ptr<TcpSocket>)> callback) {
  pthread_mutex_lock(&_internal->_mutex);
  _internal->_callback = callback;
  pthread_mutex_unlock(&_internal->_mutex);
}

void TcpListenSocket::Internal::onAccept() {
  pthread_mutex_lock(&_mutex);
  if (_fd > 0) {
    int fd = ::accept(_fd, NULL, NULL);
    pthread_mutex_unlock(&_mutex);
    if (fd > 0) {
      shared_ptr<TcpSocket> s(new TcpSocket(_em, fd));
      if (_callback) {
        _callback(s);
        // We separate registration to give AcceptCallback a chance to set up
        // receive / disconnect callbacks for the socket. The callback must
        // call start() on the socket to begin receiving traffic.
      }
    }
  } else {
    pthread_mutex_unlock(&_mutex);
  }
}
}
<commit_msg>Move from read/write to recv/send so I can avoid triggering SIG_PIPE.<commit_after>/*
 Copyright (c) 2011 Aaron Drew
 All rights reserved.

 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions
 are met:
 1. Redistributions of source code must retain the above copyright
    notice, this list of conditions and the following disclaimer.
 2. 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.
 3. Neither the name of the copyright holders nor the names of its
    contributors may be used to endorse or promote products derived from
    this software without specific prior written permission.

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

#include <errno.h>
#include <fcntl.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <sys/types.h>

#include <glog/logging.h>

namespace epoll_threadpool {

using std::tr1::function;
using std::tr1::bind;

TcpSocket::TcpSocket(EventManager* em, int fd)
    : _internal(new Internal(em, fd)) {
}

TcpSocket::~TcpSocket() {
  _internal->_disconnectCallback = NULL;
  _internal->disconnect();
}

TcpSocket::Internal::Internal(EventManager* em, int fd)
    : _em(em), _fd(fd), _isStarted(false) {
  pthread_mutex_init(&_mutex, 0);
  fcntl(_fd, F_SETFL, O_NONBLOCK);
}

TcpSocket::Internal::~Internal() {
  disconnect();
  pthread_mutex_destroy(&_mutex);
}

void TcpSocket::start() {
  _internal->start();
}

void TcpSocket::Internal::start() {
  pthread_mutex_lock(&_mutex);
  if (!_isStarted) {
    _isStarted = true;
    if (_fd > 0) {
      _em->watchFd(_fd, EventManager::EM_READ,
          bind(&TcpSocket::Internal::onReceive, shared_from_this()));
      _em->watchFd(_fd, EventManager::EM_WRITE,
          bind(&TcpSocket::Internal::onCanSend, shared_from_this()));

      // We trigger the receive handler to handle the case where we got
      // disconnected before start() completed.
      _em->enqueue(
          bind(&TcpSocket::Internal::onReceive, shared_from_this()));
      _em->enqueue(
          bind(&TcpSocket::Internal::onCanSend, shared_from_this()));
    } else {
      DLOG(INFO) << "disconnected in start()";
      if (_disconnectCallback) {
        _em->enqueue(_disconnectCallback);
        _disconnectCallback = NULL;
      }
    }
  }
  pthread_mutex_unlock(&_mutex);
}

shared_ptr<TcpSocket> TcpSocket::connect(
    EventManager* em, string host, uint16_t port) {
  struct sockaddr_in sa;
  int fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);

  if (fd == -1) {
    LOG(ERROR) << "can not create socket";
    return shared_ptr<TcpSocket>();
  }

  memset(&sa, 0, sizeof(sa));
  sa.sin_family = AF_INET;
  sa.sin_port = htons(port);
  if (inet_pton(AF_INET, host.c_str(), &sa.sin_addr) <= 0) {
    LOG(ERROR) << "Failed to resolve address: " << host;
    close(fd);
    return shared_ptr<TcpSocket>();
  }

  if (::connect(fd, (struct sockaddr*)&sa, sizeof(sa)) == -1) {
    close(fd);
    return shared_ptr<TcpSocket>();
  }

  return shared_ptr<TcpSocket>(new TcpSocket(em, fd));
}

void TcpSocket::write(IOBuffer* data) {
  _internal->write(data);
}

void TcpSocket::disconnect() {
  _internal->disconnect();
}

bool TcpSocket::isDisconnected() const {
  return _internal->_fd == -1;
}

void TcpSocket::setReceiveCallback(function<void(IOBuffer*)> callback) {
  pthread_mutex_lock(&_internal->_mutex);
  _internal->_recvCallback = callback;
  pthread_mutex_unlock(&_internal->_mutex);
}

void TcpSocket::setDisconnectCallback(function<void()> callback) {
  pthread_mutex_lock(&_internal->_mutex);
  _internal->_disconnectCallback = callback;
  pthread_mutex_unlock(&_internal->_mutex);
}

void TcpSocket::Internal::write(IOBuffer* data) {
  pthread_mutex_lock(&_mutex);
  bool wasBufferEmpty = (_sendBuffer.size() == 0);
  _sendBuffer.append(data);
  if (_fd >= 0 && _isStarted && wasBufferEmpty) {
    _em->watchFd(_fd, EventManager::EM_WRITE,
      bind(&TcpSocket::Internal::onCanSend, shared_from_this()));
  }
  pthread_mutex_unlock(&_mutex);
}

void TcpSocket::Internal::disconnect() {
  pthread_mutex_lock(&_mutex);
  if (_fd > 0) {
    _em->removeFd(_fd, EventManager::EM_READ);
    _em->removeFd(_fd, EventManager::EM_WRITE);
    _em->removeFd(_fd, EventManager::EM_ERROR);
    ::shutdown(_fd, SHUT_RDWR);
    ::close(_fd);
    _fd = -1;
    if (_disconnectCallback) {
      _em->enqueue(_disconnectCallback);
      _disconnectCallback = NULL;
    }
  }
  pthread_mutex_unlock(&_mutex);
}

void TcpSocket::Internal::onReceive() {
  pthread_mutex_lock(&_mutex);
  while (_fd > 0) {
    vector<char>* buf = new vector<char>();
    buf->resize(4096);

    int r = ::recv(_fd, &(*buf)[0], buf->size(), 0);
    if (r > 0) {
      buf->resize(r);
      _recvBuffer.append(buf);
      if (_recvCallback) {
        _recvCallback(&_recvBuffer);
      }
    } else {
      if (r < 0) {
        delete buf;
        if (errno != EAGAIN) {
          LOG(INFO) << "Read error. (" << errno << "). Disconnecting fd " << _fd;
          _em->enqueue(bind(
              &TcpSocket::Internal::disconnect, shared_from_this()));
        }
      } else {
        delete buf;
        _em->enqueue(bind(
            &TcpSocket::Internal::disconnect, shared_from_this()));
      }
      break;
    }
  }
  pthread_mutex_unlock(&_mutex);
}

void TcpSocket::Internal::onCanSend() {
  const int kMaxSendSize = 4096;
  pthread_mutex_lock(&_mutex);
  if (_fd > 0) {
    while (_sendBuffer.size()) {
      int sz = (_sendBuffer.size() > kMaxSendSize) ?
                   kMaxSendSize : _sendBuffer.size();
      const char* buf = _sendBuffer.pulldown(sz);
      if (buf) {
        int r = ::send(_fd, buf, sz, MSG_NOSIGNAL);
        if (r > 0) {
          _sendBuffer.consume(r);
        } else if (r < 0) {
          if (errno != EAGAIN) {
            LOG(INFO) << "Write error. (" << errno << "). Disconnecting fd " << _fd;
            _em->enqueue(bind(
                &TcpSocket::Internal::disconnect, shared_from_this()));
          }
          pthread_mutex_unlock(&_mutex);
          return;
        } else {
          _em->enqueue(bind(
              &TcpSocket::Internal::disconnect, shared_from_this()));
        }
      }
    }
    _em->removeFd(_fd, EventManager::EM_WRITE);
  }
  pthread_mutex_unlock(&_mutex);
}

TcpListenSocket::TcpListenSocket(EventManager* em, int fd)
    : _internal(new Internal(em, fd)) {
  em->watchFd(fd, EventManager::EM_READ, bind(
      &TcpListenSocket::Internal::onAccept, _internal));
}

TcpListenSocket::~TcpListenSocket() {
  _internal->shutdown();
}

TcpListenSocket::Internal::Internal(EventManager* em, int fd)
    : _em(em), _fd(fd) {
  pthread_mutex_init(&_mutex, 0);
  fcntl(_fd, F_SETFL, O_NONBLOCK);
}

TcpListenSocket::Internal::~Internal() {
  pthread_mutex_destroy(&_mutex);
}

void TcpListenSocket::Internal::shutdown() {
  pthread_mutex_lock(&_mutex);
  _em->removeFd(_fd, EventManager::EM_READ);
  ::shutdown(_fd, SHUT_RDWR);
  ::close(_fd);
  _fd = -1;
  pthread_mutex_unlock(&_mutex);
}

shared_ptr<TcpListenSocket> TcpListenSocket::create(
    EventManager* em, uint16_t port) {
  struct sockaddr_in sa;
  int fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);

  if (fd == -1) {
    LOG(ERROR) << "can not create socket";
    return shared_ptr<TcpListenSocket>();
  }

  memset(&sa, 0, sizeof(sa));
  sa.sin_family = AF_INET;
  sa.sin_port = htons(port);
  sa.sin_addr.s_addr = INADDR_ANY;

  if (::bind(fd, (struct sockaddr *)&sa, sizeof(sa)) == -1) {
    LOG(ERROR) << "error bind failed";
    close(fd);
    return shared_ptr<TcpListenSocket>();
  }

  if (::listen(fd, 5) == -1) {
    LOG(ERROR) << "error listen failed";
    close(fd);
    return shared_ptr<TcpListenSocket>();
  }

  return shared_ptr<TcpListenSocket>(new TcpListenSocket(em, fd));
}

void TcpListenSocket::setAcceptCallback(
    function<void(shared_ptr<TcpSocket>)> callback) {
  pthread_mutex_lock(&_internal->_mutex);
  _internal->_callback = callback;
  pthread_mutex_unlock(&_internal->_mutex);
}

void TcpListenSocket::Internal::onAccept() {
  pthread_mutex_lock(&_mutex);
  if (_fd > 0) {
    int fd = ::accept(_fd, NULL, NULL);
    pthread_mutex_unlock(&_mutex);
    if (fd > 0) {
      shared_ptr<TcpSocket> s(new TcpSocket(_em, fd));
      if (_callback) {
        _callback(s);
        // We separate registration to give AcceptCallback a chance to set up
        // receive / disconnect callbacks for the socket. The callback must
        // call start() on the socket to begin receiving traffic.
      }
    }
  } else {
    pthread_mutex_unlock(&_mutex);
  }
}
}
<|endoftext|>


<commit_before>#include <Main.h>
#include <Wheel.h>
#include <Core.h>
#include <Utility.h>
#include <UnitQuad.h>
#include <ImGuiExtensions.h>
#include <imgui.h>
#include <utility>
#include <Input.h>
#include "../imgui/imgui_internal.h"

namespace GW2Addons
{

Wheel::Wheel(uint resourceId, std::string nickname, std::string displayName, IDirect3DDevice9 * dev)
	: nickname_(std::move(nickname)), displayName_(std::move(displayName)),
	  keybind_(nickname_, "Show on mouse"), centralKeybind_(nickname_ + "_cl", "Show in center"),
	  centerBehaviorOption_("Center behavior", "center_behavior", "wheel_" + nickname_),
	  centerFavoriteOption_("Favorite choice", "center_favorite", "wheel_" + nickname_),
	  scaleOption_("Scale", "scale", "wheel_" + nickname_, 1.f),
	  centerScaleOption_("Center scale", "center_scale", "wheel_" + nickname_, 0.2f),
	  displayDelayOption_("Pop-up delay", "delay", "wheel_" + nickname_),
	  resetCursorOnLockedKeybindOption_("Reset cursor to center with Center Locked keybind", "reset_cursor_cl", "wheel_" + nickname_, true),
	  lockCameraWhenOverlayedOption_("Lock camera when overlay is displayed", "lock_camera", "wheel_" + nickname_, true)
{
	D3DXCreateTextureFromResource(dev, Core::i()->dllModule(), MAKEINTRESOURCE(resourceId), &appearance_);

	mouseMoveCallback_ = [this]() { OnMouseMove(); };
	Input::i()->AddMouseMoveCallback(&mouseMoveCallback_);
	inputChangeCallback_ = [this](bool changed, const std::set<uint>& keys, const std::list<EventKey>& changedKeys) { return OnInputChange(changed, keys, changedKeys); };
	Input::i()->AddInputChangeCallback(&inputChangeCallback_);

	SettingsMenu::i()->AddImplementer(this);
}

Wheel::~Wheel()
{
	COM_RELEASE(appearance_);

	if(auto i = Input::iNoInit(); i)
	{
		i->RemoveMouseMoveCallback(&mouseMoveCallback_);
		i->RemoveInputChangeCallback(&inputChangeCallback_);
	}

	if(auto i = SettingsMenu::iNoInit(); i)
		i->RemoveImplementer(this);
}

void Wheel::UpdateHover()
{
	const auto io = ImGui::GetIO();

	D3DXVECTOR2 mousePos;
	mousePos.x = io.MousePos.x / static_cast<float>(Core::i()->screenWidth());
	mousePos.y = io.MousePos.y / static_cast<float>(Core::i()->screenHeight());
	mousePos -= currentPosition_;

	mousePos.y *= static_cast<float>(Core::i()->screenHeight()) / static_cast<float>(Core::i()->screenWidth());

	WheelElement* lastHovered = nullptr;

	// Middle circle does not count as a hover event
	if (!wheelElements_.empty() && D3DXVec2LengthSq(&mousePos) > SQUARE(scaleOption_.value() * 0.135f * centerScaleOption_.value()))
	{
		float mouseAngle = atan2(-mousePos.y, -mousePos.x) - 0.5f * float(M_PI);
		if (mouseAngle < 0)
			mouseAngle += float(2 * M_PI);

		const float elementAngle = float(2 * M_PI) / wheelElements_.size();
		const int elementId = int((mouseAngle - elementAngle / 2) / elementAngle + 1) % wheelElements_.size();

		currentHovered_ = wheelElements_[elementId].get();
	}
	else
		currentHovered_ = nullptr;

	const auto modifiedMountHovered = ModifyCenterHoveredElement(currentHovered_);
	const auto modifiedLastMountHovered = ModifyCenterHoveredElement(lastHovered);

	if (currentHovered_ && lastHovered != currentHovered_ && modifiedLastMountHovered != modifiedMountHovered)
		currentHovered_->currentHoverTime(std::max(currentTriggerTime_ + displayDelayOption_.value(), TimeInMilliseconds()));
}

void Wheel::DrawMenu()
{
	ImGui::PushID((nickname_ + "Elements").c_str());
	ImGui::BeginGroup();
	ImGuiTitle(displayName_.c_str());

	ImGui::TextUnformatted("Set the following to your in-game keybinds:");

	for(auto& we : wheelElements_)
		ImGuiKeybindInput(we->keybind());

	ImGui::Separator();
	ImGuiSpacing();

	ImGuiKeybindInput(keybind_);
	ImGuiKeybindInput(centralKeybind_);

	ImGui::PushItemWidth(0.66f * ImGui::GetWindowContentRegionWidth());

	ImGuiConfigurationWrapper(&ImGui::SliderInt, displayDelayOption_, 0, 1000, "%d ms");
	ImGuiConfigurationWrapper(&ImGui::SliderFloat, scaleOption_, 0.25f, 4.f, "%.2f", 1.f);
	ImGuiConfigurationWrapper(&ImGui::SliderFloat, centerScaleOption_, 0.05f, 0.25f, "%.2f", 1.f);

	ImGui::PopItemWidth();

	ImGui::Text((centerBehaviorOption_.displayName() + ":").c_str());
	ImGui::SameLine();
	ImGui::PushItemWidth(0.25f * ImGui::GetWindowContentRegionWidth());

	bool (*rb)(const char*, int*, int) = &ImGui::RadioButton;
	ImGuiConfigurationWrapper(rb, "Nothing", centerBehaviorOption_, int(CenterBehavior::NOTHING));
	ImGui::SameLine();
	ImGuiConfigurationWrapper(rb, "Previous", centerBehaviorOption_, int(CenterBehavior::PREVIOUS));
	ImGui::SameLine();
	ImGuiConfigurationWrapper(rb, "Favorite", centerBehaviorOption_, int(CenterBehavior::FAVORITE));

	ImGui::PopItemWidth();

	if (CenterBehavior(centerBehaviorOption_.value()) == CenterBehavior::FAVORITE)
	{
		ImGuiIndent();

		const auto textSize = ImGui::CalcTextSize(centerFavoriteOption_.displayName().c_str());
		const auto itemSize = ImGui::CalcItemWidth() - textSize.x - ImGui::GetCurrentWindowRead()->WindowPadding.x;
		ImGui::PushItemWidth(itemSize);

		std::vector<const char*> potentialNames(wheelElements_.size());
			for (uint i = 0; i < wheelElements_.size(); i++)
				potentialNames[i] = wheelElements_[i]->displayName().c_str();

		bool (*cmb)(const char*, int*, const char* const*, int, int) = &ImGui::Combo;
		ImGuiConfigurationWrapper(cmb, centerFavoriteOption_, potentialNames.data(), int(potentialNames.size()), -1);
		ImGui::PopItemWidth();
		ImGuiUnindent();
	}

	ImGuiConfigurationWrapper(&ImGui::Checkbox, resetCursorOnLockedKeybindOption_);
	ImGuiConfigurationWrapper(&ImGui::Checkbox, lockCameraWhenOverlayedOption_);

	ImGui::EndGroup();
	ImGui::PopID();
}

void Wheel::Draw(IDirect3DDevice9* dev, ID3DXEffect* fx, UnitQuad* quad)
{
	if (isVisible_ && fx && quad)
	{
		const int screenWidth = Core::i()->screenWidth();
		const int screenHeight = Core::i()->screenHeight();

		quad->Bind();

		auto currentTime = TimeInMilliseconds();

		if (currentTime >= currentTriggerTime_ + displayDelayOption_.value())
		{
			uint passes = 0;

			// Setup viewport
			D3DVIEWPORT9 vp;
			vp.X = vp.Y = 0;
			vp.Width = screenWidth;
			vp.Height = screenHeight;
			vp.MinZ = 0.0f;
			vp.MaxZ = 1.0f;
			dev->SetViewport(&vp);

			D3DXVECTOR4 screenSize(float(screenWidth), float(screenHeight), 1.f / screenWidth, 1.f / screenHeight);

			auto activeElements = GetActiveElements();
			if (!activeElements.empty())
			{
				D3DXVECTOR4 baseSpriteDimensions;
				baseSpriteDimensions.x = currentPosition_.x;
				baseSpriteDimensions.y = currentPosition_.y;
				baseSpriteDimensions.z = scaleOption_.value() * 0.5f * screenSize.y * screenSize.z;
				baseSpriteDimensions.w = scaleOption_.value() * 0.5f;

				const float fadeTimer = std::min(1.f, (currentTime - (currentTriggerTime_ + displayDelayOption_.value())) / 1000.f * 6);

				const auto elementHovered = ModifyCenterHoveredElement(currentHovered_);

				fx->SetTechnique("BgImage");
				fx->SetTexture("texBgImage", appearance_);
				fx->SetVector("g_vSpriteDimensions", &baseSpriteDimensions);
				fx->SetFloat("g_fFadeTimer", fadeTimer);
				fx->SetFloat("g_fTimer", fmod(currentTime / 1010.f, 55000.f));
				fx->SetFloat("g_fDeadZoneScale", centerScaleOption_.value());
				fx->SetInt("g_iMountCount", int(activeElements.size()));
				fx->SetInt("g_iMountHovered", int(std::find(activeElements.begin(), activeElements.end(), elementHovered) - activeElements.begin()));
				fx->SetBool("g_bCenterGlow", elementHovered != currentHovered_);
				fx->Begin(&passes, 0);
				fx->BeginPass(0);
				quad->Draw();
				fx->EndPass();
				fx->End();

				fx->SetTechnique("MountImage");
				fx->SetTexture("texBgImage", appearance_);
				fx->SetVector("g_vScreenSize", &screenSize);
				fx->Begin(&passes, 0);
				fx->BeginPass(0);

				int n = 0;
				for (auto it : activeElements)
				{
					it->Draw(n, baseSpriteDimensions, activeElements.size(), currentTime, elementHovered, this);
					n++;
				}

				fx->EndPass();
				fx->End();
			}

			{
				const auto& io = ImGui::GetIO();

				fx->SetTechnique("Cursor");
				fx->SetTexture("texBgImage", appearance_);
				D3DXVECTOR4 spriteDimensions(io.MousePos.x * screenSize.z, io.MousePos.y * screenSize.w, 0.05f  * screenSize.y * screenSize.z, 0.05f);
				fx->SetVector("g_vSpriteDimensions", &spriteDimensions);

				fx->Begin(&passes, 0);
				fx->BeginPass(0);
				quad->Draw();
				fx->EndPass();
				fx->End();
			}
		}
	}
}

void Wheel::OnFocusLost()
{
	currentHovered_ = previousHovered_ = nullptr;
	isVisible_ = false;
	currentTriggerTime_ = 0;
}

WheelElement * Wheel::ModifyCenterHoveredElement(WheelElement * elem)
{
	if (elem)
		return elem;

	switch (CenterBehavior(centerBehaviorOption_.value()))
	{
	case CenterBehavior::PREVIOUS:
		return previousHovered_;
	case CenterBehavior::FAVORITE:
		for(const auto& e : wheelElements_)
			if(int(e->elementId()) == centerFavoriteOption_.value())
				return e.get();
	default:
		return nullptr;
	}
}

std::vector<WheelElement*> Wheel::GetActiveElements()
{
	std::vector<WheelElement*> elems;
	for(auto& we : wheelElements_)
		if(we->isActive())
			elems.push_back(we.get());

	return elems;
}

void Wheel::OnMouseMove()
{
	if(isVisible_)
		UpdateHover();
}

InputResponse Wheel::OnInputChange(bool changed, const std::set<uint>& keys, const std::list<EventKey>& changedKeys)
{
	const bool previousVisibility = isVisible_;

	const bool mountOverlay = keybind_.isSet() && std::includes(keys.begin(), keys.end(), keybind_.keys().begin(), keybind_.keys().end());
	const bool mountOverlayLocked = centralKeybind_.isSet() && std::includes(keys.begin(), keys.end(), centralKeybind_.keys().begin(), centralKeybind_.keys().end());

	isVisible_ = mountOverlayLocked || mountOverlay;

	if (isVisible_ && !previousVisibility)
	{
		// Mount overlay is turned on

		if (mountOverlayLocked)
		{
			currentPosition_.x = currentPosition_.y = 0.5f;

			// Attempt to move the cursor to the middle of the screen
			if (resetCursorOnLockedKeybindOption_.value())
			{
				RECT rect = { };
				if (GetWindowRect(Core::i()->gameWindow(), &rect))
				{
					if (SetCursorPos((rect.right - rect.left) / 2 + rect.left, (rect.bottom - rect.top) / 2 + rect.top))
					{
						auto& io = ImGui::GetIO();
						io.MousePos.x = Core::i()->screenWidth() * 0.5f;
						io.MousePos.y = Core::i()->screenHeight() * 0.5f;
					}
				}
			}
		}
		else
		{
			const auto& io = ImGui::GetIO();
			currentPosition_.x = io.MousePos.x / float(Core::i()->screenWidth());
			currentPosition_.y = io.MousePos.y / float(Core::i()->screenHeight());
		}

		currentTriggerTime_ = TimeInMilliseconds();

		UpdateHover();
		if(currentHovered_)
			currentHovered_->currentHoverTime(currentTriggerTime_ + displayDelayOption_.value());
	}
	else if (!isVisible_ && previousVisibility)
	{
		// Check for special behavior if no mount is hovered
		currentHovered_ = ModifyCenterHoveredElement(currentHovered_);

		// Mount overlay is turned off, send the keybind
		if (currentHovered_)
			Input::i()->SendKeybind(currentHovered_->keybind().keys());

		previousUsed_ = currentHovered_;
		currentHovered_ = nullptr;
	}

	bool isAnyElementBeingModified = false;
	{
		for (auto& we : wheelElements_)
			isAnyElementBeingModified = isAnyElementBeingModified || we->keybind().isBeingModified();
	}

	{
		// If a key was lifted, we consider the key combination *prior* to this key being lifted as the keybind
		bool keyLifted = false;
		auto fullKeybind = keys;
		for (const auto& ek : changedKeys)
		{
			if (!ek.down)
			{
				fullKeybind.insert(ek.vk);
				keyLifted = true;
			}
		}

		// Explicitly filter out M1 (left mouse button) from keybinds since it breaks too many things
		fullKeybind.erase(VK_LBUTTON);

		keybind_.keys(fullKeybind);
		centralKeybind_.keys(fullKeybind);

		if(keyLifted)
		{
			keybind_.isBeingModified(false);
			centralKeybind_.isBeingModified(false);
		}

		for (auto& we : wheelElements_)
		{
			we->keybind().keys(fullKeybind);
			if(keyLifted)
				we->keybind().isBeingModified(false);
		}
	}

	if(isAnyElementBeingModified)
		return InputResponse::PREVENT_ALL;

	if (isVisible_ && lockCameraWhenOverlayedOption_.value())
		return InputResponse::PREVENT_MOUSE;

	return InputResponse::PASS_TO_GAME;
}

}
<commit_msg>Fixed mismatch in render and input code on active wheel elements.<commit_after>#include <Main.h>
#include <Wheel.h>
#include <Core.h>
#include <Utility.h>
#include <UnitQuad.h>
#include <ImGuiExtensions.h>
#include <imgui.h>
#include <utility>
#include <Input.h>
#include "../imgui/imgui_internal.h"

namespace GW2Addons
{

Wheel::Wheel(uint resourceId, std::string nickname, std::string displayName, IDirect3DDevice9 * dev)
	: nickname_(std::move(nickname)), displayName_(std::move(displayName)),
	  keybind_(nickname_, "Show on mouse"), centralKeybind_(nickname_ + "_cl", "Show in center"),
	  centerBehaviorOption_("Center behavior", "center_behavior", "wheel_" + nickname_),
	  centerFavoriteOption_("Favorite choice", "center_favorite", "wheel_" + nickname_),
	  scaleOption_("Scale", "scale", "wheel_" + nickname_, 1.f),
	  centerScaleOption_("Center scale", "center_scale", "wheel_" + nickname_, 0.2f),
	  displayDelayOption_("Pop-up delay", "delay", "wheel_" + nickname_),
	  resetCursorOnLockedKeybindOption_("Reset cursor to center with Center Locked keybind", "reset_cursor_cl", "wheel_" + nickname_, true),
	  lockCameraWhenOverlayedOption_("Lock camera when overlay is displayed", "lock_camera", "wheel_" + nickname_, true)
{
	D3DXCreateTextureFromResource(dev, Core::i()->dllModule(), MAKEINTRESOURCE(resourceId), &appearance_);

	mouseMoveCallback_ = [this]() { OnMouseMove(); };
	Input::i()->AddMouseMoveCallback(&mouseMoveCallback_);
	inputChangeCallback_ = [this](bool changed, const std::set<uint>& keys, const std::list<EventKey>& changedKeys) { return OnInputChange(changed, keys, changedKeys); };
	Input::i()->AddInputChangeCallback(&inputChangeCallback_);

	SettingsMenu::i()->AddImplementer(this);
}

Wheel::~Wheel()
{
	COM_RELEASE(appearance_);

	if(auto i = Input::iNoInit(); i)
	{
		i->RemoveMouseMoveCallback(&mouseMoveCallback_);
		i->RemoveInputChangeCallback(&inputChangeCallback_);
	}

	if(auto i = SettingsMenu::iNoInit(); i)
		i->RemoveImplementer(this);
}

void Wheel::UpdateHover()
{
	const auto io = ImGui::GetIO();

	D3DXVECTOR2 mousePos;
	mousePos.x = io.MousePos.x / static_cast<float>(Core::i()->screenWidth());
	mousePos.y = io.MousePos.y / static_cast<float>(Core::i()->screenHeight());
	mousePos -= currentPosition_;

	mousePos.y *= static_cast<float>(Core::i()->screenHeight()) / static_cast<float>(Core::i()->screenWidth());

	WheelElement* lastHovered = nullptr;

	auto activeElements = GetActiveElements();

	// Middle circle does not count as a hover event
	if (!activeElements.empty() && D3DXVec2LengthSq(&mousePos) > SQUARE(scaleOption_.value() * 0.135f * centerScaleOption_.value()))
	{
		float mouseAngle = atan2(-mousePos.y, -mousePos.x) - 0.5f * float(M_PI);
		if (mouseAngle < 0)
			mouseAngle += float(2 * M_PI);

		const float elementAngle = float(2 * M_PI) / activeElements.size();
		const int elementId = int((mouseAngle - elementAngle / 2) / elementAngle + 1) % activeElements.size();

		currentHovered_ = activeElements[elementId];
	}
	else
		currentHovered_ = nullptr;

	const auto modifiedMountHovered = ModifyCenterHoveredElement(currentHovered_);
	const auto modifiedLastMountHovered = ModifyCenterHoveredElement(lastHovered);

	if (currentHovered_ && lastHovered != currentHovered_ && modifiedLastMountHovered != modifiedMountHovered)
		currentHovered_->currentHoverTime(std::max(currentTriggerTime_ + displayDelayOption_.value(), TimeInMilliseconds()));
}

void Wheel::DrawMenu()
{
	ImGui::PushID((nickname_ + "Elements").c_str());
	ImGui::BeginGroup();
	ImGuiTitle(displayName_.c_str());

	ImGui::TextUnformatted("Set the following to your in-game keybinds:");

	for(auto& we : wheelElements_)
		ImGuiKeybindInput(we->keybind());

	ImGui::Separator();
	ImGuiSpacing();

	ImGuiKeybindInput(keybind_);
	ImGuiKeybindInput(centralKeybind_);

	ImGui::PushItemWidth(0.66f * ImGui::GetWindowContentRegionWidth());

	ImGuiConfigurationWrapper(&ImGui::SliderInt, displayDelayOption_, 0, 1000, "%d ms");
	ImGuiConfigurationWrapper(&ImGui::SliderFloat, scaleOption_, 0.25f, 4.f, "%.2f", 1.f);
	ImGuiConfigurationWrapper(&ImGui::SliderFloat, centerScaleOption_, 0.05f, 0.25f, "%.2f", 1.f);

	ImGui::PopItemWidth();

	ImGui::Text((centerBehaviorOption_.displayName() + ":").c_str());
	ImGui::SameLine();
	ImGui::PushItemWidth(0.25f * ImGui::GetWindowContentRegionWidth());

	bool (*rb)(const char*, int*, int) = &ImGui::RadioButton;
	ImGuiConfigurationWrapper(rb, "Nothing", centerBehaviorOption_, int(CenterBehavior::NOTHING));
	ImGui::SameLine();
	ImGuiConfigurationWrapper(rb, "Previous", centerBehaviorOption_, int(CenterBehavior::PREVIOUS));
	ImGui::SameLine();
	ImGuiConfigurationWrapper(rb, "Favorite", centerBehaviorOption_, int(CenterBehavior::FAVORITE));

	ImGui::PopItemWidth();

	if (CenterBehavior(centerBehaviorOption_.value()) == CenterBehavior::FAVORITE)
	{
		ImGuiIndent();

		const auto textSize = ImGui::CalcTextSize(centerFavoriteOption_.displayName().c_str());
		const auto itemSize = ImGui::CalcItemWidth() - textSize.x - ImGui::GetCurrentWindowRead()->WindowPadding.x;
		ImGui::PushItemWidth(itemSize);

		std::vector<const char*> potentialNames(wheelElements_.size());
			for (uint i = 0; i < wheelElements_.size(); i++)
				potentialNames[i] = wheelElements_[i]->displayName().c_str();

		bool (*cmb)(const char*, int*, const char* const*, int, int) = &ImGui::Combo;
		ImGuiConfigurationWrapper(cmb, centerFavoriteOption_, potentialNames.data(), int(potentialNames.size()), -1);
		ImGui::PopItemWidth();
		ImGuiUnindent();
	}

	ImGuiConfigurationWrapper(&ImGui::Checkbox, resetCursorOnLockedKeybindOption_);
	ImGuiConfigurationWrapper(&ImGui::Checkbox, lockCameraWhenOverlayedOption_);

	ImGui::EndGroup();
	ImGui::PopID();
}

void Wheel::Draw(IDirect3DDevice9* dev, ID3DXEffect* fx, UnitQuad* quad)
{
	if (isVisible_ && fx && quad)
	{
		const int screenWidth = Core::i()->screenWidth();
		const int screenHeight = Core::i()->screenHeight();

		quad->Bind();

		auto currentTime = TimeInMilliseconds();

		if (currentTime >= currentTriggerTime_ + displayDelayOption_.value())
		{
			uint passes = 0;

			// Setup viewport
			D3DVIEWPORT9 vp;
			vp.X = vp.Y = 0;
			vp.Width = screenWidth;
			vp.Height = screenHeight;
			vp.MinZ = 0.0f;
			vp.MaxZ = 1.0f;
			dev->SetViewport(&vp);

			D3DXVECTOR4 screenSize(float(screenWidth), float(screenHeight), 1.f / screenWidth, 1.f / screenHeight);

			auto activeElements = GetActiveElements();
			if (!activeElements.empty())
			{
				D3DXVECTOR4 baseSpriteDimensions;
				baseSpriteDimensions.x = currentPosition_.x;
				baseSpriteDimensions.y = currentPosition_.y;
				baseSpriteDimensions.z = scaleOption_.value() * 0.5f * screenSize.y * screenSize.z;
				baseSpriteDimensions.w = scaleOption_.value() * 0.5f;

				const float fadeTimer = std::min(1.f, (currentTime - (currentTriggerTime_ + displayDelayOption_.value())) / 1000.f * 6);

				const auto elementHovered = ModifyCenterHoveredElement(currentHovered_);

				fx->SetTechnique("BgImage");
				fx->SetTexture("texBgImage", appearance_);
				fx->SetVector("g_vSpriteDimensions", &baseSpriteDimensions);
				fx->SetFloat("g_fFadeTimer", fadeTimer);
				fx->SetFloat("g_fTimer", fmod(currentTime / 1010.f, 55000.f));
				fx->SetFloat("g_fDeadZoneScale", centerScaleOption_.value());
				fx->SetInt("g_iMountCount", int(activeElements.size()));
				fx->SetInt("g_iMountHovered", int(std::find(activeElements.begin(), activeElements.end(), elementHovered) - activeElements.begin()));
				fx->SetBool("g_bCenterGlow", elementHovered != currentHovered_);
				fx->Begin(&passes, 0);
				fx->BeginPass(0);
				quad->Draw();
				fx->EndPass();
				fx->End();

				fx->SetTechnique("MountImage");
				fx->SetTexture("texBgImage", appearance_);
				fx->SetVector("g_vScreenSize", &screenSize);
				fx->Begin(&passes, 0);
				fx->BeginPass(0);

				int n = 0;
				for (auto it : activeElements)
				{
					it->Draw(n, baseSpriteDimensions, activeElements.size(), currentTime, elementHovered, this);
					n++;
				}

				fx->EndPass();
				fx->End();
			}

			{
				const auto& io = ImGui::GetIO();

				fx->SetTechnique("Cursor");
				fx->SetTexture("texBgImage", appearance_);
				D3DXVECTOR4 spriteDimensions(io.MousePos.x * screenSize.z, io.MousePos.y * screenSize.w, 0.05f  * screenSize.y * screenSize.z, 0.05f);
				fx->SetVector("g_vSpriteDimensions", &spriteDimensions);

				fx->Begin(&passes, 0);
				fx->BeginPass(0);
				quad->Draw();
				fx->EndPass();
				fx->End();
			}
		}
	}
}

void Wheel::OnFocusLost()
{
	currentHovered_ = previousHovered_ = nullptr;
	isVisible_ = false;
	currentTriggerTime_ = 0;
}

WheelElement * Wheel::ModifyCenterHoveredElement(WheelElement * elem)
{
	if (elem)
		return elem;

	switch (CenterBehavior(centerBehaviorOption_.value()))
	{
	case CenterBehavior::PREVIOUS:
		return previousHovered_;
	case CenterBehavior::FAVORITE:
		for(const auto& e : wheelElements_)
			if(int(e->elementId()) == centerFavoriteOption_.value())
				return e.get();
	default:
		return nullptr;
	}
}

std::vector<WheelElement*> Wheel::GetActiveElements()
{
	std::vector<WheelElement*> elems;
	for(auto& we : wheelElements_)
		if(we->isActive())
			elems.push_back(we.get());

	return elems;
}

void Wheel::OnMouseMove()
{
	if(isVisible_)
		UpdateHover();
}

InputResponse Wheel::OnInputChange(bool changed, const std::set<uint>& keys, const std::list<EventKey>& changedKeys)
{
	const bool previousVisibility = isVisible_;

	const bool mountOverlay = keybind_.isSet() && std::includes(keys.begin(), keys.end(), keybind_.keys().begin(), keybind_.keys().end());
	const bool mountOverlayLocked = centralKeybind_.isSet() && std::includes(keys.begin(), keys.end(), centralKeybind_.keys().begin(), centralKeybind_.keys().end());

	isVisible_ = mountOverlayLocked || mountOverlay;

	if (isVisible_ && !previousVisibility)
	{
		// Mount overlay is turned on

		if (mountOverlayLocked)
		{
			currentPosition_.x = currentPosition_.y = 0.5f;

			// Attempt to move the cursor to the middle of the screen
			if (resetCursorOnLockedKeybindOption_.value())
			{
				RECT rect = { };
				if (GetWindowRect(Core::i()->gameWindow(), &rect))
				{
					if (SetCursorPos((rect.right - rect.left) / 2 + rect.left, (rect.bottom - rect.top) / 2 + rect.top))
					{
						auto& io = ImGui::GetIO();
						io.MousePos.x = Core::i()->screenWidth() * 0.5f;
						io.MousePos.y = Core::i()->screenHeight() * 0.5f;
					}
				}
			}
		}
		else
		{
			const auto& io = ImGui::GetIO();
			currentPosition_.x = io.MousePos.x / float(Core::i()->screenWidth());
			currentPosition_.y = io.MousePos.y / float(Core::i()->screenHeight());
		}

		currentTriggerTime_ = TimeInMilliseconds();

		UpdateHover();
		if(currentHovered_)
			currentHovered_->currentHoverTime(currentTriggerTime_ + displayDelayOption_.value());
	}
	else if (!isVisible_ && previousVisibility)
	{
		// Check for special behavior if no mount is hovered
		currentHovered_ = ModifyCenterHoveredElement(currentHovered_);

		// Mount overlay is turned off, send the keybind
		if (currentHovered_)
			Input::i()->SendKeybind(currentHovered_->keybind().keys());

		previousUsed_ = currentHovered_;
		currentHovered_ = nullptr;
	}

	bool isAnyElementBeingModified = false;
	{
		for (auto& we : wheelElements_)
			isAnyElementBeingModified = isAnyElementBeingModified || we->keybind().isBeingModified();
	}

	{
		// If a key was lifted, we consider the key combination *prior* to this key being lifted as the keybind
		bool keyLifted = false;
		auto fullKeybind = keys;
		for (const auto& ek : changedKeys)
		{
			if (!ek.down)
			{
				fullKeybind.insert(ek.vk);
				keyLifted = true;
			}
		}

		// Explicitly filter out M1 (left mouse button) from keybinds since it breaks too many things
		fullKeybind.erase(VK_LBUTTON);

		keybind_.keys(fullKeybind);
		centralKeybind_.keys(fullKeybind);

		if(keyLifted)
		{
			keybind_.isBeingModified(false);
			centralKeybind_.isBeingModified(false);
		}

		for (auto& we : wheelElements_)
		{
			we->keybind().keys(fullKeybind);
			if(keyLifted)
				we->keybind().isBeingModified(false);
		}
	}

	if(isAnyElementBeingModified)
		return InputResponse::PREVENT_ALL;

	if (isVisible_ && lockCameraWhenOverlayedOption_.value())
		return InputResponse::PREVENT_MOUSE;

	return InputResponse::PASS_TO_GAME;
}

}
<|endoftext|>


<commit_before>// Copyright (c) 2010 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//     * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#include <string>

#include <stdint.h>
#include <unistd.h>
#include <signal.h>
#include <sys/poll.h>
#include <sys/socket.h>
#include <sys/uio.h>

#include "breakpad_googletest_includes.h"
#include "client/linux/handler/exception_handler.h"
#include "client/linux/minidump_writer/minidump_writer.h"
#include "common/linux/eintr_wrapper.h"
#include "common/linux/linux_libc_support.h"
#include "third_party/lss/linux_syscall_support.h"

using namespace google_breakpad;

static void sigchld_handler(int signo) { }

class ExceptionHandlerTest : public ::testing::Test {
 protected:
  void SetUp() {
    // We need to be able to wait for children, so SIGCHLD cannot be SIG_IGN.
    struct sigaction sa;
    memset(&sa, 0, sizeof(sa));
    sa.sa_handler = sigchld_handler;
    ASSERT_NE(sigaction(SIGCHLD, &sa, &old_action), -1);
  }

  void TearDown() {
    sigaction(SIGCHLD, &old_action, NULL);
  }

  struct sigaction old_action;
};

TEST(ExceptionHandlerTest, Simple) {
  ExceptionHandler handler("/tmp", NULL, NULL, NULL, true);
}

static bool DoneCallback(const char* dump_path,
                         const char* minidump_id,
                         void* context,
                         bool succeeded) {
  if (!succeeded)
    return succeeded;

  int fd = (intptr_t) context;
  uint32_t len = my_strlen(minidump_id);
  HANDLE_EINTR(sys_write(fd, &len, sizeof(len)));
  HANDLE_EINTR(sys_write(fd, minidump_id, len));
  sys_close(fd);

  return true;
}

TEST(ExceptionHandlerTest, ChildCrash) {
  int fds[2];
  ASSERT_NE(pipe(fds), -1);

  const pid_t child = fork();
  if (child == 0) {
    close(fds[0]);
    ExceptionHandler handler("/tmp", NULL, DoneCallback, (void*) fds[1],
                             true);
    *reinterpret_cast<int*>(NULL) = 0;
  }
  close(fds[1]);

  int status;
  ASSERT_NE(HANDLE_EINTR(waitpid(child, &status, 0)), -1);
  ASSERT_TRUE(WIFSIGNALED(status));
  ASSERT_EQ(WTERMSIG(status), SIGSEGV);

  struct pollfd pfd;
  memset(&pfd, 0, sizeof(pfd));
  pfd.fd = fds[0];
  pfd.events = POLLIN | POLLERR;

  const int r = HANDLE_EINTR(poll(&pfd, 1, 0));
  ASSERT_EQ(r, 1);
  ASSERT_TRUE(pfd.revents & POLLIN);

  uint32_t len;
  ASSERT_EQ(read(fds[0], &len, sizeof(len)), (ssize_t)sizeof(len));
  ASSERT_LT(len, (uint32_t)2048);
  char* filename = reinterpret_cast<char*>(malloc(len + 1));
  ASSERT_EQ(read(fds[0], filename, len), len);
  filename[len] = 0;
  close(fds[0]);

  const std::string minidump_filename = std::string("/tmp/") + filename +
                                        ".dmp";

  struct stat st;
  ASSERT_EQ(stat(minidump_filename.c_str(), &st), 0);
  ASSERT_GT(st.st_size, 0u);
  unlink(minidump_filename.c_str());
}

static const unsigned kControlMsgSize =
    CMSG_SPACE(sizeof(int)) + CMSG_SPACE(sizeof(struct ucred));

static bool
CrashHandler(const void* crash_context, size_t crash_context_size,
             void* context) {
  const int fd = (intptr_t) context;
  int fds[2];
  pipe(fds);
  struct kernel_msghdr msg = {0};
  struct kernel_iovec iov;
  iov.iov_base = const_cast<void*>(crash_context);
  iov.iov_len = crash_context_size;
  msg.msg_iov = &iov;
  msg.msg_iovlen = 1;
  char cmsg[kControlMsgSize];
  memset(cmsg, 0, kControlMsgSize);
  msg.msg_control = cmsg;
  msg.msg_controllen = sizeof(cmsg);

  struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg);
  hdr->cmsg_level = SOL_SOCKET;
  hdr->cmsg_type = SCM_RIGHTS;
  hdr->cmsg_len = CMSG_LEN(sizeof(int));
  *((int*) CMSG_DATA(hdr)) = fds[1];
  hdr = CMSG_NXTHDR((struct msghdr*) &msg, hdr);
  hdr->cmsg_level = SOL_SOCKET;
  hdr->cmsg_type = SCM_CREDENTIALS;
  hdr->cmsg_len = CMSG_LEN(sizeof(struct ucred));
  struct ucred *cred = reinterpret_cast<struct ucred*>(CMSG_DATA(hdr));
  cred->uid = getuid();
  cred->gid = getgid();
  cred->pid = getpid();

  HANDLE_EINTR(sys_sendmsg(fd, &msg, 0));
  sys_close(fds[1]);

  char b;
  HANDLE_EINTR(sys_read(fds[0], &b, 1));

  return true;
}

TEST(ExceptionHandlerTest, ExternalDumper) {
  int fds[2];
  ASSERT_NE(socketpair(AF_UNIX, SOCK_DGRAM, 0, fds), -1);
  static const int on = 1;
  setsockopt(fds[0], SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
  setsockopt(fds[1], SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));

  const pid_t child = fork();
  if (child == 0) {
    close(fds[0]);
    ExceptionHandler handler("/tmp1", NULL, NULL, (void*) fds[1], true);
    handler.set_crash_handler(CrashHandler);
    *reinterpret_cast<int*>(NULL) = 0;
  }
  close(fds[1]);
  struct msghdr msg = {0};
  struct iovec iov;
  static const unsigned kCrashContextSize =
      sizeof(ExceptionHandler::CrashContext);
  char context[kCrashContextSize];
  char control[kControlMsgSize];
  iov.iov_base = context;
  iov.iov_len = kCrashContextSize;
  msg.msg_iov = &iov;
  msg.msg_iovlen = 1;
  msg.msg_control = control;
  msg.msg_controllen = kControlMsgSize;

  const ssize_t n = HANDLE_EINTR(recvmsg(fds[0], &msg, 0));
  ASSERT_EQ(n, kCrashContextSize);
  ASSERT_EQ(msg.msg_controllen, kControlMsgSize);
  ASSERT_EQ(msg.msg_flags, 0);

  pid_t crashing_pid = -1;
  int signal_fd = -1;
  for (struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg); hdr;
       hdr = CMSG_NXTHDR(&msg, hdr)) {
    if (hdr->cmsg_level != SOL_SOCKET)
      continue;
    if (hdr->cmsg_type == SCM_RIGHTS) {
      const unsigned len = hdr->cmsg_len -
          (((uint8_t*)CMSG_DATA(hdr)) - (uint8_t*)hdr);
      ASSERT_EQ(len, sizeof(int));
      signal_fd = *((int *) CMSG_DATA(hdr));
    } else if (hdr->cmsg_type == SCM_CREDENTIALS) {
      const struct ucred *cred =
          reinterpret_cast<struct ucred*>(CMSG_DATA(hdr));
      crashing_pid = cred->pid;
    }
  }

  ASSERT_NE(crashing_pid, -1);
  ASSERT_NE(signal_fd, -1);

  char templ[] = "/tmp/exception-handler-unittest-XXXXXX";
  mktemp(templ);
  ASSERT_TRUE(WriteMinidump(templ, crashing_pid, context,
                            kCrashContextSize));
  static const char b = 0;
  HANDLE_EINTR(write(signal_fd, &b, 1));

  int status;
  ASSERT_NE(HANDLE_EINTR(waitpid(child, &status, 0)), -1);
  ASSERT_TRUE(WIFSIGNALED(status));
  ASSERT_EQ(WTERMSIG(status), SIGSEGV);

  struct stat st;
  ASSERT_EQ(stat(templ, &st), 0);
  ASSERT_GT(st.st_size, 0u);
  unlink(templ);
}
<commit_msg>Add a free() to ExceptionHandlerTest.ChildCrash. Review URL: http://breakpad.appspot.com/193001<commit_after>// Copyright (c) 2010 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//     * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#include <string>

#include <stdint.h>
#include <unistd.h>
#include <signal.h>
#include <sys/poll.h>
#include <sys/socket.h>
#include <sys/uio.h>

#include "breakpad_googletest_includes.h"
#include "client/linux/handler/exception_handler.h"
#include "client/linux/minidump_writer/minidump_writer.h"
#include "common/linux/eintr_wrapper.h"
#include "common/linux/linux_libc_support.h"
#include "third_party/lss/linux_syscall_support.h"

using namespace google_breakpad;

static void sigchld_handler(int signo) { }

class ExceptionHandlerTest : public ::testing::Test {
 protected:
  void SetUp() {
    // We need to be able to wait for children, so SIGCHLD cannot be SIG_IGN.
    struct sigaction sa;
    memset(&sa, 0, sizeof(sa));
    sa.sa_handler = sigchld_handler;
    ASSERT_NE(sigaction(SIGCHLD, &sa, &old_action), -1);
  }

  void TearDown() {
    sigaction(SIGCHLD, &old_action, NULL);
  }

  struct sigaction old_action;
};

TEST(ExceptionHandlerTest, Simple) {
  ExceptionHandler handler("/tmp", NULL, NULL, NULL, true);
}

static bool DoneCallback(const char* dump_path,
                         const char* minidump_id,
                         void* context,
                         bool succeeded) {
  if (!succeeded)
    return succeeded;

  int fd = (intptr_t) context;
  uint32_t len = my_strlen(minidump_id);
  HANDLE_EINTR(sys_write(fd, &len, sizeof(len)));
  HANDLE_EINTR(sys_write(fd, minidump_id, len));
  sys_close(fd);

  return true;
}

TEST(ExceptionHandlerTest, ChildCrash) {
  int fds[2];
  ASSERT_NE(pipe(fds), -1);

  const pid_t child = fork();
  if (child == 0) {
    close(fds[0]);
    ExceptionHandler handler("/tmp", NULL, DoneCallback, (void*) fds[1],
                             true);
    *reinterpret_cast<int*>(NULL) = 0;
  }
  close(fds[1]);

  int status;
  ASSERT_NE(HANDLE_EINTR(waitpid(child, &status, 0)), -1);
  ASSERT_TRUE(WIFSIGNALED(status));
  ASSERT_EQ(WTERMSIG(status), SIGSEGV);

  struct pollfd pfd;
  memset(&pfd, 0, sizeof(pfd));
  pfd.fd = fds[0];
  pfd.events = POLLIN | POLLERR;

  const int r = HANDLE_EINTR(poll(&pfd, 1, 0));
  ASSERT_EQ(r, 1);
  ASSERT_TRUE(pfd.revents & POLLIN);

  uint32_t len;
  ASSERT_EQ(read(fds[0], &len, sizeof(len)), (ssize_t)sizeof(len));
  ASSERT_LT(len, (uint32_t)2048);
  char* filename = reinterpret_cast<char*>(malloc(len + 1));
  ASSERT_EQ(read(fds[0], filename, len), len);
  filename[len] = 0;
  close(fds[0]);

  const std::string minidump_filename = std::string("/tmp/") + filename +
                                        ".dmp";

  struct stat st;
  ASSERT_EQ(stat(minidump_filename.c_str(), &st), 0);
  ASSERT_GT(st.st_size, 0u);
  unlink(minidump_filename.c_str());
  free(filename);
}

static const unsigned kControlMsgSize =
    CMSG_SPACE(sizeof(int)) + CMSG_SPACE(sizeof(struct ucred));

static bool
CrashHandler(const void* crash_context, size_t crash_context_size,
             void* context) {
  const int fd = (intptr_t) context;
  int fds[2];
  pipe(fds);
  struct kernel_msghdr msg = {0};
  struct kernel_iovec iov;
  iov.iov_base = const_cast<void*>(crash_context);
  iov.iov_len = crash_context_size;
  msg.msg_iov = &iov;
  msg.msg_iovlen = 1;
  char cmsg[kControlMsgSize];
  memset(cmsg, 0, kControlMsgSize);
  msg.msg_control = cmsg;
  msg.msg_controllen = sizeof(cmsg);

  struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg);
  hdr->cmsg_level = SOL_SOCKET;
  hdr->cmsg_type = SCM_RIGHTS;
  hdr->cmsg_len = CMSG_LEN(sizeof(int));
  *((int*) CMSG_DATA(hdr)) = fds[1];
  hdr = CMSG_NXTHDR((struct msghdr*) &msg, hdr);
  hdr->cmsg_level = SOL_SOCKET;
  hdr->cmsg_type = SCM_CREDENTIALS;
  hdr->cmsg_len = CMSG_LEN(sizeof(struct ucred));
  struct ucred *cred = reinterpret_cast<struct ucred*>(CMSG_DATA(hdr));
  cred->uid = getuid();
  cred->gid = getgid();
  cred->pid = getpid();

  HANDLE_EINTR(sys_sendmsg(fd, &msg, 0));
  sys_close(fds[1]);

  char b;
  HANDLE_EINTR(sys_read(fds[0], &b, 1));

  return true;
}

TEST(ExceptionHandlerTest, ExternalDumper) {
  int fds[2];
  ASSERT_NE(socketpair(AF_UNIX, SOCK_DGRAM, 0, fds), -1);
  static const int on = 1;
  setsockopt(fds[0], SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
  setsockopt(fds[1], SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));

  const pid_t child = fork();
  if (child == 0) {
    close(fds[0]);
    ExceptionHandler handler("/tmp1", NULL, NULL, (void*) fds[1], true);
    handler.set_crash_handler(CrashHandler);
    *reinterpret_cast<int*>(NULL) = 0;
  }
  close(fds[1]);
  struct msghdr msg = {0};
  struct iovec iov;
  static const unsigned kCrashContextSize =
      sizeof(ExceptionHandler::CrashContext);
  char context[kCrashContextSize];
  char control[kControlMsgSize];
  iov.iov_base = context;
  iov.iov_len = kCrashContextSize;
  msg.msg_iov = &iov;
  msg.msg_iovlen = 1;
  msg.msg_control = control;
  msg.msg_controllen = kControlMsgSize;

  const ssize_t n = HANDLE_EINTR(recvmsg(fds[0], &msg, 0));
  ASSERT_EQ(n, kCrashContextSize);
  ASSERT_EQ(msg.msg_controllen, kControlMsgSize);
  ASSERT_EQ(msg.msg_flags, 0);

  pid_t crashing_pid = -1;
  int signal_fd = -1;
  for (struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg); hdr;
       hdr = CMSG_NXTHDR(&msg, hdr)) {
    if (hdr->cmsg_level != SOL_SOCKET)
      continue;
    if (hdr->cmsg_type == SCM_RIGHTS) {
      const unsigned len = hdr->cmsg_len -
          (((uint8_t*)CMSG_DATA(hdr)) - (uint8_t*)hdr);
      ASSERT_EQ(len, sizeof(int));
      signal_fd = *((int *) CMSG_DATA(hdr));
    } else if (hdr->cmsg_type == SCM_CREDENTIALS) {
      const struct ucred *cred =
          reinterpret_cast<struct ucred*>(CMSG_DATA(hdr));
      crashing_pid = cred->pid;
    }
  }

  ASSERT_NE(crashing_pid, -1);
  ASSERT_NE(signal_fd, -1);

  char templ[] = "/tmp/exception-handler-unittest-XXXXXX";
  mktemp(templ);
  ASSERT_TRUE(WriteMinidump(templ, crashing_pid, context,
                            kCrashContextSize));
  static const char b = 0;
  HANDLE_EINTR(write(signal_fd, &b, 1));

  int status;
  ASSERT_NE(HANDLE_EINTR(waitpid(child, &status, 0)), -1);
  ASSERT_TRUE(WIFSIGNALED(status));
  ASSERT_EQ(WTERMSIG(status), SIGSEGV);

  struct stat st;
  ASSERT_EQ(stat(templ, &st), 0);
  ASSERT_GT(st.st_size, 0u);
  unlink(templ);
}
<|endoftext|>


<commit_before>#include "TilePartitionerGradient.h"
#include "tilegen/alpha/AlphaCalculatorLinear.h"
#include <utility>

namespace tilegen
{
namespace partition
{

int TilePartitionerGradient::gradientWeight(int x, int y, int x_corner, int y_corner, int margin)
{
    return std::max(0, ((int)mOptions.tileFormat.resolution - 1 - margin -
                        std::max(std::abs(x - x_corner),
                                 std::abs(y - y_corner))));
}

void TilePartitionerGradient::makePartition(TilePartition & regions,
                                            const Corners& corners)
{
    std::vector<sf::Image> masks;
    for (auto corner : corners)
    {
        sf::Image img;
        img.create(mOptions.tileFormat.resolution,
                   mOptions.tileFormat.resolution);
        masks.push_back(img);
    }
    std::vector<double> weights(corners.size(), 0.);
    const int resolution_sub_1 = mOptions.tileFormat.resolution - 1;
    const int quarter_res = mOptions.tileFormat.resolution / 4;
    alpha::AlphaCalculatorLinear ac;
    for (size_t x = 0; x < mOptions.tileFormat.resolution; x++)
    {
        for (size_t y = 0; y < mOptions.tileFormat.resolution; y++)
        {
            weights[0] = (double)gradientWeight(x, y, 0, 0, quarter_res);
            weights[1] = (double)gradientWeight(x, y, 0, resolution_sub_1, quarter_res);
            weights[2] = (double)gradientWeight(x, y, resolution_sub_1, 0, quarter_res);
            weights[3] = (double)gradientWeight(x, y, resolution_sub_1, resolution_sub_1, quarter_res);
            ac.updateAlphas(weights);
            const auto& alphas = ac.getAlphas();
            for (size_t i = 0; i < masks.size(); i++)
            {
                masks[i].setPixel(x, y, sf::Color(255, 255, 255, alphas[i]));
            }
        }
    }

    for (size_t i = 0; i < corners.size(); i++)
    {
        sf::Texture t;
        t.loadFromImage(masks[i]);
        regions.push_back({t, corners[i]});
    }
}

} // namespace partition
} // namespace tilegen
<commit_msg>static_casts in TilePartitionerGradient<commit_after>#include "TilePartitionerGradient.h"
#include "tilegen/alpha/AlphaCalculatorLinear.h"
#include <utility>

namespace tilegen
{
namespace partition
{

int TilePartitionerGradient::gradientWeight(int x, int y, int x_corner, int y_corner, int margin)
{
    return std::max(0, ((int)mOptions.tileFormat.resolution - 1 - margin -
                        std::max(std::abs(x - x_corner),
                                 std::abs(y - y_corner))));
}

void TilePartitionerGradient::makePartition(TilePartition & regions,
                                            const Corners& corners)
{
    std::vector<sf::Image> masks;
    for (auto corner : corners)
    {
        sf::Image img;
        img.create(mOptions.tileFormat.resolution,
                   mOptions.tileFormat.resolution);
        masks.push_back(img);
    }
    std::vector<double> weights(corners.size(), 0.);
    const int resolution_sub_1 = mOptions.tileFormat.resolution - 1;
    const int quarter_res = mOptions.tileFormat.resolution / 4;
    alpha::AlphaCalculatorLinear ac;
    for (size_t x = 0; x < mOptions.tileFormat.resolution; x++)
    {
        for (size_t y = 0; y < mOptions.tileFormat.resolution; y++)
        {
            weights[0] = static_cast<double>(gradientWeight(x, y, 0, 0, quarter_res));
            weights[1] = static_cast<double>(gradientWeight(x, y, 0, resolution_sub_1, quarter_res));
            weights[2] = static_cast<double>(gradientWeight(x, y, resolution_sub_1, 0, quarter_res));
            weights[3] = static_cast<double>(gradientWeight(x, y, resolution_sub_1, resolution_sub_1, quarter_res));
            ac.updateAlphas(weights);
            const auto& alphas = ac.getAlphas();
            for (size_t i = 0; i < masks.size(); i++)
            {
                masks[i].setPixel(x, y, sf::Color(255, 255, 255, alphas[i]));
            }
        }
    }

    for (size_t i = 0; i < corners.size(); i++)
    {
        sf::Texture t;
        t.loadFromImage(masks[i]);
        regions.push_back({t, corners[i]});
    }
}

} // namespace partition
} // namespace tilegen
<|endoftext|>