<commit_before>#ifndef LUACPP_STACK_VALUE_HPP
#define LUACPP_STACK_VALUE_HPP

#include "luacpp/type.hpp"
#include "luacpp/push.hpp"
#include <boost/config.hpp>
#include <silicium/config.hpp>
#include <cassert>

namespace lua
{
	template <class T>
	struct variable
	{
		T value;
	};

	template <class Size>
	struct basic_stack_value SILICIUM_FINAL : private Size, public any_local
	{
		basic_stack_value() BOOST_NOEXCEPT
		{
		}

		basic_stack_value(lua_State &state, int address, Size size = Size()) BOOST_NOEXCEPT
			: Size(size)
			, any_local(state, address)
#ifndef NDEBUG
			, m_initial_top(lua_gettop(&state))
#endif
		{
			assert(address >= 1);
			assert(m_initial_top >= Size::value);
		}

		basic_stack_value(basic_stack_value &&other) BOOST_NOEXCEPT
			: Size(std::move(other))
			, any_local(std::move(other))
#ifndef NDEBUG
			, m_initial_top(other.m_initial_top)
#endif
		{
			static_cast<any_local &>(other) = any_local();
		}

		basic_stack_value &operator = (basic_stack_value &&other) BOOST_NOEXCEPT
		{
			using boost::swap;
			swap(static_cast<Size &>(*this), static_cast<Size &>(other));
			swap(static_cast<any_local &>(*this), static_cast<any_local &>(other));
#ifndef NDEBUG
			swap(m_initial_top, other.m_initial_top);
#endif
			return *this;
		}

		~basic_stack_value() BOOST_NOEXCEPT
		{
			if (!thread())
			{
				return;
			}
#ifndef NDEBUG
			int current_top = lua_gettop(thread());
#endif
			assert(current_top == m_initial_top);
			lua_pop(thread(), Size::value);
			assert(lua_gettop(thread()) == (m_initial_top - Size::value));
		}

		void swap(basic_stack_value &other) BOOST_NOEXCEPT
		{
			std::swap(*this, other);
		}

		void pop()
		{
			basic_stack_value().swap(*this);
		}

		void release() BOOST_NOEXCEPT
		{
			assert(thread());
			static_cast<any_local &>(*this) = any_local();
		}

		int size() const BOOST_NOEXCEPT
		{
			return Size::value;
		}

		void assert_top() const
		{
			assert(thread());
			assert(lua_gettop(thread()) == from_bottom());
		}

		any_local element_address(int zero_based_offset) const BOOST_NOEXCEPT
		{
			assert(zero_based_offset < size());
			return any_local(*thread(), from_bottom() + zero_based_offset);
		}

		template <class Pushable>
		basic_stack_value<std::integral_constant<int, 1>> operator[](Pushable &&index)
		{
			using lua::push;
			assert(thread());
			push(*thread(), std::forward<Pushable>(index));
			lua_gettable(thread(), from_bottom());
			return basic_stack_value<std::integral_constant<int, 1>>(*thread(), lua_gettop(thread()));
		}

	private:

#ifndef NDEBUG
		int m_initial_top;
#endif

		SILICIUM_DELETED_FUNCTION(basic_stack_value(basic_stack_value const &))
		SILICIUM_DELETED_FUNCTION(basic_stack_value &operator = (basic_stack_value const &))
	};

	typedef basic_stack_value<std::integral_constant<int, 1>> stack_value;
	typedef basic_stack_value<variable<int>> stack_array;

	inline void replace(stack_value &replacement, stack_value &replaced)
	{
		assert(replacement.thread());
		assert(replacement.thread() == replaced.thread());
		replacement.assert_top();
		lua_replace(replacement.thread(), replaced.from_bottom());
		replacement.release();
		replacement = std::move(replaced);
	}

	template <class Size>
	any_local at(basic_stack_value<Size> const &array, int index)
	{
		assert(index < array.size());
		return any_local(*array.thread(), array.from_bottom() + index);
	}

	inline void push(lua_State &L, stack_value const &value)
	{
		if (&L == value.thread())
		{
			lua_pushvalue(&L, value.from_bottom());
		}
		else
		{
			using lua::push;
			push(*value.thread(), value);
			lua_xmove(value.thread(), &L, 1);
		}
	}

	inline void push(lua_State &L, stack_value &&value)
	{
		if (&L == value.thread() &&
			value.from_bottom() == lua_gettop(&L))
		{
			value.release();
		}
		else
		{
			push(L, static_cast<stack_value const &>(value));
		}
	}

	struct xmover
	{
		stack_value const *from;
	};

	inline void push(lua_State &L, xmover const &value)
	{
		assert(value.from);
		assert(&L != value.from->thread());
		using lua::push;
		push(*value.from->thread(), *value.from);
		lua_xmove(value.from->thread(), &L, 1);
	}
}

#endif
<commit_msg>implement stack_array::set(key, value)<commit_after>#ifndef LUACPP_STACK_VALUE_HPP
#define LUACPP_STACK_VALUE_HPP

#include "luacpp/type.hpp"
#include "luacpp/push.hpp"
#include <boost/config.hpp>
#include <silicium/config.hpp>
#include <cassert>

namespace lua
{
	template <class T>
	struct variable
	{
		T value;
	};

	template <class Size>
	struct basic_stack_value SILICIUM_FINAL : private Size, public any_local
	{
		basic_stack_value() BOOST_NOEXCEPT
		{
		}

		basic_stack_value(lua_State &state, int address, Size size = Size()) BOOST_NOEXCEPT
			: Size(size)
			, any_local(state, address)
#ifndef NDEBUG
			, m_initial_top(lua_gettop(&state))
#endif
		{
			assert(address >= 1);
			assert(m_initial_top >= Size::value);
		}

		basic_stack_value(basic_stack_value &&other) BOOST_NOEXCEPT
			: Size(std::move(other))
			, any_local(std::move(other))
#ifndef NDEBUG
			, m_initial_top(other.m_initial_top)
#endif
		{
			static_cast<any_local &>(other) = any_local();
		}

		basic_stack_value &operator = (basic_stack_value &&other) BOOST_NOEXCEPT
		{
			using boost::swap;
			swap(static_cast<Size &>(*this), static_cast<Size &>(other));
			swap(static_cast<any_local &>(*this), static_cast<any_local &>(other));
#ifndef NDEBUG
			swap(m_initial_top, other.m_initial_top);
#endif
			return *this;
		}

		~basic_stack_value() BOOST_NOEXCEPT
		{
			if (!thread())
			{
				return;
			}
#ifndef NDEBUG
			int current_top = lua_gettop(thread());
#endif
			assert(current_top == m_initial_top);
			lua_pop(thread(), Size::value);
			assert(lua_gettop(thread()) == (m_initial_top - Size::value));
		}

		void swap(basic_stack_value &other) BOOST_NOEXCEPT
		{
			std::swap(*this, other);
		}

		void pop()
		{
			basic_stack_value().swap(*this);
		}

		void release() BOOST_NOEXCEPT
		{
			assert(thread());
			static_cast<any_local &>(*this) = any_local();
		}

		int size() const BOOST_NOEXCEPT
		{
			return Size::value;
		}

		void assert_top() const
		{
			assert(thread());
			assert(lua_gettop(thread()) == from_bottom());
		}

		any_local element_address(int zero_based_offset) const BOOST_NOEXCEPT
		{
			assert(zero_based_offset < size());
			return any_local(*thread(), from_bottom() + zero_based_offset);
		}

		template <class Pushable>
		basic_stack_value<std::integral_constant<int, 1>> operator[](Pushable &&index)
		{
			using lua::push;
			assert(thread());
			push(*thread(), std::forward<Pushable>(index));
			lua_gettable(thread(), from_bottom());
			return basic_stack_value<std::integral_constant<int, 1>>(*thread(), lua_gettop(thread()));
		}

		template <class PushableKey, class PushableValue>
		void set(PushableKey &&key, PushableValue &&value) const
		{
			using lua::push;
			assert(thread());
			push(*thread(), std::forward<PushableKey>(key));
			push(*thread(), std::forward<PushableValue>(value));
			lua_settable(thread(), from_bottom());
		}

	private:

#ifndef NDEBUG
		int m_initial_top;
#endif

		SILICIUM_DELETED_FUNCTION(basic_stack_value(basic_stack_value const &))
		SILICIUM_DELETED_FUNCTION(basic_stack_value &operator = (basic_stack_value const &))
	};

	typedef basic_stack_value<std::integral_constant<int, 1>> stack_value;
	typedef basic_stack_value<variable<int>> stack_array;

	inline void replace(stack_value &replacement, stack_value &replaced)
	{
		assert(replacement.thread());
		assert(replacement.thread() == replaced.thread());
		replacement.assert_top();
		lua_replace(replacement.thread(), replaced.from_bottom());
		replacement.release();
		replacement = std::move(replaced);
	}

	template <class Size>
	any_local at(basic_stack_value<Size> const &array, int index)
	{
		assert(index < array.size());
		return any_local(*array.thread(), array.from_bottom() + index);
	}

	inline void push(lua_State &L, stack_value const &value)
	{
		if (&L == value.thread())
		{
			lua_pushvalue(&L, value.from_bottom());
		}
		else
		{
			using lua::push;
			push(*value.thread(), value);
			lua_xmove(value.thread(), &L, 1);
		}
	}

	inline void push(lua_State &L, stack_value &&value)
	{
		if (&L == value.thread() &&
			value.from_bottom() == lua_gettop(&L))
		{
			value.release();
		}
		else
		{
			push(L, static_cast<stack_value const &>(value));
		}
	}

	struct xmover
	{
		stack_value const *from;
	};

	inline void push(lua_State &L, xmover const &value)
	{
		assert(value.from);
		assert(&L != value.from->thread());
		using lua::push;
		push(*value.from->thread(), *value.from);
		lua_xmove(value.from->thread(), &L, 1);
	}
}

#endif
<|endoftext|>


<commit_before>/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.
 */
#include "sasl_authenticator.h"

#include "common/util.h"

#include <openssl/rand.h>
#include <openssl/md5.h>

#include <iomanip>
#include <map>
#include <sstream>

