<commit_before>/*
 *  Copyright 2019-2021 Diligent Graphics LLC
 *  Copyright 2015-2019 Egor Yusov
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 *  In no event and under no legal theory, whether in tort (including negligence),
 *  contract, or otherwise, unless required by applicable law (such as deliberate
 *  and grossly negligent acts) or agreed to in writing, shall any Contributor be
 *  liable for any damages, including any direct, indirect, special, incidental,
 *  or consequential damages of any character arising as a result of this License or
 *  out of the use or inability to use the software (including but not limited to damages
 *  for loss of goodwill, work stoppage, computer failure or malfunction, or any and
 *  all other commercial damages or losses), even if such Contributor has been advised
 *  of the possibility of such damages.
 */

#pragma once

/// \file
/// Implementation of the Diligent::PipelineResourceSignatureBase template class

#include <array>
#include <limits>
#include <algorithm>

#include "PrivateConstants.h"
#include "PipelineResourceSignature.h"
#include "DeviceObjectBase.hpp"
#include "RenderDeviceBase.hpp"
#include "FixedLinearAllocator.hpp"
#include "BasicMath.hpp"
#include "StringTools.hpp"

namespace Diligent
{

/// Validates pipeline resource signature description and throws an exception in case of an error.
void ValidatePipelineResourceSignatureDesc(const PipelineResourceSignatureDesc& Desc) noexcept(false);

/// Finds an immutable sampler for the resource name 'ResourceName' that is defined in shader stages 'ShaderStages'.
/// If 'SamplerSuffix' is not null, it will be appended to the 'ResourceName'.
/// Returns an index of the sampler in ImtblSamplers array, or -1 if there is no suitable sampler.
Int32 FindImmutableSampler(const ImmutableSamplerDesc* ImtblSamplers,
                           Uint32                      NumImtblSamplers,
                           SHADER_TYPE                 ShaderStages,
                           const char*                 ResourceName,
                           const char*                 SamplerSuffix);

/// Template class implementing base functionality of the pipeline resource signature object.

/// \tparam BaseInterface        - Base interface that this class will inheret
///                                (Diligent::IPipelineResourceSignatureD3D12, Diligent::PipelineResourceSignatureVk, ...).
/// \tparam RenderDeviceImplType - Type of the render device implementation
///                                (Diligent::RenderDeviceD3D12Impl or Diligent::RenderDeviceVkImpl)
template <class BaseInterface, class RenderDeviceImplType>
class PipelineResourceSignatureBase : public DeviceObjectBase<BaseInterface, RenderDeviceImplType, PipelineResourceSignatureDesc>
{
public:
    using TDeviceObjectBase = DeviceObjectBase<BaseInterface, RenderDeviceImplType, PipelineResourceSignatureDesc>;

    /// \param pRefCounters      - Reference counters object that controls the lifetime of this resource signature.
    /// \param pDevice           - Pointer to the device.
    /// \param Desc              - Resource signature description.
    /// \param bIsDeviceInternal - Flag indicating if this resource signature is an internal device object and
    ///                            must not keep a strong reference to the device.
    PipelineResourceSignatureBase(IReferenceCounters*                  pRefCounters,
                                  RenderDeviceImplType*                pDevice,
                                  const PipelineResourceSignatureDesc& Desc,
                                  bool                                 bIsDeviceInternal = false) :
        TDeviceObjectBase{pRefCounters, pDevice, Desc, bIsDeviceInternal}
    {
        // Don't read from m_Desc until it was allocated and copied in CopyDescription()
        this->m_Desc.Resources             = nullptr;
        this->m_Desc.ImmutableSamplers     = nullptr;
        this->m_Desc.CombinedSamplerSuffix = nullptr;

        ValidatePipelineResourceSignatureDesc(Desc);
    }

    ~PipelineResourceSignatureBase()
    {
        VERIFY(m_IsDestructed, "This object must be explicitly destructed with Destruct()");
    }

    IMPLEMENT_QUERY_INTERFACE_IN_PLACE(IID_PipelineResourceSignature, TDeviceObjectBase)

    size_t GetHash() const { return m_Hash; }

    PIPELINE_TYPE GetPipelineType() const { return m_PipelineType; }

    const char* GetCombinedSamplerSuffix() const { return this->m_Desc.CombinedSamplerSuffix; }

    bool IsUsingCombinedSamplers() const { return this->m_Desc.CombinedSamplerSuffix != nullptr; }
    bool IsUsingSeparateSamplers() const { return !IsUsingCombinedSamplers(); }

    Uint32 GetTotalResourceCount() const { return this->m_Desc.NumResources; }
    Uint32 GetImmutableSamplerCount() const { return this->m_Desc.NumImmutableSamplers; }

    std::pair<Uint32, Uint32> GetResourceIndexRange(SHADER_RESOURCE_VARIABLE_TYPE VarType) const
    {
        return std::pair<Uint32, Uint32>{m_ResourceOffsets[VarType], m_ResourceOffsets[VarType + 1]};
    }

    // Returns the number of shader stages that have resources.
    Uint32 GetNumActiveShaderStages() const { return m_NumShaderStages; }

    // Returns the type of the active shader stage with the given index.
    SHADER_TYPE GetActiveShaderStageType(Uint32 StageIndex) const
    {
        VERIFY_EXPR(StageIndex < m_NumShaderStages);

        SHADER_TYPE Stages = m_ShaderStages;
        for (Uint32 Index = 0; Stages != SHADER_TYPE_UNKNOWN; ++Index)
        {
            auto StageBit = ExtractLSB(Stages);

            if (Index == StageIndex)
                return StageBit;
        }

        UNEXPECTED("Index is out of range");
        return SHADER_TYPE_UNKNOWN;
    }

protected:
    void ReserveSpaceForDescription(FixedLinearAllocator& Allocator, const PipelineResourceSignatureDesc& Desc) const noexcept(false)
    {
        Allocator.AddSpace<PipelineResourceDesc>(Desc.NumResources);
        Allocator.AddSpace<ImmutableSamplerDesc>(Desc.NumImmutableSamplers);

        for (Uint32 i = 0; i < Desc.NumResources; ++i)
        {
            const auto& Res = Desc.Resources[i];

            VERIFY(Res.Name != nullptr, "Name can't be null. This error should've been caught by ValidatePipelineResourceSignatureDesc().");
            VERIFY(Res.Name[0] != '\0', "Name can't be empty. This error should've been caught by ValidatePipelineResourceSignatureDesc().");
            VERIFY(Res.ShaderStages != SHADER_TYPE_UNKNOWN, "ShaderStages can't be SHADER_TYPE_UNKNOWN. This error should've been caught by ValidatePipelineResourceSignatureDesc().");
            VERIFY(Res.ArraySize != 0, "ArraySize can't be 0. This error should've been caught by ValidatePipelineResourceSignatureDesc().");

            Allocator.AddSpaceForString(Res.Name);
        }

        for (Uint32 i = 0; i < Desc.NumImmutableSamplers; ++i)
        {
            VERIFY(Desc.ImmutableSamplers[i].SamplerOrTextureName != nullptr,
                   "SamplerOrTextureName can't be null. This error should've been caught by ValidatePipelineResourceSignatureDesc().");
            VERIFY(Desc.ImmutableSamplers[i].SamplerOrTextureName[0] != '\0',
                   "SamplerOrTextureName can't be empty. This error should've been caught by ValidatePipelineResourceSignatureDesc().");

            Allocator.AddSpaceForString(Desc.ImmutableSamplers[i].SamplerOrTextureName);
        }

        if (Desc.UseCombinedTextureSamplers)
            Allocator.AddSpaceForString(Desc.CombinedSamplerSuffix);
    }

    void CopyDescription(FixedLinearAllocator& Allocator, const PipelineResourceSignatureDesc& Desc) noexcept(false)
    {
        PipelineResourceDesc* pResources = Allocator.Allocate<PipelineResourceDesc>(Desc.NumResources);
        ImmutableSamplerDesc* pSamplers  = Allocator.Allocate<ImmutableSamplerDesc>(Desc.NumImmutableSamplers);

        for (Uint32 i = 0; i < Desc.NumResources; ++i)
        {
            auto& Dst = pResources[i];
            Dst       = Desc.Resources[i];
            VERIFY_EXPR(Desc.Resources[i].Name != nullptr && Desc.Resources[i].Name[0] != '\0');
            Dst.Name = Allocator.CopyString(Desc.Resources[i].Name);

            ++m_ResourceOffsets[Dst.VarType + 1];
        }

        // Sort resources by variable type (all static -> all mutable -> all dynamic)
        std::sort(pResources, pResources + Desc.NumResources,
                  [](const PipelineResourceDesc& lhs, const PipelineResourceDesc& rhs) {
                      return lhs.VarType < rhs.VarType;
                  });

        for (size_t i = 1; i < m_ResourceOffsets.size(); ++i)
            m_ResourceOffsets[i] += m_ResourceOffsets[i - 1];

#ifdef DILIGENT_DEBUG
        VERIFY_EXPR(m_ResourceOffsets[SHADER_RESOURCE_VARIABLE_TYPE_NUM_TYPES] == Desc.NumResources);
        for (Uint32 VarType = 0; VarType < SHADER_RESOURCE_VARIABLE_TYPE_NUM_TYPES; ++VarType)
        {
            auto IdxRange = GetResourceIndexRange(static_cast<SHADER_RESOURCE_VARIABLE_TYPE>(VarType));
            for (Uint32 idx = IdxRange.first; idx < IdxRange.second; ++idx)
                VERIFY(pResources[idx].VarType == VarType, "Unexpected resource var type");
        }
#endif

        for (Uint32 i = 0; i < Desc.NumImmutableSamplers; ++i)
        {
            auto& Dst = pSamplers[i];
            Dst       = Desc.ImmutableSamplers[i];
            VERIFY_EXPR(Desc.ImmutableSamplers[i].SamplerOrTextureName != nullptr && Desc.ImmutableSamplers[i].SamplerOrTextureName[0] != '\0');
            Dst.SamplerOrTextureName = Allocator.CopyString(Desc.ImmutableSamplers[i].SamplerOrTextureName);
        }

        this->m_Desc.Resources         = pResources;
        this->m_Desc.ImmutableSamplers = pSamplers;

        if (Desc.UseCombinedTextureSamplers)
            this->m_Desc.CombinedSamplerSuffix = Allocator.CopyString(Desc.CombinedSamplerSuffix);
    }

    void Destruct()
    {
        VERIFY(!m_IsDestructed, "This object has already been destructed");

        this->m_Desc.Resources             = nullptr;
        this->m_Desc.ImmutableSamplers     = nullptr;
        this->m_Desc.CombinedSamplerSuffix = nullptr;

#if DILIGENT_DEBUG
        m_IsDestructed = true;
#endif
    }

    Int8 GetStaticVariableCountHelper(SHADER_TYPE ShaderType, const std::array<Int8, MAX_SHADERS_IN_PIPELINE>& StaticVarIndex) const
    {
        if (!IsConsistentShaderType(ShaderType, m_PipelineType))
        {
            LOG_WARNING_MESSAGE("Unable to get the number of static variables in shader stage ", GetShaderTypeLiteralName(ShaderType),
                                " as the stage is invalid for ", GetPipelineTypeString(m_PipelineType), " pipeline resource signature '", this->m_Desc.Name, "'.");
            return -1;
        }

        const auto ShaderTypeInd = GetShaderTypePipelineIndex(ShaderType, m_PipelineType);
        const auto VarMngrInd    = StaticVarIndex[ShaderTypeInd];
        if (VarMngrInd < 0)
        {
            LOG_WARNING_MESSAGE("Unable to get the number of static variables in shader stage ", GetShaderTypeLiteralName(ShaderType),
                                " as the stage is inactive in PSO '", this->m_Desc.Name, "'.");
        }

        return VarMngrInd;
    }

    Int8 GetStaticVariableByNameHelper(SHADER_TYPE ShaderType, const Char* Name, const std::array<Int8, MAX_SHADERS_IN_PIPELINE>& StaticVarIndex) const
    {
        if (!IsConsistentShaderType(ShaderType, m_PipelineType))
        {
            LOG_WARNING_MESSAGE("Unable to find static variable '", Name, "' in shader stage ", GetShaderTypeLiteralName(ShaderType),
                                " as the stage is invalid for ", GetPipelineTypeString(m_PipelineType), " pipeline resource signature '", this->m_Desc.Name, "'.");
            return -1;
        }

        const auto ShaderTypeInd = GetShaderTypePipelineIndex(ShaderType, m_PipelineType);
        const auto VarMngrInd    = StaticVarIndex[ShaderTypeInd];
        if (VarMngrInd < 0)
        {
            LOG_WARNING_MESSAGE("Unable to find static variable '", Name, "' in shader stage ", GetShaderTypeLiteralName(ShaderType),
                                " as the stage is inactive in PSO '", this->m_Desc.Name, "'.");
        }

        return VarMngrInd;
    }

