<commit_before>/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* $Source: src/import/chips/p9/procedures/hwp/memory/lib/phy/adr32s.C $  */
/*                                                                        */
/* OpenPOWER HostBoot Project                                             */
/*                                                                        */
/* Contributors Listed Below - COPYRIGHT 2016,2019                        */
/* [+] International Business Machines Corp.                              */
/*                                                                        */
/*                                                                        */
/* 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.                         */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */

///
/// @file adr32s.C
/// @brief Subroutines for the PHY ADR32S registers
///
// *HWP HWP Owner: Brian Silver <bsilver@us.ibm.com>
// *HWP HWP Backup: Andre Marin <aamarin@us.ibm.com>
// *HWP Team: Memory
// *HWP Level: 2
// *HWP Consumed by: FSP:HB

#include <fapi2.H>
#include <lib/phy/adr32s.H>
#include <lib/utils/find.H>

using fapi2::TARGET_TYPE_MCA;
using fapi2::TARGET_TYPE_SYSTEM;
using fapi2::FAPI2_RC_SUCCESS;

namespace mss
{

// Definition of the ADR32S DLL Config registers
const std::vector<uint64_t> adr32sTraits<fapi2::TARGET_TYPE_MCA>::DLL_CNFG_REG =
{
    MCA_DDRPHY_ADR_DLL_CNTL_P0_ADR32S0,
    MCA_DDRPHY_ADR_DLL_CNTL_P0_ADR32S1
};

// Definition of the ADR32S output driver registers
const std::vector<uint64_t> adr32sTraits<fapi2::TARGET_TYPE_MCA>::OUTPUT_DRIVER_REG =
{
    MCA_DDRPHY_ADR_OUTPUT_FORCE_ATEST_CNTL_P0_ADR32S0,
    MCA_DDRPHY_ADR_OUTPUT_FORCE_ATEST_CNTL_P0_ADR32S1,
};

// Definition of the ADR32S duty cycle distortion registers
const std::vector<uint64_t> adr32sTraits<fapi2::TARGET_TYPE_MCA>::DUTY_CYCLE_DISTORTION_REG =
{
    MCA_DDRPHY_ADR_DCD_CONTROL_P0_ADR32S0,
    MCA_DDRPHY_ADR_DCD_CONTROL_P0_ADR32S1,
    MCA_DDRPHY_DP16_DCD_CONTROL0_P0_0,
    MCA_DDRPHY_DP16_DCD_CONTROL1_P0_0,
    MCA_DDRPHY_DP16_DCD_CONTROL0_P0_1,
    MCA_DDRPHY_DP16_DCD_CONTROL1_P0_1,
    MCA_DDRPHY_DP16_DCD_CONTROL0_P0_2,
    MCA_DDRPHY_DP16_DCD_CONTROL1_P0_2,
    MCA_DDRPHY_DP16_DCD_CONTROL0_P0_3,
    MCA_DDRPHY_DP16_DCD_CONTROL1_P0_3,
    MCA_DDRPHY_DP16_DCD_CONTROL0_P0_4,
    MCA_DDRPHY_DP16_DCD_CONTROL1_P0_4,
};

// Definition of the ADR32S write clock static offset registers
const std::vector<uint64_t> adr32sTraits<fapi2::TARGET_TYPE_MCA>::PR_STATIC_OFFSET_REG =
{
    MCA_DDRPHY_ADR_MCCLK_WRCLK_PR_STATIC_OFFSET_P0_ADR32S0,
    MCA_DDRPHY_ADR_MCCLK_WRCLK_PR_STATIC_OFFSET_P0_ADR32S1,
};

namespace adr32s
{

///
/// @brief Helper to iterate over ADR32S 'sides' when performing DCD cal
/// @param[in] i_target the MCA to iterate for
/// @param[in] i_reg the register (ADR0 or ADR1's register)
/// @param[in] i_seed the seed value for the adjuster
/// @param[in] i_side bool; true if this is side a, false for side b
/// @param[out] o_value the value of the adjuster when the compare bit changes state
/// @return FAPI2_RC_SUCCESS iff ok
///
fapi2::ReturnCode dcd_cal_helper( const fapi2::Target<TARGET_TYPE_MCA>& i_target,
                                  const uint64_t i_reg,
                                  const uint64_t i_seed,
                                  const bool i_side,
                                  uint64_t& o_value )
{
    typedef adr32sTraits<TARGET_TYPE_MCA> TT;

    constexpr uint64_t l_dcd_adjust_overflow =  0b1111111;
    constexpr uint64_t l_dcd_adjust_underflow = 0b0000000;

    // If compare out is 0, we tick up ...
    // Signed value which helps us increment or decrement the adjustment
    int64_t l_tick = 1;
    // ... and we expect a transition to 1
    bool l_expected = 1;
    // ... and we don't expect to overflow
    uint64_t l_overrun = l_dcd_adjust_overflow;

    fapi2::buffer<uint64_t> l_read;
    uint64_t l_current_adjust = i_seed;
    size_t l_iter = 0;

    // More or less mirror's Bialas's logic for DD2 so we can kind of try out the algorithm on DD1.

    FAPI_INF("enter dcd_cal_helper %s 0x%016lx seed: 0x%016lx side: %d",
             mss::c_str(i_target), i_reg, i_seed, i_side );

    // Prime the system with the starting values. We don't need to read/modify/write here as we're resetting
    // the world and saving a scom here is likely benificial. Clear the compare out bit as writing it set
    // will cause the PHY to throw a parity error
    l_read.insertFromRight<TT::DCD_CONTROL_DLL_ADJUST, TT::DCD_CONTROL_DLL_ADJUST_LEN>(l_current_adjust);
    l_read.writeBit<TT::DCD_CONTROL_DLL_ITER_A>(i_side);
    l_read.clearBit<TT::DCD_CONTROL_DLL_COMPARE_OUT>();
    FAPI_TRY( mss::putScom(i_target, i_reg, l_read) );

    // Algorithm waits 128ck (~100ns) between steps. However, scom takes so long (and rippling up thru
    // the platforms takes time too) that there's no need to actually delay - plenty of time has elapsed.

    // Read. Note the register is volatile in that l_read which we just wrote isn't what we'll read
    // as the distortion logic will take the seeded adjustment value and give us information on the next
    // read (so don't get cute and remove this getScom.)
    FAPI_TRY( mss::getScom(i_target, i_reg, l_read) );

    // Based on the 'direction' we're going, we have some values to setup. We setup the bit == 0 case
    // when we initialized these variables above.
    if (l_read.getBit<TT::DCD_CONTROL_DLL_COMPARE_OUT>() != 0)
    {
        // If compare out is 1, we tick down ...
        l_tick = -1;

        // ... and we expect a transition to 0
        l_expected = 0;

        // ... and we don't expect to underflow
        l_overrun = l_dcd_adjust_underflow;
    }

    do
    {
        l_iter += 1;
        bool l_current_compare = l_read.getBit<TT::DCD_CONTROL_DLL_COMPARE_OUT>();

        FAPI_INF("dcd_cal_helper: iter %d tick: %d expected: %d overrun: 0x%x out: %d adj: 0x%x",
                 l_iter, l_tick, l_expected, l_overrun, l_current_compare, l_current_adjust);

        if (l_current_compare == l_expected)
        {
            break;
        }

        // If we're here we're not done, so just adjust and try again. Clear the compare out bit, it must
        // always be 0 or the PHY will parity error
        l_read.insertFromRight<TT::DCD_CONTROL_DLL_ADJUST, TT::DCD_CONTROL_DLL_ADJUST_LEN>(l_current_adjust + l_tick);
        l_read.clearBit<TT::DCD_CONTROL_DLL_COMPARE_OUT>();
        FAPI_TRY( mss::putScom(i_target, i_reg, l_read) );

        // Wait 128ck (~100ns) ...

        FAPI_TRY( mss::getScom(i_target, i_reg, l_read) );

        l_read.extractToRight<TT::DCD_CONTROL_DLL_ADJUST, TT::DCD_CONTROL_DLL_ADJUST_LEN>(l_current_adjust);

    }
    while (l_current_adjust != l_overrun);

    FAPI_ASSERT( l_current_adjust != l_overrun,
                 fapi2::MSS_DUTY_CLOCK_DISTORTION_CAL_FAILED()
                 .set_TARGET(i_target)
                 .set_CURRENT_ADJUST(l_current_adjust)
                 .set_SIDE(i_side)
                 .set_REGISTER(i_reg)
                 .set_REGISTER_VALUE(l_read),
                 "Failed ADR DCD for %s 0x%016lx", mss::c_str(i_target), i_reg );

    // If we're here, we were done and there were no errors. So we can return back the current adjust value
    // as the output/result of our operation
    o_value = l_current_adjust;
    FAPI_INF("side: %d final adjust value: 0x%x (0x%x)", i_side, o_value, l_current_adjust);

    return FAPI2_RC_SUCCESS;

fapi_try_exit:
    return fapi2::current_err;
}

///
/// @brief Perform ADR DCD calibration - Nimbus Only
/// @param[in] i_target the MCBIST (controler) to perform calibration on
/// @return FAPI2_RC_SUCCESS iff ok
///
fapi2::ReturnCode duty_cycle_distortion_calibration( const fapi2::Target<fapi2::TARGET_TYPE_MCBIST>& i_target )
{
    typedef adr32sTraits<TARGET_TYPE_MCA> TT;

    const auto l_mca = mss::find_targets<TARGET_TYPE_MCA>(i_target);
    fapi2::buffer<uint64_t> l_read;
    uint8_t is_sim = 0;

    FAPI_TRY( FAPI_ATTR_GET(fapi2::ATTR_IS_SIMULATION, fapi2::Target<TARGET_TYPE_SYSTEM>(), is_sim) );

    // Nothing works here in cycle sim ...
    if (is_sim)
    {
        return FAPI2_RC_SUCCESS;
    }

    if (l_mca.size() == 0)
    {
        FAPI_INF("No MCA, skipping duty cycle distortion calibration");
        return FAPI2_RC_SUCCESS;
    }

    // Do a quick check to make sure this chip doesn't have the DCD logic built in (e.g., DD1 Nimbus)
    // TODO RTC:159687 For DD2 all we need to do is kick off the h/w cal and wait. We can check any ADR_DCD
    // register, they all should reflect the inclusion of the DCD logic.

    FAPI_TRY( mss::getScom(l_mca[0], TT::DUTY_CYCLE_DISTORTION_REG[0], l_read) );

    if (l_read.getBit<TT::DCD_CONTROL_DLL_CORRECT_EN>() == 1)
    {
        FAPI_ERR("seeing ADR DCD algorithm is in the logic but we didn't code it?");
        fapi2::Assert(false);
    }

    // We must calibrate each of our ports. Each has 2 ADR units and each unit needs it's A-side and B-side calibrated.
    for (const auto& p : mss::find_targets<TARGET_TYPE_MCA>(i_target))
    {
        uint64_t l_seed = TT::DCD_ADJUST_DEFAULT;

        for (const auto& r : TT::DUTY_CYCLE_DISTORTION_REG)
        {
            uint64_t l_a_side_value = 0;
            uint64_t l_b_side_value = 0;

            FAPI_TRY( dcd_cal_helper(p, r, l_seed, true, l_a_side_value) );

            // We want to seed the other side (and each subsequent port) with the
            // value found in the pervious iteration as that will likely reduce the number
            // of iterations to find the transition. We back up one so that if we're on
            // a transition, we don't 'bounce' from a 1 to a 0. This will give us a good
            // transition if the a-side value is really to be the b-side value too.
            FAPI_TRY( dcd_cal_helper(p, r, l_a_side_value - 1, false, l_b_side_value) );

            // The final value is the average of the a-side and b-side values.
            l_seed = (l_a_side_value + l_b_side_value) / 2;

            FAPI_INF("average for both sides 0x%02x", l_seed);

            // Note this writes all the other values to 0's which is OK for DD1
            FAPI_TRY( mss::putScom(p, r, l_seed) );
        }
    }

fapi_try_exit:
    return fapi2::current_err;
}

} // close namespace adrs32

} // close namespace mss
<commit_msg>Change accesses to IS_SIM to use mss accessor<commit_after>/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* $Source: src/import/chips/p9/procedures/hwp/memory/lib/phy/adr32s.C $  */
/*                                                                        */
/* OpenPOWER HostBoot Project                                             */
/*                                                                        */
/* Contributors Listed Below - COPYRIGHT 2016,2019                        */
/* [+] International Business Machines Corp.                              */
/*                                                                        */
/*                                                                        */
/* 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.                         */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */

///
/// @file adr32s.C
/// @brief Subroutines for the PHY ADR32S registers
///
// *HWP HWP Owner: Brian Silver <bsilver@us.ibm.com>
// *HWP HWP Backup: Andre Marin <aamarin@us.ibm.com>
// *HWP Team: Memory
// *HWP Level: 2
// *HWP Consumed by: FSP:HB

#include <fapi2.H>
#include <lib/phy/adr32s.H>
#include <lib/utils/find.H>

using fapi2::TARGET_TYPE_MCA;
using fapi2::TARGET_TYPE_SYSTEM;
using fapi2::FAPI2_RC_SUCCESS;

namespace mss
{

// Definition of the ADR32S DLL Config registers
const std::vector<uint64_t> adr32sTraits<fapi2::TARGET_TYPE_MCA>::DLL_CNFG_REG =
{
    MCA_DDRPHY_ADR_DLL_CNTL_P0_ADR32S0,
    MCA_DDRPHY_ADR_DLL_CNTL_P0_ADR32S1
};

// Definition of the ADR32S output driver registers
const std::vector<uint64_t> adr32sTraits<fapi2::TARGET_TYPE_MCA>::OUTPUT_DRIVER_REG =
{
    MCA_DDRPHY_ADR_OUTPUT_FORCE_ATEST_CNTL_P0_ADR32S0,
    MCA_DDRPHY_ADR_OUTPUT_FORCE_ATEST_CNTL_P0_ADR32S1,
};

// Definition of the ADR32S duty cycle distortion registers
const std::vector<uint64_t> adr32sTraits<fapi2::TARGET_TYPE_MCA>::DUTY_CYCLE_DISTORTION_REG =
{
    MCA_DDRPHY_ADR_DCD_CONTROL_P0_ADR32S0,
    MCA_DDRPHY_ADR_DCD_CONTROL_P0_ADR32S1,
    MCA_DDRPHY_DP16_DCD_CONTROL0_P0_0,
    MCA_DDRPHY_DP16_DCD_CONTROL1_P0_0,
    MCA_DDRPHY_DP16_DCD_CONTROL0_P0_1,
    MCA_DDRPHY_DP16_DCD_CONTROL1_P0_1,
    MCA_DDRPHY_DP16_DCD_CONTROL0_P0_2,
    MCA_DDRPHY_DP16_DCD_CONTROL1_P0_2,
    MCA_DDRPHY_DP16_DCD_CONTROL0_P0_3,
    MCA_DDRPHY_DP16_DCD_CONTROL1_P0_3,
    MCA_DDRPHY_DP16_DCD_CONTROL0_P0_4,
    MCA_DDRPHY_DP16_DCD_CONTROL1_P0_4,
};

// Definition of the ADR32S write clock static offset registers
const std::vector<uint64_t> adr32sTraits<fapi2::TARGET_TYPE_MCA>::PR_STATIC_OFFSET_REG =
{
    MCA_DDRPHY_ADR_MCCLK_WRCLK_PR_STATIC_OFFSET_P0_ADR32S0,
    MCA_DDRPHY_ADR_MCCLK_WRCLK_PR_STATIC_OFFSET_P0_ADR32S1,
};

namespace adr32s
{

///
/// @brief Helper to iterate over ADR32S 'sides' when performing DCD cal
/// @param[in] i_target the MCA to iterate for
/// @param[in] i_reg the register (ADR0 or ADR1's register)
/// @param[in] i_seed the seed value for the adjuster
/// @param[in] i_side bool; true if this is side a, false for side b
/// @param[out] o_value the value of the adjuster when the compare bit changes state
/// @return FAPI2_RC_SUCCESS iff ok
///
fapi2::ReturnCode dcd_cal_helper( const fapi2::Target<TARGET_TYPE_MCA>& i_target,
                                  const uint64_t i_reg,
                                  const uint64_t i_seed,
                                  const bool i_side,
                                  uint64_t& o_value )
{
    typedef adr32sTraits<TARGET_TYPE_MCA> TT;

    constexpr uint64_t l_dcd_adjust_overflow =  0b1111111;
    constexpr uint64_t l_dcd_adjust_underflow = 0b0000000;

    // If compare out is 0, we tick up ...
    // Signed value which helps us increment or decrement the adjustment
    int64_t l_tick = 1;
    // ... and we expect a transition to 1
    bool l_expected = 1;
    // ... and we don't expect to overflow
    uint64_t l_overrun = l_dcd_adjust_overflow;

    fapi2::buffer<uint64_t> l_read;
    uint64_t l_current_adjust = i_seed;
    size_t l_iter = 0;

    // More or less mirror's Bialas's logic for DD2 so we can kind of try out the algorithm on DD1.

    FAPI_INF("enter dcd_cal_helper %s 0x%016lx seed: 0x%016lx side: %d",
             mss::c_str(i_target), i_reg, i_seed, i_side );

    // Prime the system with the starting values. We don't need to read/modify/write here as we're resetting
    // the world and saving a scom here is likely benificial. Clear the compare out bit as writing it set
    // will cause the PHY to throw a parity error
    l_read.insertFromRight<TT::DCD_CONTROL_DLL_ADJUST, TT::DCD_CONTROL_DLL_ADJUST_LEN>(l_current_adjust);
    l_read.writeBit<TT::DCD_CONTROL_DLL_ITER_A>(i_side);
    l_read.clearBit<TT::DCD_CONTROL_DLL_COMPARE_OUT>();
    FAPI_TRY( mss::putScom(i_target, i_reg, l_read) );

    // Algorithm waits 128ck (~100ns) between steps. However, scom takes so long (and rippling up thru
    // the platforms takes time too) that there's no need to actually delay - plenty of time has elapsed.

    // Read. Note the register is volatile in that l_read which we just wrote isn't what we'll read
    // as the distortion logic will take the seeded adjustment value and give us information on the next
    // read (so don't get cute and remove this getScom.)
    FAPI_TRY( mss::getScom(i_target, i_reg, l_read) );

    // Based on the 'direction' we're going, we have some values to setup. We setup the bit == 0 case
    // when we initialized these variables above.
    if (l_read.getBit<TT::DCD_CONTROL_DLL_COMPARE_OUT>() != 0)
    {
        // If compare out is 1, we tick down ...
        l_tick = -1;

        // ... and we expect a transition to 0
        l_expected = 0;

        // ... and we don't expect to underflow
        l_overrun = l_dcd_adjust_underflow;
    }

    do
    {
        l_iter += 1;
        bool l_current_compare = l_read.getBit<TT::DCD_CONTROL_DLL_COMPARE_OUT>();

        FAPI_INF("dcd_cal_helper: iter %d tick: %d expected: %d overrun: 0x%x out: %d adj: 0x%x",
                 l_iter, l_tick, l_expected, l_overrun, l_current_compare, l_current_adjust);

        if (l_current_compare == l_expected)
        {
            break;
        }

        // If we're here we're not done, so just adjust and try again. Clear the compare out bit, it must
        // always be 0 or the PHY will parity error
        l_read.insertFromRight<TT::DCD_CONTROL_DLL_ADJUST, TT::DCD_CONTROL_DLL_ADJUST_LEN>(l_current_adjust + l_tick);
        l_read.clearBit<TT::DCD_CONTROL_DLL_COMPARE_OUT>();
        FAPI_TRY( mss::putScom(i_target, i_reg, l_read) );

        // Wait 128ck (~100ns) ...

        FAPI_TRY( mss::getScom(i_target, i_reg, l_read) );

        l_read.extractToRight<TT::DCD_CONTROL_DLL_ADJUST, TT::DCD_CONTROL_DLL_ADJUST_LEN>(l_current_adjust);

    }
    while (l_current_adjust != l_overrun);

    FAPI_ASSERT( l_current_adjust != l_overrun,
                 fapi2::MSS_DUTY_CLOCK_DISTORTION_CAL_FAILED()
                 .set_TARGET(i_target)
                 .set_CURRENT_ADJUST(l_current_adjust)
                 .set_SIDE(i_side)
                 .set_REGISTER(i_reg)
                 .set_REGISTER_VALUE(l_read),
                 "Failed ADR DCD for %s 0x%016lx", mss::c_str(i_target), i_reg );

    // If we're here, we were done and there were no errors. So we can return back the current adjust value
    // as the output/result of our operation
    o_value = l_current_adjust;
    FAPI_INF("side: %d final adjust value: 0x%x (0x%x)", i_side, o_value, l_current_adjust);

    return FAPI2_RC_SUCCESS;

fapi_try_exit:
    return fapi2::current_err;
}

///
/// @brief Perform ADR DCD calibration - Nimbus Only
/// @param[in] i_target the MCBIST (controler) to perform calibration on
/// @return FAPI2_RC_SUCCESS iff ok
///
fapi2::ReturnCode duty_cycle_distortion_calibration( const fapi2::Target<fapi2::TARGET_TYPE_MCBIST>& i_target )
{
    typedef adr32sTraits<TARGET_TYPE_MCA> TT;

    const auto l_mca = mss::find_targets<TARGET_TYPE_MCA>(i_target);
    fapi2::buffer<uint64_t> l_read;
    uint8_t l_sim = 0;

    FAPI_TRY( mss::is_simulation( l_sim) );

    // Nothing works here in cycle sim ...
    if (l_sim)
    {
        return FAPI2_RC_SUCCESS;
    }

    if (l_mca.size() == 0)
    {
        FAPI_INF("No MCA, skipping duty cycle distortion calibration");
        return FAPI2_RC_SUCCESS;
    }

    // Do a quick check to make sure this chip doesn't have the DCD logic built in (e.g., DD1 Nimbus)
    // TODO RTC:159687 For DD2 all we need to do is kick off the h/w cal and wait. We can check any ADR_DCD
    // register, they all should reflect the inclusion of the DCD logic.

    FAPI_TRY( mss::getScom(l_mca[0], TT::DUTY_CYCLE_DISTORTION_REG[0], l_read) );

    if (l_read.getBit<TT::DCD_CONTROL_DLL_CORRECT_EN>() == 1)
    {
        FAPI_ERR("seeing ADR DCD algorithm is in the logic but we didn't code it?");
        fapi2::Assert(false);
    }

    // We must calibrate each of our ports. Each has 2 ADR units and each unit needs it's A-side and B-side calibrated.
    for (const auto& p : mss::find_targets<TARGET_TYPE_MCA>(i_target))
    {
        uint64_t l_seed = TT::DCD_ADJUST_DEFAULT;

        for (const auto& r : TT::DUTY_CYCLE_DISTORTION_REG)
        {
            uint64_t l_a_side_value = 0;
            uint64_t l_b_side_value = 0;

            FAPI_TRY( dcd_cal_helper(p, r, l_seed, true, l_a_side_value) );

            // We want to seed the other side (and each subsequent port) with the
            // value found in the pervious iteration as that will likely reduce the number
            // of iterations to find the transition. We back up one so that if we're on
            // a transition, we don't 'bounce' from a 1 to a 0. This will give us a good
            // transition if the a-side value is really to be the b-side value too.
            FAPI_TRY( dcd_cal_helper(p, r, l_a_side_value - 1, false, l_b_side_value) );

            // The final value is the average of the a-side and b-side values.
            l_seed = (l_a_side_value + l_b_side_value) / 2;

            FAPI_INF("average for both sides 0x%02x", l_seed);

            // Note this writes all the other values to 0's which is OK for DD1
            FAPI_TRY( mss::putScom(p, r, l_seed) );
        }
    }

fapi_try_exit:
    return fapi2::current_err;
}

} // close namespace adrs32

} // close namespace mss
<|endoftext|>