namespace hdfs {

static std::string QuoteString(const std::string &src);
static std::string GetMD5Digest(const std::string &src);
static std::string BinaryToHex(const std::string &src);

static const char kDigestUri[] = "hdfs/0";
static const size_t kMaxBufferSize = 65536;

DigestMD5Authenticator::DigestMD5Authenticator(const std::string &username,
                                               const std::string &password,
                                               bool mock_nonce)
    : username_(username), password_(password), nonce_count_(0),
      TEST_mock_cnonce_(mock_nonce) {}

Status DigestMD5Authenticator::EvaluateResponse(const std::string &payload,
                                                std::string *result) {
  Status status = ParseFirstChallenge(payload);
  if (status.ok()) {
    status = GenerateFirstResponse(result);
  }
  return status;
}

size_t DigestMD5Authenticator::NextToken(const std::string &payload, size_t off,
                                         std::string *tok) {
  tok->clear();
  if (off >= payload.size()) {
    return std::string::npos;
  }

  char c = payload[off];
  if (c == '=' || c == ',') {
    *tok = c;
    return off + 1;
  }

  int quote_count = 0;
  for (; off < payload.size(); ++off) {
    char c = payload[off];
    if (c == '"') {
      ++quote_count;
      if (quote_count == 2) {
        return off + 1;
      }
      continue;
    }

    if (c == '=') {
      if (quote_count) {
        tok->append(&c, 1);
      } else {
        break;
      }
    } else if (('0' <= c && c <= '9') || ('a' <= c && c <= 'z') ||
               ('A' <= c && c <= 'Z') || c == '+' || c == '/' || c == '-' ||
               c == '_' || c == '@') {
      tok->append(&c, 1);
    } else {
      break;
    }
  }
  return off;
}

void DigestMD5Authenticator::GenerateCNonce() {
  if (!TEST_mock_cnonce_) {
    char buf[8];
    RAND_pseudo_bytes(reinterpret_cast<unsigned char *>(buf), sizeof(buf));
    cnonce_ = Base64Encode(std::string(buf, sizeof(buf)));
  }
}

Status DigestMD5Authenticator::ParseFirstChallenge(const std::string &payload) {
  std::map<std::string, std::string> props;
  std::string token;
  enum {
    kStateLVal,
    kStateEqual,
    kStateRVal,
    kStateCommaOrEnd,
  };

  int state = kStateLVal;

  std::string lval, rval;
  size_t off = 0;
  while (true) {
    off = NextToken(payload, off, &token);
    if (off == std::string::npos) {
      break;
    }

    switch (state) {
    case kStateLVal:
      lval = token;
      state = kStateEqual;
      break;
    case kStateEqual:
      state = kStateRVal;
      break;
    case kStateRVal:
      rval = token;
      props[lval] = rval;
      state = kStateCommaOrEnd;
      break;
    case kStateCommaOrEnd:
      state = kStateLVal;
      break;
    }
  }

  if (props["algorithm"] != "md5-sess" || props["charset"] != "utf-8" ||
      props.find("nonce") == props.end()) {
    return Status::Error("Invalid challenge");
  }
  realm_ = props["realm"];
  nonce_ = props["nonce"];
  qop_ = props["qop"];
  return Status::OK();
}

Status DigestMD5Authenticator::GenerateFirstResponse(std::string *result) {
  // TODO: Support auth-int and auth-conf
  // Handle cipher
  if (qop_ != "auth") {
    return Status::Unimplemented();
  }

  std::stringstream ss;
  GenerateCNonce();
  ss << "charset=utf-8,username=\"" << QuoteString(username_) << "\""
     << ",authzid=\"" << QuoteString(username_) << "\""
     << ",nonce=\"" << QuoteString(nonce_) << "\""
     << ",digest-uri=\"" << kDigestUri << "\""
     << ",maxbuf=" << kMaxBufferSize << ",cnonce=\"" << cnonce_ << "\"";

  if (realm_.size()) {
    ss << ",realm=\"" << QuoteString(realm_) << "\"";
  }

  ss << ",nc=" << std::hex << std::setw(8) << std::setfill('0')
     << ++nonce_count_;
  std::string response_value;
  GenerateResponseValue(&response_value);
  ss << ",response=" << response_value;
  *result = ss.str();
  return result->size() > 4096 ? Status::Error("Response too big")
                               : Status::OK();
}

/**
 * Generate the response value specified in S 2.1.2.1 in RFC2831.
 **/
Status
DigestMD5Authenticator::GenerateResponseValue(std::string *response_value) {
  std::stringstream begin_a1, a1_ss;
  std::string a1, a2;

  if (qop_ == "auth") {
    a2 = std::string("AUTHENTICATE:") + kDigestUri;
  } else {
    a2 = std::string("AUTHENTICATE:") + kDigestUri +
         ":00000000000000000000000000000000";
  }

  begin_a1 << username_ << ":" << realm_ << ":" << password_;
  a1_ss << GetMD5Digest(begin_a1.str()) << ":" << nonce_ << ":" << cnonce_
        << ":" << username_;

  std::stringstream combine_ss;
  combine_ss << BinaryToHex(GetMD5Digest(a1_ss.str())) << ":" << nonce_ << ":"
             << std::hex << std::setw(8) << std::setfill('0') << nonce_count_
             << ":" << cnonce_ << ":" << qop_ << ":"
             << BinaryToHex(GetMD5Digest(a2));
  *response_value = BinaryToHex(GetMD5Digest(combine_ss.str()));
  return Status::OK();
}

static std::string QuoteString(const std::string &src) {
  std::string dst;
  dst.resize(2 * src.size());
  size_t j = 0;
  for (size_t i = 0; i < src.size(); ++i) {
    if (src[i] == '"') {
      dst[j++] = '\\';
    }
    dst[j++] = src[i];
  }
  dst.resize(j);
  return dst;
}

static std::string GetMD5Digest(const std::string &src) {
  MD5_CTX ctx;
  unsigned long long res[2];
  MD5_Init(&ctx);
  MD5_Update(&ctx, src.c_str(), src.size());
  MD5_Final(reinterpret_cast<unsigned char *>(res), &ctx);
  return std::string(reinterpret_cast<char *>(res), sizeof(res));
}

static std::string BinaryToHex(const std::string &src) {
  std::stringstream ss;
  ss << std::hex << std::setfill('0');
  for (size_t i = 0; i < src.size(); ++i) {
    unsigned c = (unsigned)(static_cast<unsigned char>(src[i]));
    ss << std::setw(2) << c;
  }
  return ss.str();
}
}
<commit_msg>HDFS-9116. Suppress false positives from Valgrind on uninitialized variables in tests. Contributed by Haohui Mai.<commit_after>/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.
 */
#include "sasl_authenticator.h"

#include "common/util.h"

#include <openssl/rand.h>
#include <openssl/md5.h>

#include <iomanip>
#include <map>
#include <sstream>

namespace hdfs {

static std::string QuoteString(const std::string &src);
static std::string GetMD5Digest(const std::string &src);
static std::string BinaryToHex(const std::string &src);

static const char kDigestUri[] = "hdfs/0";
static const size_t kMaxBufferSize = 65536;

DigestMD5Authenticator::DigestMD5Authenticator(const std::string &username,
                                               const std::string &password,
                                               bool mock_nonce)
    : username_(username), password_(password), nonce_count_(0),
      TEST_mock_cnonce_(mock_nonce) {}

Status DigestMD5Authenticator::EvaluateResponse(const std::string &payload,
                                                std::string *result) {
  Status status = ParseFirstChallenge(payload);
  if (status.ok()) {
    status = GenerateFirstResponse(result);
  }
  return status;
}

size_t DigestMD5Authenticator::NextToken(const std::string &payload, size_t off,
                                         std::string *tok) {
  tok->clear();
  if (off >= payload.size()) {
    return std::string::npos;
  }

  char c = payload[off];
  if (c == '=' || c == ',') {
    *tok = c;
    return off + 1;
  }

  int quote_count = 0;
  for (; off < payload.size(); ++off) {
    char c = payload[off];
    if (c == '"') {
      ++quote_count;
      if (quote_count == 2) {
        return off + 1;
      }
      continue;
    }

    if (c == '=') {
      if (quote_count) {
        tok->append(&c, 1);
      } else {
        break;
      }
    } else if (('0' <= c && c <= '9') || ('a' <= c && c <= 'z') ||
               ('A' <= c && c <= 'Z') || c == '+' || c == '/' || c == '-' ||
               c == '_' || c == '@') {
      tok->append(&c, 1);
    } else {
      break;
    }
  }
  return off;
}

void DigestMD5Authenticator::GenerateCNonce() {
  if (!TEST_mock_cnonce_) {
    char buf[8] = {0,};
    RAND_pseudo_bytes(reinterpret_cast<unsigned char *>(buf), sizeof(buf));
    cnonce_ = Base64Encode(std::string(buf, sizeof(buf)));
  }
}

Status DigestMD5Authenticator::ParseFirstChallenge(const std::string &payload) {
  std::map<std::string, std::string> props;
  std::string token;
  enum {
    kStateLVal,
    kStateEqual,
    kStateRVal,
    kStateCommaOrEnd,
  };

  int state = kStateLVal;

  std::string lval, rval;
  size_t off = 0;
  while (true) {
    off = NextToken(payload, off, &token);
    if (off == std::string::npos) {
      break;
    }

    switch (state) {
    case kStateLVal:
      lval = token;
      state = kStateEqual;
      break;
    case kStateEqual:
      state = kStateRVal;
      break;
    case kStateRVal:
      rval = token;
      props[lval] = rval;
      state = kStateCommaOrEnd;
      break;
    case kStateCommaOrEnd:
      state = kStateLVal;
      break;
    }
  }

  if (props["algorithm"] != "md5-sess" || props["charset"] != "utf-8" ||
      props.find("nonce") == props.end()) {
    return Status::Error("Invalid challenge");
  }
  realm_ = props["realm"];
  nonce_ = props["nonce"];
  qop_ = props["qop"];
  return Status::OK();
}

Status DigestMD5Authenticator::GenerateFirstResponse(std::string *result) {
  // TODO: Support auth-int and auth-conf
  // Handle cipher
  if (qop_ != "auth") {
    return Status::Unimplemented();
  }

  std::stringstream ss;
  GenerateCNonce();
  ss << "charset=utf-8,username=\"" << QuoteString(username_) << "\""
     << ",authzid=\"" << QuoteString(username_) << "\""
     << ",nonce=\"" << QuoteString(nonce_) << "\""
     << ",digest-uri=\"" << kDigestUri << "\""
     << ",maxbuf=" << kMaxBufferSize << ",cnonce=\"" << cnonce_ << "\"";

  if (realm_.size()) {
    ss << ",realm=\"" << QuoteString(realm_) << "\"";
  }

  ss << ",nc=" << std::hex << std::setw(8) << std::setfill('0')
     << ++nonce_count_;
  std::string response_value;
  GenerateResponseValue(&response_value);
  ss << ",response=" << response_value;
  *result = ss.str();
  return result->size() > 4096 ? Status::Error("Response too big")
                               : Status::OK();
}

/**
 * Generate the response value specified in S 2.1.2.1 in RFC2831.
 **/
Status
DigestMD5Authenticator::GenerateResponseValue(std::string *response_value) {
  std::stringstream begin_a1, a1_ss;
  std::string a1, a2;

  if (qop_ == "auth") {
    a2 = std::string("AUTHENTICATE:") + kDigestUri;
  } else {
    a2 = std::string("AUTHENTICATE:") + kDigestUri +
         ":00000000000000000000000000000000";
  }

  begin_a1 << username_ << ":" << realm_ << ":" << password_;
  a1_ss << GetMD5Digest(begin_a1.str()) << ":" << nonce_ << ":" << cnonce_
        << ":" << username_;

  std::stringstream combine_ss;
  combine_ss << BinaryToHex(GetMD5Digest(a1_ss.str())) << ":" << nonce_ << ":"
             << std::hex << std::setw(8) << std::setfill('0') << nonce_count_
             << ":" << cnonce_ << ":" << qop_ << ":"
             << BinaryToHex(GetMD5Digest(a2));
  *response_value = BinaryToHex(GetMD5Digest(combine_ss.str()));
  return Status::OK();
}

static std::string QuoteString(const std::string &src) {
  std::string dst;
  dst.resize(2 * src.size());
  size_t j = 0;
  for (size_t i = 0; i < src.size(); ++i) {
    if (src[i] == '"') {
      dst[j++] = '\\';
    }
    dst[j++] = src[i];
  }
  dst.resize(j);
  return dst;
}

static std::string GetMD5Digest(const std::string &src) {
  MD5_CTX ctx;
  unsigned long long res[2];
  MD5_Init(&ctx);
  MD5_Update(&ctx, src.c_str(), src.size());
  MD5_Final(reinterpret_cast<unsigned char *>(res), &ctx);
  return std::string(reinterpret_cast<char *>(res), sizeof(res));
}

static std::string BinaryToHex(const std::string &src) {
  std::stringstream ss;
  ss << std::hex << std::setfill('0');
  for (size_t i = 0; i < src.size(); ++i) {
    unsigned c = (unsigned)(static_cast<unsigned char>(src[i]));
    ss << std::setw(2) << c;
  }
  return ss.str();
}
}
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: impprn.hxx,v $
 *
 *  $Revision: 1.3 $
 *
 *  last change: $Author: kz $ $Date: 2007-10-09 15:17:32 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#ifndef _SV_IMPPRN_HXX
#define _SV_IMPPRN_HXX

#ifndef _SV_PRINT_HXX
#include <vcl/print.hxx>
#endif
#ifndef _SV_TIMER_HXX
#include <vcl/timer.hxx>
#endif
#ifndef _VCL_IMPDEL_HXX
#include <vcl/impdel.hxx>
#endif

class Queue;
struct QueuePage;

// ----------------
// - ImplQPrinter -
// ----------------

/*
    ImplQPrinter is on most systems a simple buffer that allows a potential
    lengthy print job to be printed in the background. For this it saves all
    normal drawing operations for each printed page to a metafile, then spooling
    the metafiles timer based to a normal printer. The application can act in the meantime
    including changing the original document without influencing the print job.

    On some systems (currently Mac/Aqua Cocoa) ImplQPrinter has the additional
    purpose of adapting to the print system: here theprint systems starts a
    job and will not return from that function until it has ended; to do so
    it queries for each consecutive page to be printed. Also the Cocoa print system
    needs to know the number of pages BEFORE starting a print job. Since our Printer
    does not know that, we need to do the completing spooling to ImplQPrinter before
    we can actually print to the real print system. Let's call this the pull model
    instead of the push model (because the systems pulls the pages).
*/

class ImplQPrinter : public Printer, public vcl::DeletionNotifier
{
private:
    Printer*    mpParent;
    Queue*      mpQueue;
    AutoTimer   maTimer;
    bool        mbAborted;
    bool        mbUserCopy;
    bool        mbDestroyAllowed;
    bool        mbDestroyed;

    GDIMetaFile maCurPageMetaFile;
    long        mnMaxBmpDPIX;
    long        mnMaxBmpDPIY;
    ULONG       mnRestoreDrawMode;
    int         mnCurCopyCount;

                DECL_LINK( ImplPrintHdl, Timer* );

                ~ImplQPrinter();

    void        ImplPrintMtf( GDIMetaFile& rMtf, long nMaxBmpDPIX, long nMaxBmpDPIY );

                ImplQPrinter( const ImplQPrinter& rPrinter );
    Printer&    operator =( const ImplQPrinter& rPrinter );

    void        PrePrintPage( QueuePage* );
    void        PostPrintPage();

public:

                ImplQPrinter( Printer* pParent );
    void        Destroy();

    void        StartQueuePrint();
    void        EndQueuePrint();
    void        AbortQueuePrint();
    void        AddQueuePage( GDIMetaFile* pPage, USHORT nPage, BOOL bNewJobSetup );

    bool        IsUserCopy() const { return mbUserCopy; }
    void        SetUserCopy( bool bSet ) { mbUserCopy = bSet; }

    /**
    used by pull implementation to emit the next page
    */
    void        PrintNextPage();
    /**
    used by pull implementation to get the number of physical pages
    (that is how often PrintNextPage should be called)
    */
    ULONG       GetPrintPageCount();
};