    Int8 GetStaticVariableByIndexHelper(SHADER_TYPE ShaderType, Uint32 Index, const std::array<Int8, MAX_SHADERS_IN_PIPELINE>& StaticVarIndex) const
    {
        if (!IsConsistentShaderType(ShaderType, m_PipelineType))
        {
            LOG_WARNING_MESSAGE("Unable to get static variable at index ", Index, " in shader stage ", GetShaderTypeLiteralName(ShaderType),
                                " as the stage is invalid for ", GetPipelineTypeString(m_PipelineType), " pipeline resource signature '", this->m_Desc.Name, "'.");
            return -1;
        }

        const auto ShaderTypeInd = GetShaderTypePipelineIndex(ShaderType, m_PipelineType);
        const auto VarMngrInd    = StaticVarIndex[ShaderTypeInd];
        if (VarMngrInd < 0)
        {
            LOG_WARNING_MESSAGE("Unable to get static variable at index ", Index, " in shader stage ", GetShaderTypeLiteralName(ShaderType),
                                " as the stage is inactive in PSO '", this->m_Desc.Name, "'.");
        }

        return VarMngrInd;
    }

    // Finds a sampler that is assigned to texture Tex, when combined texture samplers are used.
    // Returns an index of the sampler in m_Desc.Resources array, or InvalidSamplerValue if there is
    // no such sampler, or if combined samplers are not used.
    Uint32 FindAssignedSampler(const PipelineResourceDesc& Tex, Uint32 InvalidSamplerValue) const
    {
        VERIFY_EXPR(Tex.ResourceType == SHADER_RESOURCE_TYPE_TEXTURE_SRV);
        Uint32 SamplerInd = InvalidSamplerValue;
        if (IsUsingCombinedSamplers())
        {
            const auto IdxRange = GetResourceIndexRange(Tex.VarType);

            for (Uint32 i = IdxRange.first; i < IdxRange.second; ++i)
            {
                const auto& Res = m_Desc.Resources[i];
                VERIFY_EXPR(Tex.VarType == Res.VarType);

                if (Res.ResourceType == SHADER_RESOURCE_TYPE_SAMPLER &&
                    (Tex.ShaderStages & Res.ShaderStages) != 0 &&
                    StreqSuff(Res.Name, Tex.Name, GetCombinedSamplerSuffix()))
                {
                    VERIFY_EXPR((Res.ShaderStages & Tex.ShaderStages) == Tex.ShaderStages);
                    SamplerInd = i;
                    break;
                }
            }
        }
        return SamplerInd;
    }

protected:
    size_t m_Hash = 0;

    // Resource offsets (e.g. index of the first resource), for each variable type.
    std::array<Uint16, SHADER_RESOURCE_VARIABLE_TYPE_NUM_TYPES + 1> m_ResourceOffsets = {};

    // Shader stages that have resources.
    SHADER_TYPE m_ShaderStages = SHADER_TYPE_UNKNOWN;

    PIPELINE_TYPE m_PipelineType = PIPELINE_TYPE_INVALID;

    // The number of shader stages that have resources.
    Uint8 m_NumShaderStages = 0;

#ifdef DILIGENT_DEBUG
    bool m_IsDestructed = false;
#endif
};

} // namespace Diligent
<commit_msg>Fixed clang/gcc build error<commit_after>/*
 *  Copyright 2019-2021 Diligent Graphics LLC
 *  Copyright 2015-2019 Egor Yusov
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 *  In no event and under no legal theory, whether in tort (including negligence),
 *  contract, or otherwise, unless required by applicable law (such as deliberate
 *  and grossly negligent acts) or agreed to in writing, shall any Contributor be
 *  liable for any damages, including any direct, indirect, special, incidental,
 *  or consequential damages of any character arising as a result of this License or
 *  out of the use or inability to use the software (including but not limited to damages
 *  for loss of goodwill, work stoppage, computer failure or malfunction, or any and
 *  all other commercial damages or losses), even if such Contributor has been advised
 *  of the possibility of such damages.
 */

#pragma once

/// \file
/// Implementation of the Diligent::PipelineResourceSignatureBase template class

#include <array>
#include <limits>
#include <algorithm>

#include "PrivateConstants.h"
#include "PipelineResourceSignature.h"
#include "DeviceObjectBase.hpp"
#include "RenderDeviceBase.hpp"
#include "FixedLinearAllocator.hpp"
#include "BasicMath.hpp"
#include "StringTools.hpp"

namespace Diligent
{

/// Validates pipeline resource signature description and throws an exception in case of an error.
void ValidatePipelineResourceSignatureDesc(const PipelineResourceSignatureDesc& Desc) noexcept(false);

/// Finds an immutable sampler for the resource name 'ResourceName' that is defined in shader stages 'ShaderStages'.
/// If 'SamplerSuffix' is not null, it will be appended to the 'ResourceName'.
/// Returns an index of the sampler in ImtblSamplers array, or -1 if there is no suitable sampler.
Int32 FindImmutableSampler(const ImmutableSamplerDesc* ImtblSamplers,
                           Uint32                      NumImtblSamplers,
                           SHADER_TYPE                 ShaderStages,
                           const char*                 ResourceName,
                           const char*                 SamplerSuffix);

/// Template class implementing base functionality of the pipeline resource signature object.

/// \tparam BaseInterface        - Base interface that this class will inheret
///                                (Diligent::IPipelineResourceSignatureD3D12, Diligent::PipelineResourceSignatureVk, ...).
/// \tparam RenderDeviceImplType - Type of the render device implementation
///                                (Diligent::RenderDeviceD3D12Impl or Diligent::RenderDeviceVkImpl)
template <class BaseInterface, class RenderDeviceImplType>
class PipelineResourceSignatureBase : public DeviceObjectBase<BaseInterface, RenderDeviceImplType, PipelineResourceSignatureDesc>
{
public:
    using TDeviceObjectBase = DeviceObjectBase<BaseInterface, RenderDeviceImplType, PipelineResourceSignatureDesc>;

    /// \param pRefCounters      - Reference counters object that controls the lifetime of this resource signature.
    /// \param pDevice           - Pointer to the device.
    /// \param Desc              - Resource signature description.
    /// \param bIsDeviceInternal - Flag indicating if this resource signature is an internal device object and
    ///                            must not keep a strong reference to the device.
    PipelineResourceSignatureBase(IReferenceCounters*                  pRefCounters,
                                  RenderDeviceImplType*                pDevice,
                                  const PipelineResourceSignatureDesc& Desc,
                                  bool                                 bIsDeviceInternal = false) :
        TDeviceObjectBase{pRefCounters, pDevice, Desc, bIsDeviceInternal}
    {
        // Don't read from m_Desc until it was allocated and copied in CopyDescription()
        this->m_Desc.Resources             = nullptr;
        this->m_Desc.ImmutableSamplers     = nullptr;
        this->m_Desc.CombinedSamplerSuffix = nullptr;

        ValidatePipelineResourceSignatureDesc(Desc);
    }

    ~PipelineResourceSignatureBase()
    {
        VERIFY(m_IsDestructed, "This object must be explicitly destructed with Destruct()");
    }

    IMPLEMENT_QUERY_INTERFACE_IN_PLACE(IID_PipelineResourceSignature, TDeviceObjectBase)

    size_t GetHash() const { return m_Hash; }

    PIPELINE_TYPE GetPipelineType() const { return m_PipelineType; }

    const char* GetCombinedSamplerSuffix() const { return this->m_Desc.CombinedSamplerSuffix; }

    bool IsUsingCombinedSamplers() const { return this->m_Desc.CombinedSamplerSuffix != nullptr; }
    bool IsUsingSeparateSamplers() const { return !IsUsingCombinedSamplers(); }

    Uint32 GetTotalResourceCount() const { return this->m_Desc.NumResources; }
    Uint32 GetImmutableSamplerCount() const { return this->m_Desc.NumImmutableSamplers; }

    std::pair<Uint32, Uint32> GetResourceIndexRange(SHADER_RESOURCE_VARIABLE_TYPE VarType) const
    {
        return std::pair<Uint32, Uint32>{m_ResourceOffsets[VarType], m_ResourceOffsets[VarType + 1]};
    }

    // Returns the number of shader stages that have resources.
    Uint32 GetNumActiveShaderStages() const { return m_NumShaderStages; }

    // Returns the type of the active shader stage with the given index.
    SHADER_TYPE GetActiveShaderStageType(Uint32 StageIndex) const
    {
        VERIFY_EXPR(StageIndex < m_NumShaderStages);

        SHADER_TYPE Stages = m_ShaderStages;
        for (Uint32 Index = 0; Stages != SHADER_TYPE_UNKNOWN; ++Index)
        {
            auto StageBit = ExtractLSB(Stages);

            if (Index == StageIndex)
                return StageBit;
        }

        UNEXPECTED("Index is out of range");
        return SHADER_TYPE_UNKNOWN;
    }

protected:
    void ReserveSpaceForDescription(FixedLinearAllocator& Allocator, const PipelineResourceSignatureDesc& Desc) const noexcept(false)
    {
        Allocator.AddSpace<PipelineResourceDesc>(Desc.NumResources);
        Allocator.AddSpace<ImmutableSamplerDesc>(Desc.NumImmutableSamplers);

        for (Uint32 i = 0; i < Desc.NumResources; ++i)
        {
            const auto& Res = Desc.Resources[i];

            VERIFY(Res.Name != nullptr, "Name can't be null. This error should've been caught by ValidatePipelineResourceSignatureDesc().");
            VERIFY(Res.Name[0] != '\0', "Name can't be empty. This error should've been caught by ValidatePipelineResourceSignatureDesc().");
            VERIFY(Res.ShaderStages != SHADER_TYPE_UNKNOWN, "ShaderStages can't be SHADER_TYPE_UNKNOWN. This error should've been caught by ValidatePipelineResourceSignatureDesc().");
            VERIFY(Res.ArraySize != 0, "ArraySize can't be 0. This error should've been caught by ValidatePipelineResourceSignatureDesc().");

            Allocator.AddSpaceForString(Res.Name);
        }

        for (Uint32 i = 0; i < Desc.NumImmutableSamplers; ++i)
        {
            VERIFY(Desc.ImmutableSamplers[i].SamplerOrTextureName != nullptr,
                   "SamplerOrTextureName can't be null. This error should've been caught by ValidatePipelineResourceSignatureDesc().");
            VERIFY(Desc.ImmutableSamplers[i].SamplerOrTextureName[0] != '\0',
                   "SamplerOrTextureName can't be empty. This error should've been caught by ValidatePipelineResourceSignatureDesc().");

            Allocator.AddSpaceForString(Desc.ImmutableSamplers[i].SamplerOrTextureName);
        }

        if (Desc.UseCombinedTextureSamplers)
            Allocator.AddSpaceForString(Desc.CombinedSamplerSuffix);
    }

    void CopyDescription(FixedLinearAllocator& Allocator, const PipelineResourceSignatureDesc& Desc) noexcept(false)
    {
        PipelineResourceDesc* pResources = Allocator.Allocate<PipelineResourceDesc>(Desc.NumResources);
        ImmutableSamplerDesc* pSamplers  = Allocator.Allocate<ImmutableSamplerDesc>(Desc.NumImmutableSamplers);

        for (Uint32 i = 0; i < Desc.NumResources; ++i)
        {
            auto& Dst = pResources[i];
            Dst       = Desc.Resources[i];
            VERIFY_EXPR(Desc.Resources[i].Name != nullptr && Desc.Resources[i].Name[0] != '\0');
            Dst.Name = Allocator.CopyString(Desc.Resources[i].Name);

            ++m_ResourceOffsets[Dst.VarType + 1];
        }

        // Sort resources by variable type (all static -> all mutable -> all dynamic)
        std::sort(pResources, pResources + Desc.NumResources,
                  [](const PipelineResourceDesc& lhs, const PipelineResourceDesc& rhs) {
                      return lhs.VarType < rhs.VarType;
                  });

        for (size_t i = 1; i < m_ResourceOffsets.size(); ++i)
            m_ResourceOffsets[i] += m_ResourceOffsets[i - 1];

#ifdef DILIGENT_DEBUG
        VERIFY_EXPR(m_ResourceOffsets[SHADER_RESOURCE_VARIABLE_TYPE_NUM_TYPES] == Desc.NumResources);
        for (Uint32 VarType = 0; VarType < SHADER_RESOURCE_VARIABLE_TYPE_NUM_TYPES; ++VarType)
        {
            auto IdxRange = GetResourceIndexRange(static_cast<SHADER_RESOURCE_VARIABLE_TYPE>(VarType));
            for (Uint32 idx = IdxRange.first; idx < IdxRange.second; ++idx)
                VERIFY(pResources[idx].VarType == VarType, "Unexpected resource var type");
        }
#endif

        for (Uint32 i = 0; i < Desc.NumImmutableSamplers; ++i)
        {
            auto& Dst = pSamplers[i];
            Dst       = Desc.ImmutableSamplers[i];
            VERIFY_EXPR(Desc.ImmutableSamplers[i].SamplerOrTextureName != nullptr && Desc.ImmutableSamplers[i].SamplerOrTextureName[0] != '\0');
            Dst.SamplerOrTextureName = Allocator.CopyString(Desc.ImmutableSamplers[i].SamplerOrTextureName);
        }

        this->m_Desc.Resources         = pResources;
        this->m_Desc.ImmutableSamplers = pSamplers;

        if (Desc.UseCombinedTextureSamplers)
            this->m_Desc.CombinedSamplerSuffix = Allocator.CopyString(Desc.CombinedSamplerSuffix);
    }