<commit_before>/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2008 by Sun Microsystems, Inc.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * $RCSfile: SlsPageObjectViewObjectContact.hxx,v $
 * $Revision: 1.13 $
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

#ifndef SD_SLIDESORTER_PAGE_OBJECT_VIEW_OBJECT_CONTACT_HXX
#define SD_SLIDESORTER_PAGE_OBJECT_VIEW_OBJECT_CONTACT_HXX

#include "model/SlsSharedPageDescriptor.hxx"
#include <svx/sdr/contact/viewobjectcontact.hxx>
#include <vcl/bitmapex.hxx>
#include <vcl/image.hxx>
#include <sfx2/viewfrm.hxx>

#include <memory>
#include <boost/shared_ptr.hpp>

class SdrPage;

namespace sdr { namespace contact {
class DisplayInfo;
} }

namespace sd { namespace slidesorter { namespace cache {
class PageCache;
} } }

namespace sd { namespace slidesorter { namespace controller {
class Properties;
} } }

namespace sd { namespace slidesorter { namespace view {

class PageNotificationObjectContact;
class SlideSorterView;

/** This object-view-contact of page objects maintains a preview bitmap for
    the page to speed up redraws of the same.  It does so by colaborating
    with a cache of bitmaps (see ../cache).

    This class started as a subclass of ::sdr::contact::VOCBitmapBuffer but
    the class specific code had to be copied and adapted.  Therefore, it is
    now derived directly from ::sdr::contact::ViewObjectContact.
*/
class PageObjectViewObjectContact
    : public ::sdr::contact::ViewObjectContact
{
public:
    /** Create a new view-object-contact object for the given contact
        objects of both model and view.
        @param pCache
            The caller should provide a pointer to a preview cache, if
            available.  If given then the cache is used to control when to
            create a preview and to store it.  If NULL is given then the
            preview is created every time when requested.
    */
    PageObjectViewObjectContact (
        ::sdr::contact::ObjectContact& rObjectContact,
        ::sdr::contact::ViewContact& rViewContact,
        const ::boost::shared_ptr<cache::PageCache>& rpCache,
        const ::boost::shared_ptr<controller::Properties>& rpProperties);
    virtual ~PageObjectViewObjectContact (void);

    /** This method is primarily for releasing the current preview cache (by
        providing a NULL pointer.)
    */
    void SetCache (const ::boost::shared_ptr<cache::PageCache>& rpCache);

    virtual void PaintObject (::sdr::contact::DisplayInfo& rDisplayInfo);

    /** The object is about to be deleted.  Tell the cache that it has not
        to rendere a preview anymore and absolutely must not call back.
    */
    virtual void PrepareDelete (void);

    /** Return the page that is painted by this object.
    */
    const SdrPage* GetPage (void) const;

    /** This fallback method is called when no preview cache is available.
        It creates a preview for the page.
    */
    BitmapEx CreatePreview (OutputDevice& rDevice) const;

    /** This paint method calls the more specialized paint methods
        that paint single aspects of the content.
    */
    virtual void PaintContent (OutputDevice& rDevice);

    /** Return the page descriptor of the slide sorter model that is
        associated with the same page object as this contact object is.
    */
    model::SharedPageDescriptor GetPageDescriptor (void) const;

    /** Return the device independent part of the page border.  This border
        is returned in pixel coordinates.  Note that the device dependent
        parts--for the page number and the page title--have to be added.
        This is done by CalculatePageModelBorder().
    */
    static SvBorder GetPagePixelBorder (void);

    /** Return the border widths in the screen coordinate system of the
        border arround the page object.  The border contains frames for
        selection, focus, the page name and number, and the indicator for
        the page transition.
        @param pDevice
            The output device is used to convert pixel coordinates into
            model coordinates.  When NULL is given then the device dependent
            part is not re-calculated but taken from an earlier calculation
            or from the default values.
        @param nPageCount
            The total number of pages is used to determine the width of the
            box that contains the page number.
    */
    static SvBorder CalculatePageModelBorder (
        OutputDevice* pDevice,
        int nPageCount);

    /** Calculate the size of the page number area so that all page numbers
        including the given number fit in.  Because this is device dependent
        we need the device as parameter.  The result is returned and stored
        in maPageNumberAreaPixelSize so that it can be used later without
        access to the device or page count.
    */
    static Size CalculatePageNumberAreaModelSize (
        OutputDevice* pDevice,
        int nPageCount);

    /** Paint all parts of the frame arround a preview.  These are the
        border, the selection frame, the focus rectangle, and the mouse over
        effect.
        @param rDevice
            The output device to paint on.
        @param bShowMouseOverEffect
            This flag specifies whether to paint the mouse over effect or not.
    */
    void PaintFrame (
        OutputDevice& rDevice,
        bool bShowMouseOverEffect = false) const;

    /** Paint a mouse over effect.
        @param bVisible
            When bVisible is <FALSE/> then paint the area of the mouse over
            effect in the background color, i.e. erase it.
    */
    void PaintMouseOverEffect (OutputDevice& rDevice, bool bVisible) const;

    enum BoundingBoxType {
        // This is the outer bounding box that includes the preview, page
        // number, title.
        PageObjectBoundingBox,
        // Bounding box of the actual preview.
        PreviewBoundingBox,
        // Bounding box of the mouse indicator indicator frame.
        MouseOverIndicatorBoundingBox,
        // Bounding box of the focus indicator frame.
        FocusIndicatorBoundingBox,
        // Bounding box of the selection indicator frame.
        SelectionIndicatorBoundingBox,
        // Bounding box of the page number.
        PageNumberBoundingBox,
        // Bounding box of the pane name.
        NameBoundingBox,
        FadeEffectIndicatorBoundingBox
    };
    enum CoordinateSystem { ModelCoordinateSystem, PixelCoordinateSystem };
    /** Return the bounding box of the page object or one of its graphical
        parts.
        @param rDevice
            This device is used to translate between model and window
            coordinates.
        @param eType
            The part of the page object for which to return the bounding
            box.
        @param eCoodinateSystem
            The bounding box can be returned in model and in pixel
            (window) coordinates.
    */
    Rectangle GetBoundingBox (
        OutputDevice& rDevice,
        BoundingBoxType eType,
        CoordinateSystem eCoordinateSystem) const;

    /** This convenience method paints a dotted or dashed rectangle.  The
        length of dots or dashes is indepent of zoom factor or map mode.
    */
    enum DashType { Dotted, Dashed };
    static void PaintDottedRectangle (
        OutputDevice& rDevice,
        const Rectangle& rRectangle,
        const DashType eDashType = Dotted);

    enum ColorSpec { CS_SELECTION, CS_BACKGROUND, CS_WINDOW, CS_TEXT };
    /** Return a color for one of the screen elements in ColorSpec.  For
        Background the background color is updated when
        mbIsBackgroundColorUpdatePending is <TRUE/>.
        @param rDevice
            Base colors are taken from the device.
        @param eSpec
            The type of color to return.
        @param nOpacity
            This parameter controls the blending between the background and
            the actual color.
    */
    Color GetColor (
        const OutputDevice& rDevice,
        const ColorSpec eSpec,
        const double nOpacity = 1.0) const;

private:
    /// Gap between border of page object and inside of selection rectangle.
    static const sal_Int32 mnSelectionIndicatorOffset;
    /// Thickness of the selection rectangle.
    static const sal_Int32 mnSelectionIndicatorThickness;
    /// Gap between border of page object and inside of focus rectangle.
    static const sal_Int32 mnFocusIndicatorOffset;
    /// Size of width and height of the fade effect indicator in pixels.
    static const sal_Int32 mnFadeEffectIndicatorSize;
    static const sal_Int32 mnFadeEffectIndicatorOffset;
    /// Gap between border of page object and number rectangle.
    static const sal_Int32 mnPageNumberOffset;
    /// Offset and thickness of the mouse over effect rectangle.
    static const sal_Int32 mnMouseOverEffectOffset;
    static const sal_Int32 mnMouseOverEffectThickness;

    model::SharedPageDescriptor mpPageDescriptor;

    /** This flag is set to <FALSE/> when PrepareDelete() is called to
        indicate that further calls made to it must not call outside.
    */
    bool mbIsValid;

    bool mbInPrepareDelete;

    /** Set this flag to <TRUE/> to update the background color on the next
        call to GetBackgroundColor().
    */
    mutable bool mbIsBackgroundColorUpdatePending;

    ::boost::shared_ptr<cache::PageCache> mpCache;

    ::std::auto_ptr<PageNotificationObjectContact> mpNotifier;

    ::boost::shared_ptr<controller::Properties> mpProperties;

    /** Do not use this member directly.  Use GetColor(Background) instead.
        That method determines the background color when
        mbIsBackgroundColorUpdatePending is <TRUE/>.
    */
    mutable Color maBackgroundColor;

    BitmapEx GetPreview (
        OutputDevice& rDevice,
        const Rectangle& rNewSizePixel);

    virtual void ActionChanged (void);

    /** Return the bounding box of where the page number is painted (when it
        is painted).
    */
    Rectangle GetPageNumberArea (OutputDevice& rDevice) const;

    void PaintBackground (OutputDevice& rDevice) const;

    /** Paint the preview bitmap.
    */
    void PaintPreview (OutputDevice& rDevice);

    /** Paint a border arround the page preview.
    */
    void PaintBorder (OutputDevice& rDevice) const;

    /** Paint the focus indicator for the specified page.
    */
    void PaintFocusIndicator (
        OutputDevice& rDevice,
        bool bEraseBackground) const;

    /** Paint the selection indicator when the page is currently selected.
        Otherwise the call is ignored.
    */
    void PaintSelectionIndicator (OutputDevice& rDevice) const;

    /** Paint the fade effect indicator which indicates whether a fade
        effect is currently associated with a page.
        @param rDescriptor
            The descriptor of the page for which to paint the fade effect
            indicator.
    */
    void PaintFadeEffectIndicator (OutputDevice& rDevice) const;

    /** Paint the name of the page to the bottom right of the page object.
    */
    void PaintPageName (OutputDevice& rDevice) const;

    /** Paint the number of the page to the upper left of the page object.
    */
    void PaintPageNumber (OutputDevice& rDevice) const;

    Color GetBackgroundColor (const OutputDevice& rDevice) const;
};

} } } // end of namespace ::sd::slidesorter::view

#endif
<commit_msg>INTEGRATION: CWS aw033 (1.10.188); FILE MERGED 2008/07/10 12:56:27 aw 1.10.188.12: #i39532# XOutputDevice removed, PrepareDelete removed 2008/05/27 15:18:02 aw 1.10.188.11: #i39532# changes DEV300 m12 resync corrections 2008/05/16 13:06:04 aw 1.10.188.10: adaptions after resync 2008/05/14 14:51:03 aw 1.10.188.9: RESYNC: (1.11-1.13); FILE MERGED 2008/03/17 14:34:20 cl 1.10.188.8: fixed merge conflict 2008/03/14 14:02:02 cl 1.10.188.7: RESYNC: (1.10-1.11); FILE MERGED 2008/01/29 10:34:21 aw 1.10.188.6: updated refresh for ActionChanged(), diverse removals 2008/01/22 12:16:41 aw 1.10.188.5: adaptions and 1st stripping 2007/10/22 10:26:50 aw 1.10.188.4: #i39523# joined #i82710# and adaptions to primitives 2007/07/06 13:46:12 aw 1.10.188.3: #i39532# moved from Primitive2DReference to Primitive2DSequence where possible to avoid extra-group primitive creations and deeper hierarchies as necessary 2006/11/28 11:13:41 aw 1.10.188.2: #i39532# 2006/11/09 17:04:09 aw 1.10.188.1: #i39532# Prepared PageObjectViewObjectContact for primitive usage, see comments there.<commit_after>/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2008 by Sun Microsystems, Inc.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * $RCSfile: SlsPageObjectViewObjectContact.hxx,v $
 * $Revision: 1.14 $
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

#ifndef SD_SLIDESORTER_PAGE_OBJECT_VIEW_OBJECT_CONTACT_HXX
#define SD_SLIDESORTER_PAGE_OBJECT_VIEW_OBJECT_CONTACT_HXX