#endif  // _SV_IMPPRN_HXX
<commit_msg>INTEGRATION: CWS nativeprintdlg01_DEV300 (1.3.62); FILE MERGED 2007/12/18 10:25:54 pl 1.3.62.2: fix avisibility warning 2007/11/26 14:48:35 pl 1.3.62.1: #i77579# changes for aqua printing system<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: impprn.hxx,v $
 *
 *  $Revision: 1.4 $
 *
 *  last change: $Author: kz $ $Date: 2008-03-05 17:00:07 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#ifndef _SV_IMPPRN_HXX
#define _SV_IMPPRN_HXX

#ifndef _SV_PRINT_HXX
#include <vcl/print.hxx>
#endif
#ifndef _SV_TIMER_HXX
#include <vcl/timer.hxx>
#endif
#ifndef _VCL_IMPDEL_HXX
#include <vcl/impdel.hxx>
#endif

#include <vector>

struct QueuePage;

// ----------------
// - ImplQPrinter -
// ----------------

/*
    ImplQPrinter is on most systems a simple buffer that allows a potential
    lengthy print job to be printed in the background. For this it saves all
    normal drawing operations for each printed page to a metafile, then spooling
    the metafiles timer based to a normal printer. The application can act in the meantime
    including changing the original document without influencing the print job.

    On some systems (currently Mac/Aqua Cocoa) ImplQPrinter has the additional
    purpose of adapting to the print system: here theprint systems starts a
    job and will not return from that function until it has ended; to do so
    it queries for each consecutive page to be printed. Also the Cocoa print system
    needs to know the number of pages BEFORE starting a print job. Since our Printer
    does not know that, we need to do the completing spooling to ImplQPrinter before
    we can actually print to the real print system. Let's call this the pull model
    instead of the push model (because the systems pulls the pages).
*/

class ImplQPrinter : public Printer, public vcl::DeletionNotifier
{
private:
    Printer*                        mpParent;
    std::vector< QueuePage* >       maQueue;
    AutoTimer                       maTimer;
    bool                            mbAborted;
    bool                            mbUserCopy;
    bool                            mbDestroyAllowed;
    bool                            mbDestroyed;

    GDIMetaFile                     maCurPageMetaFile;
    long                            mnMaxBmpDPIX;
    long                            mnMaxBmpDPIY;
    ULONG                           mnRestoreDrawMode;
    int                             mnCurCopyCount;

                DECL_LINK( ImplPrintHdl, Timer* );

                ~ImplQPrinter();

    void        ImplPrintMtf( GDIMetaFile& rMtf, long nMaxBmpDPIX, long nMaxBmpDPIY );

                ImplQPrinter( const ImplQPrinter& rPrinter );
    Printer&    operator =( const ImplQPrinter& rPrinter );

    void        PrePrintPage( QueuePage* );
    void        PostPrintPage();

public:

                ImplQPrinter( Printer* pParent );
    void        Destroy();

    void        StartQueuePrint();
    void        EndQueuePrint();
    void        AbortQueuePrint();
    void        AddQueuePage( GDIMetaFile* pPage, USHORT nPage, BOOL bNewJobSetup );

    bool        IsUserCopy() const { return mbUserCopy; }
    void        SetUserCopy( bool bSet ) { mbUserCopy = bSet; }

    /**
    used by pull implementation to emit the next page
    */
    using Printer::PrintPage;
    void        PrintPage( unsigned int nPage );
    /**
    used by pull implementation to get the number of physical pages
    (that is how often PrintNextPage should be called)
    */
    ULONG       GetPrintPageCount();
};

#endif  // _SV_IMPPRN_HXX
<|endoftext|>


<commit_before>#include "vscview.h"
#include "debug.hpp"
#include <QDesktopWidget>
#include <QApplication>
#include "vscviewconf.h"

extern Factory *gFactory;

VSCView::VSCView(QWidget *parent, QTabWidget &pTabbed, string strName)
    : QWidget(parent), m_pTabbed(pTabbed), m_currentFocus(-1),
    m_bControlEnable(TRUE), m_strName(strName)
{
    ui.setupUi(this);
    m_pParent = parent;

    QVBoxLayout* layout = new QVBoxLayout();
    m_pVideo = new VSCVideoWall(this->ui.widget);
    m_pVideo->hide();
    //layout->setSpacing(10);

    layout->addWidget(m_pVideo);
    layout->setMargin(0);

    this->ui.widget->setLayout(layout);

    m_pVideo->show();

    m_pFloating = new QAction(QIcon(tr("images/open.ico")), tr("Floating"), this);
    //m_pUnFloating = new QAction(QIcon(tr("images/open.ico")), tr("UnFloating"), this);
    //connect(m_pFloating, SIGNAL(triggered()), this, SLOT(floatingAction()));
    //connect(m_pUnFloating, SIGNAL(triggered()), this, SLOT(unFloatingAction()));
    SetupConnections();
    createContentMenu();

    m_pPlayControl = new VSCPlayControl();
    m_bPlayControl = FALSE;
    m_pPlayControl->hide();
    VSCViewDataItemDefault(m_ViewItem);
    ui.pushButton8x8->hide();
    //setMouseTracking(true);
}

void VSCView::SetupConnections()
{
    //connect(m_pFloating, SIGNAL(triggered()), this, SLOT(floatingAction()));

    connect(ui.pushButton2x2, SIGNAL(clicked()), m_pVideo, SLOT(SetLayoutMode2x2()));
    connect(ui.pushButton3x3, SIGNAL(clicked()), m_pVideo, SLOT(SetLayoutMode3x3()));
    connect(ui.pushButton4x4, SIGNAL(clicked()), m_pVideo, SLOT(SetLayoutMode4x4()));
    connect(ui.pushButton6, SIGNAL(clicked()), m_pVideo, SLOT(SetLayoutMode6()));
    connect(ui.pushButton1, SIGNAL(clicked()), m_pVideo, SLOT(SetLayoutMode1()));
    connect(ui.pushButton12p1, SIGNAL(clicked()), m_pVideo, SLOT(SetLayoutMode12p1()));
    connect(ui.pushButton5x5, SIGNAL(clicked()), m_pVideo, SLOT(SetLayoutMode5x5()));
    connect(ui.pushButton6x6, SIGNAL(clicked()), m_pVideo, SLOT(SetLayoutMode6x6()));
    connect(ui.pushButton8x8, SIGNAL(clicked()), m_pVideo, SLOT(SetLayoutMode8x8()));

    connect(ui.pushButton2x2, SIGNAL(clicked()), this, SLOT(SetLayoutMode2x2()));
    connect(ui.pushButton3x3, SIGNAL(clicked()), this, SLOT(SetLayoutMode3x3()));
    connect(ui.pushButton4x4, SIGNAL(clicked()), this, SLOT(SetLayoutMode4x4()));
    connect(ui.pushButton6, SIGNAL(clicked()), this, SLOT(SetLayoutMode6()));
    connect(ui.pushButton1, SIGNAL(clicked()), this, SLOT(SetLayoutMode1()));
    connect(ui.pushButton12p1, SIGNAL(clicked()), this, SLOT(SetLayoutMode12p1()));
    connect(ui.pushButton5x5, SIGNAL(clicked()), this, SLOT(SetLayoutMode5x5()));
    connect(ui.pushButton6x6, SIGNAL(clicked()), this, SLOT(SetLayoutMode6x6()));

    connect(ui.pushButton8x8, SIGNAL(clicked()), this, SLOT(SetLayoutMode8x8()));

    connect(ui.pushButtonView, SIGNAL(clicked()), this, SLOT(ViewClicked()));
    //connect(ui.pushButtonPB, SIGNAL(clicked()), this, SLOT(ShowPlayControl()));
    connect(m_pVideo, SIGNAL(ShowDisplayClicked(int)), this,
                                        SLOT(ShowDisplayClicked(int)));
    connect(m_pVideo, SIGNAL(ShowFloatingClicked()), this,
                                        SLOT(floatingClicked()));
    connect(m_pVideo, SIGNAL(ShowControlPanelClicked()), this,
                                        SLOT(ControlPanelClicked()));
    connect(m_pVideo, SIGNAL(ShowTabbedClicked()), this,
                                        SLOT(TabbedClicked()));
    connect(m_pVideo, SIGNAL(ShowFocusClicked(int)), this,
                                        SLOT(ShowFocusClicked(int)));
    connect(m_pVideo, SIGNAL(Layout1Clicked(int)), this,
                                        SLOT(ShowLayout1Clicked(int)));
    connect(m_pVideo, SIGNAL(ShowViewClicked(int)), this,
                                        SLOT(ShowViewClicked(int)));
}

VSCView::~VSCView()
{
    m_pPlayControl->hide();
    delete m_pPlayControl;
}

void VSCView::ShowPlayControl()
{
    if (m_bPlayControl == FALSE)
    {
        m_bPlayControl = TRUE;
        m_pPlayControl->show();
        int x, y, w, h;
        x = this->x();
        y = this->y();
        w = width();
        h = height();
        VDC_DEBUG( "%s rect x:%d y:%d w:%d h:%d \n",__FUNCTION__, x, y, w, h);
        //m_pPlayControl->move(x + w/2 - 200, y + h - 60);
    }else
    {
        m_bPlayControl = FALSE;
        m_pPlayControl->hide();
    }

}

void VSCView::ShowDisplayClicked(int nId)
{
    if (m_bFloated == FALSE)
    {
        return;
    }

    QDesktopWidget *desktop = QApplication::desktop();
    if ( 1==desktop->screenCount()  ) {
        // single monitor - use built in
        showFullScreen();
    } else {
        QRect rect = desktop->screenGeometry(nId);
        //move(rect.topLeft());
        setGeometry(desktop->screenGeometry(nId));
        setWindowState(Qt::WindowFullScreen | windowState());
        move(rect.topLeft());
        resize(rect.width(), rect.height());
    }

}

void VSCView::ShowFocusClicked(int nId)
{
    //if (m_currentFocus != nId)
    {
        if (m_currentFocus != -1)
        {
            m_pVideo->SetVideoFocus(m_currentFocus, FALSE);
        }
        m_pVideo->SetVideoFocus(nId, TRUE);
        m_currentFocus = nId;
    }
}

void VSCView::ShowLayout1Clicked(int nId)
{
    m_pVideo->SetLayout1Mode(nId);
}

void VSCView::mouseDoubleClickEvent(QMouseEvent *e)
{
    VDC_DEBUG( "%s mouseDoubleClickEvent\n",__FUNCTION__);
    QWidget::mouseDoubleClickEvent(e);
#if 0
    if(isFullScreen()) {
        //setParent(m_pParent);
        //resize(m_pParent->width(), m_pParent->height());
        //showMaximized();
        this->setWindowState(Qt::WindowMaximized);
    } else {
        //m_pParent = parentWidget();
        //setParent(NULL);
        //showFullScreen();
        this->setWindowState(Qt::WindowFullScreen);
    }
#endif
}

void VSCView::floatingClicked()
{
    if (m_bFloated == TRUE)
    {
        return;
    }

    m_pTabbed.setCurrentWidget(this);
    int thisIndex = m_pTabbed.currentIndex();

    m_pTabbed.removeTab(thisIndex);
    setParent(NULL);
    showMaximized();
    m_bFloated = TRUE;
}

void VSCView::ControlPanelClicked()
{
	if (m_bControlEnable == TRUE)
	{
		ui.widgetControl->hide();
		m_bControlEnable = FALSE;
	}else
	{
		ui.widgetControl->show();
		m_bControlEnable = TRUE;
	}

	return;
}

void VSCView::TabbedClicked()
{
    if (m_bFloated == FALSE)
    {
        return;
    }
    VideoWallLayoutMode nMode;
    m_pVideo->GetLayoutMode(nMode);
    QIcon icon1;
    switch(nMode)
    {
        case LAYOUT_MODE_2X2:
            icon1.addFile(QStringLiteral(":/view/resources/2x2.png"),
                QSize(), QIcon::Normal, QIcon::Off);
            break;
        case LAYOUT_MODE_3X3:
            icon1.addFile(QStringLiteral(":/view/resources/3x3.png"),
                QSize(), QIcon::Normal, QIcon::Off);
            break;
        case LAYOUT_MODE_4X4:
            icon1.addFile(QStringLiteral(":/view/resources/4x4.png"),
                QSize(), QIcon::Normal, QIcon::Off);
            break;
        case LAYOUT_MODE_1:
            icon1.addFile(QStringLiteral(":/view/resources/1x1.png"),
                QSize(), QIcon::Normal, QIcon::Off);
            break;
        case LAYOUT_MODE_6:
            icon1.addFile(QStringLiteral(":/view/resources/6.png"),
                QSize(), QIcon::Normal, QIcon::Off);
            break;
        case LAYOUT_MODE_12p1:
            icon1.addFile(QStringLiteral(":/view/resources/12+1.png"),
                QSize(), QIcon::Normal, QIcon::Off);
            break;
        case LAYOUT_MODE_5x5:
            icon1.addFile(QStringLiteral(":/view/resources/5x5.png"),
                QSize(), QIcon::Normal, QIcon::Off);
            break;
        case LAYOUT_MODE_6x6:
            icon1.addFile(QStringLiteral(":/view/resources/6x6.png"),
                QSize(), QIcon::Normal, QIcon::Off);
            break;
        case LAYOUT_MODE_8x8:
            icon1.addFile(QStringLiteral(":/view/resources/8x8.png"),
                QSize(), QIcon::Normal, QIcon::Off);
            break;
        case LAYOUT_MODE_ONE:
            break;
        default:
            break;
    }
    setParent(m_pParent);
    m_pTabbed.addTab(this,icon1, tr(m_strName.c_str()));
    m_pTabbed.setCurrentWidget(this);
    showMaximized();
    //showFullScreen();

    //resize(m_pParent->width(), m_pParent->height());

    m_bFloated = FALSE;
}