    void Destruct()
    {
        VERIFY(!m_IsDestructed, "This object has already been destructed");

        this->m_Desc.Resources             = nullptr;
        this->m_Desc.ImmutableSamplers     = nullptr;
        this->m_Desc.CombinedSamplerSuffix = nullptr;

#if DILIGENT_DEBUG
        m_IsDestructed = true;
#endif
    }

    Int8 GetStaticVariableCountHelper(SHADER_TYPE ShaderType, const std::array<Int8, MAX_SHADERS_IN_PIPELINE>& StaticVarIndex) const
    {
        if (!IsConsistentShaderType(ShaderType, m_PipelineType))
        {
            LOG_WARNING_MESSAGE("Unable to get the number of static variables in shader stage ", GetShaderTypeLiteralName(ShaderType),
                                " as the stage is invalid for ", GetPipelineTypeString(m_PipelineType), " pipeline resource signature '", this->m_Desc.Name, "'.");
            return -1;
        }

        const auto ShaderTypeInd = GetShaderTypePipelineIndex(ShaderType, m_PipelineType);
        const auto VarMngrInd    = StaticVarIndex[ShaderTypeInd];
        if (VarMngrInd < 0)
        {
            LOG_WARNING_MESSAGE("Unable to get the number of static variables in shader stage ", GetShaderTypeLiteralName(ShaderType),
                                " as the stage is inactive in PSO '", this->m_Desc.Name, "'.");
        }

        return VarMngrInd;
    }

    Int8 GetStaticVariableByNameHelper(SHADER_TYPE ShaderType, const Char* Name, const std::array<Int8, MAX_SHADERS_IN_PIPELINE>& StaticVarIndex) const
    {
        if (!IsConsistentShaderType(ShaderType, m_PipelineType))
        {
            LOG_WARNING_MESSAGE("Unable to find static variable '", Name, "' in shader stage ", GetShaderTypeLiteralName(ShaderType),
                                " as the stage is invalid for ", GetPipelineTypeString(m_PipelineType), " pipeline resource signature '", this->m_Desc.Name, "'.");
            return -1;
        }

        const auto ShaderTypeInd = GetShaderTypePipelineIndex(ShaderType, m_PipelineType);
        const auto VarMngrInd    = StaticVarIndex[ShaderTypeInd];
        if (VarMngrInd < 0)
        {
            LOG_WARNING_MESSAGE("Unable to find static variable '", Name, "' in shader stage ", GetShaderTypeLiteralName(ShaderType),
                                " as the stage is inactive in PSO '", this->m_Desc.Name, "'.");
        }

        return VarMngrInd;
    }

    Int8 GetStaticVariableByIndexHelper(SHADER_TYPE ShaderType, Uint32 Index, const std::array<Int8, MAX_SHADERS_IN_PIPELINE>& StaticVarIndex) const
    {
        if (!IsConsistentShaderType(ShaderType, m_PipelineType))
        {
            LOG_WARNING_MESSAGE("Unable to get static variable at index ", Index, " in shader stage ", GetShaderTypeLiteralName(ShaderType),
                                " as the stage is invalid for ", GetPipelineTypeString(m_PipelineType), " pipeline resource signature '", this->m_Desc.Name, "'.");
            return -1;
        }

        const auto ShaderTypeInd = GetShaderTypePipelineIndex(ShaderType, m_PipelineType);
        const auto VarMngrInd    = StaticVarIndex[ShaderTypeInd];
        if (VarMngrInd < 0)
        {
            LOG_WARNING_MESSAGE("Unable to get static variable at index ", Index, " in shader stage ", GetShaderTypeLiteralName(ShaderType),
                                " as the stage is inactive in PSO '", this->m_Desc.Name, "'.");
        }

        return VarMngrInd;
    }

    // Finds a sampler that is assigned to texture Tex, when combined texture samplers are used.
    // Returns an index of the sampler in m_Desc.Resources array, or InvalidSamplerValue if there is
    // no such sampler, or if combined samplers are not used.
    Uint32 FindAssignedSampler(const PipelineResourceDesc& Tex, Uint32 InvalidSamplerValue) const
    {
        VERIFY_EXPR(Tex.ResourceType == SHADER_RESOURCE_TYPE_TEXTURE_SRV);
        Uint32 SamplerInd = InvalidSamplerValue;
        if (IsUsingCombinedSamplers())
        {
            const auto IdxRange = GetResourceIndexRange(Tex.VarType);

            for (Uint32 i = IdxRange.first; i < IdxRange.second; ++i)
            {
                const auto& Res = this->m_Desc.Resources[i];
                VERIFY_EXPR(Tex.VarType == Res.VarType);

                if (Res.ResourceType == SHADER_RESOURCE_TYPE_SAMPLER &&
                    (Tex.ShaderStages & Res.ShaderStages) != 0 &&
                    StreqSuff(Res.Name, Tex.Name, GetCombinedSamplerSuffix()))
                {
                    VERIFY_EXPR((Res.ShaderStages & Tex.ShaderStages) == Tex.ShaderStages);
                    SamplerInd = i;
                    break;
                }
            }
        }
        return SamplerInd;
    }

protected:
    size_t m_Hash = 0;

    // Resource offsets (e.g. index of the first resource), for each variable type.
    std::array<Uint16, SHADER_RESOURCE_VARIABLE_TYPE_NUM_TYPES + 1> m_ResourceOffsets = {};

    // Shader stages that have resources.
    SHADER_TYPE m_ShaderStages = SHADER_TYPE_UNKNOWN;

    PIPELINE_TYPE m_PipelineType = PIPELINE_TYPE_INVALID;

    // The number of shader stages that have resources.
    Uint8 m_NumShaderStages = 0;

#ifdef DILIGENT_DEBUG
    bool m_IsDestructed = false;
#endif
};

} // namespace Diligent
<|endoftext|>


<commit_before>#ifndef RAPP_DYNAMIC_LOCALIZATION
#define RAPP_DYNAMIC_LOCALIZATION
#include "includes.ihh"
/**
 * @class Localization
 * @brief Class which defines the interface for Robot localization capabilities
 * @date 10-August-2015
 * @author Wojciech Dudek <wojciechsbox@gmail.com>
 * @note This class uses pimpl pattern to make ABI as stable as possible when deploying new library versions
 */

namespace rapp {
namespace robot {

class localization
{
  public:
  	localization(int argc=0, char ** argv = NULL);

    ~localization();
	rapp::object::pose qr_code_localization(rapp::object::qr_code_3d QRcodes,std::vector<std::vector<float>> camera_to_robot_matrix, std::istream *MapPath);

        rapp::object::pose qr_code_localization(rapp::object::qr_code_3d QRcodes, std::vector<std::vector<float>> camera_to_robot_matrix,rapp::object::qr_code_map QRmap);

	rapp::object::qr_code_map load_qr_code_map(std::string * is);

  private:
};
} // namespace dynamic
} // namespace rapp
#endif // RAPP_DYNAMIC_LOCALIZATION
<commit_msg>load qr_map via string file_path<commit_after>#ifndef RAPP_DYNAMIC_LOCALIZATION
#define RAPP_DYNAMIC_LOCALIZATION
#include "includes.ihh"
/**
 * @class Localization
 * @brief Class which defines the interface for Robot localization capabilities
 * @date 10-August-2015
 * @author Wojciech Dudek <wojciechsbox@gmail.com>
 * @note This class uses pimpl pattern to make ABI as stable as possible when deploying new library versions
 */

namespace rapp {
namespace robot {

class localization
{
  public:
  	localization(int argc=0, char ** argv = NULL);

    ~localization();
    rapp::object::pose qr_code_localization(rapp::object::qr_code_3d QRcodes,std::vector<std::vector<float>> camera_to_robot_matrix, std::string *MapPath);

        rapp::object::pose qr_code_localization(rapp::object::qr_code_3d QRcodes, std::vector<std::vector<float>> camera_to_robot_matrix,rapp::object::qr_code_map QRmap);

	rapp::object::qr_code_map load_qr_code_map(std::string * is);

  private:
};
} // namespace dynamic
} // namespace rapp
#endif // RAPP_DYNAMIC_LOCALIZATION
<|endoftext|>


<commit_before>// ========================================================================== //
// This file is part of Sara, a basic set of libraries in C++ for computer
// vision.
//
// Copyright (C) 2018 David Ok <david.ok8@gmail.com>
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License v. 2.0. If a copy of the MPL was not distributed with this file,
// you can obtain one at http://mozilla.org/MPL/2.0/.
// ========================================================================== //

//! @file

#pragma once

#include <DO/Sara/Core/ArrayIterators/CoordinatesIterator.hpp>
#include <DO/Sara/Core/MultiArray/Padding.hpp>

namespace DO { namespace Sara {

  template <typename ArrayView>
  class InfiniteArrayIterator
  {
  public:
    using self_type = InfiniteArrayIterator;
    using vector_type = typename ArrayView::vector_type;
    using value_type = typename ArrayView::value_type;

  public:
    inline InfiniteArrayIterator(const ArrayView& f,    //
                                 const vector_type& a,  //
                                 const vector_type& b)
      : _f{f}
      , _x{a, b}
    {
    }

    //! Dereferencing operator.
    inline value_type operator*() const
    {
      return _f(*_x);
    }

    //! Prefix increment operator.
    inline self_type& operator++()
    {
      ++_x;
      return *this;
    }

    //! Prefix decrement operator.
    inline self_type& operator--()
    {
      --_x;
      return *this;
    }

    //! Postfix increment operator.
    inline self_type operator++(int)
    {
      self_type old{*this};
      operator++();
      return old;
    }

    //! Postfix increment operator.
    inline self_type operator--(int)
    {
      self_type old{*this};
      operator--();
      return old;
    }

    //! Arithmetic operator (slow).
    inline self_type operator+=(const vector_type& offset)
    {
      _x += offset;
      return *this;
    }

    //! Arithmetic operator (slow).
    inline self_type operator-=(const vector_type& offset)
    {
      _x +=(-offset);
      return *this;
    }

    inline bool end() const
    {
      return _x.end();
    }

    inline auto position() const -> const vector_type&
    {
      return *_x;
    }

  private:
    const ArrayView& _f;
    CoordinatesIterator<ArrayView> _x;
  };

  template <typename ArrayView>
  class InfiniteSteppedArrayIterator
  {
  public:
    using self_type = InfiniteSteppedArrayIterator;
    using vector_type = typename ArrayView::vector_type;
    using value_type = typename ArrayView::value_type;

  public:
    inline InfiniteSteppedArrayIterator(const ArrayView& f,        //
                                        const vector_type& a,      //
                                        const vector_type& b,      //
                                        const vector_type& steps)  //
      : _f{f}
      , _x{a, b, steps}
    {
    }

    //! Dereferencing operator.
    inline value_type operator*() const
    {
      return _f(*_x);
    }

    //! Prefix increment operator.
    inline self_type& operator++()
    {
      ++_x;
      return *this;
    }

    //! Prefix decrement operator.
    inline self_type& operator--()
    {
      --_x;
      return *this;
    }

    //! Postfix increment operator.
    inline self_type operator++(int)
    {
      self_type old{*this};
      operator++();
      return old;
    }

    //! Postfix increment operator.
    inline self_type operator--(int)
    {
      self_type old{*this};
      operator--();
      return old;
    }

    //! Arithmetic operator (slow).
    inline self_type operator+=(const vector_type& offset)
    {
      _x += offset;
      return *this;
    }

    //! Arithmetic operator (slow).
    inline self_type operator-=(const vector_type& offset)
    {
      _x +=(-offset);
      return *this;
    }

    inline bool end() const
    {
      return _x.end();
    }

    inline auto position() const -> const vector_type&
    {
      return *_x;
    }

    inline auto stepped_subarray_sizes() const -> vector_type
    {
      return _x.stepped_subarray_sizes();
    }

  private:
    const ArrayView& _f;
    SteppedCoordinatesIterator<ArrayView> _x;
  };

  template <typename ArrayView, typename Padding>
  class InfiniteMultiArrayView
  {
  public:
    using vector_type = typename ArrayView::vector_type;
    using value_type = typename ArrayView::value_type;

    enum { StorageOrder = ArrayView::StorageOrder };
    enum { Dimension = ArrayView::Dimension };

    inline InfiniteMultiArrayView(const ArrayView& f, const Padding& pad)
      : _f(f)
      , _pad(pad)
    {
    }

    inline auto operator()(const vector_type& x) const -> value_type
    {
      return _pad.at(_f, x);
    }

    inline auto begin_subarray(const vector_type& a, const vector_type& b) const
        -> InfiniteArrayIterator<InfiniteMultiArrayView>
    {
      return {*this, a, b};
    }

    inline auto begin_stepped_subarray(const vector_type& a,  //
                                       const vector_type& b,  //
                                       const vector_type& steps) const
        -> InfiniteSteppedArrayIterator<InfiniteMultiArrayView>
    {
      return {*this, a, b, steps};
    }

  private:
    const ArrayView& _f;
    Padding _pad;
  };