#include <svx/sdr/contact/viewobjectcontactofpageobj.hxx>
#include "model/SlsSharedPageDescriptor.hxx"
#include <svx/sdr/contact/viewobjectcontact.hxx>
#include <vcl/bitmapex.hxx>
#include <vcl/image.hxx>
#include <sfx2/viewfrm.hxx>
#include <memory>
#include <boost/shared_ptr.hpp>

class SdrPage;

namespace sdr { namespace contact {
class DisplayInfo;
} }

namespace sd { namespace slidesorter { namespace cache {
class PageCache;
} } }

namespace sd { namespace slidesorter { namespace controller {
class Properties;
} } }

namespace sd { namespace slidesorter { namespace view {

class SlideSorterView;

/** This object-view-contact of page objects maintains a preview bitmap for
    the page to speed up redraws of the same.  It does so by colaborating
    with a cache of bitmaps (see ../cache).
*/

// needs to be derived from ViewObjectContactOfPageObj, else the calls to parent implementations
// would use ViewObjectContact and thus not enable e.g. the correct primitive creation
// for view-independent printer output
// changed: ViewObjectContact -> ViewObjectContactOfPageObj

class PageObjectViewObjectContact : public ::sdr::contact::ViewObjectContactOfPageObj
{
public:
    /** Create a new view-object-contact object for the given contact
        objects of both model and view.
        @param pCache
            The caller should provide a pointer to a preview cache, if
            available.  If given then the cache is used to control when to
            create a preview and to store it.  If NULL is given then the
            preview is created every time when requested.
    */
    PageObjectViewObjectContact (
        ::sdr::contact::ObjectContact& rObjectContact,
        ::sdr::contact::ViewContact& rViewContact,
        const ::boost::shared_ptr<cache::PageCache>& rpCache,
        const ::boost::shared_ptr<controller::Properties>& rpProperties);
    virtual ~PageObjectViewObjectContact (void);

    /** This method is primarily for releasing the current preview cache (by
        providing a NULL pointer.)
    */
    void SetCache (const ::boost::shared_ptr<cache::PageCache>& rpCache);

    /** Return the page that is painted by this object.
    */
    const SdrPage* GetPage (void) const;

    /** This fallback method is called when no preview cache is available.
        It creates a preview for the page.
    */
    BitmapEx CreatePreview (const sdr::contact::DisplayInfo& rDisplayInfo);

    /** Return the page descriptor of the slide sorter model that is
        associated with the same page object as this contact object is.
    */
    model::SharedPageDescriptor GetPageDescriptor (void) const;

    /** Return the device independent part of the page border.  This border
        is returned in pixel coordinates.  Note that the device dependent
        parts--for the page number and the page title--have to be added.
        This is done by CalculatePageModelBorder().
    */
    static SvBorder GetPagePixelBorder (void);

    /** Return the border widths in the screen coordinate system of the
        border arround the page object.  The border contains frames for
        selection, focus, the page name and number, and the indicator for
        the page transition.
        @param pDevice
            The output device is used to convert pixel coordinates into
            model coordinates.  When NULL is given then the device dependent
            part is not re-calculated but taken from an earlier calculation
            or from the default values.
        @param nPageCount
            The total number of pages is used to determine the width of the
            box that contains the page number.
    */
    static SvBorder CalculatePageModelBorder (
        OutputDevice* pDevice,
        int nPageCount);

    /** Calculate the size of the page number area so that all page numbers
        including the given number fit in.  Because this is device dependent
        we need the device as parameter.  The result is returned and stored
        in maPageNumberAreaPixelSize so that it can be used later without
        access to the device or page count.
    */
    static Size CalculatePageNumberAreaModelSize (
        OutputDevice* pDevice,
        int nPageCount);

    /** Paint a mouse over effect.
        @param bVisible
            When bVisible is <FALSE/> then paint the area of the mouse over
            effect in the background color, i.e. erase it.
    */
    void PaintMouseOverEffect (OutputDevice& rDevice, bool bVisible) const;

    enum BoundingBoxType {
        // This is the outer bounding box that includes the preview, page
        // number, title.
        PageObjectBoundingBox,
        // Bounding box of the actual preview.
        PreviewBoundingBox,
        // Bounding box of the mouse indicator indicator frame.
        MouseOverIndicatorBoundingBox,
        // Bounding box of the focus indicator frame.
        FocusIndicatorBoundingBox,
        // Bounding box of the selection indicator frame.
        SelectionIndicatorBoundingBox,
        // Bounding box of the page number.
        PageNumberBoundingBox,
        // Bounding box of the pane name.
        NameBoundingBox,
        FadeEffectIndicatorBoundingBox
    };
    enum CoordinateSystem { ModelCoordinateSystem, PixelCoordinateSystem };

    /** Return the bounding box of the page object or one of its graphical
        parts.
        @param rDevice
            This device is used to translate between model and window
            coordinates.
        @param eType
            The part of the page object for which to return the bounding
            box.
        @param eCoodinateSystem
            The bounding box can be returned in model and in pixel
            (window) coordinates.
    */
    Rectangle GetBoundingBox (
        OutputDevice& rDevice,
        BoundingBoxType eType,
        CoordinateSystem eCoordinateSystem) const;

    // create the graphical visualisation data
    virtual drawinglayer::primitive2d::Primitive2DSequence createPrimitive2DSequence(const sdr::contact::DisplayInfo& rDisplayInfo) const;

    // access to the current page content primitive vector which may be used for visualisation
    const drawinglayer::primitive2d::Primitive2DSequence& getCurrentPageContents() const { return mxCurrentPageContents; }

    /** This convenience method paints a dotted or dashed rectangle.  The
        length of dots or dashes is indepent of zoom factor or map mode.
    */
    enum DashType { Dotted, Dashed };
    static void PaintDottedRectangle (
        OutputDevice& rDevice,
        const Rectangle& rRectangle,
        const DashType eDashType = Dotted);

    enum ColorSpec { CS_SELECTION, CS_BACKGROUND, CS_WINDOW, CS_TEXT };
    /** Return a color for one of the screen elements in ColorSpec.  For
        Background the background color is updated when
        mbIsBackgroundColorUpdatePending is <TRUE/>.
        @param rDevice
            Base colors are taken from the device.
        @param eSpec
            The type of color to return.
        @param nOpacity
            This parameter controls the blending between the background and
            the actual color.
    */
    Color GetColor (
        const OutputDevice& rDevice,
        const ColorSpec eSpec,
        const double nOpacity = 1.0) const;

    virtual void ActionChanged (void);

private:
    /// Gap between border of page object and inside of selection rectangle.
    static const sal_Int32 mnSelectionIndicatorOffset;
    /// Thickness of the selection rectangle.
    static const sal_Int32 mnSelectionIndicatorThickness;
    /// Gap between border of page object and inside of focus rectangle.
    static const sal_Int32 mnFocusIndicatorOffset;
    /// Size of width and height of the fade effect indicator in pixels.
    static const sal_Int32 mnFadeEffectIndicatorSize;
    static const sal_Int32 mnFadeEffectIndicatorOffset;
    /// Gap between border of page object and number rectangle.
    static const sal_Int32 mnPageNumberOffset;
    /// Offset and thickness of the mouse over effect rectangle.
    static const sal_Int32 mnMouseOverEffectOffset;
    static const sal_Int32 mnMouseOverEffectThickness;

    /** This flag is set to <TRUE/> when the destructor is called to
        indicate that further calls made to it must not call outside.
    */
    bool mbInDestructor;

    /** Set this flag to <TRUE/> to update the background color on the next
        call to GetBackgroundColor().
    */
    mutable bool mbIsBackgroundColorUpdatePending;

    /// The primitive sequence of the page contents, completely scaled
    /// and prepared for painiting
    drawinglayer::primitive2d::Primitive2DSequence      mxCurrentPageContents;

    ::boost::shared_ptr<cache::PageCache> mpCache;

    ::boost::shared_ptr<controller::Properties> mpProperties;

    /** Do not use this member directly.  Use GetColor(Background) instead.
        That method determines the background color when
        mbIsBackgroundColorUpdatePending is <TRUE/>.
    */
    mutable Color maBackgroundColor;

    BitmapEx GetPreview (
        const sdr::contact::DisplayInfo& rDisplayInfo,
        const Rectangle& rNewSizePixel);

    /** Return the bounding box of where the page number is painted (when it
        is painted).
    */
    Rectangle GetPageNumberArea (OutputDevice& rDevice) const;

    void PaintBackground (OutputDevice& rDevice) const;

    /** Paint the preview bitmap.
    */
    void PaintPreview (OutputDevice& rDevice);

    /** Paint a border arround the page preview.
    */
    void PaintBorder (OutputDevice& rDevice) const;

    /** Paint the focus indicator for the specified page.
    */
    void PaintFocusIndicator (
        OutputDevice& rDevice,
        bool bEraseBackground) const;

    /** Paint the selection indicator when the page is currently selected.
        Otherwise the call is ignored.
    */
    void PaintSelectionIndicator (OutputDevice& rDevice) const;

    /** Paint the fade effect indicator which indicates whether a fade
        effect is currently associated with a page.
        @param rDescriptor
            The descriptor of the page for which to paint the fade effect
            indicator.
    */
    void PaintFadeEffectIndicator (OutputDevice& rDevice) const;

    /** Paint the name of the page to the bottom right of the page object.
    */
    void PaintPageName (OutputDevice& rDevice) const;

    /** Paint the number of the page to the upper left of the page object.
    */
    void PaintPageNumber (
        ::sdr::contact::DisplayInfo& rDisplayInfo);

    Color GetBackgroundColor (const OutputDevice& rDevice) const;
};

} } } // end of namespace ::sd::slidesorter::view

#endif
<|endoftext|>


<commit_before>/**
* OpenCV (OCV) - Video capture on BBB
*
* GOAL:
* This program takes continuous images from the host camera and detect a circular
* object of specific color. The color and other configuration are provided as
* input argument to the program.
*
* INPUT:
* The program takes two arguments:
* - The video device node (0/1)
* - The object color (blue/red)
*
* OUTPUT:
* On each frame captured by the program, three files are exported into the
* host filesystem (apache directory specifically):
* - A colored image with a green circular around the circular object
* - A binarized image of the colored image (previous bullet)
* - A text file containing information about the object captured
* In addition to storing images, at each frame iteration, the program executes a shell script
* to communicate information about the frame with the ATMEGA328.
*
* HOST REQUIREMENTS:
* The following packages are required to be installed on the host:
* - Apache2 server
* - OpenSSH server
**/

#include <fstream>
#include <iostream>
#include <cstring>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <string>
#include <vector>
#include <math.h>
#include <stdlib.h>
#include <cstdlib>
#include <sstream>
#include <stdio.h>
#include <ctime>
#include <unistd.h>

#define PI 3.14159265

using namespace cv;
using namespace std;

// Keep track of video camera frame number
long frameNumber = 0;

// Write to files every time the counter reaches zero
const int FRAME_EVERY = 3;
int currentFrame = FRAME_EVERY;

// Assign unique ID for each direction.
// The ID must be in sync with the GUI direction values
// (Please refer to the documentation for more information about the GUI code)

int direction = 0;
clock_t start_time;

const int FORWARD = 1;
const int REVERSE = 2;
const int RIGHT = 3;
const int LEFT = 4;
const int ROTATE = 5;
const int PAUSE = 6;
const int STOP = 7;

/**
* Get the distance between the object and the camera
* Please refer to the documentation report for more information
* about the calculations
* @return Distance in cm
**/
double getDistance(double realDimention, double digitalDimention) {
	double FOCAL_LENGTH = 339.079; // in pixels
	int ERROR_MARGIN = 0; //pixels lost due to selection of circular shape
	return realDimention * FOCAL_LENGTH / (digitalDimention + ERROR_MARGIN);
}