void VSCView::createContentMenu()
{
    //this->addAction(m_pFloating);
    //this->addAction(m_pUnFloating);
    //this->setContextMenuPolicy(Qt::ActionsContextMenu);
}

void VSCView::ViewClicked()
{
	VideoWallLayoutMode nMode;
	m_pVideo->GetLayoutMode(nMode);
       VSCViewConf view;
	view.SetName(m_ViewItem.Name);
       view.show();
       view.exec();

	VSCViewConfType type = view.GetType();
	if (type == VSC_VIEW_CONF_LAST)
	{
		return;
	}
	m_ViewItem.Mode = nMode;
	string strName = "View";
	view.GetName(strName);
	m_pVideo->GetPlayMap(m_ViewItem.Map, VSC_CONF_VIEW_CH_MAX);
	strcpy(m_ViewItem.Name, strName.c_str());
	if (type == VSC_VIEW_CONF_MODIFY && m_ViewItem.nId > 0)
	{
		gFactory->DelView(m_ViewItem.nId);
	}
	u32 nId = gFactory->AddView(m_ViewItem);
	gFactory->GetViewById(m_ViewItem, nId);

}

void VSCView::ShowViewClicked(int nId)
{
	gFactory->GetViewById(m_ViewItem, nId);
	m_pVideo->SetPlayMap(m_ViewItem.Map, VSC_CONF_VIEW_CH_MAX, m_ViewItem.Mode);
}<commit_msg>view add hover event<commit_after>#include "vscview.h"
#include "debug.hpp"
#include <QDesktopWidget>
#include <QApplication>
#include <QMouseEvent>
#include <QHoverEvent>
#include "vscviewconf.h"

extern Factory *gFactory;

VSCView::VSCView(QWidget *parent, QTabWidget &pTabbed, string strName)
    : QWidget(parent), m_pTabbed(pTabbed), m_currentFocus(-1),
    m_bControlEnable(TRUE), m_strName(strName)
{
    ui.setupUi(this);
    m_pParent = parent;
    m_lastHoverTime = time(NULL);

    this->setAttribute(Qt::WA_Hover,true);
    QVBoxLayout* layout = new QVBoxLayout();
    m_pVideo = new VSCVideoWall(this->ui.widget);
    m_pVideo->hide();
    //layout->setSpacing(10);

    layout->addWidget(m_pVideo);
    layout->setMargin(0);

    this->ui.widget->setLayout(layout);

    m_pVideo->show();

    m_pFloating = new QAction(QIcon(tr("images/open.ico")), tr("Floating"), this);
    //m_pUnFloating = new QAction(QIcon(tr("images/open.ico")), tr("UnFloating"), this);
    //connect(m_pFloating, SIGNAL(triggered()), this, SLOT(floatingAction()));
    //connect(m_pUnFloating, SIGNAL(triggered()), this, SLOT(unFloatingAction()));
    SetupConnections();
    createContentMenu();

    m_pPlayControl = new VSCPlayControl();
    m_bPlayControl = FALSE;
    m_pPlayControl->hide();
    VSCViewDataItemDefault(m_ViewItem);
    ui.pushButton8x8->hide();
    setMouseTracking(true);

    m_Timer = new QTimer(this);
    connect(m_Timer,SIGNAL(timeout()),this,SLOT(UpdateVideoControl()));
    m_Timer->start(1000);
}

void VSCView::UpdateVideoControl()
{
   time_t current = time(NULL);

    if (current - m_lastHoverTime > 3)
    {
        //m_pVideo->OffAllFocus();
    }
}

void VSCView::SetupConnections()
{
    //connect(m_pFloating, SIGNAL(triggered()), this, SLOT(floatingAction()));

    connect(ui.pushButton2x2, SIGNAL(clicked()), m_pVideo, SLOT(SetLayoutMode2x2()));
    connect(ui.pushButton3x3, SIGNAL(clicked()), m_pVideo, SLOT(SetLayoutMode3x3()));
    connect(ui.pushButton4x4, SIGNAL(clicked()), m_pVideo, SLOT(SetLayoutMode4x4()));
    connect(ui.pushButton6, SIGNAL(clicked()), m_pVideo, SLOT(SetLayoutMode6()));
    connect(ui.pushButton1, SIGNAL(clicked()), m_pVideo, SLOT(SetLayoutMode1()));
    connect(ui.pushButton12p1, SIGNAL(clicked()), m_pVideo, SLOT(SetLayoutMode12p1()));
    connect(ui.pushButton5x5, SIGNAL(clicked()), m_pVideo, SLOT(SetLayoutMode5x5()));
    connect(ui.pushButton6x6, SIGNAL(clicked()), m_pVideo, SLOT(SetLayoutMode6x6()));
    connect(ui.pushButton8x8, SIGNAL(clicked()), m_pVideo, SLOT(SetLayoutMode8x8()));

    connect(ui.pushButton2x2, SIGNAL(clicked()), this, SLOT(SetLayoutMode2x2()));
    connect(ui.pushButton3x3, SIGNAL(clicked()), this, SLOT(SetLayoutMode3x3()));
    connect(ui.pushButton4x4, SIGNAL(clicked()), this, SLOT(SetLayoutMode4x4()));
    connect(ui.pushButton6, SIGNAL(clicked()), this, SLOT(SetLayoutMode6()));
    connect(ui.pushButton1, SIGNAL(clicked()), this, SLOT(SetLayoutMode1()));
    connect(ui.pushButton12p1, SIGNAL(clicked()), this, SLOT(SetLayoutMode12p1()));
    connect(ui.pushButton5x5, SIGNAL(clicked()), this, SLOT(SetLayoutMode5x5()));
    connect(ui.pushButton6x6, SIGNAL(clicked()), this, SLOT(SetLayoutMode6x6()));

    connect(ui.pushButton8x8, SIGNAL(clicked()), this, SLOT(SetLayoutMode8x8()));

    connect(ui.pushButtonView, SIGNAL(clicked()), this, SLOT(ViewClicked()));
    //connect(ui.pushButtonPB, SIGNAL(clicked()), this, SLOT(ShowPlayControl()));
    connect(m_pVideo, SIGNAL(ShowDisplayClicked(int)), this,
                                        SLOT(ShowDisplayClicked(int)));
    connect(m_pVideo, SIGNAL(ShowFloatingClicked()), this,
                                        SLOT(floatingClicked()));
    connect(m_pVideo, SIGNAL(ShowControlPanelClicked()), this,
                                        SLOT(ControlPanelClicked()));
    connect(m_pVideo, SIGNAL(ShowTabbedClicked()), this,
                                        SLOT(TabbedClicked()));
    connect(m_pVideo, SIGNAL(ShowFocusClicked(int)), this,
                                        SLOT(ShowFocusClicked(int)));
    connect(m_pVideo, SIGNAL(Layout1Clicked(int)), this,
                                        SLOT(ShowLayout1Clicked(int)));
    connect(m_pVideo, SIGNAL(ShowViewClicked(int)), this,
                                        SLOT(ShowViewClicked(int)));
}

VSCView::~VSCView()
{
    m_pPlayControl->hide();
    delete m_pPlayControl;
}

void VSCView::mouseMoveEvent(QMouseEvent *event)
{
	QPoint posView = mapToGlobal(QPoint(0,0));
	QPoint posEvent = event->globalPos();
        VDC_DEBUG( "%s View (%d, %d)  Event (%d, %d)\n",
			__FUNCTION__, posView.x(), posView.y(),
			posEvent.x(), posEvent.y());
        //m_pVideo->OffAllFocus();
}

bool VSCView::event(QEvent *e)
{
#if 1

	QHoverEvent *event = static_cast<QHoverEvent*> (e);
	if (event == NULL)
	{
		return true;
	}

   if(e->type() == QEvent::HoverMove)
    {
	QPoint posView = mapToGlobal(QPoint(0,0));
	QPoint posEvent = event->pos();
        //VDC_DEBUG( "%s View (%d, %d)  Event (%d, %d)\n",
	//		__FUNCTION__, posView.x(), posView.y(),
	//		posEvent.x(), posEvent.y());
        //m_pVideo->OffAllFocus();
        m_lastHoverTime = time(NULL);
    }
#endif
#if 1
    if(e->type() == QEvent::HoverLeave)
    {
	QPoint posView = mapToGlobal(QPoint(0,0));
	QPoint posEvent = event->pos();
        VDC_DEBUG( "%s Leave View (%d, %d)  Event (%d, %d)\n",
			__FUNCTION__, posView.x(), posView.y(),
			posEvent.x(), posEvent.y());
        m_pVideo->OffAllFocus();
    }
#endif

    return QWidget::event(e);
}

void VSCView::ShowPlayControl()
{
    if (m_bPlayControl == FALSE)
    {
        m_bPlayControl = TRUE;
        m_pPlayControl->show();
        int x, y, w, h;
        x = this->x();
        y = this->y();
        w = width();
        h = height();
        VDC_DEBUG( "%s rect x:%d y:%d w:%d h:%d \n",__FUNCTION__, x, y, w, h);
        //m_pPlayControl->move(x + w/2 - 200, y + h - 60);
    }else
    {
        m_bPlayControl = FALSE;
        m_pPlayControl->hide();
    }

}

void VSCView::ShowDisplayClicked(int nId)
{
    if (m_bFloated == FALSE)
    {
        return;
    }

    QDesktopWidget *desktop = QApplication::desktop();
    if ( 1==desktop->screenCount()  ) {
        // single monitor - use built in
        showFullScreen();
    } else {
        QRect rect = desktop->screenGeometry(nId);
        //move(rect.topLeft());
        setGeometry(desktop->screenGeometry(nId));
        setWindowState(Qt::WindowFullScreen | windowState());
        move(rect.topLeft());
        resize(rect.width(), rect.height());
    }

}

void VSCView::ShowFocusClicked(int nId)
{
    //if (m_currentFocus != nId)
    {
        if (m_currentFocus != -1)
        {
            m_pVideo->SetVideoFocus(m_currentFocus, FALSE);
        }
        m_pVideo->SetVideoFocus(nId, TRUE);
        m_currentFocus = nId;
    }
}

void VSCView::ShowLayout1Clicked(int nId)
{
    m_pVideo->SetLayout1Mode(nId);
}

void VSCView::mouseDoubleClickEvent(QMouseEvent *e)
{
    VDC_DEBUG( "%s mouseDoubleClickEvent\n",__FUNCTION__);
    QWidget::mouseDoubleClickEvent(e);
#if 0
    if(isFullScreen()) {
        //setParent(m_pParent);
        //resize(m_pParent->width(), m_pParent->height());
        //showMaximized();
        this->setWindowState(Qt::WindowMaximized);
    } else {
        //m_pParent = parentWidget();
        //setParent(NULL);
        //showFullScreen();
        this->setWindowState(Qt::WindowFullScreen);
    }
#endif
}

void VSCView::floatingClicked()
{
    if (m_bFloated == TRUE)
    {
        return;
    }

    m_pTabbed.setCurrentWidget(this);
    int thisIndex = m_pTabbed.currentIndex();

    m_pTabbed.removeTab(thisIndex);
    setParent(NULL);
    showMaximized();
    m_bFloated = TRUE;
}

void VSCView::ControlPanelClicked()
{
	if (m_bControlEnable == TRUE)
	{
		ui.widgetControl->hide();
		m_bControlEnable = FALSE;
	}else
	{
		ui.widgetControl->show();
		m_bControlEnable = TRUE;
	}

	return;
}