  template <typename ArrayView, typename Padding>
  inline auto make_infinite(const ArrayView& f, const Padding& pad)
      -> InfiniteMultiArrayView<ArrayView, Padding>
  {
    return {f, pad};
  }

} /* namespace Sara */
} /* namespace DO */
<commit_msg>MAINT: fix unit tests on GCC 4.8.<commit_after>// ========================================================================== //
// This file is part of Sara, a basic set of libraries in C++ for computer
// vision.
//
// Copyright (C) 2018 David Ok <david.ok8@gmail.com>
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License v. 2.0. If a copy of the MPL was not distributed with this file,
// you can obtain one at http://mozilla.org/MPL/2.0/.
// ========================================================================== //

//! @file

#pragma once

#include <DO/Sara/Core/ArrayIterators/CoordinatesIterator.hpp>
#include <DO/Sara/Core/MultiArray/Padding.hpp>

namespace DO { namespace Sara {

  template <typename ArrayView>
  class InfiniteArrayIterator
  {
  public:
    using self_type = InfiniteArrayIterator;
    using vector_type = typename ArrayView::vector_type;
    using value_type = typename ArrayView::value_type;

  public:
    inline InfiniteArrayIterator(const ArrayView& f,    //
                                 const vector_type& a,  //
                                 const vector_type& b)
      : _f(f)
      , _x(a, b)
    {
    }

    //! Dereferencing operator.
    inline value_type operator*() const
    {
      return _f(*_x);
    }

    //! Prefix increment operator.
    inline self_type& operator++()
    {
      ++_x;
      return *this;
    }

    //! Prefix decrement operator.
    inline self_type& operator--()
    {
      --_x;
      return *this;
    }

    //! Postfix increment operator.
    inline self_type operator++(int)
    {
      self_type old{*this};
      operator++();
      return old;
    }

    //! Postfix increment operator.
    inline self_type operator--(int)
    {
      self_type old{*this};
      operator--();
      return old;
    }

    //! Arithmetic operator (slow).
    inline self_type operator+=(const vector_type& offset)
    {
      _x += offset;
      return *this;
    }

    //! Arithmetic operator (slow).
    inline self_type operator-=(const vector_type& offset)
    {
      _x +=(-offset);
      return *this;
    }

    inline bool end() const
    {
      return _x.end();
    }

    inline auto position() const -> const vector_type&
    {
      return *_x;
    }

  private:
    const ArrayView& _f;
    CoordinatesIterator<ArrayView> _x;
  };

  template <typename ArrayView>
  class InfiniteSteppedArrayIterator
  {
  public:
    using self_type = InfiniteSteppedArrayIterator;
    using vector_type = typename ArrayView::vector_type;
    using value_type = typename ArrayView::value_type;

  public:
    inline InfiniteSteppedArrayIterator(const ArrayView& f,        //
                                        const vector_type& a,      //
                                        const vector_type& b,      //
                                        const vector_type& steps)  //
      : _f(f)
      , _x(a, b, steps)
    {
    }

    //! Dereferencing operator.
    inline value_type operator*() const
    {
      return _f(*_x);
    }

    //! Prefix increment operator.
    inline self_type& operator++()
    {
      ++_x;
      return *this;
    }

    //! Prefix decrement operator.
    inline self_type& operator--()
    {
      --_x;
      return *this;
    }

    //! Postfix increment operator.
    inline self_type operator++(int)
    {
      self_type old{*this};
      operator++();
      return old;
    }

    //! Postfix increment operator.
    inline self_type operator--(int)
    {
      self_type old{*this};
      operator--();
      return old;
    }

    //! Arithmetic operator (slow).
    inline self_type operator+=(const vector_type& offset)
    {
      _x += offset;
      return *this;
    }

    //! Arithmetic operator (slow).
    inline self_type operator-=(const vector_type& offset)
    {
      _x +=(-offset);
      return *this;
    }

    inline bool end() const
    {
      return _x.end();
    }

    inline auto position() const -> const vector_type&
    {
      return *_x;
    }

    inline auto stepped_subarray_sizes() const -> vector_type
    {
      return _x.stepped_subarray_sizes();
    }

  private:
    const ArrayView& _f;
    SteppedCoordinatesIterator<ArrayView> _x;
  };

  template <typename ArrayView, typename Padding>
  class InfiniteMultiArrayView
  {
  public:
    using vector_type = typename ArrayView::vector_type;
    using value_type = typename ArrayView::value_type;

    enum { StorageOrder = ArrayView::StorageOrder };
    enum { Dimension = ArrayView::Dimension };

    inline InfiniteMultiArrayView(const ArrayView& f, const Padding& pad)
      : _f(f)
      , _pad(pad)
    {
    }

    inline auto operator()(const vector_type& x) const -> value_type
    {
      return _pad.at(_f, x);
    }

    inline auto begin_subarray(const vector_type& a, const vector_type& b) const
        -> InfiniteArrayIterator<InfiniteMultiArrayView>
    {
      return {*this, a, b};
    }

    inline auto begin_stepped_subarray(const vector_type& a,  //
                                       const vector_type& b,  //
                                       const vector_type& steps) const
        -> InfiniteSteppedArrayIterator<InfiniteMultiArrayView>
    {
      return {*this, a, b, steps};
    }

  private:
    const ArrayView& _f;
    Padding _pad;
  };

  template <typename ArrayView, typename Padding>
  inline auto make_infinite(const ArrayView& f, const Padding& pad)
      -> InfiniteMultiArrayView<ArrayView, Padding>
  {
    return {f, pad};
  }

} /* namespace Sara */
} /* namespace DO */
<|endoftext|>


<commit_before>/**
 * @file llinspecttoast.cpp
 * @brief Toast inspector implementation.
 *
 * $LicenseInfo:firstyear=2003&license=viewergpl$
 *
 * Copyright (c) 2003-2009, Linden Research, Inc.
 *
 * Second Life Viewer Source Code
 * The source code in this file ("Source Code") is provided by Linden Lab
 * to you under the terms of the GNU General Public License, version 2.0
 * ("GPL"), unless you have obtained a separate licensing agreement
 * ("Other License"), formally executed by you and Linden Lab.  Terms of
 * the GPL can be found in doc/GPL-license.txt in this distribution, or
 * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
 *
 * There are special exceptions to the terms and conditions of the GPL as
 * it is applied to this Source Code. View the full text of the exception
 * in the file doc/FLOSS-exception.txt in this software distribution, or
 * online at
 * http://secondlifegrid.net/programs/open_source/licensing/flossexception
 *
 * By copying, modifying or distributing this software, you acknowledge
 * that you have read and understood your obligations described above,
 * and agree to abide by those obligations.
 *
 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
 * COMPLETENESS OR PERFORMANCE.
 * $/LicenseInfo$
 */

#include "llviewerprecompiledheaders.h" // must be first include

#include "llinspecttoast.h"
#include "llinspect.h"
#include "llfloaterreg.h"
#include "llscreenchannel.h"
#include "llchannelmanager.h"
#include "lltransientfloatermgr.h"

using namespace LLNotificationsUI;

/**
 * Represents inspectable toast .
 */
class LLInspectToast: public LLInspect
{
public:

	LLInspectToast(const LLSD& notification_idl);
	virtual ~LLInspectToast();

	/*virtual*/ void onOpen(const LLSD& notification_id);
private:
	void onToastDestroy(LLToast * toast);

private:
	LLPanel* mPanel;
	LLScreenChannel* mScreenChannel;
};

LLInspectToast::LLInspectToast(const LLSD& notification_id) :
	LLInspect(LLSD()), mPanel(NULL)
{
	LLScreenChannelBase* channel = LLChannelManager::getInstance()->findChannelByID(
																LLUUID(gSavedSettings.getString("NotificationChannelUUID")));
	mScreenChannel = dynamic_cast<LLScreenChannel*>(channel);
	if(NULL == mScreenChannel)
	{
		llwarns << "Could not get requested screen channel." << llendl;
		return;
	}

	LLTransientFloaterMgr::getInstance()->addControlView(this);
}
LLInspectToast::~LLInspectToast()
{
	LLTransientFloaterMgr::getInstance()->removeControlView(this);
}

void LLInspectToast::onOpen(const LLSD& notification_id)
{
	LLInspect::onOpen(notification_id);
	LLToast* toast = mScreenChannel->getToastByNotificationID(notification_id);
	if (toast == NULL)
	{
		llwarns << "Could not get requested toast  from screen channel." << llendl;
		return;
	}
	toast->setOnToastDestroyedCallback(boost::bind(&LLInspectToast::onToastDestroy, this, _1));

	LLPanel * panel = toast->getPanel();
	panel->setVisible(TRUE);
	panel->setMouseOpaque(FALSE);
	if(mPanel != NULL && mPanel->getParent() == this)
	{
		removeChild(mPanel);
	}
	addChild(panel);
	panel->setFocus(TRUE);
	mPanel = panel;

	LLRect panel_rect;
	panel_rect = panel->getRect();
	reshape(panel_rect.getWidth(), panel_rect.getHeight());

	LLUI::positionViewNearMouse(this);
}

void LLInspectToast::onToastDestroy(LLToast * toast)
{
	closeFloater(false);
}

void LLNotificationsUI::registerFloater()
{
	LLFloaterReg::add("inspect_toast", "inspect_toast.xml",
			&LLFloaterReg::build<LLInspectToast>);
}

<commit_msg>Automated merge with ssh://hg.lindenlab.com/q/viewer-release<commit_after>/**
 * @file llinspecttoast.cpp
 * @brief Toast inspector implementation.
 *
 * $LicenseInfo:firstyear=2003&license=viewergpl$
 *
 * Copyright (c) 2003-2009, Linden Research, Inc.
 *
 * Second Life Viewer Source Code
 * The source code in this file ("Source Code") is provided by Linden Lab
 * to you under the terms of the GNU General Public License, version 2.0
 * ("GPL"), unless you have obtained a separate licensing agreement
 * ("Other License"), formally executed by you and Linden Lab.  Terms of
 * the GPL can be found in doc/GPL-license.txt in this distribution, or
 * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
 *
 * There are special exceptions to the terms and conditions of the GPL as
 * it is applied to this Source Code. View the full text of the exception
 * in the file doc/FLOSS-exception.txt in this software distribution, or
 * online at
 * http://secondlifegrid.net/programs/open_source/licensing/flossexception
 *
 * By copying, modifying or distributing this software, you acknowledge
 * that you have read and understood your obligations described above,
 * and agree to abide by those obligations.
 *
 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
 * COMPLETENESS OR PERFORMANCE.
 * $/LicenseInfo$
 */

#include "llviewerprecompiledheaders.h" // must be first include

#include "llinspecttoast.h"
#include "llinspect.h"
#include "llfloaterreg.h"
#include "llscreenchannel.h"
#include "llchannelmanager.h"
#include "lltransientfloatermgr.h"

using namespace LLNotificationsUI;

/**
 * Represents inspectable toast .
 */
class LLInspectToast: public LLInspect
{
public:

	LLInspectToast(const LLSD& notification_idl);
	virtual ~LLInspectToast();

	/*virtual*/ void onOpen(const LLSD& notification_id);
private:
	void onToastDestroy(LLToast * toast);

	boost::signals2::scoped_connection mConnection;
	LLPanel* mPanel;
	LLScreenChannel* mScreenChannel;
};

LLInspectToast::LLInspectToast(const LLSD& notification_id) :
	LLInspect(LLSD()), mPanel(NULL)
{
	LLScreenChannelBase* channel = LLChannelManager::getInstance()->findChannelByID(
																LLUUID(gSavedSettings.getString("NotificationChannelUUID")));
	mScreenChannel = dynamic_cast<LLScreenChannel*>(channel);
	if(NULL == mScreenChannel)
	{
		llwarns << "Could not get requested screen channel." << llendl;
		return;
	}

	LLTransientFloaterMgr::getInstance()->addControlView(this);
}
LLInspectToast::~LLInspectToast()
{
	LLTransientFloaterMgr::getInstance()->removeControlView(this);
}

void LLInspectToast::onOpen(const LLSD& notification_id)
{
	LLInspect::onOpen(notification_id);
	LLToast* toast = mScreenChannel->getToastByNotificationID(notification_id);
	if (toast == NULL)
	{
		llwarns << "Could not get requested toast  from screen channel." << llendl;
		return;
	}
	mConnection = toast->setOnToastDestroyedCallback(boost::bind(&LLInspectToast::onToastDestroy, this, _1));

	LLPanel * panel = toast->getPanel();
	panel->setVisible(TRUE);
	panel->setMouseOpaque(FALSE);
	if(mPanel != NULL && mPanel->getParent() == this)
	{
		removeChild(mPanel);
	}
	addChild(panel);
	panel->setFocus(TRUE);
	mPanel = panel;

	LLRect panel_rect;
	panel_rect = panel->getRect();
	reshape(panel_rect.getWidth(), panel_rect.getHeight());

	LLUI::positionViewNearMouse(this);
}

void LLInspectToast::onToastDestroy(LLToast * toast)
{
	closeFloater(false);
}

void LLNotificationsUI::registerFloater()
{
	LLFloaterReg::add("inspect_toast", "inspect_toast.xml",
			&LLFloaterReg::build<LLInspectToast>);
}

<|endoftext|>


<commit_before>#include <imap.hpp>

#include <vector>
#include <iostream>

using iter::imap;