/**
* Detect the edge point of the object. This information allows the
* conclusion of the object digial radius
* @param imageDest Current video frame
* @param def Default point value in case no object was found
* @return arbitrary point on the object edge
**/
Point edgePoint(Mat imageDest, Point def) {
	int thresh = 100;

	// Canny Algorithm for object edge detection
	Canny(imageDest, imageDest, thresh /*threshold1*/, thresh*2 /*threshold2*/, 3/*apertureSize*/);

	// Prepare data structure to store the edge points
	vector<vector<Point> > contours;
	vector<Vec4i> hierarchy;

	// Perform the countour finder
	findContours(imageDest, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));

	// If no object foud, return the provided default value
	if(contours.size() == 0) {
		return def;
	}

	// Return a point from the countour
	return contours[0][0];
}

/**
* Predefined set of colors that can be detected by the program
* The colors supported by the current program are blue and red
* @param specs Data strucutre to hold the HSV channel color information
* based on the color specified
* @param color Color of the object (blue/red)
**/
void get_color_specs(vector<vector<int> > &specs, string color){

	if (!color.compare("blue")) {
		specs[0][0] = 100;
		specs[0][1] = 115;
		specs[0][2] = 50;
		specs[1][0] = 130;
		specs[1][1] = 255;
		specs[1][2] = 255;
	} else if (!color.compare("red")) {
		specs[0][0] = 0;
		specs[0][1] = 197;
		specs[0][2] = 109;
		specs[1][0] = 182;
		specs[1][1] = 255;
		specs[1][2] = 255;
	} else {
		specs[0][0] = 255;
		specs[0][1] = 255;
		specs[0][2] = 255;
		specs[1][0] = 255;
		specs[1][1] = 255;
		specs[1][2] = 255;
	}
}

void drive(int left, int right) {

	stringstream ss;
	ss << "/home/debian/mr_robot/tools/control/write " << left << "," << right << "#" << endl;
	cout << left << "," << right << "#" << endl;
	system(ss.str().c_str());
}

/**
* Drive the car based on the angle and distance
* Decisions are mainly taken based on experiments
* @param angle Car angel
* @param distance Distance of the car from the camera
* @param diameter Digital diameter of the circular object
* @param loop_count Necessary to keep from writing to Atmega328p faster than it can read messages
* @return Direction code
**/
void find_ball(double angle, double distance, double diameter, int &loop_count) {

	int full_speed = 255;
	int rotation_speed = 140;
	int turn_speed = 40;
	int pausing_distance = 45;
	int target_angle = 4;

	if (loop_count > 1) {

		// If no object found: rotate
		if(diameter == 0 && direction != ROTATE) {
			cout << endl << "rotating ";
			drive(rotation_speed, -rotation_speed);
			direction = ROTATE;
			loop_count = 0;

			start_time = clock();

		// If object is within pausing_distance and visible: pause
		} else if(distance <= pausing_distance && diameter > 0 && direction != PAUSE) {
			cout << endl << "pausing ";
			drive(0, 0);
			direction = PAUSE;
			loop_count = 0;

			cout << "\n**** BALL FOUND ****\n" << endl;

		// If object more than target_angle degrees to right and farther than pausing distance: turn right
		} else if (angle > target_angle && distance >= pausing_distance && direction != RIGHT){
			cout << endl << "turning right ";
			drive(turn_speed, -turn_speed);
			direction = RIGHT;
			loop_count = 0;

		// If object more than target_angle degrees to left and farther than pausing distance: turn left
		} else if (angle < -target_angle && distance >= pausing_distance && direction != LEFT) {
			cout << endl << "turning left ";
			drive(-turn_speed, turn_speed);
			direction = LEFT;
			loop_count = 0;

		// If ball is past pausing distance and within target_angle: forward
		} else if (distance > pausing_distance && angle < target_angle && angle > -target_angle && direction != FORWARD) {
			cout << endl << "going forward ";
			drive(full_speed, full_speed);
			direction = FORWARD;
			loop_count = 0;

		// If ball rotates ~360 degrees and doesn't see ball: stop
		} else if (direction == ROTATE) {
			clock_t rotation_duration = (clock() - start_time) / (double)(CLOCKS_PER_SEC);
			cout << "\trot time: " << rotation_duration << " s" << endl;
			if (rotation_duration > 12) {
				drive(0, 0);
				cout << "\n**** BALL NOT FOUND ****\n" << endl;
				direction = STOP;
			}
		}
	}

	loop_count++;
}

/**
* Start the OpenCV program
**/
int main( int argc, char** argv ) {

	// Capture video number is: /dev/video(0/1)
	int cap_num  = atoi(argv[1]);

	// Color blue/red
	string color = argv[2];

	// Start camera
	 VideoCapture cap;
	 if(!cap.open(cap_num))
		 return 1;

	// Configure the camera for fast capture and good resolution
	cap.set(CV_CAP_PROP_FRAME_WIDTH, 432);
	cap.set(CV_CAP_PROP_FRAME_HEIGHT,240);
	cap.set(CV_CAP_PROP_FPS , 30);

	// loop_count is used to make sure that the find_ball function has looped at least twice
	// before sending a message to the Atmega328p. This was created in response to a bug found
	// when writing too fast to the Atmega328p. The direction variable would be modified however
	// the write message wouldn't be interpreted. This causes the car to get stuck in a
	// certain direction. Initialized to 2 so that the car begins moving immediately
	int loop_count = 2;

	// Keep taking pictures
	while(1) {

		// Store the frame in a matrix
		Mat imageSrc;
		cap >> imageSrc;

		// Fetch the color information
		vector<vector<int> > color_specs(2, vector<int>(3));
		get_color_specs(color_specs, color);

		// HSV low-high values
		int lowH = color_specs[0][0];
		int highH = color_specs[1][0];

		int lowS = color_specs[0][1];
		int highS = color_specs[1][1];

		int lowV = color_specs[0][2];
		int highV = color_specs[1][2];

		// Destination image
		Mat imageDest;

		// Convert BGR to HSV
		cvtColor(imageSrc, imageDest, COLOR_BGR2HSV);

		// Get colors in specified range
		inRange(imageDest, Scalar(lowH, lowS, lowV), Scalar(highH, highS, highV), imageDest);

		// Morphological opening
		erode(imageDest, imageDest, getStructuringElement(MORPH_ELLIPSE, Size(5, 5)) );
		dilate(imageDest, imageDest, getStructuringElement(MORPH_ELLIPSE, Size(5, 5)) );

		// Morphological closing
		dilate(imageDest, imageDest, getStructuringElement(MORPH_ELLIPSE, Size(5, 5)) );
		erode(imageDest, imageDest, getStructuringElement(MORPH_ELLIPSE, Size(5, 5)) );

		// Create moment
		Moments mmts = moments(imageDest);

		// Calculate center x and y (Centroids)
		double x_object = mmts.m10 / mmts.m00;
		double y_object = mmts.m01 / mmts.m00;

		// Center of image
		cv::Size size = imageSrc.size();
		double x_center = size.width/2.0f;
		double y_center = size.height/2.0f;

		// Contour
		Mat tmpDest = imageDest.clone();
		Point point = edgePoint(tmpDest, Point(x_center, y_center));

		// Calculate digital diameter in cm
		double diameter = norm(Point(x_object, y_object)- point)*2;
		double realDiameter = 6.5;

		// Calculate the real distance
		double distance = getDistance(realDiameter, diameter);

		// Get rotation angle
		int digitalDiff = x_object - x_center;
		double realDiff = digitalDiff * (realDiameter / diameter);
		double rotation_angle = atan(realDiff / distance) * 180 / PI;

		// If no object found, then diameter is set to zero
		if(isnan((double)x_object) && isnan((double)y_object)) {
			diameter = 0.0;;
		}

		// Log the calculated information
		cout << endl;
		printf("\t%-10s%4.2f\n", "angle:", rotation_angle);
		printf("\t%-10s%4.2f\n", "distance:", distance);
		printf("\t%-10s%4.2f\n", "diameter:", diameter);

		// Draw circle at x and y
		Mat tmpSource = imageSrc.clone();
		circle(tmpSource, Point(x_object,y_object), 3, Scalar(229, 240, 76), 2);
		circle(tmpSource, Point(x_object,y_object), diameter/2, Scalar(44, 252, 14), 3);

		// Center
		circle(tmpSource, Point(x_center,y_center), 2, Scalar(255, 255, 255), 2);

		// Logic to navigate to ball
		find_ball(rotation_angle, distance, diameter, loop_count);

		// Write images and text into the file system/
		// Director /var/www/html correspond to the path for
		// Apache2 server. All files placed in this directory will be
		// accessible on all users in the network over host IP and port 80

		string path = "/home/debian/mr_robot/dashboard/mr-robot-node/public/debug/";
		string outPath = path + "out.jpg";
		string bwPath = path + "bw.jpg";
		string infoPath = path + "info.txt";

		if(--currentFrame == 0) {
			imwrite(outPath, tmpSource);
			imwrite(bwPath, imageDest);
			currentFrame = FRAME_EVERY;
		}
		ofstream myfile;
		myfile.open (infoPath.c_str());
		myfile << "Distance from camera: " << distance << " cm\n";
		myfile << "Rotation angle: " << rotation_angle << "\n";
		myfile << "Digital diameter: " << diameter << " px\n";
		myfile << "Frame number: " << ++frameNumber << "\n";

		string dir_message = "Direction: ";
		switch (direction) {
			case FORWARD:
				myfile << dir_message << "Forward" << endl;
				break;
			case REVERSE:
				myfile << dir_message << "Reversing" << endl;
				break;
			case RIGHT:
				myfile << dir_message << "Right" << endl;
				break;
			case LEFT:
				myfile << dir_message << "Left" << endl;
				break;
			case ROTATE:
				myfile << dir_message << "Rotating" << endl;
				break;
			case PAUSE:
				myfile << dir_message << "Pause" << endl;
				break;
			case STOP:
				myfile << dir_message << "Stop" << endl;
				break;
		}
		myfile << "DIR_CODE: " << direction << "\n";
		myfile.close();

		if (direction == STOP) {
			return 0;
		}

	}

	return 0;
}
<commit_msg>Changed resolution of OCV<commit_after>/**
* OpenCV (OCV) - Video capture on BBB
*
* GOAL:
* This program takes continuous images from the host camera and detect a circular
* object of specific color. The color and other configuration are provided as
* input argument to the program.
*
* INPUT:
* The program takes two arguments:
* - The video device node (0/1)
* - The object color (blue/red)
*
* OUTPUT:
* On each frame captured by the program, three files are exported into the
* host filesystem (apache directory specifically):
* - A colored image with a green circular around the circular object
* - A binarized image of the colored image (previous bullet)
* - A text file containing information about the object captured
* In addition to storing images, at each frame iteration, the program executes a shell script
* to communicate information about the frame with the ATMEGA328.
*
* HOST REQUIREMENTS:
* The following packages are required to be installed on the host:
* - Apache2 server
* - OpenSSH server
**/

#include <fstream>
#include <iostream>
#include <cstring>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <string>
#include <vector>
#include <math.h>
#include <stdlib.h>
#include <cstdlib>
#include <sstream>
#include <stdio.h>
#include <ctime>
#include <unistd.h>

#define PI 3.14159265

using namespace cv;
using namespace std;

// Keep track of video camera frame number
long frameNumber = 0;

// Write to files every time the counter reaches zero
const int FRAME_EVERY = 3;
int currentFrame = FRAME_EVERY;

// Assign unique ID for each direction.
// The ID must be in sync with the GUI direction values
// (Please refer to the documentation for more information about the GUI code)

int direction = 0;
clock_t start_time;

const int FORWARD = 1;
const int REVERSE = 2;
const int RIGHT = 3;
const int LEFT = 4;
const int ROTATE = 5;
const int PAUSE = 6;
const int STOP = 7;

/**
* Get the distance between the object and the camera
* Please refer to the documentation report for more information
* about the calculations
* @return Distance in cm
**/
double getDistance(double realDimention, double digitalDimention) {
	double FOCAL_LENGTH = 339.079; // in pixels
	int ERROR_MARGIN = 0; //pixels lost due to selection of circular shape
	return realDimention * FOCAL_LENGTH / (digitalDimention + ERROR_MARGIN);
}