void VSCView::TabbedClicked()
{
    if (m_bFloated == FALSE)
    {
        return;
    }
    VideoWallLayoutMode nMode;
    m_pVideo->GetLayoutMode(nMode);
    QIcon icon1;
    switch(nMode)
    {
        case LAYOUT_MODE_2X2:
            icon1.addFile(QStringLiteral(":/view/resources/2x2.png"),
                QSize(), QIcon::Normal, QIcon::Off);
            break;
        case LAYOUT_MODE_3X3:
            icon1.addFile(QStringLiteral(":/view/resources/3x3.png"),
                QSize(), QIcon::Normal, QIcon::Off);
            break;
        case LAYOUT_MODE_4X4:
            icon1.addFile(QStringLiteral(":/view/resources/4x4.png"),
                QSize(), QIcon::Normal, QIcon::Off);
            break;
        case LAYOUT_MODE_1:
            icon1.addFile(QStringLiteral(":/view/resources/1x1.png"),
                QSize(), QIcon::Normal, QIcon::Off);
            break;
        case LAYOUT_MODE_6:
            icon1.addFile(QStringLiteral(":/view/resources/6.png"),
                QSize(), QIcon::Normal, QIcon::Off);
            break;
        case LAYOUT_MODE_12p1:
            icon1.addFile(QStringLiteral(":/view/resources/12+1.png"),
                QSize(), QIcon::Normal, QIcon::Off);
            break;
        case LAYOUT_MODE_5x5:
            icon1.addFile(QStringLiteral(":/view/resources/5x5.png"),
                QSize(), QIcon::Normal, QIcon::Off);
            break;
        case LAYOUT_MODE_6x6:
            icon1.addFile(QStringLiteral(":/view/resources/6x6.png"),
                QSize(), QIcon::Normal, QIcon::Off);
            break;
        case LAYOUT_MODE_8x8:
            icon1.addFile(QStringLiteral(":/view/resources/8x8.png"),
                QSize(), QIcon::Normal, QIcon::Off);
            break;
        case LAYOUT_MODE_ONE:
            break;
        default:
            break;
    }
    setParent(m_pParent);
    m_pTabbed.addTab(this,icon1, tr(m_strName.c_str()));
    m_pTabbed.setCurrentWidget(this);
    showMaximized();
    //showFullScreen();

    //resize(m_pParent->width(), m_pParent->height());

    m_bFloated = FALSE;
}

void VSCView::createContentMenu()
{
    //this->addAction(m_pFloating);
    //this->addAction(m_pUnFloating);
    //this->setContextMenuPolicy(Qt::ActionsContextMenu);
}

void VSCView::ViewClicked()
{
	VideoWallLayoutMode nMode;
	m_pVideo->GetLayoutMode(nMode);
       VSCViewConf view;
	view.SetName(m_ViewItem.Name);
       view.show();
       view.exec();

	VSCViewConfType type = view.GetType();
	if (type == VSC_VIEW_CONF_LAST)
	{
		return;
	}
	m_ViewItem.Mode = nMode;
	string strName = "View";
	view.GetName(strName);
	m_pVideo->GetPlayMap(m_ViewItem.Map, VSC_CONF_VIEW_CH_MAX);
	strcpy(m_ViewItem.Name, strName.c_str());
	if (type == VSC_VIEW_CONF_MODIFY && m_ViewItem.nId > 0)
	{
		gFactory->DelView(m_ViewItem.nId);
	}
	u32 nId = gFactory->AddView(m_ViewItem);
	gFactory->GetViewById(m_ViewItem, nId);

}

void VSCView::ShowViewClicked(int nId)
{
	gFactory->GetViewById(m_ViewItem, nId);
	m_pVideo->SetPlayMap(m_ViewItem.Map, VSC_CONF_VIEW_CH_MAX, m_ViewItem.Mode);
}<|endoftext|>


<commit_before>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2017 Nathan Osman
 *
 * 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 <cstring>

#include <QJsonDocument>
#include <QJsonObject>
#include <QMetaObject>
#include <QMetaProperty>
#include <QtEndian>

#include <nitroshare/application.h>
#include <nitroshare/bundle.h>
#include <nitroshare/handler.h>
#include <nitroshare/handlerregistry.h>
#include <nitroshare/item.h>
#include <nitroshare/packet.h>
#include <nitroshare/transfer.h>
#include <nitroshare/transport.h>

#include "transfer_p.h"

TransferPrivate::TransferPrivate(Transfer *parent, Application *application, Transport *transport,
                                 Bundle *bundle, Transfer::Direction direction)
    : QObject(parent),
      q(parent),
      application(application),
      transport(transport),
      bundle(bundle),
      protocolState(TransferHeader),
      direction(direction),
      state(Transfer::Connecting),
      progress(0),
      itemIndex(0),
      itemCount(bundle ? bundle->rowCount(QModelIndex()) : 0),
      bytesTransferred(0),
      bytesTotal(bundle ? bundle->totalSize() : 0),
      currentItem(nullptr),
      currentItemBytesTransferred(0),
      currentItemBytesTotal(0)
{
    // If sending data, trigger the first packet after connection
    if (direction == Transfer::Send) {
        connect(transport, &Transport::connected, this, &TransferPrivate::onPacketSent);
    }

    connect(transport, &Transport::packetReceived, this, &TransferPrivate::onPacketReceived);
    connect(transport, &Transport::packetSent, this, &TransferPrivate::onPacketSent);
    connect(transport, &Transport::error, this, &TransferPrivate::onError);
}

void TransferPrivate::sendTransferHeader()
{
    QJsonObject object{
        { "name", "unknown" },
        { "count", QString::number(bundle->rowCount(QModelIndex())) },
        { "size", QString::number(bundle->totalSize()) }
    };

    Packet packet(Packet::Json, QJsonDocument(object).toJson());
    transport->sendPacket(&packet);

    // The next packet will be an item header
    protocolState = ItemHeader;
}

void TransferPrivate::sendItemHeader()
{
    // Grab the next item and attempt to open it
    currentItem = bundle->index(itemIndex, 0).data(Qt::UserRole).value<Item*>();
    if (!currentItem->open(Item::Read)) {
        setError(tr("unable to open \"%1\" for reading").arg(currentItem->name()), true);
        return;
    }

    // Reset transfer stats
    currentItemBytesTransferred = 0;
    currentItemBytesTotal = currentItem->size();

    // Build a JSON object with all of the properties
    const QMetaObject *metaObj = currentItem->metaObject();
    QJsonObject object;

    for (int i = metaObj->propertyOffset(); i < metaObj->propertyCount(); ++i) {
        const char *name = metaObj->property(i).name();
        object.insert(name, QJsonValue::fromVariant(currentItem->property(name)));
    }

    // Send the item header
    Packet packet(Packet::Json, QJsonDocument(object).toJson());
    transport->sendPacket(&packet);

    // If the item has a size, switch states; otherwise send the next item
    if (currentItemBytesTotal) {
        protocolState = ItemContent;
    } else {
        sendNext();
    }
}

void TransferPrivate::sendItemContent()
{
    QByteArray data = currentItem->read();
    Packet packet(Packet::Binary, data);
    transport->sendPacket(&packet);

    // Increment the number of bytes written to the socket
    bytesTransferred += data.length();
    currentItemBytesTransferred += data.length();

    updateProgress();

    // If the item completed, send the next one
    if (currentItemBytesTransferred >= currentItemBytesTotal) {
        sendNext();
    }
}

void TransferPrivate::sendNext()
{
    // Close the current item and increment the index
    currentItem->close();
    ++itemIndex;

    // If all items have been sent, move to the finished state and wait for
    // the success packet; otherwise, prepare to send the next item
    if (itemIndex == itemCount) {
        protocolState = Finished;
    } else {
        protocolState = ItemHeader;
    }
}

void TransferPrivate::processTransferHeader(Packet *packet)
{
    QJsonObject object = QJsonDocument::fromJson(packet->content()).object();

    // If the device name was provided, use it
    deviceName = object.value("name").toString();
    if (!deviceName.isEmpty()) {
        emit q->deviceNameChanged(deviceName);
    }

    // Strings must be used for 64-bit numbers
    itemCount = object.value("count").toString().toInt();
    bytesTotal = object.value("size").toString().toLongLong();

    // Prepare to receive the first item
    protocolState = ItemHeader;
}

void TransferPrivate::processItemHeader(Packet *packet)
{
    QJsonObject object = QJsonDocument::fromJson(packet->content()).object();

    // In order to maintain compatibility with legacy versions (which is very
    // desirable), if "type" is not in the object, assume "file" unless
    // "directory" is present (in which case, use that)
    QString type;
    if (object.contains("type")) {
        type = object.value("type").toString();
    } else {
        if (object.contains("directory")) {
            type = "directory";
        } else {
            type = "file";
        }
    }

    // Attempt to locate a handler for the type
    Handler *handler = application->handlerRegistry()->find(type);
    if (!handler) {
        setError(tr("unrecognized item type \"%1\"").arg(type), true);
        return;
    }

    // Use the handler to create an item and open it
    currentItem = handler->createItem(type, object.toVariantMap());
    currentItem->setParent(this);
    if (!currentItem->open(Item::Write)) {
        setError(tr("unable to open \"%1\" for writing").arg(currentItem->name()), true);
        return;
    }

    // Reset transfer stats
    currentItemBytesTransferred = 0;
    currentItemBytesTotal = currentItem->size();

    // If the item has a size, switch states; otherwise receive the next item
    if (currentItemBytesTotal) {
        protocolState = ItemContent;
    } else {
        processNext();
    }
}

void TransferPrivate::processItemContent(Packet *packet)
{
    currentItem->write(packet->content());

    // Add the number of bytes to the global & current item totals
    bytesTransferred += packet->content().size();
    currentItemBytesTransferred += packet->content().size();

    updateProgress();

    // If the current item is complete, advance to the next item or finish
    if (currentItemBytesTransferred >= currentItemBytesTotal) {
        processNext();
    }
}

void TransferPrivate::processNext()
{
    // Close & free the current item and increment the index
    currentItem->close();
    delete currentItem;
    ++itemIndex;

    // If there are no more items, send the success packet
    if (itemIndex == itemCount) {
        setSuccess(true);
    } else {
        protocolState = ItemHeader;
    }
}

void TransferPrivate::updateProgress()
{
    int oldProgress = progress;
    int newProgress = static_cast<int>(100.0 * bytesTotal ?
        static_cast<double>(bytesTransferred) / static_cast<double>(bytesTotal) : 0.0);

    // Only update progress if it has actually changed
    if (newProgress != oldProgress) {
        emit q->progressChanged(progress = newProgress);
    }
}

void TransferPrivate::setSuccess(bool send)
{
    if (send) {
        Packet packet(Packet::Success);
        transport->sendPacket(&packet);
    }

    emit q->stateChanged(state = Transfer::Succeeded);

    // Both peers should be aware that the transfer succeeded at this point
    transport->close();
}

void TransferPrivate::setError(const QString &message, bool send)
{
    if (send) {
        Packet packet(Packet::Error, message.toUtf8());
        transport->sendPacket(&packet);
    }

    emit q->errorChanged(error = message);
    emit q->stateChanged(state = Transfer::Failed);

    // An error on either end necessitates the transport be closed
    transport->close();
}

void TransferPrivate::onPacketReceived(Packet *packet)
{
    // If an error packet is received, set the error and quit
    if (packet->type() == Packet::Error) {
        setError(packet->content());
        return;
    }

    if (direction == Transfer::Send) {

        // The only packet expected when sending items is the success packet
        // which indicates the receiver got all of the files
        if (protocolState == Finished && packet->type() == Packet::Success) {
            setSuccess();
            return;
        }

    } else {

        // Dispatch the packet to the appropriate method based on state
        switch (protocolState) {
        case TransferHeader:
            processTransferHeader(packet);
            return;
        case ItemHeader:
            processItemHeader(packet);
            return;
        case ItemContent:
            processItemContent(packet);
            return;
        }
    }

    // Any other packet was unexpected - assume this is an error
    setError(tr("protocol error - unexpected packet"), true);
}

void TransferPrivate::onPacketSent()
{
    // We don't care about sent packets when receiving data
    if (direction == Transfer::Receive) {
        return;
    }

    switch (protocolState) {
    case TransferHeader:
        sendTransferHeader();
    case ItemHeader:
        sendItemHeader();
    case ItemContent:
        sendItemContent();
    }
}

void TransferPrivate::onError(const QString &message)
{
    setError(message, true);
}

Transfer::Transfer(Application *application, Transport *transport, QObject *parent)
    : QObject(parent),
      d(new TransferPrivate(this, application, transport, nullptr, Transfer::Receive))
{
}

Transfer::Transfer(Application *application, Transport *transport, Bundle *bundle, QObject *parent)
    : QObject(parent),
      d(new TransferPrivate(this, application, transport, bundle, Transfer::Send))
{
}

Transfer::Direction Transfer::direction() const
{
    return d->direction;
}

Transfer::State Transfer::state() const
{
    return d->state;
}

int Transfer::progress() const
{
    return d->progress;
}

QString Transfer::deviceName() const
{
    return d->deviceName;
}

QString Transfer::error() const
{
    return d->error;
}

void Transfer::cancel()
{
    d->setError(tr("transfer cancelled"), true);
}
<commit_msg>Display transfer errors in log.<commit_after>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2017 Nathan Osman
 *
 * 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 <cstring>

#include <QJsonDocument>
#include <QJsonObject>
#include <QMetaObject>
#include <QMetaProperty>
#include <QtEndian>

#include <nitroshare/application.h>
#include <nitroshare/bundle.h>
#include <nitroshare/handler.h>
#include <nitroshare/handlerregistry.h>
#include <nitroshare/item.h>
#include <nitroshare/logger.h>
#include <nitroshare/message.h>
#include <nitroshare/packet.h>
#include <nitroshare/transfer.h>
#include <nitroshare/transport.h>

#include "transfer_p.h"

const QString MessageTag = "Transfer";