int main() {
    std::vector<int> vec1 = {1, 2, 3, 4, 5, 6};
    std::vector<int> vec2 = {10, 20, 30, 40, 50, 60};
    for (auto i : imap([] (int x, int y) { return x + y; }, vec1, vec2)) {
        std::cout << i << '\n';
    }

    std::vector<int> vec3 = {100, 200, 300, 400, 500, 600};
    for (auto i : imap([] (int a, int b, int c) { return a + b + c; },
                vec1, vec2, vec3)) {
        std::cout << i << '\n';
    }

    for (auto i : imap([] (int i) {return i * i; }, vec1)) {
        std::cout << i << '\n';
    }

    std::vector<int> vec{1, 2, 3, 4, 5};
    for (auto i : imap([] (int x) {return x * x;}, vec)) {
        std::cout << i << '\n';
    }

    std::vector<int> vec4{1, 2, 3};
    for (auto i : imap([] (int a, int b) { return a + b; }, vec, vec4)) {
        std::cout << i << '\n';
    }

    return 0;
}
<commit_msg>Adds test with temps<commit_after>#include <imap.hpp>
#include <range.hpp>

#include <vector>
#include <iostream>

using iter::imap;

int main() {
    std::vector<int> vec1 = {1, 2, 3, 4, 5, 6};
    std::vector<int> vec2 = {10, 20, 30, 40, 50, 60};
    for (auto i : imap([] (int x, int y) { return x + y; }, vec1, vec2)) {
        std::cout << i << '\n';
    }

    std::vector<int> vec3 = {100, 200, 300, 400, 500, 600};
    for (auto i : imap([] (int a, int b, int c) { return a + b + c; },
                vec1, vec2, vec3)) {
        std::cout << i << '\n';
    }

    for (auto i : imap([] (int i) {return i * i; }, vec1)) {
        std::cout << i << '\n';
    }

    std::vector<int> vec{1, 2, 3, 4, 5};
    for (auto i : imap([] (int x) {return x * x;}, vec)) {
        std::cout << i << '\n';
    }

    std::vector<int> vec4{1, 2, 3};
    for (auto i : imap([] (int a, int b) { return a + b; }, vec, vec4)) {
        std::cout << i << '\n';
    }

    for (auto i : imap([] (const int x) { return x*x; }, iter::range(10))) {
        std::cout << i << '\n';
    }

    return 0;
}
<|endoftext|>


<commit_before>/**
 * Clever programming language
 * Copyright (c) 2011-2012 Clever Team
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */

#include <iostream>
#include "vm/vm.h"
#include "vm/opcode.h"
#include "compiler/compiler.h"
#include "compiler/scope.h"

namespace clever {

CallStack VM::s_call;
ValueVStack VM::s_arg_vars;
ValueVStack VM::s_arg_values;

/**
 * Destroy the opcodes data
 */
VM::~VM() {
	OpcodeList::const_iterator it = m_opcodes.begin(),
		end(m_opcodes.end());

	while (it != end) {
		CLEVER_SAFE_DELREF((*it)->getResult());

		delete *it;
		++it;
	}
}

/**
 * Update the Value* ptr of variables in the function scope
 */
void VM::update_vars(Scope* scope, const FunctionArgs& fargs,
	const ValueVector* args) {
	ValueVector* vec = new ValueVector;
	ValueVector* vec_copy = new ValueVector;
	ValueVector* vec_curr = NULL;
	size_t i = 0;

	if (!s_arg_vars.empty()) {
		vec_curr = s_arg_values.top();
	}

	FunctionArgs::const_iterator it(fargs.begin()), end(fargs.end());

	/* TODO: Find a better way to do this */
	while (it != end) {
		Value* val = scope->getValue(CSTRING((*it).first));
		Value* tmp = new Value;

		val->copy(args->at(i++));
		vec->push_back(val);

		tmp->copy(val);
		vec_copy->push_back(tmp);

		++it;
	}
	i = args->size();

	// Make the copy in the scopes level
	if (scope->hasChildren()) {
		const ScopeVector& scopes = scope->getChildren();
		ScopeVector::const_iterator scope_it(scopes.begin()),
			scope_end(scopes.end());

		while (scope_it != scope_end) {
			const SymbolMap& symbols = (*scope_it)->getSymbols();
			SymbolMap::const_iterator sym(symbols.begin()),
				last_sym(symbols.end());

			while (sym != last_sym) {
				Symbol* symbol = sym->second;

				if (symbol->isValue()) {
					Value* val = symbol->getValue();

					if (!val->isCallable()) {
						Value* tmp = new Value;

						tmp->copy(val);

						if (vec_curr) {
							vec_curr->at(i++)->copy(tmp);
							tmp->addRef();
						}

						vec_copy->push_back(tmp);
						vec->push_back(val);

						val->initialize();
					}
				}
				++sym;
			}
			++scope_it;
		}
	}

	s_arg_vars.push(vec);
	s_arg_values.push(vec_copy);
}

/**
 * Pop arguments onto the call stack
 */
void VM::pop_args(const Opcode* const op) {
	// Check if the function has arguments
	if (op->getOp2() == NULL) {
		return;
	}

	ValueVector* vec_copy = s_arg_values.top();

	for (size_t i = 0, j = vec_copy->size(); i < j; ++i) {
		delete vec_copy->at(i);
	}
	delete vec_copy;

	s_arg_values.pop();

	delete s_arg_vars.top();
	s_arg_vars.pop();

	if (!s_arg_vars.empty()) {
		restore_args();
	}
}

/**
 * Restore the parameter argument values from the previous stack frame
 */
void VM::restore_args() {
	ValueVector* vec = s_arg_vars.top();
	ValueVector* vec_copy = s_arg_values.top();

	for (size_t i = 0, j = vec->size(); i < j; ++i) {
		vec->at(i)->copy(vec_copy->at(i));
	}
}

/**
 * Execute the collected opcodes
 */
void VM::run() {
	long last_op = m_opcodes.size();

	for (long next_op = 0; next_op < last_op && next_op >= 0; ++next_op) {
		const Opcode& opcode = *m_opcodes[next_op];

		// opcode.dump();

		/* Invoke the opcode handler */
		opcode.getHandler()(opcode, next_op);
	}
}

/**
 * JMPZ - Jump if zero
 */
CLEVER_VM_HANDLER(VM::jmpz_handler) {
	const Value* const value = opcode.getOp1();
	Value* result = opcode.getResult();

	if (!value->getValueAsBool()) {
		if (result) result->setBoolean(false);
		CLEVER_VM_GOTO(opcode.getJmpAddr1());
	}

	if (result) {
		result->setBoolean(true);
	}
}

/**
 * JMPNZ - Jump if non zero
 */
CLEVER_VM_HANDLER(VM::jmpnz_handler) {
	const Value* const value = opcode.getOp1();

	if (value->getValueAsBool()) {
		opcode.getResult()->setBoolean(true);
		CLEVER_VM_GOTO(opcode.getJmpAddr1());
	}

	opcode.getResult()->setBoolean(false);
}

/**
 * JMP
 */
CLEVER_VM_HANDLER(VM::jmp_handler) {
	CLEVER_VM_GOTO(opcode.getJmpAddr2());
}

/**
 * Break statement
 */
CLEVER_VM_HANDLER(VM::break_handler) {
	CLEVER_VM_GOTO(opcode.getJmpAddr1());
}

/**
 * Performs a function call
 */
CLEVER_VM_HANDLER(VM::fcall_handler) {
	const CallableValue* func = static_cast<CallableValue*>(opcode.getOp1());
	const ValueVector* args = opcode.getOp2Vector();
	Value* result = opcode.getResult();

	// Check if it's an user function
	if (func->isNearCall()) {
		s_call.push(&opcode);

		if (args) {
			update_vars(func->getScope(), func->getFunction()->getArgs(), args);
		}

		func->call(next_op);
	} else {
		func->call(result, args);
	}
}

/**
 * Performs a method call (directly or via operators)
 */
CLEVER_VM_HANDLER(VM::mcall_handler) {
	const CallableValue* const var = static_cast<CallableValue*>(opcode.getOp1());
	const ValueVector* args = opcode.getOp2Vector();
	Value* result = opcode.getResult();

	var->call(result, args);
}

/**
 * Marks the end of a function
 */
CLEVER_VM_HANDLER(VM::end_func_handler) {
	const Opcode* const op = s_call.top();

	/**
	 * pop + restore arguments from stack
	 */
	pop_args(op);

	s_call.pop();

	/**
	 * Go to after the caller command
	 */
	CLEVER_VM_GOTO(op->getOpNum());
}

/**
 * Returns to the caller or terminates the execution
 */
CLEVER_VM_HANDLER(VM::return_handler) {
	if (!s_call.empty()) {
		const Value* const value = opcode.getOp1();
		const Opcode* call = s_call.top();

		if (value) {
			call->getResult()->copy(value);
		}
		// pop + restore arguments from stack
		pop_args(call);

		s_call.pop();

		// Go back to the caller
		CLEVER_VM_GOTO(call->getOpNum());
	} else {
		// Terminates the execution
		CLEVER_VM_GOTO(-2);
	}
}

/**
 * Add arithmetic operation
 */
CLEVER_VM_HANDLER(VM::plus_handler) {
	const CallableValue* const var = static_cast<CallableValue*>(opcode.getOp1());
	const ValueVector* const args = opcode.getOp2Vector();
	Value* result = opcode.getResult();

	var->call(result, args);
}

/**
 * Minus arithmetic operation
 */
CLEVER_VM_HANDLER(VM::minus_handler) {
	const CallableValue* const var = static_cast<CallableValue*>(opcode.getOp1());
	const ValueVector* const args = opcode.getOp2Vector();
	Value* result = opcode.getResult();

	var->call(result, args);
}

/**
 * Div arithmetic operation
 */
CLEVER_VM_HANDLER(VM::div_handler) {
	const CallableValue* const var = static_cast<CallableValue*>(opcode.getOp1());
	const ValueVector* const args = opcode.getOp2Vector();
	Value* result = opcode.getResult();

	var->call(result, args);
}

/**
 * Mult arithmetic operation
 */
CLEVER_VM_HANDLER(VM::mult_handler) {
	const CallableValue* const var = static_cast<CallableValue*>(opcode.getOp1());
	const ValueVector* const args = opcode.getOp2Vector();
	Value* result = opcode.getResult();

	var->call(result, args);
}

/**
 * Mod arithmetic operation
 */
CLEVER_VM_HANDLER(VM::mod_handler) {
	const CallableValue* const var = static_cast<CallableValue*>(opcode.getOp1());
	const ValueVector* const args = opcode.getOp2Vector();
	Value* result = opcode.getResult();

	var->call(result, args);
}

/**
 * Inc operation (x++, ++x)
 */
CLEVER_VM_HANDLER(VM::inc_handler) {
	const CallableValue* const var = static_cast<CallableValue*>(opcode.getOp1());
	const ValueVector* const args = opcode.getOp2Vector();
	Value* result = opcode.getResult();

	var->call(result, args);
}

/**
 * Dec arithmetic operation (x--, --x)
 */
CLEVER_VM_HANDLER(VM::dec_handler) {
	const CallableValue* const var = static_cast<CallableValue*>(opcode.getOp1());
	const ValueVector* const args = opcode.getOp2Vector();
	Value* result = opcode.getResult();

	var->call(result, args);
}

/**
 * Xor bit-wise operation
 */
CLEVER_VM_HANDLER(VM::xor_handler) {
	const CallableValue* const var = static_cast<CallableValue*>(opcode.getOp1());
	const ValueVector* const args = opcode.getOp2Vector();
	Value* result = opcode.getResult();

	var->call(result, args);
}

/**
 * OR bit-wise operation
 */
CLEVER_VM_HANDLER(VM::bw_or_handler) {
	const CallableValue* const var = static_cast<CallableValue*>(opcode.getOp1());
	const ValueVector* const args = opcode.getOp2Vector();
	Value* result = opcode.getResult();

	var->call(result, args);
}

/**
 * AND bit-wise operation
 */
CLEVER_VM_HANDLER(VM::bw_and_handler) {
	const CallableValue* const var = static_cast<CallableValue*>(opcode.getOp1());
	const ValueVector* const args = opcode.getOp2Vector();
	Value* result = opcode.getResult();

	var->call(result, args);
}

/**
 * NOT bit-wise operation
 */
CLEVER_VM_HANDLER(VM::bw_not_handler) {
	const CallableValue* const var = static_cast<CallableValue*>(opcode.getOp1());
	const ValueVector* const args = opcode.getOp2Vector();
	Value* result = opcode.getResult();

	var->call(result, args);
}

/**
 * Greater logical operation (x > y)
 */
CLEVER_VM_HANDLER(VM::greater_handler) {
	const CallableValue* const var = static_cast<CallableValue*>(opcode.getOp1());
	const ValueVector* const args = opcode.getOp2Vector();
	Value* result = opcode.getResult();

	var->call(result, args);
}

/**
 * Less logical operation (x < y)
 */
CLEVER_VM_HANDLER(VM::less_handler) {
	const CallableValue* const var = static_cast<CallableValue*>(opcode.getOp1());
	const ValueVector* const args = opcode.getOp2Vector();
	Value* result = opcode.getResult();

	var->call(result, args);
}

/**
 * Greater or equal logical operation (x >= y)
 */
CLEVER_VM_HANDLER(VM::ge_handler) {
	const CallableValue* const var = static_cast<CallableValue*>(opcode.getOp1());
	const ValueVector* const args = opcode.getOp2Vector();
	Value* result = opcode.getResult();

	var->call(result, args);
}

/**
 * Less or equal logical operation (x <= y)
 */
CLEVER_VM_HANDLER(VM::le_handler) {
	const CallableValue* const var = static_cast<CallableValue*>(opcode.getOp1());
	const ValueVector* const args = opcode.getOp2Vector();
	Value* result = opcode.getResult();

	var->call(result, args);
}

/**
 * Equal logical operation (x == y)
 */
CLEVER_VM_HANDLER(VM::equal_handler) {
	const CallableValue* const var = static_cast<CallableValue*>(opcode.getOp1());
	const ValueVector* const args = opcode.getOp2Vector();
	Value* result = opcode.getResult();

	var->call(result, args);
}

/**
 * Not equal logical operation (x != y)
 */
CLEVER_VM_HANDLER(VM::ne_handler) {
	const CallableValue* const var = static_cast<CallableValue*>(opcode.getOp1());
	const ValueVector* const args = opcode.getOp2Vector();
	Value* result = opcode.getResult();

	var->call(result, args);
}

/**
 * Left shift logical operation (x << y)
 */
CLEVER_VM_HANDLER(VM::lshift_handler) {
	const CallableValue* const var = static_cast<CallableValue*>(opcode.getOp1());
	const ValueVector* const args = opcode.getOp2Vector();
	Value* result = opcode.getResult();

	var->call(result, args);
}

/**
 * Right shift logical operation (x >> y)
 */
CLEVER_VM_HANDLER(VM::rshift_handler) {
	const CallableValue* const var = static_cast<CallableValue*>(opcode.getOp1());
	const ValueVector* const args = opcode.getOp2Vector();
	Value* result = opcode.getResult();

	var->call(result, args);
}

/**
 * NOT logical operation (x >> y)
 */
CLEVER_VM_HANDLER(VM::not_handler) {
	const CallableValue* const var = static_cast<CallableValue*>(opcode.getOp1());
	const ValueVector* const args = opcode.getOp2Vector();
	Value* result = opcode.getResult();

	var->call(result, args);
}

} // clever
<commit_msg>- Clean up<commit_after>/**
 * Clever programming language
 * Copyright (c) 2011-2012 Clever Team
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */

#include <iostream>
#include "vm/vm.h"
#include "vm/opcode.h"
#include "compiler/compiler.h"
#include "compiler/scope.h"

namespace clever {

CallStack VM::s_call;
ValueVStack VM::s_arg_vars;
ValueVStack VM::s_arg_values;

/**
 * Destroy the opcodes data
 */
VM::~VM() {
	OpcodeList::const_iterator it = m_opcodes.begin(),
		end(m_opcodes.end());

	while (it != end) {
		CLEVER_SAFE_DELREF((*it)->getResult());

		delete *it;
		++it;
	}
}

/**
 * Update the Value* ptr of variables in the function scope
 */
void VM::update_vars(Scope* scope, const FunctionArgs& fargs,
	const ValueVector* args) {
	ValueVector* vec = new ValueVector;
	ValueVector* vec_copy = new ValueVector;
	ValueVector* vec_curr = NULL;
	size_t i = 0;

	if (!s_arg_vars.empty()) {
		vec_curr = s_arg_values.top();
	}

	FunctionArgs::const_iterator it(fargs.begin()), end(fargs.end());

	vec->reserve(args->size());
	vec_copy->reserve(args->size());

	/* TODO: Find a better way to do this */
	while (it != end) {
		Value* val = scope->getValue(CSTRING((*it).first));
		Value* tmp = new Value;

		val->copy(args->at(i++));
		vec->push_back(val);

		tmp->copy(val);
		vec_copy->push_back(tmp);

		++it;
	}
	i = args->size();

	// Make the copy in the scopes level
	if (EXPECTED(scope->hasChildren())) {
		const ScopeVector& scopes = scope->getChildren();
		ScopeVector::const_iterator scope_it(scopes.begin()),
			scope_end(scopes.end());

		while (EXPECTED(scope_it != scope_end)) {
			const SymbolMap& symbols = (*scope_it)->getSymbols();
			SymbolMap::const_iterator sym(symbols.begin()),
				last_sym(symbols.end());

			while (EXPECTED(sym != last_sym)) {
				Symbol* symbol = sym->second;

				if (EXPECTED(symbol->isValue())) {
					Value* val = symbol->getValue();

					if (val->isCallable()) {
						continue;
					}

					Value* tmp = new Value;

					tmp->copy(val);

					if (vec_curr) {
						vec_curr->at(i++)->copy(tmp);
						tmp->addRef();
					}

					vec_copy->push_back(tmp);
					vec->push_back(val);
					val->initialize();
				}
				++sym;
			}
			++scope_it;
		}
	}

	s_arg_vars.push(vec);
	s_arg_values.push(vec_copy);
}

/**
 * Pop arguments onto the call stack
 */
void VM::pop_args(const Opcode* const op) {
	// Check if the function has arguments
	if (op->getOp2() == NULL) {
		return;
	}

	ValueVector* vec_copy = s_arg_values.top();

	for (size_t i = 0, j = vec_copy->size(); i < j; ++i) {
		delete vec_copy->at(i);
	}
	delete vec_copy;

	s_arg_values.pop();

	delete s_arg_vars.top();
	s_arg_vars.pop();

	if (!s_arg_vars.empty()) {
		restore_args();
	}
}

/**
 * Restore the parameter argument values from the previous stack frame
 */
void VM::restore_args() {
	ValueVector* vec = s_arg_vars.top();
	ValueVector* vec_copy = s_arg_values.top();

	for (size_t i = 0, j = vec->size(); i < j; ++i) {
		vec->at(i)->copy(vec_copy->at(i));
	}
}

/**
 * Execute the collected opcodes
 */
void VM::run() {
	long last_op = m_opcodes.size();

	for (long next_op = 0; next_op < last_op && next_op >= 0; ++next_op) {
		const Opcode& opcode = *m_opcodes[next_op];

		// opcode.dump();

		/* Invoke the opcode handler */
		opcode.getHandler()(opcode, next_op);
	}
}

/**
 * JMPZ - Jump if zero
 */
CLEVER_VM_HANDLER(VM::jmpz_handler) {
	const Value* const value = opcode.getOp1();
	Value* result = opcode.getResult();

	if (!value->getValueAsBool()) {
		if (result) result->setBoolean(false);
		CLEVER_VM_GOTO(opcode.getJmpAddr1());
	}

	if (result) {
		result->setBoolean(true);
	}
}

/**
 * JMPNZ - Jump if non zero
 */
CLEVER_VM_HANDLER(VM::jmpnz_handler) {
	const Value* const value = opcode.getOp1();

	if (value->getValueAsBool()) {
		opcode.getResult()->setBoolean(true);
		CLEVER_VM_GOTO(opcode.getJmpAddr1());
	}

	opcode.getResult()->setBoolean(false);
}

/**
 * JMP
 */
CLEVER_VM_HANDLER(VM::jmp_handler) {
	CLEVER_VM_GOTO(opcode.getJmpAddr2());
}

/**
 * Break statement
 */
CLEVER_VM_HANDLER(VM::break_handler) {
	CLEVER_VM_GOTO(opcode.getJmpAddr1());
}

/**
 * Performs a function call
 */
CLEVER_VM_HANDLER(VM::fcall_handler) {
	const CallableValue* func = static_cast<CallableValue*>(opcode.getOp1());
	const ValueVector* args = opcode.getOp2Vector();
	Value* result = opcode.getResult();

	// Check if it's an user function
	if (func->isNearCall()) {
		s_call.push(&opcode);

		if (args) {
			update_vars(func->getScope(), func->getFunction()->getArgs(), args);
		}

		func->call(next_op);
	} else {
		func->call(result, args);
	}
}

/**
 * Performs a method call (directly or via operators)
 */
CLEVER_VM_HANDLER(VM::mcall_handler) {
	const CallableValue* const var = static_cast<CallableValue*>(opcode.getOp1());
	const ValueVector* args = opcode.getOp2Vector();
	Value* result = opcode.getResult();

	var->call(result, args);
}

/**
 * Marks the end of a function
 */
CLEVER_VM_HANDLER(VM::end_func_handler) {
	const Opcode* const op = s_call.top();

	/**
	 * pop + restore arguments from stack
	 */
	pop_args(op);

	s_call.pop();

	/**
	 * Go to after the caller command
	 */
	CLEVER_VM_GOTO(op->getOpNum());
}

/**
 * Returns to the caller or terminates the execution
 */
CLEVER_VM_HANDLER(VM::return_handler) {
	if (!s_call.empty()) {
		const Value* const value = opcode.getOp1();
		const Opcode* call = s_call.top();

		if (value) {
			call->getResult()->copy(value);
		}
		// pop + restore arguments from stack
		pop_args(call);

		s_call.pop();

		// Go back to the caller
		CLEVER_VM_GOTO(call->getOpNum());
	} else {
		// Terminates the execution
		CLEVER_VM_GOTO(-2);
	}
}

/**
 * Add arithmetic operation
 */
CLEVER_VM_HANDLER(VM::plus_handler) {
	const CallableValue* const var = static_cast<CallableValue*>(opcode.getOp1());
	const ValueVector* const args = opcode.getOp2Vector();
	Value* result = opcode.getResult();

	var->call(result, args);
}

/**
 * Minus arithmetic operation
 */
CLEVER_VM_HANDLER(VM::minus_handler) {
	const CallableValue* const var = static_cast<CallableValue*>(opcode.getOp1());
	const ValueVector* const args = opcode.getOp2Vector();
	Value* result = opcode.getResult();

	var->call(result, args);
}

/**
 * Div arithmetic operation
 */
CLEVER_VM_HANDLER(VM::div_handler) {
	const CallableValue* const var = static_cast<CallableValue*>(opcode.getOp1());
	const ValueVector* const args = opcode.getOp2Vector();
	Value* result = opcode.getResult();

	var->call(result, args);
}

/**
 * Mult arithmetic operation
 */
CLEVER_VM_HANDLER(VM::mult_handler) {
	const CallableValue* const var = static_cast<CallableValue*>(opcode.getOp1());
	const ValueVector* const args = opcode.getOp2Vector();
	Value* result = opcode.getResult();

	var->call(result, args);
}

/**
 * Mod arithmetic operation
 */
CLEVER_VM_HANDLER(VM::mod_handler) {
	const CallableValue* const var = static_cast<CallableValue*>(opcode.getOp1());
	const ValueVector* const args = opcode.getOp2Vector();
	Value* result = opcode.getResult();

	var->call(result, args);
}

/**
 * Inc operation (x++, ++x)
 */
CLEVER_VM_HANDLER(VM::inc_handler) {
	const CallableValue* const var = static_cast<CallableValue*>(opcode.getOp1());
	const ValueVector* const args = opcode.getOp2Vector();
	Value* result = opcode.getResult();

	var->call(result, args);
}

/**
 * Dec arithmetic operation (x--, --x)
 */
CLEVER_VM_HANDLER(VM::dec_handler) {
	const CallableValue* const var = static_cast<CallableValue*>(opcode.getOp1());
	const ValueVector* const args = opcode.getOp2Vector();
	Value* result = opcode.getResult();

	var->call(result, args);
}

/**
 * Xor bit-wise operation
 */
CLEVER_VM_HANDLER(VM::xor_handler) {
	const CallableValue* const var = static_cast<CallableValue*>(opcode.getOp1());
	const ValueVector* const args = opcode.getOp2Vector();
	Value* result = opcode.getResult();

	var->call(result, args);
}

/**
 * OR bit-wise operation
 */
CLEVER_VM_HANDLER(VM::bw_or_handler) {
	const CallableValue* const var = static_cast<CallableValue*>(opcode.getOp1());
	const ValueVector* const args = opcode.getOp2Vector();
	Value* result = opcode.getResult();

	var->call(result, args);
}

/**
 * AND bit-wise operation
 */
CLEVER_VM_HANDLER(VM::bw_and_handler) {
	const CallableValue* const var = static_cast<CallableValue*>(opcode.getOp1());
	const ValueVector* const args = opcode.getOp2Vector();
	Value* result = opcode.getResult();

	var->call(result, args);
}

/**
 * NOT bit-wise operation
 */
CLEVER_VM_HANDLER(VM::bw_not_handler) {
	const CallableValue* const var = static_cast<CallableValue*>(opcode.getOp1());
	const ValueVector* const args = opcode.getOp2Vector();
	Value* result = opcode.getResult();

	var->call(result, args);
}

/**
 * Greater logical operation (x > y)
 */
CLEVER_VM_HANDLER(VM::greater_handler) {
	const CallableValue* const var = static_cast<CallableValue*>(opcode.getOp1());
	const ValueVector* const args = opcode.getOp2Vector();
	Value* result = opcode.getResult();

	var->call(result, args);
}

/**
 * Less logical operation (x < y)
 */
CLEVER_VM_HANDLER(VM::less_handler) {
	const CallableValue* const var = static_cast<CallableValue*>(opcode.getOp1());
	const ValueVector* const args = opcode.getOp2Vector();
	Value* result = opcode.getResult();

	var->call(result, args);
}

/**
 * Greater or equal logical operation (x >= y)
 */
CLEVER_VM_HANDLER(VM::ge_handler) {
	const CallableValue* const var = static_cast<CallableValue*>(opcode.getOp1());
	const ValueVector* const args = opcode.getOp2Vector();
	Value* result = opcode.getResult();

	var->call(result, args);
}

/**
 * Less or equal logical operation (x <= y)
 */
CLEVER_VM_HANDLER(VM::le_handler) {
	const CallableValue* const var = static_cast<CallableValue*>(opcode.getOp1());
	const ValueVector* const args = opcode.getOp2Vector();
	Value* result = opcode.getResult();

	var->call(result, args);
}

/**
 * Equal logical operation (x == y)
 */
CLEVER_VM_HANDLER(VM::equal_handler) {
	const CallableValue* const var = static_cast<CallableValue*>(opcode.getOp1());
	const ValueVector* const args = opcode.getOp2Vector();
	Value* result = opcode.getResult();

	var->call(result, args);
}

/**
 * Not equal logical operation (x != y)
 */
CLEVER_VM_HANDLER(VM::ne_handler) {
	const CallableValue* const var = static_cast<CallableValue*>(opcode.getOp1());
	const ValueVector* const args = opcode.getOp2Vector();
	Value* result = opcode.getResult();

	var->call(result, args);
}

/**
 * Left shift logical operation (x << y)
 */
CLEVER_VM_HANDLER(VM::lshift_handler) {
	const CallableValue* const var = static_cast<CallableValue*>(opcode.getOp1());
	const ValueVector* const args = opcode.getOp2Vector();
	Value* result = opcode.getResult();

	var->call(result, args);
}

/**
 * Right shift logical operation (x >> y)
 */
CLEVER_VM_HANDLER(VM::rshift_handler) {
	const CallableValue* const var = static_cast<CallableValue*>(opcode.getOp1());
	const ValueVector* const args = opcode.getOp2Vector();
	Value* result = opcode.getResult();

	var->call(result, args);
}

/**
 * NOT logical operation (x >> y)
 */
CLEVER_VM_HANDLER(VM::not_handler) {
	const CallableValue* const var = static_cast<CallableValue*>(opcode.getOp1());
	const ValueVector* const args = opcode.getOp2Vector();
	Value* result = opcode.getResult();

	var->call(result, args);
}

} // clever
<|endoftext|>