/**
* Detect the edge point of the object. This information allows the
* conclusion of the object digial radius
* @param imageDest Current video frame
* @param def Default point value in case no object was found
* @return arbitrary point on the object edge
**/
Point edgePoint(Mat imageDest, Point def) {
	int thresh = 100;

	// Canny Algorithm for object edge detection
	Canny(imageDest, imageDest, thresh /*threshold1*/, thresh*2 /*threshold2*/, 3/*apertureSize*/);

	// Prepare data structure to store the edge points
	vector<vector<Point> > contours;
	vector<Vec4i> hierarchy;

	// Perform the countour finder
	findContours(imageDest, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));

	// If no object foud, return the provided default value
	if(contours.size() == 0) {
		return def;
	}

	// Return a point from the countour
	return contours[0][0];
}

/**
* Predefined set of colors that can be detected by the program
* The colors supported by the current program are blue and red
* @param specs Data strucutre to hold the HSV channel color information
* based on the color specified
* @param color Color of the object (blue/red)
**/
void get_color_specs(vector<vector<int> > &specs, string color){

	if (!color.compare("blue")) {
		specs[0][0] = 100;
		specs[0][1] = 115;
		specs[0][2] = 50;
		specs[1][0] = 130;
		specs[1][1] = 255;
		specs[1][2] = 255;
	} else if (!color.compare("red")) {
		specs[0][0] = 0;
		specs[0][1] = 197;
		specs[0][2] = 109;
		specs[1][0] = 182;
		specs[1][1] = 255;
		specs[1][2] = 255;
	} else {
		specs[0][0] = 255;
		specs[0][1] = 255;
		specs[0][2] = 255;
		specs[1][0] = 255;
		specs[1][1] = 255;
		specs[1][2] = 255;
	}
}

void drive(int left, int right) {

	stringstream ss;
	ss << "/home/debian/mr_robot/tools/control/write " << left << "," << right << "#" << endl;
	cout << left << "," << right << "#" << endl;
	system(ss.str().c_str());
}

/**
* Drive the car based on the angle and distance
* Decisions are mainly taken based on experiments
* @param angle Car angel
* @param distance Distance of the car from the camera
* @param diameter Digital diameter of the circular object
* @param loop_count Necessary to keep from writing to Atmega328p faster than it can read messages
* @return Direction code
**/
void find_ball(double angle, double distance, double diameter, int &loop_count) {

	int full_speed = 255;
	int rotation_speed = 140;
	int turn_speed = 40;
	int pausing_distance = 45;
	int target_angle = 4;

	if (loop_count > 1) {

		// If no object found: rotate
		if(diameter == 0 && direction != ROTATE) {
			cout << endl << "rotating ";
			drive(rotation_speed, -rotation_speed);
			direction = ROTATE;
			loop_count = 0;

			start_time = clock();

		// If object is within pausing_distance and visible: pause
		} else if(distance <= pausing_distance && diameter > 0 && direction != PAUSE) {
			cout << endl << "pausing ";
			drive(0, 0);
			direction = PAUSE;
			loop_count = 0;

			cout << "\n**** BALL FOUND ****\n" << endl;

		// If object more than target_angle degrees to right and farther than pausing distance: turn right
		} else if (angle > target_angle && distance >= pausing_distance && direction != RIGHT){
			cout << endl << "turning right ";
			drive(turn_speed, -turn_speed);
			direction = RIGHT;
			loop_count = 0;

		// If object more than target_angle degrees to left and farther than pausing distance: turn left
		} else if (angle < -target_angle && distance >= pausing_distance && direction != LEFT) {
			cout << endl << "turning left ";
			drive(-turn_speed, turn_speed);
			direction = LEFT;
			loop_count = 0;

		// If ball is past pausing distance and within target_angle: forward
		} else if (distance > pausing_distance && angle < target_angle && angle > -target_angle && direction != FORWARD) {
			cout << endl << "going forward ";
			drive(full_speed, full_speed);
			direction = FORWARD;
			loop_count = 0;

		// If ball rotates ~360 degrees and doesn't see ball: stop
		} else if (direction == ROTATE) {
			clock_t rotation_duration = (clock() - start_time) / (double)(CLOCKS_PER_SEC);
			cout << "\trot time: " << rotation_duration << " s" << endl;
			if (rotation_duration > 12) {
				drive(0, 0);
				cout << "\n**** BALL NOT FOUND ****\n" << endl;
				direction = STOP;
			}
		}
	}

	loop_count++;
}

/**
* Start the OpenCV program
**/
int main( int argc, char** argv ) {

	// Capture video number is: /dev/video(0/1)
	int cap_num  = atoi(argv[1]);

	// Color blue/red
	string color = argv[2];

	// Start camera
	 VideoCapture cap;
	 if(!cap.open(cap_num))
		 return 1;

	// Configure the camera for fast capture and good resolution
	cap.set(CV_CAP_PROP_FRAME_WIDTH, 320);
	cap.set(CV_CAP_PROP_FRAME_HEIGHT,180);
	cap.set(CV_CAP_PROP_FPS , 30);

	// loop_count is used to make sure that the find_ball function has looped at least twice
	// before sending a message to the Atmega328p. This was created in response to a bug found
	// when writing too fast to the Atmega328p. The direction variable would be modified however
	// the write message wouldn't be interpreted. This causes the car to get stuck in a
	// certain direction. Initialized to 2 so that the car begins moving immediately
	int loop_count = 2;

	// Keep taking pictures
	while(1) {

		// Store the frame in a matrix
		Mat imageSrc;
		cap >> imageSrc;

		// Fetch the color information
		vector<vector<int> > color_specs(2, vector<int>(3));
		get_color_specs(color_specs, color);

		// HSV low-high values
		int lowH = color_specs[0][0];
		int highH = color_specs[1][0];

		int lowS = color_specs[0][1];
		int highS = color_specs[1][1];

		int lowV = color_specs[0][2];
		int highV = color_specs[1][2];

		// Destination image
		Mat imageDest;

		// Convert BGR to HSV
		cvtColor(imageSrc, imageDest, COLOR_BGR2HSV);

		// Get colors in specified range
		inRange(imageDest, Scalar(lowH, lowS, lowV), Scalar(highH, highS, highV), imageDest);

		// Morphological opening
		erode(imageDest, imageDest, getStructuringElement(MORPH_ELLIPSE, Size(5, 5)) );
		dilate(imageDest, imageDest, getStructuringElement(MORPH_ELLIPSE, Size(5, 5)) );

		// Morphological closing
		dilate(imageDest, imageDest, getStructuringElement(MORPH_ELLIPSE, Size(5, 5)) );
		erode(imageDest, imageDest, getStructuringElement(MORPH_ELLIPSE, Size(5, 5)) );

		// Create moment
		Moments mmts = moments(imageDest);

		// Calculate center x and y (Centroids)
		double x_object = mmts.m10 / mmts.m00;
		double y_object = mmts.m01 / mmts.m00;

		// Center of image
		cv::Size size = imageSrc.size();
		double x_center = size.width/2.0f;
		double y_center = size.height/2.0f;

		// Contour
		Mat tmpDest = imageDest.clone();
		Point point = edgePoint(tmpDest, Point(x_center, y_center));

		// Calculate digital diameter in cm
		double diameter = norm(Point(x_object, y_object)- point)*2;
		double realDiameter = 6.5;

		// Calculate the real distance
		double distance = getDistance(realDiameter, diameter);

		// Get rotation angle
		int digitalDiff = x_object - x_center;
		double realDiff = digitalDiff * (realDiameter / diameter);
		double rotation_angle = atan(realDiff / distance) * 180 / PI;

		// If no object found, then diameter is set to zero
		if(isnan((double)x_object) && isnan((double)y_object)) {
			diameter = 0.0;;
		}

		// Log the calculated information
		cout << endl;
		printf("\t%-10s%4.2f\n", "angle:", rotation_angle);
		printf("\t%-10s%4.2f\n", "distance:", distance);
		printf("\t%-10s%4.2f\n", "diameter:", diameter);

		// Draw circle at x and y
		Mat tmpSource = imageSrc.clone();
		circle(tmpSource, Point(x_object,y_object), 3, Scalar(229, 240, 76), 2);
		circle(tmpSource, Point(x_object,y_object), diameter/2, Scalar(44, 252, 14), 3);

		// Center
		circle(tmpSource, Point(x_center,y_center), 2, Scalar(255, 255, 255), 2);

		// Logic to navigate to ball
		find_ball(rotation_angle, distance, diameter, loop_count);

		// Write images and text into the file system/
		// Director /var/www/html correspond to the path for
		// Apache2 server. All files placed in this directory will be
		// accessible on all users in the network over host IP and port 80

		string path = "/home/debian/mr_robot/dashboard/mr-robot-node/public/debug/";
		string outPath = path + "out.jpg";
		string bwPath = path + "bw.jpg";
		string infoPath = path + "info.txt";

		if(--currentFrame == 0) {
			imwrite(outPath, tmpSource);
			imwrite(bwPath, imageDest);
			currentFrame = FRAME_EVERY;
		}
		ofstream myfile;
		myfile.open (infoPath.c_str());
		myfile << "Distance from camera: " << distance << " cm\n";
		myfile << "Rotation angle: " << rotation_angle << "\n";
		myfile << "Digital diameter: " << diameter << " px\n";
		myfile << "Frame number: " << ++frameNumber << "\n";

		string dir_message = "Direction: ";
		switch (direction) {
			case FORWARD:
				myfile << dir_message << "Forward" << endl;
				break;
			case REVERSE:
				myfile << dir_message << "Reversing" << endl;
				break;
			case RIGHT:
				myfile << dir_message << "Right" << endl;
				break;
			case LEFT:
				myfile << dir_message << "Left" << endl;
				break;
			case ROTATE:
				myfile << dir_message << "Rotating" << endl;
				break;
			case PAUSE:
				myfile << dir_message << "Pause" << endl;
				break;
			case STOP:
				myfile << dir_message << "Stop" << endl;
				break;
		}
		myfile << "DIR_CODE: " << direction << "\n";
		myfile.close();

		if (direction == STOP) {
			return 0;
		}

	}

	return 0;
}
<|endoftext|>


<commit_before>#include "cache.h"
#include <stdlib.h>

BlockCache::BlockCache(void)
{
	firstItem = NULL;
	lastItem = NULL;
}

BlockCache::~BlockCache(void)
{
	// TODO: Free all items
}

void BlockCache::Set(void *key, void *val)
{
	BlockCacheItem *item;

	item = (BlockCacheItem *)malloc(sizeof(BlockCacheItem));
	item->key = key;
	item->val = val;
	item->next = NULL;

	if (!firstItem) {
		firstItem = item;
	}
	else {
		lastItem->next = item;
	}

	lastItem = item;
}

void *BlockCache::Get(void *key)
{
	BlockCacheItem *cur;

	cur = firstItem;
	while (cur) {
		if (cur->key == key)
			return cur->val;

		cur = cur->next;
	}

	return NULL;
}

ColorMaterialCache::ColorMaterialCache(void)
{
	firstItem = NULL;
	lastItem = NULL;
}

ColorMaterialCache::~ColorMaterialCache(void)
{
	// TODO: Free all items
}

void ColorMaterialCache::Set(awd_color color, AWDMaterial *mtl)
{
	ColorMaterialCacheItem *item;

	item = (ColorMaterialCacheItem *)malloc(sizeof(ColorMaterialCacheItem));
	item->color = color;
	item->mtl = mtl;
	item->next = NULL;

	if (!firstItem) {
		firstItem = item;
	}
	else {
		lastItem->next = item;
	}

	lastItem = item;
}

AWDMaterial *ColorMaterialCache::Get(awd_color color)
{
	ColorMaterialCacheItem *cur;

	cur = firstItem;
	while (cur) {
		if (cur->color == color)
			return cur->mtl;

		cur = cur->next;
	}

	return NULL;
}

SkeletonCacheItem::SkeletonCacheItem(INode *maxRootBone)
{
	next = NULL;
	rootBone = maxRootBone;
	firstJoint = NULL;
	lastJoint = NULL;
	numJoints = 0;

	// Create AWD skeleton structure and begin
	// gathering joints recursively
	char *name = rootBone->GetName();
	awdSkel = new AWDSkeleton(name, strlen(name));
	GatherJoint(rootBone, NULL);
}