TransferPrivate::TransferPrivate(Transfer *parent, Application *application, Transport *transport,
                                 Bundle *bundle, Transfer::Direction direction)
    : QObject(parent),
      q(parent),
      application(application),
      transport(transport),
      bundle(bundle),
      protocolState(TransferHeader),
      direction(direction),
      state(Transfer::Connecting),
      progress(0),
      itemIndex(0),
      itemCount(bundle ? bundle->rowCount(QModelIndex()) : 0),
      bytesTransferred(0),
      bytesTotal(bundle ? bundle->totalSize() : 0),
      currentItem(nullptr),
      currentItemBytesTransferred(0),
      currentItemBytesTotal(0)
{
    // If sending data, trigger the first packet after connection
    if (direction == Transfer::Send) {
        connect(transport, &Transport::connected, this, &TransferPrivate::onPacketSent);
    }

    connect(transport, &Transport::packetReceived, this, &TransferPrivate::onPacketReceived);
    connect(transport, &Transport::packetSent, this, &TransferPrivate::onPacketSent);
    connect(transport, &Transport::error, this, &TransferPrivate::onError);
}

void TransferPrivate::sendTransferHeader()
{
    QJsonObject object{
        { "name", "unknown" },
        { "count", QString::number(bundle->rowCount(QModelIndex())) },
        { "size", QString::number(bundle->totalSize()) }
    };

    Packet packet(Packet::Json, QJsonDocument(object).toJson());
    transport->sendPacket(&packet);

    // The next packet will be an item header
    protocolState = ItemHeader;
}

void TransferPrivate::sendItemHeader()
{
    // Grab the next item and attempt to open it
    currentItem = bundle->index(itemIndex, 0).data(Qt::UserRole).value<Item*>();
    if (!currentItem->open(Item::Read)) {
        setError(tr("unable to open \"%1\" for reading").arg(currentItem->name()), true);
        return;
    }

    // Reset transfer stats
    currentItemBytesTransferred = 0;
    currentItemBytesTotal = currentItem->size();

    // Build a JSON object with all of the properties
    const QMetaObject *metaObj = currentItem->metaObject();
    QJsonObject object;

    for (int i = metaObj->propertyOffset(); i < metaObj->propertyCount(); ++i) {
        const char *name = metaObj->property(i).name();
        object.insert(name, QJsonValue::fromVariant(currentItem->property(name)));
    }

    // Send the item header
    Packet packet(Packet::Json, QJsonDocument(object).toJson());
    transport->sendPacket(&packet);

    // If the item has a size, switch states; otherwise send the next item
    if (currentItemBytesTotal) {
        protocolState = ItemContent;
    } else {
        sendNext();
    }
}

void TransferPrivate::sendItemContent()
{
    QByteArray data = currentItem->read();
    Packet packet(Packet::Binary, data);
    transport->sendPacket(&packet);

    // Increment the number of bytes written to the socket
    bytesTransferred += data.length();
    currentItemBytesTransferred += data.length();

    updateProgress();

    // If the item completed, send the next one
    if (currentItemBytesTransferred >= currentItemBytesTotal) {
        sendNext();
    }
}

void TransferPrivate::sendNext()
{
    // Close the current item and increment the index
    currentItem->close();
    ++itemIndex;

    // If all items have been sent, move to the finished state and wait for
    // the success packet; otherwise, prepare to send the next item
    if (itemIndex == itemCount) {
        protocolState = Finished;
    } else {
        protocolState = ItemHeader;
    }
}

void TransferPrivate::processTransferHeader(Packet *packet)
{
    QJsonObject object = QJsonDocument::fromJson(packet->content()).object();

    // If the device name was provided, use it
    deviceName = object.value("name").toString();
    if (!deviceName.isEmpty()) {
        emit q->deviceNameChanged(deviceName);
    }

    // Strings must be used for 64-bit numbers
    itemCount = object.value("count").toString().toInt();
    bytesTotal = object.value("size").toString().toLongLong();

    // Prepare to receive the first item
    protocolState = ItemHeader;
}

void TransferPrivate::processItemHeader(Packet *packet)
{
    QJsonObject object = QJsonDocument::fromJson(packet->content()).object();

    // In order to maintain compatibility with legacy versions (which is very
    // desirable), if "type" is not in the object, assume "file" unless
    // "directory" is present (in which case, use that)
    QString type;
    if (object.contains("type")) {
        type = object.value("type").toString();
    } else {
        if (object.contains("directory")) {
            type = "directory";
        } else {
            type = "file";
        }
    }

    // Attempt to locate a handler for the type
    Handler *handler = application->handlerRegistry()->find(type);
    if (!handler) {
        setError(tr("unrecognized item type \"%1\"").arg(type), true);
        return;
    }

    // Use the handler to create an item and open it
    currentItem = handler->createItem(type, object.toVariantMap());
    currentItem->setParent(this);
    if (!currentItem->open(Item::Write)) {
        setError(tr("unable to open \"%1\" for writing").arg(currentItem->name()), true);
        return;
    }

    // Reset transfer stats
    currentItemBytesTransferred = 0;
    currentItemBytesTotal = currentItem->size();

    // If the item has a size, switch states; otherwise receive the next item
    if (currentItemBytesTotal) {
        protocolState = ItemContent;
    } else {
        processNext();
    }
}

void TransferPrivate::processItemContent(Packet *packet)
{
    currentItem->write(packet->content());

    // Add the number of bytes to the global & current item totals
    bytesTransferred += packet->content().size();
    currentItemBytesTransferred += packet->content().size();

    updateProgress();

    // If the current item is complete, advance to the next item or finish
    if (currentItemBytesTransferred >= currentItemBytesTotal) {
        processNext();
    }
}

void TransferPrivate::processNext()
{
    // Close & free the current item and increment the index
    currentItem->close();
    delete currentItem;
    ++itemIndex;

    // If there are no more items, send the success packet
    if (itemIndex == itemCount) {
        setSuccess(true);
    } else {
        protocolState = ItemHeader;
    }
}

void TransferPrivate::updateProgress()
{
    int oldProgress = progress;
    int newProgress = static_cast<int>(100.0 * bytesTotal ?
        static_cast<double>(bytesTransferred) / static_cast<double>(bytesTotal) : 0.0);

    // Only update progress if it has actually changed
    if (newProgress != oldProgress) {
        emit q->progressChanged(progress = newProgress);
    }
}

void TransferPrivate::setSuccess(bool send)
{
    if (send) {
        Packet packet(Packet::Success);
        transport->sendPacket(&packet);
    }

    emit q->stateChanged(state = Transfer::Succeeded);

    // Both peers should be aware that the transfer succeeded at this point
    transport->close();
}

void TransferPrivate::setError(const QString &message, bool send)
{
    application->logger()->log(new Message(
        Message::Error,
        MessageTag,
        message
    ));

    if (send) {
        Packet packet(Packet::Error, message.toUtf8());
        transport->sendPacket(&packet);
    }

    emit q->errorChanged(error = message);
    emit q->stateChanged(state = Transfer::Failed);

    // An error on either end necessitates the transport be closed
    transport->close();
}

void TransferPrivate::onPacketReceived(Packet *packet)
{
    // If an error packet is received, set the error and quit
    if (packet->type() == Packet::Error) {
        setError(packet->content());
        return;
    }

    if (direction == Transfer::Send) {

        // The only packet expected when sending items is the success packet
        // which indicates the receiver got all of the files
        if (protocolState == Finished && packet->type() == Packet::Success) {
            setSuccess();
            return;
        }

    } else {

        // Dispatch the packet to the appropriate method based on state
        switch (protocolState) {
        case TransferHeader:
            processTransferHeader(packet);
            return;
        case ItemHeader:
            processItemHeader(packet);
            return;
        case ItemContent:
            processItemContent(packet);
            return;
        }
    }

    // Any other packet was unexpected - assume this is an error
    setError(tr("protocol error - unexpected packet"), true);
}

void TransferPrivate::onPacketSent()
{
    // We don't care about sent packets when receiving data
    if (direction == Transfer::Receive) {
        return;
    }

    switch (protocolState) {
    case TransferHeader:
        sendTransferHeader();
    case ItemHeader:
        sendItemHeader();
    case ItemContent:
        sendItemContent();
    }
}

void TransferPrivate::onError(const QString &message)
{
    setError(message, true);
}

Transfer::Transfer(Application *application, Transport *transport, QObject *parent)
    : QObject(parent),
      d(new TransferPrivate(this, application, transport, nullptr, Transfer::Receive))
{
}

Transfer::Transfer(Application *application, Transport *transport, Bundle *bundle, QObject *parent)
    : QObject(parent),
      d(new TransferPrivate(this, application, transport, bundle, Transfer::Send))
{
}

Transfer::Direction Transfer::direction() const
{
    return d->direction;
}

Transfer::State Transfer::state() const
{
    return d->state;
}

int Transfer::progress() const
{
    return d->progress;
}

QString Transfer::deviceName() const
{
    return d->deviceName;
}

QString Transfer::error() const
{
    return d->error;
}

void Transfer::cancel()
{
    d->setError(tr("transfer cancelled"), true);
}
<|endoftext|>


<commit_before>/* +------------------------------------------------------------------------+
   |                     Mobile Robot Programming Toolkit (MRPT)            |
   |                          http://www.mrpt.org/                          |
   |                                                                        |
   | Copyright (c) 2005-2018, Individual contributors, see AUTHORS file     |
   | See: http://www.mrpt.org/Authors - All rights reserved.                |
   | Released under BSD License. See details in http://www.mrpt.org/License |
   +------------------------------------------------------------------------+ */

#define MRPT_NO_WARN_BIG_HDR  // Yes, we really want to include all classes.
#include <mrpt/obs.h>

#include <mrpt/io/CMemoryStream.h>
#include <gtest/gtest.h>
#include <CTraitsTest.h>

using namespace mrpt;
using namespace mrpt::obs;
using namespace mrpt::io;
using namespace mrpt::math;
using namespace mrpt::serialization;
using namespace std;

#define TEST_CLASS_MOVE_COPY_CTORS(_classname) \
	template class mrpt::CTraitsTest<_classname>

TEST_CLASS_MOVE_COPY_CTORS(CObservation2DRangeScan);
TEST_CLASS_MOVE_COPY_CTORS(CObservation3DRangeScan);
TEST_CLASS_MOVE_COPY_CTORS(CObservationRGBD360);
TEST_CLASS_MOVE_COPY_CTORS(CObservationBearingRange);
TEST_CLASS_MOVE_COPY_CTORS(CObservationBatteryState);
TEST_CLASS_MOVE_COPY_CTORS(CObservationWirelessPower);
TEST_CLASS_MOVE_COPY_CTORS(CObservationRFID);
TEST_CLASS_MOVE_COPY_CTORS(CObservationBeaconRanges);
TEST_CLASS_MOVE_COPY_CTORS(CObservationComment);
TEST_CLASS_MOVE_COPY_CTORS(CObservationGasSensors);
TEST_CLASS_MOVE_COPY_CTORS(CObservationGPS);
TEST_CLASS_MOVE_COPY_CTORS(CObservationReflectivity);
TEST_CLASS_MOVE_COPY_CTORS(CObservationIMU);
TEST_CLASS_MOVE_COPY_CTORS(CObservationOdometry);
TEST_CLASS_MOVE_COPY_CTORS(CObservationRange);
#if MRPT_HAS_OPENCV  // These classes need CImage serialization
TEST_CLASS_MOVE_COPY_CTORS(CObservationImage);
TEST_CLASS_MOVE_COPY_CTORS(CObservationStereoImages);
#endif
TEST_CLASS_MOVE_COPY_CTORS(CObservationCANBusJ1939);
TEST_CLASS_MOVE_COPY_CTORS(CObservationRawDAQ);
TEST_CLASS_MOVE_COPY_CTORS(CObservation6DFeatures);
TEST_CLASS_MOVE_COPY_CTORS(CObservationVelodyneScan);
TEST_CLASS_MOVE_COPY_CTORS(CActionRobotMovement2D);
TEST_CLASS_MOVE_COPY_CTORS(CActionRobotMovement3D);

const mrpt::rtti::TRuntimeClassId* lstClasses[] = {
	// Observations:
	CLASS_ID(CObservation2DRangeScan), CLASS_ID(CObservation3DRangeScan),
	CLASS_ID(CObservationRGBD360), CLASS_ID(CObservationBearingRange),
	CLASS_ID(CObservationBatteryState), CLASS_ID(CObservationWirelessPower),
	CLASS_ID(CObservationRFID), CLASS_ID(CObservationBeaconRanges),
	CLASS_ID(CObservationComment), CLASS_ID(CObservationGasSensors),
	CLASS_ID(CObservationGPS), CLASS_ID(CObservationReflectivity),
	CLASS_ID(CObservationIMU), CLASS_ID(CObservationOdometry),
	CLASS_ID(CObservationRange),
#if MRPT_HAS_OPENCV  // These classes need CImage serialization
	CLASS_ID(CObservationImage), CLASS_ID(CObservationStereoImages),
#endif
	CLASS_ID(CObservationCANBusJ1939), CLASS_ID(CObservationRawDAQ),
	CLASS_ID(CObservation6DFeatures), CLASS_ID(CObservationVelodyneScan),
	// Actions:
	CLASS_ID(CActionRobotMovement2D), CLASS_ID(CActionRobotMovement3D)};