<commit_before>// Copyright (c) 2014 Josh Rickmar.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

#include <algorithm>
#include <string>

#include <volo.h>
#include <gdk/gdkkeysyms.h>

using namespace volo;

const std::array<std::string, 2> recognized_uri_schemes = { {
	"http://",
	"https://",
} };

// has_prefix returns whether p is a prefix of s.
template <typename T>
bool has_prefix(const T& s, const T& p) {
	return s.size() >= p.size() && std::equal(p.begin(), p.end(), s.begin());
}

void guess_uri(std::string& uri) {
	for (auto& scheme : recognized_uri_schemes) {
		if (has_prefix(uri, scheme)) {
			return;
		}
	}
	uri = "http://" + uri;
}

browser::browser(const std::vector<const char *>&uris) {
	back->set_can_focus(false);
	fwd->set_can_focus(false);
	auto histnav_style = histnav->get_style_context();
	histnav_style->add_class("raised");
	histnav_style->add_class("linked");
	histnav->add(*back);
	histnav->add(*fwd);
	navbar->pack_start(*histnav);

	navbar->set_custom_title(*nav_entry);

	new_tab->set_can_focus(false);
	new_tab->set_relief(GTK_RELIEF_NONE);
	new_tab->show();
	navbar->pack_end(*new_tab);

	navbar->set_show_close_button(true);

	auto num_uris = uris.size();
	nb->set_show_tabs(num_uris > 1);

	window->set_title("volo");
	window->set_default_size(1024, 768);
	window->set_titlebar(*navbar);
	nb->set_scrollable(true);
	window->add(*nb);

	tabs.reserve(num_uris);
	for (auto& uri : uris) {
		open_new_tab(uri);
	}

	nav_entry->connect_activate(*this, on_nav_entry_activate);
	nb->connect_switch_page(*this, on_notebook_switch_page);
	nb->connect_page_added(*this, on_notebook_page_added);
	nb->connect_page_removed(*this, on_notebook_page_removed);
	new_tab->connect_clicked(*this, on_new_tab_clicked);
	window->connect_key_press_event(*this, on_window_key_press_event);
	window->connect_destroy(*this, on_window_destroy);

	show_webview(0, *tabs.front().wv);

	window->show_all();
}

void browser::on_nav_entry_activate(uri_entry& entry) {
	std::string uri = nav_entry->get_text();
	guess_uri(uri);
	visable_tab.web_view->load_uri(uri);
	visable_tab.web_view->grab_focus();
}

void browser::on_notebook_switch_page(gtk::notebook& notebook, gtk::widget& page,
	unsigned int page_num) {

	switch_page(page_num);
}

void browser::on_notebook_page_added(gtk::notebook& notebook, gtk::widget& child,
	unsigned int page_num) {

	notebook.set_show_tabs(true);
}

void browser::on_notebook_page_removed(gtk::notebook& notebook, gtk::widget& child,
	unsigned int page_num) {

	notebook.set_show_tabs(notebook.get_n_pages() > 1);
}

void browser::on_new_tab_clicked(gtk::button& button) {
	auto n = open_new_tab("");
	nb->set_current_page(n);
}

bool browser::on_window_key_press_event(gtk::window& window, GdkEventKey& ev) {
	auto kv = ev.keyval;
	auto state = ev.state;

	if (state == (GDK_CONTROL_MASK|GDK_SHIFT_MASK)) {
		if (kv == GDK_KEY_ISO_Left_Tab) {
			auto n = nb->get_current_page();
			if (n == 0) {
				n = tabs.size() - 1;
			} else {
				--n;
			}
			switch_page(n);
			nb->set_current_page(n);
			return true;
		}
	} else if (state == GDK_CONTROL_MASK) {
		if (kv == GDK_KEY_Tab) {
			auto n = nb->get_current_page();
			if (++n == tabs.size()) {
				n = 0;
			}
			switch_page(n);
			nb->set_current_page(n);
			return true;
		}
	}

	// Let the window begin handling the event.  This is done before some
	// of the Ctrl keybindings below to allow various events which modify
	// text fields (such as ^W to delete the preivous word) to be handled
	// by the child, rather than closing the current tab.
	if (window.key_press_event(ev)) {
		return true;
	}

	if (state == GDK_CONTROL_MASK) {
		if (kv == GDK_KEY_l) {
			nav_entry->grab_focus();
			return true;
		} else if (kv == GDK_KEY_t) {
			auto n = open_new_tab("");
			nb->set_current_page(n);
			return true;
		} else if (kv == GDK_KEY_w) {
			tabs.erase(tabs.begin() + visable_tab.tab_index);
			if (tabs.size() == 0) {
				window.destroy();
			}
			return true;
		} else if (kv == GDK_KEY_q) {
			tabs.clear();
			window.destroy();
			return true;
		} else if (kv >= GDK_KEY_1 && kv <= GDK_KEY_8) {
			auto n = kv - GDK_KEY_1;
			if (tabs.size() > n) {
				switch_page(n);
				nb->set_current_page(n);
			}
			return true;
		} else if (kv == GDK_KEY_9) {
			auto n = tabs.size() - 1;
			switch_page(n);
			nb->set_current_page(n);
			return true;
		}
	}

	return true;
}

void browser::on_window_destroy(gtk::window& w) {
	gtk_main_quit();
}

browser_tab::browser_tab(const char *uri) :
	wv{gtk::make_unique<webkit::web_view>(uri)},
	tab_title{gtk::make_unique<gtk::label>("New tab")} {

	tab_title->set_can_focus(false);
	tab_title->set_hexpand(true);
	tab_title->set_ellipsize(PANGO_ELLIPSIZE_END);
	tab_title->set_size_request(50, -1);
}

void browser::on_web_view_load_changed(webkit::web_view& wv, WebKitLoadEvent load_event) {
	GTlsCertificate *certificate = nullptr;
	GTlsCertificateFlags errors{};

	switch (load_event) {
	case WEBKIT_LOAD_STARTED:
		break;

	case WEBKIT_LOAD_REDIRECTED:
		break;

	case WEBKIT_LOAD_COMMITTED:
		if (wv.get_tls_info(certificate, errors)) {
		} else {
		}
		break;

	case WEBKIT_LOAD_FINISHED:
		break;
	}
}

void browser::on_web_view_notify_title(webkit::web_view& wv, GParamSpec& param_spec) {
	auto title = wv.get_title();
	if (visable_tab.web_view == &wv) {
		window->set_title(title);
		tabs[visable_tab.tab_index].tab_title->set_text(title);
		return;
	}

	// If the notified webview is not the currently-shown tab, we must
	// search for the correct tab title to modify.
	for (auto& tab : tabs) {
		if (tab.wv.get() == &wv) {
			tab.tab_title->set_text(title);
			break;
		}
	}
}

int browser::open_new_tab(const char *uri) {
	tabs.emplace_back(uri);
	auto& tab = tabs.back();
	auto& wv = *tab.wv;

	auto tab_content = gtk::box::create();
	tab_content->set_can_focus(false);
	tab_content->add(*tab.tab_title);
	tab_content->add(*tab.tab_close);

	wv.show_all();
	tab_content->show_all();
	const auto n = nb->append_page(wv, *tab_content);
	nb->set_tab_reorderable(wv, true);

	tab.tab_close->connect_clicked(*this, on_tab_close_clicked);
	wv.connect_notify_title(*this, on_web_view_notify_title);

	return n;
}

void browser::show_window() {
	window->show();
}

void browser::on_tab_close_clicked(gtk::button& tab_close) {
	for (auto it = tabs.begin(), ite = tabs.end(); it != ite; ++it) {
		// Compare using pointer equality.
		if (it->tab_close->ptr() != &tab_close) {
			continue;
		}
		auto removed_index = std::distance(tabs.begin(), it);
		tabs.erase(it);

		if (tabs.size() == 0) {
			window->destroy();
			break;
		}

		// If the removed tab had an index smaller than the visable tab,
		// the visable tab index must be decremented.
		auto prev_index = visable_tab.tab_index;
		if (removed_index < prev_index) {
			visable_tab.tab_index = prev_index - 1;
		}

		break;
	}
}

void browser::on_back_button_clicked(gtk::button& back) {
	visable_tab.web_view->go_back();
}

void browser::on_fwd_button_clicked(gtk::button& fwd) {
	visable_tab.web_view->go_forward();
}

void browser::on_back_forward_list_changed(WebKitBackForwardList& bfl,
	WebKitBackForwardListItem&, gpointer) {

	if (visable_tab.bfl == &bfl) {
		update_histnav(*visable_tab.web_view);
	}
}

void browser::on_web_view_notify_uri(webkit::web_view& web_view, GParamSpec& param_spec) {
	nav_entry->set_uri(web_view.get_uri());
}

static void on_nav_entry_refresh_clicked(uri_entry *entry, webkit::web_view *web_view) {
	web_view->reload();
	web_view->grab_focus();
}

void browser::on_notebook_page_reordered(gtk::notebook& notebook, gtk::widget& child,
	unsigned int new_idx) {

	// NOTE: This only works when reordering the current visable tab.
	// However, this appears to be a safe assumption since, at least
	// when using the mouse to drag and drop tabs (which is the only
	// method we have of reordering them), the tab being reordered
	// is always focused first.
	auto old_idx = visable_tab.tab_index;
	auto tmp = std::move(tabs[old_idx]);
	if (old_idx < new_idx) {
		// Moving the range [old_idx+1, new_idx+1) to [old_idx, new_idx).
		std::move(tabs.begin()+old_idx+1, tabs.begin()+new_idx+1,
			tabs.begin()+old_idx);
	} else {
		// Moving the range [new_idx, old_idx) to [new_idx+1, old_idx+1).
		std::move_backward(tabs.begin()+new_idx, tabs.begin()+old_idx,
			tabs.begin()+old_idx+1);
	}
	tabs[new_idx] = std::move(tmp);
	visable_tab.tab_index = new_idx;
}

void browser::show_webview(unsigned int page_num, webkit::web_view& wv) {
	visable_tab = {page_num, wv};

	// Update navbar/titlebar with the current state of the webview being
	// shown.
	auto c_title = webkit_web_view_get_title(wv.ptr());
	window->set_title(c_title ? c_title : "volo");
	update_histnav(wv);
	auto uri = wv.get_uri();
	nav_entry->set_uri(uri);

	page_signals = { {
		back->connect_clicked(*this, on_back_button_clicked),
		fwd->connect_clicked(*this, on_fwd_button_clicked),
		wv.connect_back_forward_list_changed(*this, on_back_forward_list_changed),
		wv.connect_notify_uri(*this, on_web_view_notify_uri),
		wv.connect_load_changed(*this, on_web_view_load_changed),
		nav_entry->connect_refresh_clicked(wv, ::on_nav_entry_refresh_clicked),
		nb->connect_page_reordered(*this, on_notebook_page_reordered),
	} };

	// Grab URI entry focus if the shown tab is blank.
	//
	// TODO: If this webview is being shown by clicking another notebook
	// tab, grabbing the entry focus has no effect.
	if (!strcmp(uri, "")) {
		nav_entry->grab_focus();
	} else {
		wv.grab_focus();
	}
}