SkeletonCacheItem::~SkeletonCacheItem(void)
{
	// TODO: Delete all joints
}

void SkeletonCacheItem::AppendCacheJoint(SkeletonCacheJoint *cacheJoint)
{
	if (!firstJoint) {
		firstJoint = cacheJoint;
	}
	else {
		lastJoint->next = cacheJoint;
	}

	lastJoint = cacheJoint;
	lastJoint->index = numJoints++;
	lastJoint->next = NULL;
}

/**
 * Gather joints using the same order that the AWD SDK uses, so that the
 * binding code can then look-up indices using this structure.
*/
void SkeletonCacheItem::GatherJoint(INode *bone, AWDSkeletonJoint *awdParent)
{
	int i;
	char *name;
	SkeletonCacheJoint *cacheJoint;
    AWDSkeletonJoint *awdJoint;

    name = bone->GetName();
    awdJoint = new AWDSkeletonJoint(name, strlen(name), NULL);

    if (awdParent != NULL) {
		awdParent->add_child_joint(awdJoint);
    }
    else {
        awdSkel->set_root_joint(awdJoint);
    }

	// Add to cache
	cacheJoint = (SkeletonCacheJoint*)malloc(sizeof(SkeletonCacheJoint));
	cacheJoint->awdJoint = awdJoint;
	cacheJoint->maxBone = bone;
	AppendCacheJoint(cacheJoint);

	for (i=0; i<bone->NumberOfChildren(); i++) {
		INode *child = bone->GetChildNode(i);
		Object *obj = bone->GetObjectRef();
		if (obj && obj->ClassID()==BONE_OBJ_CLASSID) {
			GatherJoint(child, awdJoint);
		}
	}
}

int SkeletonCacheItem::IndexOfBone(INode *bone)
{
	SkeletonCacheJoint *cur;

	cur = firstJoint;
	while (cur) {
		if (cur->maxBone == bone)
			return cur->index;

		cur = cur->next;
	}

	return -1;
}

SkeletonCache::SkeletonCache(void)
{
	firstItem = NULL;
	lastItem = NULL;
	cur = NULL;
}

SkeletonCache::~SkeletonCache(void)
{
	// TODO: Free all items
}

AWDSkeleton *SkeletonCache::Add(INode *rootBone)
{
	SkeletonCacheItem *item;

	item = new SkeletonCacheItem(rootBone);

	if (!firstItem) {
		firstItem = item;
	}
	else {
		lastItem->next = item;
	}

	lastItem = item;

	// Return skeleton
	return item->awdSkel;
}

SkeletonCacheItem *SkeletonCache::GetFromBone(INode *bone)
{
	SkeletonCacheItem *cur;

	cur = firstItem;
	while (cur) {
		// Check if this cache item is a skeleton
		// containing the supplied bone.
		if (cur->IndexOfBone(bone) >= 0)
			return cur;

		cur = cur->next;
	}

	return NULL;
}

bool SkeletonCache::HasItems()
{
	return (firstItem != NULL);
}

void SkeletonCache::IterReset()
{
	cur = firstItem;
}

SkeletonCacheItem *SkeletonCache::IterNext()
{
	// Stop if end was reached
	if (!cur)
		return NULL;

	SkeletonCacheItem *ret = cur;
	cur = cur->next;
	return ret;
}<commit_msg>Using bone node TM as default bind matrix. Will be overwritten by skin export.<commit_after>#include "cache.h"
#include "utils.h"
#include <stdlib.h>

BlockCache::BlockCache(void)
{
	firstItem = NULL;
	lastItem = NULL;
}

BlockCache::~BlockCache(void)
{
	// TODO: Free all items
}

void BlockCache::Set(void *key, void *val)
{
	BlockCacheItem *item;

	item = (BlockCacheItem *)malloc(sizeof(BlockCacheItem));
	item->key = key;
	item->val = val;
	item->next = NULL;

	if (!firstItem) {
		firstItem = item;
	}
	else {
		lastItem->next = item;
	}

	lastItem = item;
}

void *BlockCache::Get(void *key)
{
	BlockCacheItem *cur;

	cur = firstItem;
	while (cur) {
		if (cur->key == key)
			return cur->val;

		cur = cur->next;
	}

	return NULL;
}

ColorMaterialCache::ColorMaterialCache(void)
{
	firstItem = NULL;
	lastItem = NULL;
}

ColorMaterialCache::~ColorMaterialCache(void)
{
	// TODO: Free all items
}

void ColorMaterialCache::Set(awd_color color, AWDMaterial *mtl)
{
	ColorMaterialCacheItem *item;

	item = (ColorMaterialCacheItem *)malloc(sizeof(ColorMaterialCacheItem));
	item->color = color;
	item->mtl = mtl;
	item->next = NULL;

	if (!firstItem) {
		firstItem = item;
	}
	else {
		lastItem->next = item;
	}

	lastItem = item;
}

AWDMaterial *ColorMaterialCache::Get(awd_color color)
{
	ColorMaterialCacheItem *cur;

	cur = firstItem;
	while (cur) {
		if (cur->color == color)
			return cur->mtl;

		cur = cur->next;
	}

	return NULL;
}

SkeletonCacheItem::SkeletonCacheItem(INode *maxRootBone)
{
	next = NULL;
	rootBone = maxRootBone;
	firstJoint = NULL;
	lastJoint = NULL;
	numJoints = 0;

	// Create AWD skeleton structure and begin
	// gathering joints recursively
	char *name = rootBone->GetName();
	awdSkel = new AWDSkeleton(name, strlen(name));
	GatherJoint(rootBone, NULL);
}

SkeletonCacheItem::~SkeletonCacheItem(void)
{
	// TODO: Delete all joints
}

void SkeletonCacheItem::AppendCacheJoint(SkeletonCacheJoint *cacheJoint)
{
	if (!firstJoint) {
		firstJoint = cacheJoint;
	}
	else {
		lastJoint->next = cacheJoint;
	}

	lastJoint = cacheJoint;
	lastJoint->index = numJoints++;
	lastJoint->next = NULL;
}

/**
 * Gather joints using the same order that the AWD SDK uses, so that the
 * binding code can then look-up indices using this structure.
*/
void SkeletonCacheItem::GatherJoint(INode *bone, AWDSkeletonJoint *awdParent)
{
	int i;
	char *name;
	SkeletonCacheJoint *cacheJoint;
	AWDSkeletonJoint *awdJoint;
	Matrix3 boneTM;
	awd_float64 *bindMtx;

	// Use bone transform as bind matrix for now. This will be
	// overwritten by the skin export if exporting skins.
	boneTM = bone->GetNodeTM(0);
	bindMtx = (awd_float64*)malloc(sizeof(awd_float64)*12);
	SerializeMatrix3(Inverse(boneTM), bindMtx);

	name = bone->GetName();
	awdJoint = new AWDSkeletonJoint(name, strlen(name), bindMtx);

	if (awdParent != NULL) {
		awdParent->add_child_joint(awdJoint);
	}
	else {
		awdSkel->set_root_joint(awdJoint);
	}

	// Add to cache
	cacheJoint = (SkeletonCacheJoint*)malloc(sizeof(SkeletonCacheJoint));
	cacheJoint->awdJoint = awdJoint;
	cacheJoint->maxBone = bone;
	AppendCacheJoint(cacheJoint);

	for (i=0; i<bone->NumberOfChildren(); i++) {
		INode *child = bone->GetChildNode(i);
		Object *obj = bone->GetObjectRef();
		if (obj && obj->ClassID()==BONE_OBJ_CLASSID) {
			GatherJoint(child, awdJoint);
		}
	}
}

int SkeletonCacheItem::IndexOfBone(INode *bone)
{
	SkeletonCacheJoint *cur;

	cur = firstJoint;
	while (cur) {
		if (cur->maxBone == bone)
			return cur->index;

		cur = cur->next;
	}

	return -1;
}

SkeletonCache::SkeletonCache(void)
{
	firstItem = NULL;
	lastItem = NULL;
	cur = NULL;
}

SkeletonCache::~SkeletonCache(void)
{
	// TODO: Free all items
}

AWDSkeleton *SkeletonCache::Add(INode *rootBone)
{
	SkeletonCacheItem *item;

	item = new SkeletonCacheItem(rootBone);

	if (!firstItem) {
		firstItem = item;
	}
	else {
		lastItem->next = item;
	}

	lastItem = item;

	// Return skeleton
	return item->awdSkel;
}

SkeletonCacheItem *SkeletonCache::GetFromBone(INode *bone)
{
	SkeletonCacheItem *cur;

	cur = firstItem;
	while (cur) {
		// Check if this cache item is a skeleton
		// containing the supplied bone.
		if (cur->IndexOfBone(bone) >= 0)
			return cur;

		cur = cur->next;
	}

	return NULL;
}

bool SkeletonCache::HasItems()
{
	return (firstItem != NULL);
}

void SkeletonCache::IterReset()
{
	cur = firstItem;
}

SkeletonCacheItem *SkeletonCache::IterNext()
{
	// Stop if end was reached
	if (!cur)
		return NULL;

	SkeletonCacheItem *ret = cur;
	cur = cur->next;
	return ret;
}<|endoftext|>