// Create a set of classes, then serialize and deserialize to test possible
// bugs:
TEST(SerializeTestObs, WriteReadToMem)
{
	for (size_t i = 0; i < sizeof(lstClasses) / sizeof(lstClasses[0]); i++)
	{
		try
		{
			CMemoryStream buf;
			auto arch = mrpt::serialization::archiveFrom(buf);
			{
				CSerializable* o =
					static_cast<CSerializable*>(lstClasses[i]->createObject());
				arch << *o;
				delete o;
			}

			CSerializable::Ptr recons;
			buf.Seek(0);
			arch >> recons;
		}
		catch (std::exception& e)
		{
			GTEST_FAIL() << "Exception during serialization test for class '"
						 << lstClasses[i]->className << "':\n"
						 << e.what() << endl;
		}
	}
}

// Also try to convert them to octect vectors:
TEST(SerializeTestObs, WriteReadToOctectVectors)
{
	for (size_t i = 0; i < sizeof(lstClasses) / sizeof(lstClasses[0]); i++)
	{
		try
		{
			std::vector<uint8_t> buf;
			{
				CSerializable* o =
					static_cast<CSerializable*>(lstClasses[i]->createObject());
				mrpt::serialization::ObjectToOctetVector(o, buf);
				delete o;
			}

			CSerializable::Ptr recons;
			mrpt::serialization::OctetVectorToObject(buf, recons);
		}
		catch (std::exception& e)
		{
			GTEST_FAIL() << "Exception during serialization test for class '"
						 << lstClasses[i]->className << "':\n"
						 << e.what() << endl;
		}
	}
}
<commit_msg>unit tests: observations copy ctor and assign op<commit_after>/* +------------------------------------------------------------------------+
   |                     Mobile Robot Programming Toolkit (MRPT)            |
   |                          http://www.mrpt.org/                          |
   |                                                                        |
   | Copyright (c) 2005-2018, Individual contributors, see AUTHORS file     |
   | See: http://www.mrpt.org/Authors - All rights reserved.                |
   | Released under BSD License. See details in http://www.mrpt.org/License |
   +------------------------------------------------------------------------+ */

#define MRPT_NO_WARN_BIG_HDR  // Yes, we really want to include all classes.
#include <mrpt/obs.h>

#include <mrpt/io/CMemoryStream.h>
#include <gtest/gtest.h>
#include <CTraitsTest.h>
#include <sstream>

using namespace mrpt;
using namespace mrpt::obs;
using namespace mrpt::io;
using namespace mrpt::math;
using namespace mrpt::serialization;
using namespace std;

#define TEST_CLASS_MOVE_COPY_CTORS(_classname) \
	template class mrpt::CTraitsTest<_classname>

TEST_CLASS_MOVE_COPY_CTORS(CObservation2DRangeScan);
TEST_CLASS_MOVE_COPY_CTORS(CObservation3DRangeScan);
TEST_CLASS_MOVE_COPY_CTORS(CObservationRGBD360);
TEST_CLASS_MOVE_COPY_CTORS(CObservationBearingRange);
TEST_CLASS_MOVE_COPY_CTORS(CObservationBatteryState);
TEST_CLASS_MOVE_COPY_CTORS(CObservationWirelessPower);
TEST_CLASS_MOVE_COPY_CTORS(CObservationRFID);
TEST_CLASS_MOVE_COPY_CTORS(CObservationBeaconRanges);
TEST_CLASS_MOVE_COPY_CTORS(CObservationComment);
TEST_CLASS_MOVE_COPY_CTORS(CObservationGasSensors);
TEST_CLASS_MOVE_COPY_CTORS(CObservationGPS);
TEST_CLASS_MOVE_COPY_CTORS(CObservationReflectivity);
TEST_CLASS_MOVE_COPY_CTORS(CObservationIMU);
TEST_CLASS_MOVE_COPY_CTORS(CObservationOdometry);
TEST_CLASS_MOVE_COPY_CTORS(CObservationRange);
#if MRPT_HAS_OPENCV  // These classes need CImage serialization
TEST_CLASS_MOVE_COPY_CTORS(CObservationImage);
TEST_CLASS_MOVE_COPY_CTORS(CObservationStereoImages);
#endif
TEST_CLASS_MOVE_COPY_CTORS(CObservationCANBusJ1939);
TEST_CLASS_MOVE_COPY_CTORS(CObservationRawDAQ);
TEST_CLASS_MOVE_COPY_CTORS(CObservation6DFeatures);
TEST_CLASS_MOVE_COPY_CTORS(CObservationVelodyneScan);
TEST_CLASS_MOVE_COPY_CTORS(CActionRobotMovement2D);
TEST_CLASS_MOVE_COPY_CTORS(CActionRobotMovement3D);

const mrpt::rtti::TRuntimeClassId* lstClasses[] = {
	// Observations:
	CLASS_ID(CObservation2DRangeScan), CLASS_ID(CObservation3DRangeScan),
	CLASS_ID(CObservationRGBD360), CLASS_ID(CObservationBearingRange),
	CLASS_ID(CObservationBatteryState), CLASS_ID(CObservationWirelessPower),
	CLASS_ID(CObservationRFID), CLASS_ID(CObservationBeaconRanges),
	CLASS_ID(CObservationComment), CLASS_ID(CObservationGasSensors),
	CLASS_ID(CObservationGPS), CLASS_ID(CObservationReflectivity),
	CLASS_ID(CObservationIMU), CLASS_ID(CObservationOdometry),
	CLASS_ID(CObservationRange),
#if MRPT_HAS_OPENCV  // These classes need CImage serialization
	CLASS_ID(CObservationImage), CLASS_ID(CObservationStereoImages),
#endif
	CLASS_ID(CObservationCANBusJ1939), CLASS_ID(CObservationRawDAQ),
	CLASS_ID(CObservation6DFeatures), CLASS_ID(CObservationVelodyneScan),
	// Actions:
	CLASS_ID(CActionRobotMovement2D), CLASS_ID(CActionRobotMovement3D)};

// Create a set of classes, then serialize and deserialize to test possible
// bugs:
TEST(Observations, WriteReadToMem)
{
	for (size_t i = 0; i < sizeof(lstClasses) / sizeof(lstClasses[0]); i++)
	{
		try
		{
			CMemoryStream buf;
			auto arch = mrpt::serialization::archiveFrom(buf);
			{
				CSerializable* o =
					static_cast<CSerializable*>(lstClasses[i]->createObject());
				arch << *o;
				delete o;
			}

			CSerializable::Ptr recons;
			buf.Seek(0);
			arch >> recons;
		}
		catch (std::exception& e)
		{
			GTEST_FAIL() << "Exception during serialization test for class '"
						 << lstClasses[i]->className << "':\n"
						 << e.what() << endl;
		}
	}
}

// Also try to convert them to octect vectors:
TEST(Observations, WriteReadToOctectVectors)
{
	for (size_t i = 0; i < sizeof(lstClasses) / sizeof(lstClasses[0]); i++)
	{
		try
		{
			std::vector<uint8_t> buf;
			{
				CSerializable* o =
					static_cast<CSerializable*>(lstClasses[i]->createObject());
				mrpt::serialization::ObjectToOctetVector(o, buf);
				delete o;
			}

			CSerializable::Ptr recons;
			mrpt::serialization::OctetVectorToObject(buf, recons);
		}
		catch (std::exception& e)
		{
			GTEST_FAIL() << "Exception during serialization test for class '"
						 << lstClasses[i]->className << "':\n"
						 << e.what() << endl;
		}
	}
}

// Try to invoke a copy ctor and = operator:
template <class T>
void run_copy_tests()
{
	std::stringstream ss;
	{
		auto ptr_org = T::Create();
		auto ptr_copy_op = T::Create();
		*ptr_copy_op = *ptr_org;  // copy op
		ptr_org.reset();
		// make sure the copy works without erroneous mem accesses,etc.
		ptr_copy_op->getDescriptionAsText(ss);
	}
	{
		auto ptr_org = T::Create();
		auto ptr_copy_ctor = T::Create(*ptr_org);  // copy ctor
		ptr_org.reset();
		// make sure the copy works without erroneous mem accesses,etc.
		ptr_copy_ctor->getDescriptionAsText(ss);
	}
}

TEST(Observations, CopyCtorAssignOp)
{
	run_copy_tests<CObservation2DRangeScan>();
	run_copy_tests<CObservation3DRangeScan>();
	run_copy_tests<CObservationGPS>();
	run_copy_tests<CObservationIMU>();
	run_copy_tests<CObservationOdometry>();
}
<|endoftext|>


<commit_before>/* +------------------------------------------------------------------------+
   |                     Mobile Robot Programming Toolkit (MRPT)            |
   |                          https://www.mrpt.org/                         |
   |                                                                        |
   | Copyright (c) 2005-2022, Individual contributors, see AUTHORS file     |
   | See: https://www.mrpt.org/Authors - All rights reserved.               |
   | Released under BSD License. See: https://www.mrpt.org/License          |
   +------------------------------------------------------------------------+ */

#include <gtest/gtest.h>
#include <mrpt/opengl/CBox.h>
#include <mrpt/opengl/CFBORender.h>
#include <mrpt/opengl/CGridPlaneXY.h>
#include <mrpt/opengl/CMesh.h>
#include <mrpt/opengl/CMeshFast.h>
#include <mrpt/opengl/CSphere.h>
#include <mrpt/opengl/CText3D.h>
#include <mrpt/opengl/stock_objects.h>
#include <mrpt/system/filesystem.h>
//
#include <Eigen/Dense>
//
#include <mrpt/config.h>  // for MRPT_HAS_*
#include <test_mrpt_common.h>

static float imageDiff(
	const mrpt::img::CImage& im1, const mrpt::img::CImage& im2)
{
	mrpt::math::CMatrixFloat r1, g1, b1;
	im1.getAsRGBMatrices(r1, g1, b1);

	mrpt::math::CMatrixFloat r2, g2, b2;
	im2.getAsRGBMatrices(r2, g2, b2);

	return (r1 - r2).asEigen().array().abs().sum() +
		(g1 - g2).asEigen().array().abs().sum() +
		(b1 - b2).asEigen().array().abs().sum();
}