void browser::update_histnav(webkit::web_view& wv) {
	back->set_sensitive(wv.can_go_back());
	fwd->set_sensitive(wv.can_go_forward());
}

void browser::switch_page(unsigned int page_num) {
	// Disconnect previous web_view's signals before showing and connecting
	// the new web_view.
	for (auto& sig : page_signals) {
		sig.disconnect();
	}

	show_webview(page_num, *tabs.at(page_num).wv);
}

int main(int argc, char **argv) {
	gtk_init(&argc, &argv);

	auto web_cxt = webkit::web_context::get_default();
	web_cxt->set_process_model(WEBKIT_PROCESS_MODEL_MULTIPLE_SECONDARY_PROCESSES);

	auto b = browser{};
	b.show_window();
	gtk_main();
}
<commit_msg>Add some TODOs intended for the previous commit.<commit_after>// Copyright (c) 2014 Josh Rickmar.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

#include <algorithm>
#include <string>

#include <volo.h>
#include <gdk/gdkkeysyms.h>

using namespace volo;

const std::array<std::string, 2> recognized_uri_schemes = { {
	"http://",
	"https://",
} };

// has_prefix returns whether p is a prefix of s.
template <typename T>
bool has_prefix(const T& s, const T& p) {
	return s.size() >= p.size() && std::equal(p.begin(), p.end(), s.begin());
}

void guess_uri(std::string& uri) {
	for (auto& scheme : recognized_uri_schemes) {
		if (has_prefix(uri, scheme)) {
			return;
		}
	}
	uri = "http://" + uri;
}

browser::browser(const std::vector<const char *>&uris) {
	back->set_can_focus(false);
	fwd->set_can_focus(false);
	auto histnav_style = histnav->get_style_context();
	histnav_style->add_class("raised");
	histnav_style->add_class("linked");
	histnav->add(*back);
	histnav->add(*fwd);
	navbar->pack_start(*histnav);

	navbar->set_custom_title(*nav_entry);

	new_tab->set_can_focus(false);
	new_tab->set_relief(GTK_RELIEF_NONE);
	new_tab->show();
	navbar->pack_end(*new_tab);

	navbar->set_show_close_button(true);

	auto num_uris = uris.size();
	nb->set_show_tabs(num_uris > 1);

	window->set_title("volo");
	window->set_default_size(1024, 768);
	window->set_titlebar(*navbar);
	nb->set_scrollable(true);
	window->add(*nb);

	tabs.reserve(num_uris);
	for (auto& uri : uris) {
		open_new_tab(uri);
	}

	nav_entry->connect_activate(*this, on_nav_entry_activate);
	nb->connect_switch_page(*this, on_notebook_switch_page);
	nb->connect_page_added(*this, on_notebook_page_added);
	nb->connect_page_removed(*this, on_notebook_page_removed);
	new_tab->connect_clicked(*this, on_new_tab_clicked);
	window->connect_key_press_event(*this, on_window_key_press_event);
	window->connect_destroy(*this, on_window_destroy);

	show_webview(0, *tabs.front().wv);

	window->show_all();
}

void browser::on_nav_entry_activate(uri_entry& entry) {
	std::string uri = nav_entry->get_text();
	guess_uri(uri);
	visable_tab.web_view->load_uri(uri);
	visable_tab.web_view->grab_focus();
}

void browser::on_notebook_switch_page(gtk::notebook& notebook, gtk::widget& page,
	unsigned int page_num) {

	switch_page(page_num);
}

void browser::on_notebook_page_added(gtk::notebook& notebook, gtk::widget& child,
	unsigned int page_num) {

	notebook.set_show_tabs(true);
}

void browser::on_notebook_page_removed(gtk::notebook& notebook, gtk::widget& child,
	unsigned int page_num) {

	notebook.set_show_tabs(notebook.get_n_pages() > 1);
}

void browser::on_new_tab_clicked(gtk::button& button) {
	auto n = open_new_tab("");
	nb->set_current_page(n);
}

bool browser::on_window_key_press_event(gtk::window& window, GdkEventKey& ev) {
	auto kv = ev.keyval;
	auto state = ev.state;

	if (state == (GDK_CONTROL_MASK|GDK_SHIFT_MASK)) {
		if (kv == GDK_KEY_ISO_Left_Tab) {
			auto n = nb->get_current_page();
			if (n == 0) {
				n = tabs.size() - 1;
			} else {
				--n;
			}
			switch_page(n);
			nb->set_current_page(n);
			return true;
		}
	} else if (state == GDK_CONTROL_MASK) {
		if (kv == GDK_KEY_Tab) {
			auto n = nb->get_current_page();
			if (++n == tabs.size()) {
				n = 0;
			}
			switch_page(n);
			nb->set_current_page(n);
			return true;
		}
	}

	// Let the window begin handling the event.  This is done before some
	// of the Ctrl keybindings below to allow various events which modify
	// text fields (such as ^W to delete the preivous word) to be handled
	// by the child, rather than closing the current tab.
	if (window.key_press_event(ev)) {
		return true;
	}

	if (state == GDK_CONTROL_MASK) {
		if (kv == GDK_KEY_l) {
			nav_entry->grab_focus();
			return true;
		} else if (kv == GDK_KEY_t) {
			auto n = open_new_tab("");
			nb->set_current_page(n);
			return true;
		} else if (kv == GDK_KEY_w) {
			tabs.erase(tabs.begin() + visable_tab.tab_index);
			if (tabs.size() == 0) {
				window.destroy();
			}
			return true;
		} else if (kv == GDK_KEY_q) {
			tabs.clear();
			window.destroy();
			return true;
		} else if (kv >= GDK_KEY_1 && kv <= GDK_KEY_8) {
			auto n = kv - GDK_KEY_1;
			if (tabs.size() > n) {
				switch_page(n);
				nb->set_current_page(n);
			}
			return true;
		} else if (kv == GDK_KEY_9) {
			auto n = tabs.size() - 1;
			switch_page(n);
			nb->set_current_page(n);
			return true;
		}
	}

	return true;
}

void browser::on_window_destroy(gtk::window& w) {
	gtk_main_quit();
}

browser_tab::browser_tab(const char *uri) :
	wv{gtk::make_unique<webkit::web_view>(uri)},
	tab_title{gtk::make_unique<gtk::label>("New tab")} {

	tab_title->set_can_focus(false);
	tab_title->set_hexpand(true);
	tab_title->set_ellipsize(PANGO_ELLIPSIZE_END);
	tab_title->set_size_request(50, -1);
}

void browser::on_web_view_load_changed(webkit::web_view& wv, WebKitLoadEvent load_event) {
	GTlsCertificate *certificate = nullptr;
	GTlsCertificateFlags errors{};

	switch (load_event) {
	case WEBKIT_LOAD_STARTED:
		break;

	case WEBKIT_LOAD_REDIRECTED:
		break;

	case WEBKIT_LOAD_COMMITTED:
		if (wv.get_tls_info(certificate, errors)) {
			// TODO: Display certificate details.
		} else {
			// TODO: Clear any displayed HTTPS details.
		}
		break;

	case WEBKIT_LOAD_FINISHED:
		break;
	}
}

void browser::on_web_view_notify_title(webkit::web_view& wv, GParamSpec& param_spec) {
	auto title = wv.get_title();
	if (visable_tab.web_view == &wv) {
		window->set_title(title);
		tabs[visable_tab.tab_index].tab_title->set_text(title);
		return;
	}

	// If the notified webview is not the currently-shown tab, we must
	// search for the correct tab title to modify.
	for (auto& tab : tabs) {
		if (tab.wv.get() == &wv) {
			tab.tab_title->set_text(title);
			break;
		}
	}
}

int browser::open_new_tab(const char *uri) {
	tabs.emplace_back(uri);
	auto& tab = tabs.back();
	auto& wv = *tab.wv;

	auto tab_content = gtk::box::create();
	tab_content->set_can_focus(false);
	tab_content->add(*tab.tab_title);
	tab_content->add(*tab.tab_close);

	wv.show_all();
	tab_content->show_all();
	const auto n = nb->append_page(wv, *tab_content);
	nb->set_tab_reorderable(wv, true);

	tab.tab_close->connect_clicked(*this, on_tab_close_clicked);
	wv.connect_notify_title(*this, on_web_view_notify_title);

	return n;
}

void browser::show_window() {
	window->show();
}

void browser::on_tab_close_clicked(gtk::button& tab_close) {
	for (auto it = tabs.begin(), ite = tabs.end(); it != ite; ++it) {
		// Compare using pointer equality.
		if (it->tab_close->ptr() != &tab_close) {
			continue;
		}
		auto removed_index = std::distance(tabs.begin(), it);
		tabs.erase(it);

		if (tabs.size() == 0) {
			window->destroy();
			break;
		}

		// If the removed tab had an index smaller than the visable tab,
		// the visable tab index must be decremented.
		auto prev_index = visable_tab.tab_index;
		if (removed_index < prev_index) {
			visable_tab.tab_index = prev_index - 1;
		}

		break;
	}
}

void browser::on_back_button_clicked(gtk::button& back) {
	visable_tab.web_view->go_back();
}

void browser::on_fwd_button_clicked(gtk::button& fwd) {
	visable_tab.web_view->go_forward();
}

void browser::on_back_forward_list_changed(WebKitBackForwardList& bfl,
	WebKitBackForwardListItem&, gpointer) {

	if (visable_tab.bfl == &bfl) {
		update_histnav(*visable_tab.web_view);
	}
}

void browser::on_web_view_notify_uri(webkit::web_view& web_view, GParamSpec& param_spec) {
	nav_entry->set_uri(web_view.get_uri());
}

static void on_nav_entry_refresh_clicked(uri_entry *entry, webkit::web_view *web_view) {
	web_view->reload();
	web_view->grab_focus();
}

void browser::on_notebook_page_reordered(gtk::notebook& notebook, gtk::widget& child,
	unsigned int new_idx) {

	// NOTE: This only works when reordering the current visable tab.
	// However, this appears to be a safe assumption since, at least
	// when using the mouse to drag and drop tabs (which is the only
	// method we have of reordering them), the tab being reordered
	// is always focused first.
	auto old_idx = visable_tab.tab_index;
	auto tmp = std::move(tabs[old_idx]);
	if (old_idx < new_idx) {
		// Moving the range [old_idx+1, new_idx+1) to [old_idx, new_idx).
		std::move(tabs.begin()+old_idx+1, tabs.begin()+new_idx+1,
			tabs.begin()+old_idx);
	} else {
		// Moving the range [new_idx, old_idx) to [new_idx+1, old_idx+1).
		std::move_backward(tabs.begin()+new_idx, tabs.begin()+old_idx,
			tabs.begin()+old_idx+1);
	}
	tabs[new_idx] = std::move(tmp);
	visable_tab.tab_index = new_idx;
}

void browser::show_webview(unsigned int page_num, webkit::web_view& wv) {
	visable_tab = {page_num, wv};

	// Update navbar/titlebar with the current state of the webview being
	// shown.
	auto c_title = webkit_web_view_get_title(wv.ptr());
	window->set_title(c_title ? c_title : "volo");
	update_histnav(wv);
	auto uri = wv.get_uri();
	nav_entry->set_uri(uri);

	page_signals = { {
		back->connect_clicked(*this, on_back_button_clicked),
		fwd->connect_clicked(*this, on_fwd_button_clicked),
		wv.connect_back_forward_list_changed(*this, on_back_forward_list_changed),
		wv.connect_notify_uri(*this, on_web_view_notify_uri),
		wv.connect_load_changed(*this, on_web_view_load_changed),
		nav_entry->connect_refresh_clicked(wv, ::on_nav_entry_refresh_clicked),
		nb->connect_page_reordered(*this, on_notebook_page_reordered),
	} };

	// Grab URI entry focus if the shown tab is blank.
	//
	// TODO: If this webview is being shown by clicking another notebook
	// tab, grabbing the entry focus has no effect.
	if (!strcmp(uri, "")) {
		nav_entry->grab_focus();
	} else {
		wv.grab_focus();
	}
}

void browser::update_histnav(webkit::web_view& wv) {
	back->set_sensitive(wv.can_go_back());
	fwd->set_sensitive(wv.can_go_forward());
}

void browser::switch_page(unsigned int page_num) {
	// Disconnect previous web_view's signals before showing and connecting
	// the new web_view.
	for (auto& sig : page_signals) {
		sig.disconnect();
	}

	show_webview(page_num, *tabs.at(page_num).wv);
}

int main(int argc, char **argv) {
	gtk_init(&argc, &argv);

	auto web_cxt = webkit::web_context::get_default();
	web_cxt->set_process_model(WEBKIT_PROCESS_MODEL_MULTIPLE_SECONDARY_PROCESSES);

	auto b = browser{};
	b.show_window();
	gtk_main();
}
<|endoftext|>