<commit_before>/*
 *  This file is part of Poedit (https://poedit.net)
 *
 *  Copyright (C) 2016 Vaclav Slavik
 *
 *  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 "win10_menubar.h"

#include "utility.h"

#include <wx/msw/uxtheme.h>
#include <wx/nativewin.h>
#include <wx/recguard.h>

#include <mCtrl/menubar.h>

namespace
{

int g_mctrlInitialized = 0;

} // anonymous namespace

class wxFrameWithWindows10Menubar::MenuBarWindow : public wxWindow
{
public:
    MenuBarWindow(wxWindow *parent)
        : wxWindow(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxFULL_REPAINT_ON_RESIZE),
          m_mctrlWin(nullptr), m_mctrlHandle(0), m_flagReenterMctrl(0)
    {
        if (g_mctrlInitialized++ == 0)
            mcMenubar_Initialize();

        m_mctrlHandle = CreateWindowEx
        (
            WS_EX_COMPOSITED,
            MC_WC_MENUBAR,
            _T(""),
            WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | CCS_NORESIZE | CCS_NOPARENTALIGN,
            0, 0, 1000, 23*2,
            (HWND)this->GetHWND(),
            (HMENU) -1,
            wxGetInstance(),
            NULL
        );
        SendMessage(m_mctrlHandle, TB_SETEXTENDEDSTYLE, 0, TBSTYLE_EX_HIDECLIPPEDBUTTONS);
        SendMessage(m_mctrlHandle, CCM_SETNOTIFYWINDOW, (WPARAM)parent->GetHWND(), 0);

        m_mctrlWin = new mCtrlWrapper(this, m_mctrlHandle);

        {
            wxUxThemeHandle hTheme(this, L"ExplorerMenu::Toolbar");
            SetBackgroundColour(wxRGBToColour(wxUxThemeEngine::GetIfActive()->GetThemeSysColor(hTheme, COLOR_WINDOW)));
        }
    }

    ~MenuBarWindow()
    {
        m_mctrlWin->Destroy();
        ::DestroyWindow(m_mctrlHandle);

        if (--g_mctrlInitialized == 0)
            mcMenubar_Terminate();
    }

    void SetHMENU(WXHMENU menu)
    {
        SendMessage(m_mctrlHandle, MC_MBM_SETMENU, 0, (LPARAM) menu);
        SendMessage(m_mctrlHandle, MC_MBM_REFRESH, 0, 0);
    }

    bool TranslateMenubarMessage(WXMSG *pMsg)
    {
        wxRecursionGuard guard(m_flagReenterMctrl);

        if (!guard.IsInside())
        {
            MSG *msg = (MSG *) pMsg;
            if (mcIsMenubarMessage(m_mctrlHandle, msg))
                return true;
        }
        return false;
    }

    void DoSetSize(int x, int y, int width, int height, int sizeFlags) override
    {
        wxWindow::DoSetSize(x, y, width, height, sizeFlags);
        SendMessage(m_mctrlHandle, MC_MBM_REFRESH, 0, 0);
    }

    wxSize DoGetBestSize() const override
    {
        wxSize sizeBest = wxDefaultSize;
        SIZE size;
        if (::SendMessage(m_mctrlHandle, TB_GETMAXSIZE, 0, (LPARAM) &size))
        {
            sizeBest.x = size.cx;
            sizeBest.y = size.cy + 1;
            CacheBestSize(sizeBest);
        }
        return sizeBest;
    }

private:
    class mCtrlWrapper : public wxNativeWindow
    {
    public:
        mCtrlWrapper(wxWindow *parent, WXHWND wnd) : wxNativeWindow(parent, wxID_ANY, wnd) {}

        WXLRESULT MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) override
        {
            switch (nMsg)
            {
                case WM_COMMAND:
                case WM_NOTIFY:
                    return MSWDefWindowProc(nMsg, wParam, lParam);
            }
            return wxNativeWindow::MSWWindowProc(nMsg, wParam, lParam);
        }
    };

    mCtrlWrapper *m_mctrlWin;
    WXHWND m_mctrlHandle;
    wxRecursionGuardFlag m_flagReenterMctrl;
};

wxFrameWithWindows10Menubar::wxFrameWithWindows10Menubar(wxWindow *parent,
                                                         wxWindowID id,
                                                         const wxString& title,
                                                         const wxPoint& pos,
                                                         const wxSize& size,
                                                         long style,
                                                         const wxString& name)
    : wxFrame(parent, id, title, pos, size, style, name), m_menuBar(nullptr)
{
    if (ShouldUse())
    {
        m_menuBar = new MenuBarWindow(this);
    }
}

bool wxFrameWithWindows10Menubar::ShouldUse() const
{
    if (!IsWindows10OrGreater())
        return false;

    if (!wxUxThemeEngine::GetIfActive())
        return false;

    // Detect screen readers and use normal menubar with them, because the
    // mCtrl one isn't accessible.
    BOOL running;
    BOOL ret = SystemParametersInfo(SPI_GETSCREENREADER, 0, &running, 0);
    if (ret && running)
        return false;

    return true;
}

wxPoint wxFrameWithWindows10Menubar::GetClientAreaOrigin() const
{
    wxPoint pt = wxFrame::GetClientAreaOrigin();
    if (IsUsed())
    {
        pt.y += m_menuBar->GetSize().y;
    }
    return pt;
}

void wxFrameWithWindows10Menubar::PositionToolBar()
{
    // Position both the toolbar and our menu bar (which is really another toolbar) here.
    if (!IsUsed())
    {
        wxFrame::PositionToolBar();
        return;
    }

    // don't call our (or even wxTopLevelWindow) version because we want
    // the real (full) client area size, not excluding the tool/status bar
    int width, height;
    wxWindow::DoGetClientSize(&width, &height);

    int y = 0;

    // use the 'real' MSW position here, don't offset relatively to the
    // client area origin
    int mch = m_menuBar->GetBestSize().y;
    m_menuBar->SetSize(0, y, width, mch, wxSIZE_NO_ADJUSTMENTS);
    y += mch;

    wxToolBar *toolbar = GetToolBar();
    if (toolbar && toolbar->IsShown())
    {
        int tbh = toolbar->GetSize().y;
        toolbar->SetSize(0, y, width, tbh, wxSIZE_NO_ADJUSTMENTS);
    }
}

bool wxFrameWithWindows10Menubar::MSWTranslateMessage(WXMSG *msg)
{
    if (wxFrame::MSWTranslateMessage(msg))
        return true;

    if (IsUsed() && m_menuBar->TranslateMenubarMessage(msg))
        return true;

    return false;
}

WXLRESULT wxFrameWithWindows10Menubar::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam)
{
    if (IsUsed())
    {
        // mCtrl doesn't play nice with the wxMSW's menus interaction where accelerators are updated
        // when a menu is opened (which works because TranslateAccelerators() normally sends a fake
        // event for that... if there's a normal menu). We need to refresh menus before accelerators
        // are used so that e.g. disabled state is accurately updated.
        if (message == WM_COMMAND && HIWORD(wParam) == 1/*accel*/)
            GetMenuBar()->UpdateMenus();
    }

    return wxFrame::MSWWindowProc(message, wParam, lParam);
}

void wxFrameWithWindows10Menubar::InternalSetMenuBar()
{
    if (IsUsed())
    {
        m_menuBar->SetHMENU(m_hMenu);
    }
    else
    {
        wxFrame::InternalSetMenuBar();
    }
}
<commit_msg>Fix rendering artifacts around mCtrl menu on Win10<commit_after>/*
 *  This file is part of Poedit (https://poedit.net)
 *
 *  Copyright (C) 2016 Vaclav Slavik
 *
 *  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 "win10_menubar.h"

#include "utility.h"

#include <wx/msw/uxtheme.h>
#include <wx/nativewin.h>
#include <wx/recguard.h>

#include <mCtrl/menubar.h>

namespace
{

int g_mctrlInitialized = 0;

const int MENUBAR_OFFSET = -2;

} // anonymous namespace

class wxFrameWithWindows10Menubar::MenuBarWindow : public wxWindow
{
public:
    MenuBarWindow(wxWindow *parent)
        : wxWindow(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxFULL_REPAINT_ON_RESIZE),
          m_mctrlWin(nullptr), m_mctrlHandle(0), m_flagReenterMctrl(0)
    {
        if (g_mctrlInitialized++ == 0)
            mcMenubar_Initialize();

        m_mctrlHandle = CreateWindowEx
        (
            WS_EX_COMPOSITED,
            MC_WC_MENUBAR,
            _T(""),
            WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | CCS_NORESIZE | CCS_NOPARENTALIGN,
            0, 0, 1000, 23*2,
            (HWND)this->GetHWND(),
            (HMENU) -1,
            wxGetInstance(),
            NULL
        );
        SendMessage(m_mctrlHandle, TB_SETEXTENDEDSTYLE, 0, TBSTYLE_EX_HIDECLIPPEDBUTTONS);
        SendMessage(m_mctrlHandle, CCM_SETNOTIFYWINDOW, (WPARAM)parent->GetHWND(), 0);

        m_mctrlWin = new mCtrlWrapper(this, m_mctrlHandle);

        {
            wxUxThemeHandle hTheme(this, L"ExplorerMenu::Toolbar");
            SetBackgroundColour(wxRGBToColour(wxUxThemeEngine::GetIfActive()->GetThemeSysColor(hTheme, COLOR_WINDOW)));
        }
    }

    ~MenuBarWindow()
    {
        m_mctrlWin->Destroy();
        ::DestroyWindow(m_mctrlHandle);

        if (--g_mctrlInitialized == 0)
            mcMenubar_Terminate();
    }

    void SetHMENU(WXHMENU menu)
    {
        SendMessage(m_mctrlHandle, MC_MBM_SETMENU, 0, (LPARAM) menu);
        SendMessage(m_mctrlHandle, MC_MBM_REFRESH, 0, 0);
    }

    bool TranslateMenubarMessage(WXMSG *pMsg)
    {
        wxRecursionGuard guard(m_flagReenterMctrl);

        if (!guard.IsInside())
        {
            MSG *msg = (MSG *) pMsg;
            if (mcIsMenubarMessage(m_mctrlHandle, msg))
                return true;
        }
        return false;
    }

    void DoSetSize(int x, int y, int width, int height, int sizeFlags) override
    {
        wxWindow::DoSetSize(x, y, width, height, sizeFlags);
        SendMessage(m_mctrlHandle, MC_MBM_REFRESH, 0, 0);
    }

    wxSize DoGetBestSize() const override
    {
        wxSize sizeBest = wxDefaultSize;
        SIZE size;
        if (::SendMessage(m_mctrlHandle, TB_GETMAXSIZE, 0, (LPARAM) &size))
        {
            sizeBest.x = size.cx;
            sizeBest.y = size.cy + 1;
            CacheBestSize(sizeBest);
        }
        return sizeBest;
    }

private:
    class mCtrlWrapper : public wxNativeWindow
    {
    public:
        mCtrlWrapper(wxWindow *parent, WXHWND wnd) : wxNativeWindow(parent, wxID_ANY, wnd) {}

        WXLRESULT MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) override
        {
            switch (nMsg)
            {
                case WM_COMMAND:
                case WM_NOTIFY:
                    return MSWDefWindowProc(nMsg, wParam, lParam);
            }
            return wxNativeWindow::MSWWindowProc(nMsg, wParam, lParam);
        }
    };

    mCtrlWrapper *m_mctrlWin;
    WXHWND m_mctrlHandle;
    wxRecursionGuardFlag m_flagReenterMctrl;
};

wxFrameWithWindows10Menubar::wxFrameWithWindows10Menubar(wxWindow *parent,
                                                         wxWindowID id,
                                                         const wxString& title,
                                                         const wxPoint& pos,
                                                         const wxSize& size,
                                                         long style,
                                                         const wxString& name)
    : wxFrame(parent, id, title, pos, size, style, name), m_menuBar(nullptr)
{
    if (ShouldUse())
    {
        m_menuBar = new MenuBarWindow(this);
    }
}

bool wxFrameWithWindows10Menubar::ShouldUse() const
{
    if (!IsWindows10OrGreater())
        return false;

    if (!wxUxThemeEngine::GetIfActive())
        return false;

    // Detect screen readers and use normal menubar with them, because the
    // mCtrl one isn't accessible.
    BOOL running;
    BOOL ret = SystemParametersInfo(SPI_GETSCREENREADER, 0, &running, 0);
    if (ret && running)
        return false;

    return true;
}

wxPoint wxFrameWithWindows10Menubar::GetClientAreaOrigin() const
{
    wxPoint pt = wxFrame::GetClientAreaOrigin();
    if (IsUsed())
    {
        pt.y += m_menuBar->GetSize().y + MENUBAR_OFFSET;
    }
    return pt;
}

void wxFrameWithWindows10Menubar::PositionToolBar()
{
    // Position both the toolbar and our menu bar (which is really another toolbar) here.
    if (!IsUsed())
    {
        wxFrame::PositionToolBar();
        return;
    }

    // don't call our (or even wxTopLevelWindow) version because we want
    // the real (full) client area size, not excluding the tool/status bar
    int width, height;
    wxWindow::DoGetClientSize(&width, &height);

    int y = MENUBAR_OFFSET;

    // use the 'real' MSW position here, don't offset relatively to the
    // client area origin
    int mch = m_menuBar->GetBestSize().y;
    m_menuBar->SetSize(0, y, width, mch, wxSIZE_NO_ADJUSTMENTS);
    y += mch;

    wxToolBar *toolbar = GetToolBar();
    if (toolbar && toolbar->IsShown())
    {
        int tbh = toolbar->GetSize().y;
        toolbar->SetSize(0, y, width + 8, tbh, wxSIZE_NO_ADJUSTMENTS);
    }
}

bool wxFrameWithWindows10Menubar::MSWTranslateMessage(WXMSG *msg)
{
    if (wxFrame::MSWTranslateMessage(msg))
        return true;

    if (IsUsed() && m_menuBar->TranslateMenubarMessage(msg))
        return true;

    return false;
}

WXLRESULT wxFrameWithWindows10Menubar::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam)
{
    if (IsUsed())
    {
        // mCtrl doesn't play nice with the wxMSW's menus interaction where accelerators are updated
        // when a menu is opened (which works because TranslateAccelerators() normally sends a fake
        // event for that... if there's a normal menu). We need to refresh menus before accelerators
        // are used so that e.g. disabled state is accurately updated.
        if (message == WM_COMMAND && HIWORD(wParam) == 1/*accel*/)
            GetMenuBar()->UpdateMenus();
    }

    return wxFrame::MSWWindowProc(message, wParam, lParam);
}

void wxFrameWithWindows10Menubar::InternalSetMenuBar()
{
    if (IsUsed())
    {
        m_menuBar->SetHMENU(m_hMenu);
    }
    else
    {
        wxFrame::InternalSetMenuBar();
    }
}
<|endoftext|>