static void test_opengl_CFBORender(const bool useCameraFromIntrinsics)
{
	using namespace mrpt;  // _deg
	using namespace std::string_literals;  // s
	using namespace mrpt::opengl;

	const std::string expected_RGB_img_file = UNITTEST_BASEDIR +
		"/tests/CFBORender_expected_rgb_"s +
		(useCameraFromIntrinsics ? "camInt"s : "camFOV"s) + ".png"s;

	const std::string expected_depth_img_file = UNITTEST_BASEDIR +
		"/tests/CFBORender_expected_depth_"s +
		(useCameraFromIntrinsics ? "camInt"s : "camFOV"s) + ".png"s;

	COpenGLScene scene;

	{
		auto obj = mrpt::opengl::CGridPlaneXY::Create(-20, 20, -20, 20, 0, 5);
		obj->setColor(0.4f, 0.4f, 0.4f);
		scene.insert(obj);
	}
	{
		auto obj = mrpt::opengl::CBox::Create(
			mrpt::math::TPoint3D(0, 0, 0), mrpt::math::TPoint3D(.1, .1, .1));
		obj->setColor(1.0f, 0.f, 0.f);
		obj->setName("x");
		obj->enableShowName(true);
		obj->setLocation(1.0, 0, 0);
		scene.insert(obj);
	}
	{
		auto obj = mrpt::opengl::CTexturedPlane::Create();
		obj->setPlaneCorners(-10, 10, -10, 10);
		obj->setColor_u8(0x00, 0xff, 0xff, 0xff);
		obj->setLocation(0, 0, -14);
		scene.insert(obj);
	}
	{
		auto obj = mrpt::opengl::CSphere::Create();
		obj->setColor(0, 0, 1);
		obj->setRadius(1.0f);
		obj->setLocation(0, 1, 0);
		obj->setName("ball_1");
		scene.insert(obj);
	}

	{
		auto obj = mrpt::opengl::CText3D::Create("Hi there!");
		obj->setLocation(5.0, 3.0, 1.0);
		scene.insert(obj);
	}
	{
		auto obj = opengl::stock_objects::CornerXYZ(2.0f);
		obj->setLocation(-3, -2, 0.5);
		scene.insert(obj);
	}

	// CMeshFast, CMesh:
	{
		double off_x = -5.0, STEP_X = 1.0;
		opengl::CMeshFast::Ptr obj1 = opengl::CMeshFast::Create();
		opengl::CMeshFast::Ptr obj2 = opengl::CMeshFast::Create();
		opengl::CMesh::Ptr obj3 = opengl::CMesh::Create();
		opengl::CMesh::Ptr obj4 = opengl::CMesh::Create();

		obj1->setXBounds(-1, 1);
		obj1->setYBounds(-1, 1);

		const int W = 128, H = 128;

		mrpt::math::CMatrixDynamic<float> Z(H, W);

		for (int r = 0; r < H; r++)
			for (int c = 0; c < W; c++)
				Z(r, c) = sin(0.05 * (c + r) - 0.5) * cos(0.9 - 0.03 * r);

		const std::string texture_file = mrpt::system::getShareMRPTDir() +
			"datasets/sample-texture-terrain.jpg"s;

		mrpt::img::CImage im;

		// obj1:
		obj1->setZ(Z);
		obj1->enableColorFromZ(true);
		obj1->setPointSize(2.0);
		obj1->setLocation(off_x, 0, 0);
		scene.insert(obj1);

		// obj 2:
		if (im.loadFromFile(texture_file))
		{
			obj2->assignImageAndZ(im, Z);
			obj2->setPointSize(2.0);
			obj2->setLocation(off_x, 3, 0);
			scene.insert(obj2);
		}

		off_x += STEP_X;

		// obj 3:
		obj3->setZ(Z);
		obj3->enableColorFromZ(true, mrpt::img::cmJET);
		obj3->enableWireFrame(true);
		obj3->setLocation(off_x, 0, 0);
		obj3->cullFaces(mrpt::opengl::TCullFace::BACK);
		scene.insert(obj3);

		// obj 4:
		if (im.getWidth() > 1)
		{
			obj4->assignImageAndZ(im, Z);
			obj4->setLocation(off_x, 3, 0);
			obj4->cullFaces(mrpt::opengl::TCullFace::BACK);
			scene.insert(obj4);
		}
	}

	int width = 500, height = 400;
	const double cameraFOVdeg = 120.0;

	CFBORender renderer(width, height);
	mrpt::img::CImage frame(width, height, mrpt::img::CH_RGB);
	mrpt::math::CMatrixFloat depth;

	scene.getViewport()->setCustomBackgroundColor({0.3f, 0.3f, 0.3f, 1.0f});
	const float clipMax = 25.0f;
	scene.getViewport()->setViewportClipDistances(0.1, clipMax);

	{
		CCamera& camera = renderer.getCamera(scene);

		if (useCameraFromIntrinsics)
		{
			mrpt::img::TCamera c1;
			c1.ncols = width;
			c1.nrows = height;
			c1.fx(width * 0.5);
			c1.fy(width * 0.5);
			c1.cx(width / 2);
			c1.cy(height / 2);

			camera.setProjectiveFromPinhole(c1);
		}
		else
		{
			camera.setProjectiveFOVdeg(cameraFOVdeg);
		}

		// Defined by setPose() instead of orbit values:
		camera.set6DOFMode(true);

		// Reference camera pose:
		auto robotPose = mrpt::poses::CPose3D(
			0, 0, 10.0, 0.0_deg /*yaw*/, 90.0_deg /*pitch*/, 0.0_deg /*roll*/);

		// Convert to +Z pointing forward camera axes:
		auto camPose =
			robotPose +
			mrpt::poses::CPose3D::FromYawPitchRoll(
				90.0_deg /*yaw*/, 0.0_deg /*pitch*/, 90.0_deg /*roll*/);

		camera.setPose(camPose);
	}

	renderer.render_RGBD(scene, frame, depth);

	mrpt::img::CImage gt_frame;
	bool readOk_rgb = gt_frame.loadFromFile(expected_RGB_img_file);

	EXPECT_TRUE(readOk_rgb);
	const float rgb_diff = imageDiff(gt_frame, frame);
	std::cout << "rgb_diff=" << rgb_diff << "\n";
	EXPECT_LT(rgb_diff, 100.0f);

	{
		mrpt::img::CImage imDepth;
		depth *= (1.0f / clipMax);
		imDepth.setFromMatrix(depth, true);

		mrpt::img::CImage gt_imDepth;
		bool readOk_depth = gt_imDepth.loadFromFile(expected_depth_img_file);

		EXPECT_TRUE(readOk_depth);

		const float depth_diff = imageDiff(gt_imDepth, imDepth);
		std::cout << "depth_diff=" << depth_diff << "\n";
		EXPECT_LT(depth_diff, 100.0f);
	}
}

#if MRPT_HAS_OPENCV && MRPT_HAS_OPENGL_GLUT
TEST(OpenGL, CFBORender_camera_intrinsics)
#else
TEST(OpenGL, DISABLED_CFBORender_camera_intrinsics)
#endif
{
	test_opengl_CFBORender(true);
}

#if MRPT_HAS_OPENCV && MRPT_HAS_OPENGL_GLUT
TEST(OpenGL, CFBORender_camera_fov)
#else
TEST(OpenGL, DISABLED_CFBORender_camera_fov)
#endif
{
	test_opengl_CFBORender(false);
}
<commit_msg>fix macro check condition<commit_after>/* +------------------------------------------------------------------------+
   |                     Mobile Robot Programming Toolkit (MRPT)            |
   |                          https://www.mrpt.org/                         |
   |                                                                        |
   | Copyright (c) 2005-2022, Individual contributors, see AUTHORS file     |
   | See: https://www.mrpt.org/Authors - All rights reserved.               |
   | Released under BSD License. See: https://www.mrpt.org/License          |
   +------------------------------------------------------------------------+ */

#include <gtest/gtest.h>
#include <mrpt/opengl/CBox.h>
#include <mrpt/opengl/CFBORender.h>
#include <mrpt/opengl/CGridPlaneXY.h>
#include <mrpt/opengl/CMesh.h>
#include <mrpt/opengl/CMeshFast.h>
#include <mrpt/opengl/CSphere.h>
#include <mrpt/opengl/CText3D.h>
#include <mrpt/opengl/stock_objects.h>
#include <mrpt/system/filesystem.h>
//
#include <Eigen/Dense>
//
#include <mrpt/config.h>  // for MRPT_HAS_*
#include <test_mrpt_common.h>

static float imageDiff(
	const mrpt::img::CImage& im1, const mrpt::img::CImage& im2)
{
	mrpt::math::CMatrixFloat r1, g1, b1;
	im1.getAsRGBMatrices(r1, g1, b1);

	mrpt::math::CMatrixFloat r2, g2, b2;
	im2.getAsRGBMatrices(r2, g2, b2);

	return (r1 - r2).asEigen().array().abs().sum() +
		(g1 - g2).asEigen().array().abs().sum() +
		(b1 - b2).asEigen().array().abs().sum();
}

static void test_opengl_CFBORender(const bool useCameraFromIntrinsics)
{
	using namespace mrpt;  // _deg
	using namespace std::string_literals;  // s
	using namespace mrpt::opengl;

	const std::string expected_RGB_img_file = UNITTEST_BASEDIR +
		"/tests/CFBORender_expected_rgb_"s +
		(useCameraFromIntrinsics ? "camInt"s : "camFOV"s) + ".png"s;

	const std::string expected_depth_img_file = UNITTEST_BASEDIR +
		"/tests/CFBORender_expected_depth_"s +
		(useCameraFromIntrinsics ? "camInt"s : "camFOV"s) + ".png"s;

	COpenGLScene scene;

	{
		auto obj = mrpt::opengl::CGridPlaneXY::Create(-20, 20, -20, 20, 0, 5);
		obj->setColor(0.4f, 0.4f, 0.4f);
		scene.insert(obj);
	}
	{
		auto obj = mrpt::opengl::CBox::Create(
			mrpt::math::TPoint3D(0, 0, 0), mrpt::math::TPoint3D(.1, .1, .1));
		obj->setColor(1.0f, 0.f, 0.f);
		obj->setName("x");
		obj->enableShowName(true);
		obj->setLocation(1.0, 0, 0);
		scene.insert(obj);
	}
	{
		auto obj = mrpt::opengl::CTexturedPlane::Create();
		obj->setPlaneCorners(-10, 10, -10, 10);
		obj->setColor_u8(0x00, 0xff, 0xff, 0xff);
		obj->setLocation(0, 0, -14);
		scene.insert(obj);
	}
	{
		auto obj = mrpt::opengl::CSphere::Create();
		obj->setColor(0, 0, 1);
		obj->setRadius(1.0f);
		obj->setLocation(0, 1, 0);
		obj->setName("ball_1");
		scene.insert(obj);
	}

	{
		auto obj = mrpt::opengl::CText3D::Create("Hi there!");
		obj->setLocation(5.0, 3.0, 1.0);
		scene.insert(obj);
	}
	{
		auto obj = opengl::stock_objects::CornerXYZ(2.0f);
		obj->setLocation(-3, -2, 0.5);
		scene.insert(obj);
	}

	// CMeshFast, CMesh:
	{
		double off_x = -5.0, STEP_X = 1.0;
		opengl::CMeshFast::Ptr obj1 = opengl::CMeshFast::Create();
		opengl::CMeshFast::Ptr obj2 = opengl::CMeshFast::Create();
		opengl::CMesh::Ptr obj3 = opengl::CMesh::Create();
		opengl::CMesh::Ptr obj4 = opengl::CMesh::Create();

		obj1->setXBounds(-1, 1);
		obj1->setYBounds(-1, 1);

		const int W = 128, H = 128;

		mrpt::math::CMatrixDynamic<float> Z(H, W);

		for (int r = 0; r < H; r++)
			for (int c = 0; c < W; c++)
				Z(r, c) = sin(0.05 * (c + r) - 0.5) * cos(0.9 - 0.03 * r);

		const std::string texture_file = mrpt::system::getShareMRPTDir() +
			"datasets/sample-texture-terrain.jpg"s;

		mrpt::img::CImage im;

		// obj1:
		obj1->setZ(Z);
		obj1->enableColorFromZ(true);
		obj1->setPointSize(2.0);
		obj1->setLocation(off_x, 0, 0);
		scene.insert(obj1);

		// obj 2:
		if (im.loadFromFile(texture_file))
		{
			obj2->assignImageAndZ(im, Z);
			obj2->setPointSize(2.0);
			obj2->setLocation(off_x, 3, 0);
			scene.insert(obj2);
		}

		off_x += STEP_X;

		// obj 3:
		obj3->setZ(Z);
		obj3->enableColorFromZ(true, mrpt::img::cmJET);
		obj3->enableWireFrame(true);
		obj3->setLocation(off_x, 0, 0);
		obj3->cullFaces(mrpt::opengl::TCullFace::BACK);
		scene.insert(obj3);

		// obj 4:
		if (im.getWidth() > 1)
		{
			obj4->assignImageAndZ(im, Z);
			obj4->setLocation(off_x, 3, 0);
			obj4->cullFaces(mrpt::opengl::TCullFace::BACK);
			scene.insert(obj4);
		}
	}

	int width = 500, height = 400;
	const double cameraFOVdeg = 120.0;

	CFBORender renderer(width, height);
	mrpt::img::CImage frame(width, height, mrpt::img::CH_RGB);
	mrpt::math::CMatrixFloat depth;

	scene.getViewport()->setCustomBackgroundColor({0.3f, 0.3f, 0.3f, 1.0f});
	const float clipMax = 25.0f;
	scene.getViewport()->setViewportClipDistances(0.1, clipMax);

	{
		CCamera& camera = renderer.getCamera(scene);

		if (useCameraFromIntrinsics)
		{
			mrpt::img::TCamera c1;
			c1.ncols = width;
			c1.nrows = height;
			c1.fx(width * 0.5);
			c1.fy(width * 0.5);
			c1.cx(width / 2);
			c1.cy(height / 2);

			camera.setProjectiveFromPinhole(c1);
		}
		else
		{
			camera.setProjectiveFOVdeg(cameraFOVdeg);
		}

		// Defined by setPose() instead of orbit values:
		camera.set6DOFMode(true);

		// Reference camera pose:
		auto robotPose = mrpt::poses::CPose3D(
			0, 0, 10.0, 0.0_deg /*yaw*/, 90.0_deg /*pitch*/, 0.0_deg /*roll*/);

		// Convert to +Z pointing forward camera axes:
		auto camPose =
			robotPose +
			mrpt::poses::CPose3D::FromYawPitchRoll(
				90.0_deg /*yaw*/, 0.0_deg /*pitch*/, 90.0_deg /*roll*/);

		camera.setPose(camPose);
	}

	renderer.render_RGBD(scene, frame, depth);

	mrpt::img::CImage gt_frame;
	bool readOk_rgb = gt_frame.loadFromFile(expected_RGB_img_file);

	EXPECT_TRUE(readOk_rgb);
	const float rgb_diff = imageDiff(gt_frame, frame);
	std::cout << "rgb_diff=" << rgb_diff << "\n";
	EXPECT_LT(rgb_diff, 100.0f);

	{
		mrpt::img::CImage imDepth;
		depth *= (1.0f / clipMax);
		imDepth.setFromMatrix(depth, true);

		mrpt::img::CImage gt_imDepth;
		bool readOk_depth = gt_imDepth.loadFromFile(expected_depth_img_file);

		EXPECT_TRUE(readOk_depth);

		const float depth_diff = imageDiff(gt_imDepth, imDepth);
		std::cout << "depth_diff=" << depth_diff << "\n";
		EXPECT_LT(depth_diff, 100.0f);
	}
}

#if MRPT_HAS_OPENCV && MRPT_HAS_OPENGL_GLUT && MRPT_HAS_EGL
TEST(OpenGL, CFBORender_camera_intrinsics)
#else
TEST(OpenGL, DISABLED_CFBORender_camera_intrinsics)
#endif
{
	test_opengl_CFBORender(true);
}

#if MRPT_HAS_OPENCV && MRPT_HAS_OPENGL_GLUT && MRPT_HAS_EGL
TEST(OpenGL, CFBORender_camera_fov)
#else
TEST(OpenGL, DISABLED_CFBORender_camera_fov)
#endif
{
	test_opengl_CFBORender(false);
}
<|endoftext|>